slk
slk

Reputation: 46

Doctrine 2 - Zend Framework 2 - Can't create new associations with Form

I'm developing my first Zend Framework application with Doctrine2 and I can't make work a simple association. The editing works fine, but I can't get the creation of entities work properly.

My entities are these:

<?php

namespace Application\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 */
class SpellListDetail{


     /** 
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Application\Entity\SpellList", inversedBy="spellDetails") 
     * @ORM\JoinColumn(name="SpellList_id", referencedColumnName="id", nullable=false) 
     */
    private $sList;
    /** 
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Application\Entity\Spell") 
     * @ORM\JoinColumn(name="Spell_id", referencedColumnName="id", nullable=false) 
     */
    private $spell;

    /** @ORM\Column(type="integer") */
    private $level;

    /** @ORM\Column(type="float") */
    private $areaValueOne;
    /** @ORM\Column(type="float") */
    private $areaValueTwo;
    /** @ORM\Column(type="float") */
    private $areaValueThree;
    /** @ORM\ManyToOne(targetEntity="Application\Entity\AreaType") */
    private $areaType;
    /** @ORM\Column(type="boolean") */
    private $areaLevel = false;

    /** @ORM\Column(type="integer") */
    private $durationValue;
    /** @ORM\Column(type="integer") */
    private $durationType;
    /** @ORM\Column(type="integer") */
    private $durationFail;           

    /** @ORM\Column(type="float") */
    private $rangeValue = 0;

    /** @ORM\Column(type="integer") */
    private $rangeType;

    /** @ORM\Column(length=2) */
    private $primaryClass;

    /** @ORM\Column(length=1) */
    private $subClass;

    /** @ORM\Column(type="boolean") */
    private $instantaneous = false;

    /** @ORM\Column(type="boolean") */
    private $points = true;

//Setters & getter

<?php

namespace Application\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity (repositoryClass="Application\Repositories\SpellList") 
 */
class SpellList{
    const OPEN_LIST = 1000;
    const CLOSED_LIST = 2000;
    const EVIL_LIST = 3000;

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(length=40)
     */
    private $name;

    /**
     * @ORM\Column(type="text")
     */
    private $description;

    /**
     * @ORM\ManyToOne(targetEntity="Application\Entity\Realm")
     */
    private $realm;

    /**
     * @ORM\Column(type="integer")
     */
    private $type;

     /** @ORM\OneToMany(targetEntity="Application\Entity\SpellListDetail", mappedBy="sList", cascade={"persist", "remove"}) 
      *  @ORM\OrderBy({"level" = "ASC"})*/
    private $spellDetails;
//Setters & getters

The third entity, Spell, is a simple entity with id, name and description, with no mapping because I'm trying to avoid unnecesary database calls

And this is the controller

public function createAction()
    {
        $form = new \Magic\Form\ListForm($this->model->getEntityManager());
        $form->setAttribute('action', '/magic/list/create');

        $spellList = new \Application\Entity\SpellList();
        $form->bind($spellList);

        if ($this->getRequest()->isPost()) {
            $form->setData($this->getRequest()->getPost());         
            if ($form->isValid()){
                foreach($spellList->getSpellDetails() as $detail){
                    $spell = $detail->getSpell();
                    if ($spell->getId() == null){
                        $this->model->getEntityManager()->persist($spell);
                        $this->model->getEntityManager()->flush();
                    }
                }                   
                $this->model->getEntityManager()->persist($spellList);
                //$this->model->getEntityManager()->flush();    
                $this->model->getEntityManager()->flush();
            }           
        }       
        $viewModel = new ViewModel(array('form' => $form));
        $viewModel->setTemplate('magic/list/edit.phtml');   
        return $viewModel;
    }

and I get the next error message

Entity of type Application\Entity\SpellListDetail has identity through a foreign entity Application\Entity\SpellList, however this entity has no identity itself. You have to call EntityManager#persist() on the related entity and make sure that an identifier was generated before trying to persist 'Application\Entity\SpellListDetail'. In case of Post Insert ID Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you have to call EntityManager#flush() between both persist operations.

So, if I understand the message, doctrine can't persist the whole association because he's trying to persist first the SpellListDetails than the SpellList and throws the Exception because SpellList has no id. I thought that this can be avoided with the mappedBy and persist options in Entity, so what I'm doing wrong?

Is any way to manually call the persist operation on the SpellList Entity before?

Thanks.

Upvotes: 1

Views: 579

Answers (1)

noobie-php
noobie-php

Reputation: 7233

There are few mistakes here that i would point out striaght forward

your many side must have some thing like this which i believe is spelldetail

  /**
 * @ORM\Id
 * @ORM\Column(type="integer")
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

2ndly Remove ID clause from the same Entity which is detail (your code below)

    /** 
 * @ORM\Id
 * @ORM\ManyToOne(targetEntity="Application\Entity\SpellList", inversedBy="spellDetails") 
 * @ORM\JoinColumn(name="SpellList_id", referencedColumnName="id", nullable=false) 
 */
private $sList;
/** 
 * @ORM\Id
 * @ORM\ManyToOne(targetEntity="Application\Entity\Spell") 
 * @ORM\JoinColumn(name="Spell_id", referencedColumnName="id", nullable=false) 
 */
private $spell

Change it to

     /** 
 * @ORM\ManyToOne(targetEntity="Application\Entity\SpellList", inversedBy="spellDetails") 
 * @ORM\JoinColumn(name="SpellList_id", referencedColumnName="id", nullable=false) 
 */
private $sList;
/** 
 * @ORM\ManyToOne(targetEntity="Application\Entity\Spell",inversedBy="spellDetails") 
 * @ORM\JoinColumn(name="Spell_id", referencedColumnName="id", nullable=false) 
 */
private $spell

WHat you need to know is that when there is relation and if relation is correct Doctrine will acutoatically map relation for foregin keys along with constraints.

Also you missed

  inversedBy="spellDetails"

with your relation with spell entity and i have placed that in the edited code.I am only answering in refrence to actuall question which was associations. I havent seen your controller yet so if you experience a problem persisting/Flush (saving data) comment below

Upvotes: 3

Related Questions