Symfony2表单和Doctrine2-更新分配的实体中的外键失败 [英] Symfony2 form and Doctrine2 - update foreign key in assigned entities fails

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

问题描述

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

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.

代码为:

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)字母。底层数据库结构为

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

buidler的形式为:

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

Javascript部分

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

和Twig模板

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

出于测试目的,我从study.profile_id的数据库约束NOT NOT NULL中删除,然后保存了所有内容,除了study.profile_id = null。

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.

在@Gremo回答后编辑

一些测试。不幸的是,它没有帮助:(我用代码纠正了他的错误

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;

,当我以表格形式添加新的Study实体并将其发布到服务器时,我收到一个错误:通过关系'Alden\xxxBundle\Entity\Profile#study'找到了一个新实体配置为级联实体的持久化操作:Alden\xxxBundle\Entity\Study @ 0000000073eb1a8b00000000198469ff。显式持久化新实体或配置关系上的级联持久化操作。问题实现工具 Alden\xxxxBundle\Entity\Study #__ toString()以获取线索。

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;

然后我得到了像开头这样的错误:SQLSTATE [23000]:违反完整性约束:1048

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

解决方案

在Profile类中,重要的部分是层叠,批注中的orphanRemoval和setStudies()中的foreach循环(由于建议@Parhs)

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

学习班

/**
 * @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.

推荐答案

如果我对您的理解正确,则您的关系是双向的,因此您必须指定一个拥有侧和一个反侧。作为Doctrine 2文档:

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:


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

  • 拥有方必须使用OneToOne,
    ManyToOne或ManyToMany映射声明的inversedBy属性
  • ManyToOne总是拥有方,而OneToMany总是
    反面

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

请注意,您的示例与关于Doctrine2文档。

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

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

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