2012-05-14 84 views
3

我有配置文件和研究。一個人可以完成很多研究。表單呈現正確。有一個按鈕「添加新的研究」,並與jQuery我添加基於數據原型的另一個子窗體,這很好。當我提出這樣的形式與新的子窗體,我得到一個數據庫錯誤Symfony2形式和Doctrine2 - 更新分配的實體中的外鍵失敗[解決]

Integrity constraint violation: 1048 Column 'profile_id' cannot be null 

我明白了這個錯誤,但我不知道如何擺脫它。我知道我可以在控制器中綁定後更新研究集合,但我希望有一種方法可以在註釋中正確配置它。當我只更新實體時,一切正常。

的代碼是:

class Profile { 
    /** 
    * @var integer $profileId 
    * 
    * @ORM\Column(name="profile_id", type="integer", nullable=false) 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="IDENTITY") 
    */ 
    private $profileId; 
... 
    /** 
    * 
    * @var Study 
    * @ORM\OneToMany(targetEntity="Study", mappedBy="profile", cascade={"persist", "remove"}) 
    */ 
    private $study; 

    public function __construct() 
    { 
     $this->study = new \Doctrine\Common\Collections\ArrayCollection(); 
    } 
... 
} 

class Study { 
    /** 
    * @var integer $studyId 
    * 
    * @ORM\Column(name="study_id", type="integer", nullable=false) 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="IDENTITY") 
    */ 
    private $studyId; 
... 
    /** 
    * @var Profile 
    * 
    * @ORM\ManyToOne(targetEntity="Profile") 
    * @ORM\JoinColumns({ 
    * @ORM\JoinColumn(name="profile_id", referencedColumnName="profile_id") 
    * }) 
    */ 
    private $profile; 
... 
} 

以s(G)埃特斯。墊層數據庫結構是

CREATE TABLE IF NOT EXISTS `profile` (
    `profile_id` int(11) NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY (`profile_id`), 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

CREATE TABLE IF NOT EXISTS `study` (
    `study_id` int(11) NOT NULL AUTO_INCREMENT, 
    `profile_id` int(11) NOT NULL, 
    PRIMARY KEY (`study_id`), 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

ALTER TABLE `study` 
    ADD CONSTRAINT `study_fk2` FOREIGN KEY (`profile_id`) REFERENCES `profile` (`profile_id`); 

形式buidlers是:

class ProfileType extends AbstractType { 

    public function buildForm(FormBuilder $builder, array $options) 
    { 
     $builder->add('study', 'collection', array(
        'type' => new StudyType(), 
        'allow_add' => true, 
        'allow_delete' => true 
         ) 
       ) 
    } 
... 
} 

class StudyType extends AbstractType { 

    public function buildForm(FormBuilder $builder, array $options) 
    { 
     $builder 
       ->add('city') //example field not included in above structures 
    } 
... 
} 

JavaScript部分

function profileNewStudy() { 
    var nr = $('[class^=profile_study_][class*=type2]').last().parent(); 
    nr = nr.attr('id').split('_'); 
    nr = nr.pop()*1 + 1; 
    var proto = $('#profile_study').data('prototype'); 
    proto = proto.replace(/\$\$name\$\$/g, nr); 
    $('#profile_study').append(proto).find('.profile_study_' + nr + ' input').val('qpa' + nr); 
} 

和嫩枝模板

<form action="#" method="post" {{ form_enctype(form) }}> 
    {{ form_widget(form) }} 
    <input type="submit" value="Zapisz"/> 
</form> 

出於測試目的,我從數據庫約束中刪除了study.profile_id上​​的NOT NULL,然後除了study.profile_id = null之外,一切都保存了。

@Gremo答案

我做了一些測試之後,編輯。遺憾的是它並沒有幫助:(我糾正了自己的錯誤,代碼

class Profile 
    /** 
    * @var Study 
    * @ORM\OneToMany(targetEntity="Study", mappedBy="profile") 
    */ 
    private $study; 
class Study 
    /** 
    * @var Profile 
    * @ORM\ManyToOne(targetEntity="Profile", inversedBy="study") 
    * @ORM\JoinColumn(nullable=false, onDelete="CASCADE", referencedColumnName="profile_id") 
    */ 
    private $profile; 

,當我在形式上增加了新的研究實體,並將其發佈到服務器我得到了一個錯誤:一個新的實體是通過關係「奧爾登發現\ xxxBundle \ Entity \ Profile#study'未配置爲級聯實體的持久化操作:Alden \ xxxBundle \ Entity \ Study @ 0000000073eb1a8b00000000198469ff。顯式持久化新實體或配置關係中的級聯持久化操作如果找不到實體導致問題實現'Alden \ xxxxBundle \ Entity \ Study #__ toString()'得到線索。

所以我添加級聯到配置文件:

/** 
* @var Study 
* @ORM\OneToMany(targetEntity="Study", mappedBy="profile", cascade={"persist"}) 
*/ 
private $study; 

而且我得到了像開始時的錯誤:SQLSTATE [23000]:完整性約束違規:1048列'profile_id'不能爲空。

編輯 我的控制器代碼:

$request = $this->getRequest(); 
    $r = $this->getProfileRepository(); 
    $profile = $id ? $r->find($id) : new \Alden\BonBundle\Entity\Profile(); 
    /* @var $profile \Alden\BonBundle\Entity\Profile */ 
    $form = $this->createForm(new ProfileType(), $profile); 
    if ($request->getMethod() == 'POST') 
    { 
     $form->bindRequest($request); 
     if ($form->isValid()) 
     { 
      $vacation = $profile->getVacation(); 
      foreach($vacation as $v) { 
       $r=5; 
      } 
      $em = $this->getEntityManager(); 
      $em->persist($profile); 
      $em->flush(); 
      //return $this->redirect($this->generateUrl('profile_list')); 
     } 
    } 
    return array(
     'profile' => $profile, 
     'form' => $form->createView() 
    ); 

SOLUTION

在Profile類,重要的部分是級聯,orphanRemoval在註釋和在setStudies foreach循環()(由於建議@ Parhs)

/** 
* @var \Doctrine\Common\Collections\ArrayCollection 
* @ORM\OneToMany(targetEntity="Study", mappedBy="profile", cascade={"ALL"}, orphanRemoval=true) 
*/ 
private $studies; 

public function __construct() 
{ 
    $this->studies = new \Doctrine\Common\Collections\ArrayCollection(); 
} 

/** 
* @return Doctrine\Common\Collections\Collection 
*/ 
public function getStudies() 
{ 
    return $this->studies; 
} 

public function setStudies($studies) 
{ 
    foreach ($studies as $v) 
    { 
     if (is_null($v->getProfile())) 
     { 
      $v->setProfile($this); 
     } 
    } 
    $this->studies = $studies; 
} 

I n Study class

/** 
* @var Profile 
* @ORM\ManyToOne(targetEntity="Profile", inversedBy="studies") 
* @ORM\JoinColumn(name="profile_id", nullable=false, onDelete="CASCADE", referencedColumnName="profile_id") 
*/ 
private $profile; 

和通常的獲得者和設定者。

回答

3

如果我正確理解你的關係是雙向的,那麼你必須指定一個擁有方反方。作爲教義2文檔:

  • 逆側具有使用該OneToOne的的mappedBy屬性, 一對多,或多對多映射聲明
  • 持有端具有使用該OneToOne的inversedBy屬性, 多對一,或多對多映射聲明
  • 多對一總是擁有側和一對多始終是 逆側

因此,我在第一個答案中搞砸了你的組織。在你的情況下,Profile必須是反面,而研究應該是擁有的一面。但是,您正在使用的Profile,所以你需要在配置文件中的cascade註釋堅持新的實體:

class Profile 
{ 
    /** 
    * Bidirectional (INVERSE SIDE) 
    * 
    * @ORM\OneToMany(targetEntity="Study", mappedBy="profile", 
    *  cascade={"persist", "remove"}) 
    */ 
    private $studies; 
} 

class Study 
{ 
    /** 
    * Bidirectional (OWNING SIDE - FK) 
    * 
    * @ORM\ManyToOne(targetEntity="Profile", inversedBy="studies") 
    * @ORM\JoinColumn(nullable=false, onDelete="CASCADE") 
    */ 
    private $profile; 
} 

請注意,你的例子是完全一樣that上Doctrine2文檔。

+0

我做了同樣的測試 - 請在主線程的結果。 – koral

+0

@koral對不起,我搞砸了擁有的東西。我要更新答案。 – gremo

+0

我設法整理擁有的東西,你可以看到:)但問題仍然存在 – koral

1

您必須將配置文件設置爲實例。在控制器中。您尚未粘貼控制器代碼.. 您應該遍歷$ profile-> getStudy集合併爲每個項目設置 - > setProfile = $ profile。

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/composite-primary-keys.html#use-case-1-dynamic-attributes

正在發生的事情有犯規適用於forms..He居然有這種reference..Hm也許添加這是一種自動修復,以避免重複,不知道它是否有效,我請嘗試

+0

@Parths基於示例我添加了方法Profile :: addStudy(),但它永遠不會被調用。應該以某種方式被激活? – koral

+0

正如我告訴你,你應該粘貼控制器代碼..你沒有告訴我們的控制器代碼..它不會自動調用! – GorillaApe

0

您的解決方案對我無效。事實上,我已經建立了關係,並定義了加法器,卸妝器,吸氣劑,並在看到您的解決方案後添加了setter。

但是,當提交表單時,我的二傳手並未被調用。

我的解決方案是另一個。

在加法器方法中,我添加了一個調用來設置所有者。

public function addStudie($studie) 
{ 
    $studie->setProfile($this); 
    $this->studies->add($studie); 
} 

而且有這樣的加法器當表單提交需要添加到表單收集領域的by_reference屬性設置爲false調用。

$builder->add('study', 'collection', array(
       'type' => new StudyType(), 
       'allow_add' => true, 
       `by_reference` => false, 
       'allow_delete' => true 
        ) 
      ) 

下面是文檔:

Similarly, if you're using the CollectionType field where your underlying collection data is an object (like with Doctrine's ArrayCollection), then by_reference must be set to false if you need the adder and remover (e.g. addAuthor() and removeAuthor()) to be called.

http://symfony.com/doc/current/reference/forms/types/collection.html#by-reference