Doctrine2 - 在刷新之前,列变为null

I am trying to insert a new object/row in a database. The object is created like so:

$nodeaccess = new Nodeaccess(); // A by doctrine2 generated entity
$nodeaccess->setAccesslevel(0);
$nodeaccess->setDraw(0);
$nodeaccess->setUserid($userid);
$nodeaccess->setNodename($this->getUser()->getUsername() . ' Node');
$nodeaccess->setMac($node);

All columns of the table are set. When I print $nodeaccess->getUserid() and $nodeaccess->getMac() the desired results are printed. And they are both not null.

But when the object is persisted like so:

$em = $this->getDoctrine()->getManager();
$em->persist($nodeaccess);
$em->flush();

the following error happens:

An exception occurred while executing 'INSERT INTO nodeaccess (mac, userID, accessLevel, nodeName, draw) VALUES (?, ?, ?, ?, ?)' with params {"1":null,"2":null,"3":0,"4":"Example Node","5":0}:

SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'mac' cannot be null

The mac and userID combined is the primary key and they are both foreign keys aswell. They are setup in the model like this:

/**
 * @var integer
 *
 * @ORM\Column(name="mac", type="bigint", nullable=false)
 * @ORM\Id
 */
private $mac;

/**
 * @var integer
 *
 * @ORM\Column(name="userID", type="integer", nullable=false)
 * @ORM\Id
 **/
private $userid;

Public accessors are implemented and I have tried changing the fields to public but it did not help.

Update The accessors:

public function getMac()
{
    return $this->mac;
}

public function setMac($mac)
{
    $this->mac = $mac;
}

public function getUserid()
{
    return $this->userid;
}

public function setUserid($userid)
{
    $this->userid = $userid;
}

Update 2 I have changed the table, now only the mac field is NULL. The new model:

/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer", nullable=false)
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="IDENTITY")
 */
private $id;

/**
 * @var integer
 *
 * @ORM\Column(name="mac", type="bigint", nullable=false)
 */
private $mac;

/**
 * @var integer
 *
 * @ORM\Column(name="userID", type="integer", nullable=false)
 **/
private $userid;

UPDATE The controller action:

public function inviteAction() {
    $repository = $this->getDoctrine()
            ->getRepository('GeninnoEDSBundle:Nodeaccess');
    $options = $repository->createQueryBuilder('na')
            .......
            ->getQuery()
            ->getResult();

    $form = $this->createFormBuilder()
            ->add('user', 'text', array(
                'attr' => array(
                    'placeholder' => '+ Gebruiker'
                )
            ))
            ->add('node', 'hidden')
            ->getForm();

    if ($this->getRequest()->isMethod('POST')) {
        $form->bind($this->getRequest());

        if ($form->isValid()) {
            $data = $form->getData();

            $user_repository = $this->getDoctrine()
                    ->getRepository('GeninnoEDSBundle:User');
            $user = $user_repository->findOneBy(array('username' => $data['user']));

            $node_repository = $this->getDoctrine()
                    ->getRepository('GeninnoEDSBundle:Node');
            $node = $node_repository->find($data['node']);

            $nodeaccess = new Nodeaccess();
            $nodeaccess->setAccesslevel(0);
            $nodeaccess->setDraw(0);
            $nodeaccess->setUserid($user);
            $nodeaccess->setNodename($this->getUser()->getUsername() . ' Node');
            $nodeaccess->setMac($node);

            $em = $this->getDoctrine()->getManager();
            $em->persist($nodeaccess);
            $em->flush();
        }
    }

    return array('options' => $options, 'form' => $form->createView());
}

The problem has been fixed by regenerating this particular entity (oh oh). The fields have changed to:

/**
 * @var \User
 *
 * @ORM\ManyToOne(targetEntity="User")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="userID", referencedColumnName="id")
 * })
 */
private $userid;

/**
 * @var \Node
 *
 * @ORM\ManyToOne(targetEntity="Node")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="mac", referencedColumnName="mac")
 * })
 */
private $mac;

UPDATE

That might not have been the fix I thought it was. Had the problem again after adding some fields back to the class. I found out that by removing the following code the user was no longer assigned to null while persisting.

/**
 * @var \Doctrine\Common\Collections\Collection
 *
 * @ORM\ManyToOne(targetEntity="User", inversedBy="accessNodes")
 * @ORM\JoinColumn(name="userID", referencedColumnName="id")
 **/
private $accessUsers;

Short Answer: Your hidden form field for node uses the __toString() value, which is not an id and therefore will not be found by your repository call, and hence is null at persist time.

Long Answer: Use a transformer:

In your Controller:

use GeninnoEDSBundle\Transformer\NodeTransformer;

And modify the form builder:

$transformer = new NodeTransformer( $em );
$form = $this->createFormBuilder()
        ->add('user', 'text', array(
            'attr' => array(
                'placeholder' => '+ Gebruiker'
            )
        ))
        ->add($builder->create( 'node', 'hidden')
            ->addModelTransformer( $transformer )
        )
        ->getForm();

Your transformer ( new file: GeninnoEDSBundle\Transformer\NodeTransformer.php ):

<?php

namespace GeninnoEDSBundle\Transformer;

use Doctrine\Common\Persistence\ObjectManager;
use GeninnoEDSBundle\Entity\Node;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;

class NodeTransformer implements DataTransformerInterface
{
    /**
    * @var ObjectManager
    */
    private $em;

    //  If selector is multiple (Many2One or Many2Many)

    private $multi;

    /**
    * @param ObjectManager $em
    */
    public function __construct(ObjectManager $em, $multi=false)
    {
        $this->em = $em;
        $this->multi=$multi;
    }

    /**
    * Transforms an object (node) to a string (id).
    *
    * @param  Issue|null $node
    * @return string
    */
    public function transform($node)
    {
        if (null === $node) {return "";}
        if (is_object($node) && method_exists($node, "toArray")){
            $node=$node->map(function ($ob){return $ob->getId();});
            return implode(",",$node->toArray());
        }

        if ('array' == gettype($node)) {
            return implode(','
                          , array_map(function($element){
                                          return $element->getId();
                                      }
                                      ,$node));
        }
// var_dump($node);
        return $node->getId();
    }

    /**
    * Transforms a string (id) to an object (node).
    *
    * @param  string $id
    * @return Issue|null
    * @throws TransformationFailedException if object (node) is not found.
    */
    public function reverseTransform($id)
    {

        if (!$id) {
            if($this->multi) {return array();}
            return null;
        }

        if (strpos($id,',') !== false) {
            $id=explode(',',$id);
        }

        $qb=$this->em
        ->getRepository('GeninnoEDSBundle:Node')
        ->createQueryBuilder('r');
        $qb->andWhere($qb->expr()->in('r.id', $id));
        if (is_array($id) || $this->multi){
            $node=$qb->getQuery()
            ->getResult();
        } else {
            $node=$qb->getQuery()
            ->getSingleResult();
        }

        if (null === $node) {
            throw new TransformationFailedException(sprintf(
                'An node with id "%s" does not exist!',
                $id
                ));
        }

        return $node;
    }
}

Please note: I've added my transformer, but you may need to fiddle with the logic for your application.

More on transformers: http://symfony.com/doc/current/cookbook/form/data_transformers.html