Doctrine 2 - Insert new item in database - doctrine-orm

I'm trying to make something very simple.. but I do wrong, and I don't know what is the problem. Just I'm trying to insert new item to database with Doctrine 2:
$favouriteBook = new UserFavouriteBook;
$favouriteBook->user_id = 5;
$favouriteBook->book_id = 8;
$favouriteBook->created_at = new DateTime("now");
$this->_em->persist($favouriteBook);
$this->_em->flush();
As you can see.. is very simple, but that, give me next error:
Error: Message: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'user_id' cannot be null
Obviosly, if I make a "dump" before "persist" and "flush" of $favouriteBook, all looks be correct..
This is my "favouriteBook" entity:
/** #Column(type="integer")
* #Id
*/
private $user_id;
/** #Column(type="integer")
* #Id
*/
private $book_id;
/**
* #ManyToOne(targetEntity="Book", inversedBy="usersFavourite")
* #JoinColumn(name="book_id", referencedColumnName="id")
*/
private $book;
/**
* #ManyToOne(targetEntity="User", inversedBy="favouriteBooks")
* #JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
/** #Column(type="datetime") */
private $created_at;
public function __get($property) {
return $this->$property;
}
public function __set($property, $value) {
$this->$property = $value;
}
Anyone can image what is the problem? .. I don't know what else try.. Thanks

I think what beberlei is saying is that within your favouriteBook entity, you don't need to define the user_id and book_id as class properties, b/c the book and user properties you have set already recognize these as the relevant join columns. Also, your attempt to persist the favouriteBook entity failed because you need to set the book and user entity associations within the favouriteBook entity, not the foreign keys. So it would be:
$favouriteBook = new UserFavouriteBook;
$favouriteBook->book = $book;
$favouriteBook->user = $user;
$favouriteBook->created_at = new DateTime("now");
$this->_em->persist($favouriteBook);
$this->_em->flush();

You are mapping foreign keys and the associations. You have to modify the association not the foreign key field. Its bad-practice to map them both, you should remove $book_id and $user_id completly.

Related

How to join column and target entity manually in manytoone relation?

I have two fields : account_type_id and account_id.
How can i manually map doctrine TargetEntity to join Company Entity if accountTypeId = 1 OR join User Entity if account_type_id = 2 ?
<?php
/** #Entity */
class Accounts
{
// 1= Company, 2 = User
private $accountType;
/**
* #ManyToOne(targetEntity="Companies")
*/
private $company;
/**
* #ManyToOne(targetEntity="Users")
*/
private $user;
//...
}
Unfortunately, joining different columns on the fly cannot be done automatically, but you can have both fields set as nullable and only set the correct one when persisting the Account entity.
This would be the annotation:
/**
* #ORM\ManyToOne(targetEntity="Users", inversedBy="users")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=true)
*/
private $user;
Keep in mind that nullable=true is the default anyway, I'm just being specific here.
If you want to go defensively about this, you can have an additional check in getter
/**
* #return User
* #throws \Exception
*/
public function getUser()
{
if ($this->accountType !== 2) {
throw new \Exception("Entity is not of type 'user'");
}
return $this->user;
}

How to persist nested Doctrine Entities correctly

I'm using Doctrine 2 in Zendframework 3.
I got an Order object with an 1:n relation to OrderAdditions which has a 1:1 Relation to OrderItems.
In the Order entity:
/**
* #ORM\OneToMany(targetEntity="LwsProject\Entity\OrderAddition", mappedBy="order", cascade={"persist", "remove"})
*/
protected $orderAdditions;
public function addOrderAdditions($orderAdditions) {
if($orderAdditions instanceof OrderAddition) {
$this->orderAdditions->add($orderAdditions);
$orderAdditions->setOrder($this);
}else if($orderAdditions instanceof ArrayCollection){
foreach($orderAdditions as $orderAddition){
$this->orderAdditions->add($orderAddition);
$orderAddition->setOrder($this);
}
}
return $this->orderAdditions;
}
public function removeOrderAdditions($orderAdditions) {
if($orderAdditions instanceof OrderAddition) {
$this->orderAdditions->removeElement($orderAdditions);
}else if($orderAdditions instanceof ArrayCollection){
foreach($orderAdditions as $orderAddition){
$this->orderAdditions->removeElement($orderAddition);
}
}
return $this->orderAdditions;
}
In the OrderAddition:
/**
* #ORM\OneToOne(targetEntity="\LwsProject\Entity\OrderItem", cascade={"persist", "remove"}, fetch="EAGER")
* #ORM\JoinColumn(name="order_item_id", referencedColumnName="id")
*/
protected $orderItem;
And in the OrderItem:
/**
* #ORM\OneToOne(targetEntity="LwsProject\Entity\OrderAddition")
* #ORM\JoinColumn(name="order_addition_id", referencedColumnName="id")
*/
protected $orderAddition;
Im Creating a new OrderAddition with a new OrderItem for several posted Items from a form.
// $order is either a new Order() or an entity from database.
$orderAddition = new OrderAddition();
//setting Data from post
$orderItem = new OrderItem();
//settign data from post
$orderAddition->setOrderItem($orderItem);
$orderItem->setOrderAddition($orderAddition);
$order->addOrderAdditions($orderAddition);
When I'm flushing while using $em->persist($order) with a new Order() everything gets persisted as expected, when im using an existing Order from the database i get:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'order_item_id' cannot be null

Doctrine2, uniqueConstraint on JoinColumn for ZF2?

I have a data feed that has duplicated content (no idea why, it's an external feed), however we need to insert all items with a constraint on the title and type, i.e.
These can exist:
Name_A, Type_A
Name_A, Type_B
But only one of these should exist:
Name_A, Type_A
Name_A, Type_A
Here's the Entity code I'm using:
/**
* Restauration
*
* #ORM\Table(name="restauration", uniqueConstraints={#ORM\UniqueConstraint(name="name_unique", columns={"name_1", "restauration_type"})})
* #ORM\Entity(repositoryClass="iMotionTools\Repository\RestaurationRepository")
*/
class Restauration
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="NONE")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name_1", type="string", length=128, nullable=true)
*/
private $name1;
/**
* #var string
*
* #ORM\ManyToOne(targetEntity="RestaurationType", cascade={"persist"})
* #ORM\JoinColumn(name="restauration_type", referencedColumnName="id")
*/
private $type;
}
But I get this error when parsing and inserting the data:
SQLSTATE[23000]: Integrity constraint violation: 19 columns name_1, restauration_type are not unique:91:C:\coding\currate\vendor\doctrine\dbal\lib\Doctrine\DBAL\DBALException.php
I'm wondering whether the easy way is to just ignore the thrown exception? Looks like it's a driverExceptionDuringQuery that gets thrown during my call to $em->persist(); but I'm not sure how I would ignore if the call contains the above error?
If you want to ignore that, stop using constraint integrity.
{#ORM\UniqueConstraint(name="name_unique", columns={"name_1" //etc...
Your data has name_1 not unique this is why you have this error, integrity constraint check this, you can't ignore that, without remove the unique constraint parameters.
Edit :
You have to then, before persist data, check with your actual data in the table, if there is a duplicate entry for both name_1 AND Type, and do not persist them.
for check both you can use :
#UniqueEntity({"name", "type"})
found here :
validation multiple constraint columns
Even if it's for SF2, it's the same concept
I've removed the UniqueConstraint attribute from the table and added a function to check the objects list (which later get $entity->persist()-ed), using an array so that I can easily use it for different entity types, and it seems to work well now.
$key = $hashList ? '' : $page['id'];
foreach ($hashList as $method) {
$val = $object->{$method}();
if(is_object($val)) {
$val = $val->getId();
}
$key .= $val;
}
$key = md5($key);
$objects[$key] = $object;
Where $hashList = array('getName', 'getType') - and getType returns an object (since it's another entity), but which always has the getId() function... probably not the best solution but it works for my situation...

Doctrine: Removing entity in self-referencing (many-to-many)

I would like to ask your help in deleting association.
My User entity:
class User
{
...
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="following")
**/
private $followers;
/**
* #ORM\ManyToMany(targetEntity="User", inversedBy="followers")
* #ORM\JoinTable(name="friends",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="friend_user_id", referencedColumnName="id")}
* )
**/
private $following;
I have two actions:
Profile:follow
// followAction
$entityManager = $this->getDoctrine()->getEntityManager();
$me->addFollowing($targetUser);
$targetUser->addFollower($me);
$entityManager->persist($me);
$entityManager->persist($targetUser);
$entityManager->flush();
Profile:unfollow
$entityManager = $this->getDoctrine()->getEntityManager();
$me->removeFollowing($targetUser);
$targetUser->removeFollower($me);
$entityManager->persist($me);
$entityManager->persist($targetUser);
$entityManager->flush();
Process of following is working in a proper way, and I see appropriate records friends table.
But when I am trying to unfollow user, I receive exception:
An exception occurred while executing 'INSERT INTO friends (user_id, friend_user_id) VALUES (?, ?)' with params {"1":2,"2":10}:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '2-10' for key 'PRIMARY'
What am I doing wrong? I've tried with persist and without it, the same. Maybe something in association configs?
Your first mistake is 2 persist actions, you only need one. Check this:
// class User
public function switchFollowingUser(User $user)
{
if ( $this->following->contains($user) )
$this->following->removeElement($user) ;
else
$this->following->add($user) ;
}
and controller would be just
$follower->switchFollowingUser($user) ;
Extract this method into two methods if you want but I kinda prefer this way because it is shorter.
Second thing:
Did you put
$this->following = new ArrayCollection() ;
$this->followers = new ArrayCollection() ;
in __construct() ?
Try if this works.

doctrine2 OneToMany relationship inserts NULL as the foreign key

I have a bi-directional OneToMany relationship using Doctrine 2 as an ORM within the ZendFramework 1.11.2.
Note: Doctrine did not create the database tables. The database is MySQL.
For some reason, when I persist and flush a new link entity to the link table (see below) the foreign key field (container_id) gets set to NULL. However, if the '#' symbol is removed from the 'ManyToOne(targetEntity="Shepherd\Navigation\Domain\Container\Model", inversedBy="links")' line, the foreign key field is populated properly.
Since the entity is added properly to the database when the '#' symbol is removed, there is something wrong with the OneToMany relationship somewhere.
For example, if I have a link model named $link (see pseudo-code below)...
$link (Shepherd\Navigation\Domain\Link\Model)
{
id: '' // auto generated value
cid: 23 // the foreign key value
label: test
uri: test.com
... // other values not listed here for brevity
}
...when the new link model is persisted and the entity manager is flushed, the container_id (foreign key) value from the newly inserted row in the link (shepherd_navigation_link) table is NULL.
$em // Assume $em is the Entity Manager
$em->persist($link);
$em->flush();
// The container_id in the newly added row in the
// link table (shepherd_navigation_link) is NULL
The link table schema:
CREATE TABLE `shepherd_navigation_link` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`container_id` int(10) unsigned DEFAULT NULL,
`node_id` int(10) unsigned DEFAULT NULL,
`parent_id` int(10) unsigned DEFAULT NULL,
`label` varchar(100) NOT NULL,
`options` text,
`events` text,
`privilege` varchar(100) NOT NULL,
`resource` varchar(100) DEFAULT NULL,
`uri` varchar(300) NOT NULL,
`visible` int(10) unsigned DEFAULT '1',
PRIMARY KEY (`id`),
KEY `container_id` (`container_id`)
) ENGINE=InnoDB
ALTER TABLE `shepherd_navigation_link` ADD FOREIGN KEY (container_id) REFERENCES shepherd_navigation_container(id)
Link entity model:
/**
* #Entity
* #Table(name="shepherd_navigation_link")
*/
class
{
/**
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
* #Column(name="container_id", type="integer", nullable=false)
*/
protected $cid;
/**
* #Column(name="node_id", type="integer")
*/
protected $nid;
/**
* #Column(name="parent_id", type="integer", nullable=false)
*/
protected $pid;
/**
* #Column
*/
protected $label;
/**
* #Column(nullable=true)
*/
protected $options;
/**
* #Column(nullable=true)
*/
protected $events;
/**
* #Column
*/
protected $privilege;
/**
* #Column(nullable=true)
*/
protected $resource;
/**
* #Column
*/
protected $uri;
/**
* #Column(type="integer", nullable=true)
*/
protected $visible;
/**
* #OneToMany(targetEntity="Model", mappedBy="parent")
*/
private $children;
/**
* #ManyToOne(targetEntity="Model", inversedBy="children")
*/
private $parent;
/**
*) #ManyToOne(targetEntity="Shepherd\Navigation\Domain\Container\Model", inversedBy="links"
*/
private $container;
/**
* #OneToOne(targetEntity="Shepherd\Navigation\Domain\Link\Position", inversedBy="link")
*/
private $node;
public function __construct()
{
$this->children = new \Doctrine\Common\Collections\ArrayCollection();
}
/** Accessors and Mutators excluded for brevity **/
}
Note: the protected property $cid maps to the container_id column above.
The container table schema:
CREATE TABLE `shepherd_navigation_container` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`description` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
The container entity model:
/**
* #Entity
* #Table(name="shepherd_navigation_container")
*/
class Model
{
/**
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
* #Column
*/
protected $name;
/**
* #Column(nullable=true)
*/
protected $description;
/**
* #OneToMany(targetEntity="Shepherd\Navigation\Domain\Link\Model", mappedBy="container")
*/
private $links;
/**
* Constructor
*/
public function __construct()
{
$this->links = new \Doctrine\Common\Collections\ArrayCollection();
}
/** Accessors and Mutators excluded for brevity **/
}
What am I missing? What am I doing wrong?
I figured out the problem (by reading the documentation http://www.doctrine-project.org/docs/orm/2.0/en/tutorials/getting-started-xml-edition.html). It turns out there were actually a few problems.
Problem 1 => I did not provide a method to set the container variable.
// Inside the Link Entity class...
public function setContainer($container)
{
$this->container = $container;
}
Problem 2 => I did not set the container value. In error, I thought Doctrine 2 did this internally, but I found out the container variable needs to be set prior to flushing.
Foolish oversight on my part.
$link = new Link();
$link->setContainer($container);
// $em is the Entity Manager
$em->persist($link);
$em->flush();
Problem 3 => The container ($container) needed to either be persisted prior to flushing or the #OneToMany definition on the container entity needed to change. I chose to update the container entity definition. Take a look here (http://www.doctrine-project.org/docs/orm/2.0/en/reference/working-with-associations.html#transitive-persistence-cascade-operations) for more information.
// Inside the Container Entity class...
/**
* #OneToMany(targetEntity="Shepherd\Navigation\Domain\Link\Model", mappedBy="container", cascade={"persist"})
*/
After making these changes and removing the #OneToOne node relationship in the link entity class (turns out I didn't need it), everything worked fine. I hope this helps someone.