Doctrine 2: How to update one-to-one bidirectional - php

I have problem with updating one-to-one bidirectional association.
User Entity
/**
*
* #ORM\Table(name="test_user")
*/
class User
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="login", type="string", length=32, nullable=false, unique=true)
*/
private $login;
/**
*
* #ORM\OneToOne(targetEntity="Points", mappedBy="user", cascade={"persist"})
*/
private $points;
...
/**
* Set points
*/
public function setPoints(array $points)
{
$this->points = new Points($points);
$this->points->setUser($this);
return $this;
}
/**
* Get points
*/
public function getPoints()
{
return $this->points;
}
}
Points Entity
/**
* Points
*
* #ORM\Table(name="test_user_points")
* #ORM\Entity
*/
class Points {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(type="integer", nullable=false)
*/
private $points;
/**
* #var string
*
* #ORM\Column(name="period", type="string", length=24)
*/
private $period;
/**
* #var User
*
* #ORM\OneToOne(targetEntity="User", inversedBy="points")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id",onDelete="CASCADE", nullable=false)
*/
private $user;
/**
* Constructor
*/
public function __construct(array $params = array())
{
$hydrator = new MyHydrator();
$hydrator->hydrate($params, $this);
}
...
/**
* Set user
*
* #param User $user
*/
public function setUser(User $user = null)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return User
*/
public function getUser()
{
return $this->user;
}
}
Class MyHydrator is converting from array(first param) to object(second param). It is very important and I have to use it.
My save function looks like this:
public function save(array $data)
{
...
// This is how my input data looks
$data = array(
'login' => 'Test',
array(
'points' => 999,
'period' => 'monthly'
)
);
if ($userExists) {
// UPDATE
$hydrator = new MyHydrator();
$hydrator->hydrate($data, $userExists);
$this->em->persist($userExists);
$this->em->flush();
} else {
// INSERT
$user = new User($data);
$this->em->persist($user);
$this->em->flush();
}
}
Inserting to database works perfect, but when I try to update record I get error:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '4' for key 'UNIQ_DAD93D6EA76ED395'
4 is a value of user_id column in points table
How can I edit existing record without error about duplicate id?

It's old but since I encoutered a similar issue, I resolved it this way :
in User
public function setPoints($points)
{
if ($this->points !== null) {
$this->points->setUser(null);
}
$this->points = $points;
$points->setUser($this);
}

Related

Array does not keep previous elements after being updated

Job entity has a property called candidates which is an array of Users and it is not mapped or related to any other entity.On given route we fetch specific Job object by id.We update our candidates array by assigning the user who we get from the current session.It turns out that the user is being saved, but on each call we lose those users which had been saved before.
Why does that happen?
/**
* #Route("/job/apply/{id}", requirements={"id"="\d+"}, name="student_candidate", methods={"POST"})
*
* #param int $id
*
* #return JsonResponse
*/
public function apply(int $id)
{
$job = $this->getDoctrine()->getRepository(Job::class)->find($id);
$candidate = $this->getUser();
$job->addCandidate($candidate);
$this->getDoctrine()->getManager()->flush();
return new JsonResponse([
'status' => 'success',
'message' => 'some_success_message'
]);
}
class Job
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="content", type="text", length=65535)
*/
private $content;
/**
* #var User[]|ArrayCollection
*/
private $candidates;
public function __construct()
{
$this->candidates = new ArrayCollection();
}
/**
* #return User[]|ArrayCollection
*/
public function getCandidates()
{
return $this->candidates;
}
/**
* #param User $user
*
* #return Job
*/
public function addCandidate(User $user)
{
$this->candidates[] = $user;
return $this;
}
}

Doctrine 3 does not create the foreign key - PDOException: "Column 'tour_id' cannot be null"

I'm trying to insert a new record in the database.
I have two tables bootstrap_tour and bootstrap_tour_step.
id of boostrap_tour table is the foreign key tour_id in the bootstrap_tour_step table.
The corresponding entities look as follows:
BootstrapTour.php
/**
* #var int
*
* #ORM\Column(name="id", type="integer", options={"unsigned"=true})
* #ORM\Id
*
* #JMS\Groups({"auth_read_postbootstraptours"})
* #JMS\Type("integer")
* #JMS\Accessor(getter="getId")
*/
protected $id;
/**
* #var ArrayCollection[BootstrapTourStep]
*
* #ORM\OneToMany(targetEntity="BootstrapTourStep", mappedBy="bootstrapTour", cascade={"persist"})
*
* #JMS\Groups({"auth_read_postbootstraptours"})
*/
private $bootstrapTourSteps;
/**
* Object instantiation.
*/
public function __construct()
{
parent::__construct();
$this->bootstrapTourSteps = new ArrayCollection();
}
/**
* Sets a collection of BootstrapTourStep objects.
*
* #param ArrayCollection|null $bootstrapTourSteps
*
* #return BootstrapTour
*/
public function setBootstrapTourSteps(?ArrayCollection $bootstrapTourSteps): BootstrapTour
{
$this->bootstrapTourSteps = $bootstrapTourSteps;
return $this;
}
/**
* Returns a collection of BootstrapTourStep objects.
*
* #return Collection[BootstrapTourStep]|null
*/
public function getBootstrapTourSteps(): ?Collection
{
return $this->bootstrapTourSteps;
}
/**
* Adds a Step to the tour.
*
* #return BootstrapTour
*/
public function addBootstrapTourStep(BootstrapTourStep $bootstrapTourStep): BootstrapTour
{
$bootstrapTourStep->setBootstrapTour($this);
$this->bootstrapTourSteps[] = $bootstrapTourStep;
return $this;
}
BootstrapTourStep.php
/**
* #var int
*
* #ORM\Column(name="id", type="integer", options={"unsigned"=true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
* #JMS\Groups({"auth_read_getbootstraptours"})
* #JMS\Type("integer")
* #JMS\Accessor(getter="getId")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="BootstrapTour", inversedBy="bootstrapTourSteps")
* #ORM\JoinColumn(name="tour_id", referencedColumnName="id", nullable=false)
*
* #JMS\Groups({"auth_read_postbootstraptours"})
* #JMS\Type("EN\CentralAdmin\DoctrineBundle\Entity\BootstrapTour")
* #JMS\Accessor(getter="getBootstrapTour", setter="setBootstrapTour")
*/
private $bootstrapTour;
/**
* Gets the BootstrapTour
*
* #return BootstrapTour|null
*/
public function getBootstrapTour(): ?BootstrapTour
{
return $this->bootstrapTour;
}
/**
* Sets a BootstrapTour
*
* #param BootstrapTour $bootstrapTour
* #return BootstrapTourStep
*/
public function setBootstrapTour(BootstrapTour $bootstrapTour): BootstrapTourStep
{
$this->bootstrapTour = $bootstrapTour;
return $this;
}
/**
* A list of reference proxies.
*
* #return array
*/
public function getReferenceProxies(): array
{
return [
'BootstrapTour'
];
}
My controller Action :
$bootstrapTourService = $this->getCentralAdminEntityService('BootstrapTour');
$bootstrapTourService->persist($tourType, true);
I am able to select the data using this but in case of adding new record I am getting the following exception:
PDOException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'tour_id' cannot be null
How can I resolve this issue?
You're not setting the relation to the tour on the steps when adding a collection of steps. This way the step entities are added to the tour but the steps themselves don't know which tour they belong to.
If now doctrine tries to persist the steps their reference to the tour is missing and therefore you get the missing tour_id exception.
This ...
public function setBootstrapTourSteps(?ArrayCollection $bootstrapTourSteps): BootstrapTour
{
$this->bootstrapTourSteps = $bootstrapTourSteps;
return $this;
}
... should be:
public function setBootstrapTourSteps(?ArrayCollection $bootstrapTourSteps): BootstrapTour
{
$this->bootstrapTourSteps = new ArrayCollection();
foreach ($bootstrapTourSteps as $step) {
$step->setBootstrapTour($this);
$this->bootstrapTourSteps->add($step);
}
return $this;
}

Integrity constraint violation: 1062 Duplicate entry 'Portable tools' for key 'UNIQ_63B58042B36786B'"

I'm trying to insert some data in a given table, i'm using Entity Repository to define a function that's create an entity if it's not existing, of find it if it's exist.
My entity file is as following:
/**
* SemanticTag
*
* #ORM\Table(name="Semantictag")
* #ORM\Entity(repositoryClass="VCycle\SemanticTagsBundle\Repository\SemanticTagRepository")
*/
class SemanticTag
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255, unique=true)
*/
private $title;
/**
* #var \DateTime
*
* #ORM\Column(name="created_at", type="datetime")
*/
private $createdAt;
public function __construct()
{
$this->createdAt = new \DateTime();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
* #return SemanticTag
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
public function getNormalizedTitle()
{
return mb_strtolower($this->title);
}
/**
* Set createdAt
*
* #param \DateTime $createdAt
* #return $this
*/
public function setCreatedAt(\DateTime $createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt
*
* #return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
}
my repositoty file is as follows:
/**
* SemanticTagRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class SemanticTagRepository extends EntityRepository
{
/**
* #param array $titles
*
* #return SemanticTag[]
*/
public function findOrCreateByTitles(array $titles)
{
$Semantictags = $this->findBy(array('title' => $titles));
/* #var $tags Tag[] */
$SemantictagsCollection = array();
foreach ($Semantictags as $Semantictag) {
$SemantictagsCollection[$Semantictag->getNormalizedTitle()] = $Semantictag;
}
$normalizedTitles = array();
foreach ($titles as $title) {
$normalizedTitles[mb_strtolower($title)] = $title;
}
$SemantictagsToCreate = array_diff($normalizedTitles, array_keys($SemantictagsCollection));
foreach ($SemantictagsToCreate as $title) {
$Semantictag = new SemanticTag();
$Semantictag->setTitle($title);
$this->_em->persist($Semantictag);
$SemantictagsCollection[$Semantictag->getNormalizedTitle()] = $Semantictag;
}
return $SemantictagsCollection;
}
}
I tried with:
php app/console doctrine:cache:clear-metadata
php app/console cache:clear
But it gives me always
"message": "An exception occurred while executing 'INSERT INTO Semantictag (title, created_at) VALUES (?, ?)' with params [\"Portable tools\", \"2016-08-08 13:48:13\"]:\n\nSQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'Portable tools' for key 'UNIQ_63B58042B36786B'"
The other entity is as follows
/**
* Tag_Semantictag
*
* #ORM\Table(name="tag_semantictag",
* uniqueConstraints={
* #ORM\UniqueConstraint(name="UNIQ_tag_semantictag", columns= {"tag_id", "semantic_tag_id"})
* })
* #ORM\Entity(repositoryClass="VCycle\TagsBundle\Repository\TagSemantictagRepository")
*/
class TagSemantictag
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Tag", inversedBy="TagSemantictags")
* #ORM\JoinColumn(name="tag_id", nullable=false, onDelete="CASCADE")
*
* #var Tag
*/
private $tag;
/**
* #ORM\ManyToOne(targetEntity="VCycle\SemanticTagsBundle\Entity\SemanticTag")
* #ORM\JoinColumn(name="semantic_tag_id", nullable=false, onDelete="CASCADE")
*
* #var SemanticTag
*/
private $semantictag;
/**
* #var \DateTime
*
* #ORM\Column(name="created_at", type="datetime")
*/
private $createdAt;
public function __construct()
{
$this->createdAt = new \DateTime();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
function getTag() {
return $this->tag;
}
function getSemantictag() {
return $this->semantictag;
}
function setTag(Tag $tag) {
$this->tag = $tag;
}
function setSemantictag(SemanticTag $semantictag) {
$this->semantictag = $semantictag;
}
/**
* Set createdAt
*
* #param \DateTime $createdAt
* #return Tag_SemanticTag
*/
public function setCreatedAt(\DateTime $createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt
*
* #return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
}
You are doing another insert with the same combination of columns that are already in database. Some existing record has a Portable tools title. You need to create unique title for each entity you want to insert.

integrity constraint violation 1062 with ORM and Symfony2

I have an Entity with a primary key like this:
/**
* #var integer
*
* #ORM\Column(name="product_id", type="integer", nullable=false)
* #ORM\Id
*/
protected $productId;
....
/**
* Set productId
*
* #param integer $productId
* #return Products
*/
public function setProductId($productId)
{
$this->productId = $productId;
return $this;
}
/**
* Get productId
*
* #return integer
*/
public function getProductId()
{
return $this->productId;
}
But when I try to insert an ProductId with set method, I get this error:
integrity constraint violation 1062 duplicate entry '0' for key 'primary'
I tried with * #ORM\GeneratedValue(strategy="NONE") but the result it's the same, I need to set the Product Id because the sequence isn't 1, 2, 3... is different.
And I can't create a new Id because my current ProductId is used by other entities like Foreing Keys.
Any solution?
Thanks in advance.
-----Edit with the file where I have the error-----
$prod = new Products();
$prod->setProductId("65");
$manager->persist($prod);
$manager->flush();
----Edit with whole Entity----
namespace My\WebBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
class Products
{
/**
* #var integer
*
* #ORM\Column(name="product_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="NONE")
*/
protected $productId;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=50, nullable=false)
*/
private $name;
/**
* #var integer
*
* #ORM\Column(name="version", type="integer", nullable=true)
*/
private $version;
/**
* #var string
*
* #ORM\Column(name="code", type="string", length=10, nullable=false)
*/
private $code;
/**
* #var integer
*
* #ORM\Column(name="price", type="integer", nullable=false)
*/
private $price;
/**
* Set productId
*
* #param integer $productId
* #return Products
*/
public function setProductId($productId)
{
$this->productId = $productId;
return $this;
}
/**
* Get productId
*
* #return integer
*/
public function getProductId()
{
return $this->productId;
}
/**
* Set name
*
* #param string $name
* #return Products
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set version
*
* #param integer $version
* #return Products
*/
public function setVersion($version)
{
$this->version = $version;
return $this;
}
/**
* Get version
*
* #return integer
*/
public function getVersion()
{
return $this->version;
}
/**
* Set code
*
* #param string $code
* #return Products
*/
public function setCode($code)
{
$this->code = $code;
return $this;
}
/**
* Get code
*
* #return string
*/
public function getCode()
{
return $this->code;
}
/**
* Set price
*
* #param integer $price
* #return Products
*/
public function setPrice($price)
{
$this->price = $price;
return $this;
}
/**
* Get price
*
* #return integer
*/
public function getPrice()
{
return $this->price;
}
When using no identifier generation strategy you should not forget that you have to assign the custom ID before you call EntityManagers persist() method.
I think you are persisting the new Entity before assigning the custom ID which means your $productId property is set to null and will be casted to 0 (zero) if you try to flush it. That will cause your error.
Doctrine Doc
Marcus post a good answer to your problem. You can solve it by adding id into you Entity, and use productId as secondary key.
Add id and set it to Auto increment, like
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
Then you can use your productId with:
/**
* #var integer
*
* #ORM\Column(name="product_id", type="integer", nullable=false)
*/
protected $productId;
With this solution you will use $productId as secondary key. Don't forget to clear data in your table.
You also have an error here:
$prod->setProductId(65);
Now you try set data which is string - in your table is integer.

Persist link in One-to-many / many-to-many - Doctrine and Symfony2

I had a many-to-many using a User and Account entity. I needed to add an isOwner field to the association table so I changed it to a one-to-many / many-to-one relation. Below are my 3 entities (user, account, useraccount).
When I persist the user, the user record and account record are both added, but the association record is not. I am also unsure as to how I can set the isOwner field whilst persisting the user.
Does anyone know how the association record can be persisted? Should the association record be added automatically?
User:
/**
* xxx\CoreBundle\Entity\User
*
* #ORM\Table(name="user")
* #ORM\Entity
*/
class User implements UserInterface
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string $firstName
*
* #ORM\Column(name="firstName", type="string", length=255, nullable=false)
* #Assert\NotBlank()
*/
private $firstName;
/**
* #var Account
*
* #ORM\OneToMany(targetEntity="UserAccount", mappedBy="user", cascade={"persist"})
*/
private $account;
public function __construct()
{
$this->account = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set firstName
*
* #param string $firstName
*/
public function setFirstName($firstName)
{
$this->firstName = $firstName;
}
/**
* Get firstName
*
* #return string
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* Add account
*
* #param xxx\CoreBundle\Entity\Account $account
*/
public function addAccount(\xxx\CoreBundle\Entity\Account $account)
{
$this->account[] = $account;
}
/**
* Get account
*
* #return Doctrine\Common\Collections\Collection
*/
public function getAccount()
{
return $this->account;
}
}
Account:
/**
* xxx\CoreBundle\Entity\Account
*
* #ORM\Table(name="account")
* #ORM\Entity(repositoryClass="xxx\CoreBundle\Repository\AccountRepository")
*/
class Account
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=255, nullable=false)
* #Assert\NotBlank()
*/
private $name;
/**
* #var User
*
* #ORM\OneToMany(targetEntity="UserAccount", mappedBy="account", cascade={"persist"})
*/
private $user;
public function __construct()
{
$this->user = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Add user
*
* #param xxx\CoreBundle\Entity\User $user
*/
public function addUser(\xxx\CoreBundle\Entity\User $user)
{
$this->user[] = $user;
}
/**
* Get user
*
* #return Doctrine\Common\Collections\Collection
*/
public function getUser()
{
return $this->user;
}
}
UserAccount:
/**
* xxx\CoreBundle\Entity\UserAccount
*
* #ORM\Table(name="user_account")
* #ORM\Entity
*/
class UserAccount
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="account", cascade={"persist"})
*/
private $user;
/**
* #ORM\ManyToOne(targetEntity="Account", inversedBy="user", cascade={"persist"})
*/
private $account;
/**
* #var boolean $isOwner
*
* #ORM\Column(name="isOwner", type="boolean", nullable=false)
*/
private $isOwner;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Get isOwner
*
* #return bool
*/
public function getIsOwner()
{
return $this->isOwner;
}
/**
* Set isOwner
*
* #param string $isOwner
*/
public function setIsOwner($isOwner)
{
$this->isOwner = $isOwner;
}
/**
* Set user
*
* #param xxx\CoreBundle\Entity\User $user
*/
public function setUser(\xxx\CoreBundle\Entity\User $user)
{
$this->user = $user;
}
/**
* Get user
*
* #return xxx\CoreBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
/**
* Set account
*
* #param xxx\CoreBundle\Entity\Account $account
*/
public function setAccount(\xxx\CoreBundle\Entity\Account $account)
{
$this->account = $account;
}
/**
* Get account
*
* #return xxx\CoreBundle\Entity\Account
*/
public function getAccount()
{
return $this->account;
}
}
Controller:
$account = new Account();
$account->setName('Test account');
$user = new User();
$user->setFirstName('John');
$user->addAccount($account);
$manager->persist($user);
$manager->flush();
You are trying to persist an account as UserAccount entity. Try this:
$account = new Account();
$account->setName('Test account');
$user = new User();
$user->setFirstName('John');
$user_account = new UserAccount();
$user_account->setAccount($account);
$user_account->setUser($user);
$manager->persist($user);
$manager->flush();

Categories