I have a doctrine setup where i cant use the many side of collections.
The objects used:
User.php:
class User extends \App\Entity
{
/**
* #Id #Column(type="integer")
* #GeneratedValue
*/
private $id;
/**
* #ManyToOne(targetEntity="Company", inversedBy="users")
*/
private $company;
public function getCompany()
{
return $this->company;
}
public function setCompany($company)
{
$this->company = $company;
}
}
Company.php:
class Company extends \App\Entity
{
/**
* #Id #Column(type="integer", nullable=false)
* #GeneratedValue
*/
private $id;
/**
* #OneToMany(targetEntity="User", mappedBy="company")
*/
private $users;
public function __construct()
{
$this->users = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getUsers()
{
return $this->users;
}
}
When i create the relation seems to be working. Get no errors. Then i tried the following:
$company = $this->em->getRepository('App\Entity\Company')->findOneByName("Walmart");
$user = $this->em->getRepository('App\Entity\User')->findOneByName("Niek");
$user->setCompany($company);
$company2 = $this->em->getRepository('App\Entity\Company')->findOneByName("Ford");
$user2 = $this->em->getRepository('App\Entity\User')->findOneByName("Henk");
$company2->getUsers()->add($user2);
$this->em->flush();
When i inspect the database for the first user the company is set. Relation is there. The seconds does not persists. and when i do this:
print_r(\Doctrine\Common\Util\Debug::dump($company->getUsers(),$doctrineDepth));
print_r(\Doctrine\Common\Util\Debug::dump($company->getUsers2(),$doctrineDepth));
i get 2 empty arrays.
So it seems that the array isnt connected. It only behaves like this on OneToMany ore ManyToOne relationships. Got one ManyToMany and that one works perfect in the same project
Any ideas?
You still need to set both sides of the relationship with a one-to-many or a many-to-one relationship:
$company2 = $this->em->getRepository('App\Entity\Company')->findOneByName("Ford");
$user2 = $this->em->getRepository('App\Entity\User')->findOneByName("Henk");
$company2->getUsers()->add($user2);
$user2->setCompany($company2);
$this->em->flush();
The reason your many-to-many works is because you don't need to set both sides of the relationship.
Related
I have a Product entity with a ManyToOne relationship with Category.
use Doctrine\ORM\Mapping as ORM;
class Product
{
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Category")
* #ORM\JoinColumn(nullable=true)
*/
private $category;
}
class Category
{
use BlameableTrait;
...
}
The Category entity implements a trait, with properties to record when a record is created, updated, etc.
use App\Entity\User\User;
use Gedmo\Mapping\Annotation as Gedmo;
trait BlameableTrait
{
/**
* #Gedmo\Blameable(on="create")
* #ORM\ManyToOne(targetEntity="App\Entity\User\User")
* #ORM\JoinColumn(nullable=true, onDelete="SET NULL")
* #var User
*/
private $createdBy;
/**
* #var User|null
*
* #Gedmo\Blameable(on="update")
* #ORM\ManyToOne(targetEntity="App\Entity\User\User")
* #ORM\JoinColumn(nullable=true, onDelete="SET NULL")
*/
private $updatedBy;
public function getCreatedBy(): ?User
{
return $this->createdBy;
}
public function setCreatedBy(?User $createdBy): self
{
$this->createdBy = $createdBy;
return $this;
}
public function setUpdatedBy(?User $updatedBy): self
{
$this->updatedBy = $updatedBy;
return $this;
}
public function getUpdatedBy(): ?User
{
return $this->updatedBy;
}
}
When I try to update the product entity I have an error indicating that an error has occurred while trying to update the Category entity, but I have not indicated that it should be updated.
This only happens to me in the production environment and randomly, one time it works, another time it doesn't.
Locally, debugging the symfony profiler, for the same curl, only one update is done on the Product entity, which is fine.
I don't understand where Symfony or Doctrine try to update the Category entity.
Both the production and local environments run on the same Docker image.
I'd like to store a a record from this table in the "parent" field. THe error produced is as follows
Column name 'id' referenced for relation from MODL119\Entity\Role to MODL119\Entity\Role does not exist
And the PHP Entity class that produced the error
<?php
namespace MODL119\Entity;
use Doctrine\ORM\Mapping as ORM;
/** #ORM\Entity #ORM\Table(name="role")
*/
class Role
{
/** #ORM\Id
* #ORM\Column(name="role_id", type="bigint")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $roleId;
/** #ORM\ManyToOne(targetEntity="MODL119\Entity\Role")
* #ORM\JoinColumn(name="role_id", referencedColumnName="role_id")
*/
protected $parent;
public function setParent(Role $parent)
{
$this->parent = $parent;
}
public function getParent()
{
return $this->parent;
}
public function setRoleId($roleId)
{
$this->roleId = $roleId;
}
public function getRoleId()
{
return $this->roleId;
}
}
Any ideas?
The property Role::$parent needs to be represented in the underlying table as a foreign key reference, so you need to amend your annotation and change the JoinColumn.name as follows:
/**
* #ORM\ManyToOne(targetEntity="MODL119\Entity\Role")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="role_id")
*/
protected $parent;
This establishes a one-to-one self-referencing association in your entity, and creates a parent_id column in the underlying table.
See also:
one-to-many self-referencing association
many-to-many self-referencing association
Hope this helps :)
I'm trying to use relations in MongoDB, using Symfony2 and DoctrineMongoDBBundle
According slide 49 of the Doctrine MongoDB Object Document Mapper presentation,
it's enough to assign $User->setOrganization($Organization), to make $Organization::users[0] referred to user object.
In the documentation says i have to use inversedBy and mappedBy options.
I have the similar scheme (User belongs to Group), but I can't get both update work:
$Group = new \MyVendor\MongoBundle\Document\Group();
$User = new \MyVendor\MongoBundle\Document\User();
$User->setGroup($Group);
/** #var \Doctrine\ODM\MongoDB\DocumentManager $dm */
$dm = $this->get('doctrine_mongodb')->getManager();
$dm->persist($Group);
$dm->persist($User);
$dm->flush();
Results in MongoDB:
Group
{
"_id": ObjectId("5043e24acdc2929a0500000d"),
}
User
{
"_id": ObjectId("5043e24acdc2929a0500000c"),
"group": {
"$ref": "Group",
"$id": ObjectId("5043e24acdc2929a0500000d"),
"$db": "my_db"
}
}
src/MyVendor/MongoBundle/Document/User.php
<?php
namespace MyVendor\MongoBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document(repositoryClass="MyVendor\MongoBundle\Repository\UserRepository")
*/
class User
{
/**
* #MongoDB\Id
*/
private $id;
/**
* #var
* #MongoDB\ReferenceOne(targetDocument="Group", inversedBy="users")
*/
private $group;
/**
* Set group
*
* #param MyVendor\MongoBundle\Document\Group $group
* #return User
*/
public function setGroup(\MyVendor\MongoBundle\Document\Group $group)
{
$this->group = $group;
return $this;
}
}
src/MyVendor/MongoBundle/Document/Group.php
<?php
namespace MyVendor\MongoBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document
*/
class Group
{
/**
* #MongoDB\Id
*/
private $id;
/**
* #MongoDB\ReferenceMany(targetDocument="User", mappedBy="group")
* #var User[]
*/
private $users;
public function __construct()
{
$this->users = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add users
*
* #param MyVendor\MongoBundle\Document\User $users
*/
public function addUsers(\MyVendor\MongoBundle\Document\User $users)
{
$this->users[] = $users;
}
}
The question is why do you need $refs in both documents? That's not an effective way because you need to maintain two objects separately. If you really need it, then you need to set references on both ends.
public function setGroup(\MyVendor\MongoBundle\Document\Group $group)
{
$this->group = $group;
$group->addUsers($this);
return $this;
}
The second option is to keep $ref only on one of the documents. Doctrine will handle all the job for you. For this to work you only need to set inverse and owning side (don't need to use $group->addUsers($this);).
For user:
* #MongoDB\ReferenceOne(targetDocument="Group", inversedBy="users")
For Group:
* #MongoDB\ReferenceMany(targetDocument="User", mappedBy="group")
And it's always better to use the documentation than presentations.
ps: the OP changed the question according to this answer. Check the history before downvoting correct answers.
I do not understad why with some Entity objects I can set the Id and for others objects I get an error and says me that the Id can't be null and I have to pass an object instead.
e.g.:
$log = new Log();
$log->setTypeId(1);
$log->setUserId(1);
$entityManager->persist($log);
$entityManager->flush();
If I try the code above I get error that says: Integrity constraint violation: 1048 Column 'user_id' cannot be null. And I have to first create the Type Object and de User object and the pass them:
$log->setType($TypeObject)
$log->setUser($UserObject)
But for other entity objects I have no problem assigning the value directly, why is that?
This is my Entity Log:
<?php
/**
* #Entity
* #Table(name="log")
* #HasLifecycleCallbacks
*/
class Log
{
/**
* #var type
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
*
* #var type
* #Column(type="integer")
*/
protected $user_id;
/**
*
* #var type
* #Column(type="integer")
*/
protected $type_id;
/**
*
* #var type
* #Column(type="datetime")
*/
protected $created;
/**
*
* #var type
* #ManyToOne(targetEntity="User", inversedBy="logs")
*/
protected $user;
/**
*
* #ManyToOne(targetEntity="Type", inversedBy="logs")
*/
protected $type;
public function getId()
{
return $this->id;
}
public function getUserId()
{
return $this->user_id;
}
public function getTypeId()
{
return $this->type_id;
}
public function getCreated()
{
return $this->created;
}
public function setUserId($userId)
{
$this->user_id = $userId;
}
public function setTypeId($typeId)
{
$this->type_id = $typeId;
}
public function setCreated($created)
{
$this->created = $created;
}
public function setUser($user)
{
$this->user = $user;
}
public function setType($type)
{
$this->type = $type;
}
/**
* #PrePersist
*/
public function prePersist()
{
$this->setCreated(new DateTime());
}
}
?>
The existing answer never did sit well with me. There are many valid scenarios where loading an object just to define the relationship while already having the FK handy just does not make any sense at all.
A better solution is to use Doctrine's EntityManager's getRefrence method.
Reference Proxies...
The method EntityManager#getReference($entityName, $identifier) lets
you obtain a reference to an entity for which the identifier is known,
without loading that entity from the database. This is useful, for
example, as a performance enhancement, when you want to establish an
association to an entity for which you have the identifier. You could
simply do this:
<?php
// $em instanceof EntityManager, $cart instanceof MyProject\Model\Cart
// $itemId comes from somewhere, probably a request parameter
$item = $em->getReference(\MyProject\Model\Item::class, $itemId);
$cart->addItem($item);
Maybe this was not available when this question was first posted - I don't know.
EDIT
I found this statement on the website of Doctrine2. It's a best practice that you might want to follow when coding your models.
Doctrine2 Best Practices
25.9. Don’t map foreign keys to fields in an entity
Foreign keys have no meaning whatsoever in an object model. Foreign keys are how a relational database establishes relationships. Your object model establishes relationships through object references. Thus mapping foreign keys to object fields heavily leaks details of the relational model into the object model, something you really should not do
EDIT
Doctrine does the mapping from your objects to their respective Ids.
What you've done here is a bit redundant.
You've essentially told doctrine the same thing twice.
You've told it that it has a 'user_id' column AND that it also has a User object, which are the same thing. But doctrine can already guess that this relationship will have a user_id column based on the fact that the log class has a user object inside.
You should simply do the following instead
<?php
/**
* #Entity
* #Table(name="log")
* #HasLifecycleCallbacks
*/
class Log
{
/**
* #var type
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
*
* #var type
* #Column(type="datetime")
*/
protected $created;
/**
*
* #var type
* #ManyToOne(targetEntity="User", inversedBy="logs")
*/
protected $user;
/**
*
* #ManyToOne(targetEntity="Type", inversedBy="logs")
*/
protected $type;
public function getId()
{
return $this->id;
}
public function getCreated()
{
return $this->created;
}
public function setCreated($created)
{
$this->created = $created;
}
public function setUser($user)
{
$this->user = $user;
}
public function setType($type)
{
$this->type = $type;
}
/**
* #PrePersist
*/
public function prePersist()
{
$this->setCreated(new DateTime());
}
}
Doctrine will worry about the user_id and type_id on it's own. You don't have to worry about it. This way you get to work with full fledged objects, making it easier to program, instead of having to worry about id's. Doctrine will handle that.
If ALL you have is an id, because that's what you're using on the front end, then just fetch the object associated with that id using the Entitymanager.
$user = $em->getEntity( 'User', $idFromWeb );
$log = new Log();
$log->setUser( $user );
i think my question is not clear but i try to illustrate my point here. assuming i have a many to many, self referencing relationship where a user can be a teacher (say u post answers at SO) and a teacher can be a student (u may answer questions but may ask too) too.
namespace Entities;
/** #Entity #Table(name="users")) */
class User {
/**
* #Id #Column(type="integer")
* #GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #Column(type="string", length="30")
*/
private $name;
/**
* #ManyToMany(targetEntity="User", inversedBy="teachers")
* #JoinTable(name="Teachers_Students",
* joinColumns={#JoinColumn(name="teacher", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="student", referencedColumnName="id")}
* )
*/
private $students;
/**
* #ManyToMany(targetEntity="User", mappedBy="students")
*/
private $teachers;
function getName() {
return $this->name;
}
function setName($name) {
$this->name = $name;
}
function getStudents() {
return $this->students;
}
function getTeachers() {
return $this->teachers;
}
}
say i have a few users
$user1 = new User;
$user1->setName("user 1");
$user2 = new User;
$user2->setName("user 2");
$user3 = new User;
$user3->setName("user 3");
$user4 = new User;
$user3->setName("user 4");
and i like to setup teacher-student relationships between them, i was reading up doctrine reference, saw that u can use the Collections::add() to add elements to a collection
// user1 is a teacher to user2 & 3
$user1->getStudents()->add($user2);
$user1->getStudents()->add($user3);
// user2 is a teacher to user3
$user2->getStudents()->add($user3);
// user4 is a student to user2
// tests if adding something from the inverse side works
$user4->getTeachers()->add($user2);
but this fails with
Fatal error: Call to a member function
add() on a non-object in
D:\ResourceLibrary\Frameworks\Doctrine\tools\sandbox\index.php
on line 70
how can i add elements to a collection or a relationship?
Remember that your collection variables are just regular ol' class properties. Which means they'll be null until you initialize them. The typical thing to do is instantiate them using Doctrine's ArrayCollection class, which will allow you to use the methods you described.
Try this:
public function __construct()
{
$this->students = new \Doctrine\Common\Collections\ArrayCollection();
$this->teachers = new \Doctrine\Common\Collections\ArrayCollection();
}