Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Doctrine2, pass Id or Object?

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());
    }

}
?>
like image 861
PachinSV Avatar asked Jul 10 '12 14:07

PachinSV


2 Answers

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.

like image 175
ficuscr Avatar answered Nov 07 '22 07:11

ficuscr


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 );
like image 40
Jerry Saravia Avatar answered Nov 07 '22 06:11

Jerry Saravia