Symfony2表单和Doctrine2 - 更新指定实体的外键失败[已解决] [英] Symfony2 form and Doctrine2 - update foreign key in assigned entities fails [solved]

查看:423
本文介绍了Symfony2表单和Doctrine2 - 更新指定实体的外键失败[已解决]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有个人资料和研究。一个人可以完成许多研究。表单正确呈现。有一个按钮添加新的学习,并使用jQuery我添加另一个基于数据原型的子表单,这很好。当我使用新的子表单提交这样的表单时,我收到一个数据库错误

 完整性约束违规:1048列'profile_id'不能为空

我明白这个错误,但我不知道如何摆脱它。我知道我可以在控制器中绑定后更新研究收藏,但我希望有一种方法可以在注释中正确配置。当我只更新实体时,一切都可以正常工作。



代码是:

  class Profile {
/ **
* @var integer $ profileId
*
* @ ORM\Column(name =profile_id,type =integer nullable = false)
* @ ORM\Id
* @ ORM\GeneratedValue(strategy =IDENTITY)
* /
private $ profileId;
...
/ **
*
* @var学习
* @ 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个人资料
*
* @ ORM\ManyToOne(targetEntity =个人资料)
* @ ORM\JoinColumns({
* @ ORM\JoinColumn(name =profile_id,referencedColumnName =profile_id)
*})
* /
private $ profile;
...
}

与s(g)etters。底层数据库结构是

  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`)参考`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 $选项)
{
$ builder
- > add('city')//上面结构中不包含的示例字段
}
...
}

Javascript部分

  function profileNewStudy(){
var nr = $('[class ^ = profile_study _] [类* = 2型])最后()的父()。;
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)
}

和Twig模板

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

为了测试目的,我从study.profile_id上​​的数据库约束NOT 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;

当我在表单中添加新的Study实体并将其发布到服务器时,我发现错误:通过关系Alden\xxxBundle\Entity\Profile#study找到一个新实体配置为级联实体的持久化操作:Alden\xxxBundle\Entity\Study @ 0000000073eb1a8b00000000198469ff。明确地保留新实体或对关系配置级联持久性操作,如果y ou找不到哪个实体导致问题实现'Alden\xxxxBundle\Entity\Study #__ toString()'得到一个线索。



所以我添加了级联到个人资料:

  / ** 
* @var学习
* @ ORM\OneToMany(targetEntity =Study,mappedBy =profile,cascade = {persist})
* /
private $研究;

而不是像在开始时遇到的错误: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'));
}
}
返回数组(
'profile'=> $ profile,
'form'=> $ form-> createView()
);

解决方案



在Profile类中,重要的部分是在setStudies()中的注释和foreach循环中的cascade,orphanRemoval(感谢建议@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($研究为$ v)
{
if(is_null $ v-> getProfile()))
{
$ v-> setProfile($ this);
}
}
$ this-> studies = $ studies;
}

在学习课

  / ** 
* @var个人资料
* @ ORM\ManyToOne(targetEntity =个人资料,inversedBy =研究)
* @ ORM\JoinColumn(name =profile_id,nullable = false,onDelete =CASCADE,referencedColumnName =profile_id)
* /
private $ profile;

和通常的getter和setter。

解决方案

如果我正确理解您的关系是双向的,则必须指定拥有方反方。作为Doctrine 2文档:




  • 反面必须使用OneToOne的mappedBy属性,
    OneToMany或ManyToMany映射声明

  • 拥有方必须使用OneToOne,
    ManyToOne或ManyToMany映射声明的inversedBy属性, li>
  • ManyToOne始终是拥有的一方,OneToMany始终是
    反面



    • 所以我弄错了你的协会在我的第一个答案。在你的情况下,简介必须是相反的一面,而学习应该是拥有的一面。但是您正在使用配置文件,因此您需要在配置文件上使用级联注释来持久存储新实体:

        class Profile 
      {
      / **
      *双向(INVERSE SIDE)
      *
      * @ ORM\OneToMany(targetEntity =Study,mappedBy =profile,
      * cascade = {persist,remove})
      * /
      private $研究;
      }

      类学习
      {
      / **
      *双向(OWNING SIDE - FK)
      *
      * ORM\ManyToOne(targetEntity =Profile,inversedBy =研究)
      * @ ORM\JoinColumn(nullable = false,onDelete =CASCADE)
      * /
      private $简介;
      }

      请注意,您的示例与那个在Doctrine2文档。


      I have profiles and studies. One person can finish many studies. The form renders correctly. There is a button "Add new study" and with jQuery I add another subform based on data-prototype and this works well. When I submit such a form with new subforms I get an database error

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

      I understand this error but I don't know how to get rid of it. I know I can update collection of studies after binding in controller but I hope there is a way to configure it properly in annotations. When I only update entities everything works fine.

      The code is:

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

      and

          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;
      ...
      }
      

      with s(g)etters. Underlaying database structure is

      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`);
      

      Form buidlers are:

      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
                              )
                      )
          }
      ...
      }
      

      and

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

      The Javascript part

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

      and Twig template

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

      For testing purposes I removed from database constraint NOT NULL on study.profile_id and then everything was saved except that study.profile_id=null.

      edited after @Gremo answer

      I did some tests. Unfortunately it didn't help :( I corrected his mistakes with code

      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;
      

      and when I added new Study entity in form and posted it to server I got an error: A new entity was found through the relationship 'Alden\xxxBundle\Entity\Profile#study' that was not configured to cascade persist operations for entity: Alden\xxxBundle\Entity\Study@0000000073eb1a8b00000000198469ff. Explicitly persist the new entity or configure cascading persist operations on the relationship. If you cannot find out which entity causes the problem implement 'Alden\xxxxBundle\Entity\Study#__toString()' to get a clue.

      So I added cascade to profile:

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

      and than I got an error like in the begining: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'profile_id' cannot be null.

      edited My controller code:

          $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

      In Profile class, important parts are cascade, orphanRemoval in annotations and foreach loop in setStudies() (thanks to suggestion @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;
      }
      

      In Study class

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

      and usual getters and setters.

      解决方案

      If i understand you correctly your relation is bidirectional, so you have to specify an owning side and an inverse side. As Doctrine 2 documentation:

      • The inverse side has to use the mappedBy attribute of the OneToOne, OneToMany, or ManyToMany mapping declaration
      • The owning side has to use the inversedBy attribute of the OneToOne, ManyToOne, or ManyToMany mapping declaration
      • ManyToOne is always the owning side and OneToMany is always the inverse side

      So i messed up with your association in my first answer. In your case Profile has to be the inverse side while study should be the owning side. But you are working on Profile, so you need the cascade annotation on profile to persist new entities:

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

      Note that your example is exactly the same as that on Doctrine2 documentation.

      这篇关于Symfony2表单和Doctrine2 - 更新指定实体的外键失败[已解决]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆