Doctrine OneToOne authentication via exception of external object on flash

I have User and UserProfile ORM entities related to OneToOne. They should always exist as a couple, there should be no User without UserProfile .

The user must get its identifier from auto-increment, while UserProfile must have a user id. Thus, both of them must have the same identifier, and there is no other column to establish the relationship ( Doctrine docs: Identity via external objects ).

UserProfile is both a primary key (PK) and a foreign key (FK).

I managed to configure it, but this requires that the User be saved first, and only later is UserProfile created and saved in a separate step.

I want UserProfile to always be created using User in the constructor, but if I do, I get this exception:

Doctrine\ORM\ORMInvalidArgumentException: The given entity of type 'AppBundle\Entity\UserProfile' (AppBundle\Entity\ UserProfile@0000000052e1b1eb00000000409c6f2c ) has no identity/no id values set. It cannot be added to the identity map.

Please see the code below - it works, but not the way I want. Php comments show what I want to achieve.

Test.php :

 /** * It works, both saving and loading. * BUT, it requires that I create and save UserProfile * in a separate step than saving User step. */ // create and save User $user = new User(); $objectManager->persist($user); $objectManager->flush(); // create and save UserProfile (this should be unnecessary) $user->createProfile() $objectManager->flush(); 

User.php :

 use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity(repositoryClass="AppBundle\Entity\UserRepository") * @ORM\Table(name="users") */ class User { /** * @var int * * @ORM\Column(name="uid", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * It NULL at first, I create it later (after saving User). * * @var UserProfile|null * * @ORM\OneToOne(targetEntity="UserProfile", mappedBy="user", cascade="persist") */ private $profile = null; public function __construct() { // I want to create UserProfile inside User constructor, // so that it is always present (never NULL): //$this->profile = new UserProfile($this); // but this would give me error: // // Doctrine\ORM\ORMInvalidArgumentException: // The given entity of type 'AppBundle\Entity\UserProfile' // (AppBundle\Entity\ UserProfile@0000000058af220a0000000079dc875a ) // has no identity/no id values set. It cannot be added to the identity map. } public function createProfile() { $this->profile = new UserProfile($this); } } 

UserProfile.php :

 use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity * @ORM\Table(name="profiles") */ class UserProfile { /** * – UserProfile "uid" column points to User "uid" column * – it is PK (primary key) * - it is FK (foreign key) as well * – "owning side" * * @var User * * @ORM\Id * @ORM\OneToOne(targetEntity="User", inversedBy="profile") * @ORM\JoinColumn(name="uid", referencedColumnName="uid", nullable=false) */ private $user; public function __construct(User $user) { $this->user = $user; } } 

Test application: https://github.com/MacDada/DoctrineOneToOneTest

+5
source share
1 answer

Keep in mind that the actual object must be saved by the EntityManager. Just providing a class as a reference to another class does not make entityManager aware that both classes exist.

You must save the actual userProfile for the EntityManager in order to be able to maintain the relationship.

UPDATE due to negative comment:

Please read the Doctrine docs ... You must persist!

The following example is an extension to the User-Comment example in this chapter. Suppose that in our application, the user is created whenever he writes his first comment. In this case, we will use the following code:

 <?php $user = new User(); $myFirstComment = new Comment(); $user->addComment($myFirstComment); $em->persist($user); $em->persist($myFirstComment); $em->flush(); 

Even if you save a new user that contains our new comment, this code would not succeed if you removed the call to EntityManager # persist ($ myFirstComment). Doctrine 2 does not cascade the persist operation to all nested objects that are also new.

Update2: I understand what you want to achieve, but by design you should not move this logic inside your entities. Entities should represent as little logic as possible, since they represent your modal.

If that said, I think you could accomplish what you are trying to do as follows:

 $user = new User(); $profile = $user->getProfile(); $objectManager->persist($user); $objectManager->persist($profile); $objectManager->flush(); 

However, you should consider creating a userService containing the entitymanager and make it responsible for creating, linking, and saving the user + userProfile object.

+1
source

All Articles