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

查看:12
本文介绍了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
     *
     * @ORMColumn(name="profile_id", type="integer", nullable=false)
     * @ORMId
     * @ORMGeneratedValue(strategy="IDENTITY")
     */
    private $profileId;
...
    /**
     *
     * @var Study
     * @ORMOneToMany(targetEntity="Study", mappedBy="profile", cascade={"persist", "remove"})
     */
    private $study;

    public function __construct()
    {
        $this->study = new DoctrineCommonCollectionsArrayCollection();
    }
...
}

    class Study {
    /**
     * @var integer $studyId
     *
     * @ORMColumn(name="study_id", type="integer", nullable=false)
     * @ORMId
     * @ORMGeneratedValue(strategy="IDENTITY")
     */
    private $studyId;
...
    /**
     * @var Profile
     *
     * @ORMManyToOne(targetEntity="Profile")
     * @ORMJoinColumns({
     *   @ORMJoinColumn(name="profile_id", referencedColumnName="profile_id")
     * })
     */
    private $profile;
...
}

带有 s(g)eters.底层数据库结构是

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

表单构建器是:

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 部分

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

和树枝模板

<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 之外的所有内容.

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
     * @ORMOneToMany(targetEntity="Study", mappedBy="profile")
     */
    private $study;
class Study
    /**
     * @var Profile
     * @ORMManyToOne(targetEntity="Profile", inversedBy="study")
     * @ORMJoinColumn(nullable=false, onDelete="CASCADE", referencedColumnName="profile_id")
     */
    private $profile;

当我在表单中添加新的 Study 实体并将其发布到服务器时,我收到一个错误:通过关系 'AldenxxxBundleEntityProfile#study' 发现了一个新实体,该实体未配置为级联持久操作实体:AldenxxxBundleEntityStudy@0000000073eb1a8b00000000198469ff.显式持久化新实体或在关系上配置级联持久化操作.如果您无法找出导致问题的实体,请执行 'AldenxxxxBundleEntityStudy#__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 'AldenxxxBundleEntityProfile#study' that was not configured to cascade persist operations for entity: AldenxxxBundleEntityStudy@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 'AldenxxxxBundleEntityStudy#__toString()' to get a clue.

所以我在配置文件中添加了级联:

So I added cascade to profile:

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

然后我得到了一个错误,就像在开始时一样:SQLSTATE[23000]: 完整性约束违规:1048 列 'profile_id' 不能为空.

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

已编辑我的控制器代码:

    $request = $this->getRequest();
    $r = $this->getProfileRepository();
    $profile = $id ? $r->find($id) : new AldenBonBundleEntityProfile();
    /* @var $profile AldenBonBundleEntityProfile */
    $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 DoctrineCommonCollectionsArrayCollection
 * @ORMOneToMany(targetEntity="Study", mappedBy="profile", cascade={"ALL"}, orphanRemoval=true)
 */
private $studies;

public function __construct()
{
    $this->studies = new DoctrineCommonCollectionsArrayCollection();
}

/**
 * @return DoctrineCommonCollectionsCollection
 */
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
 * @ORMManyToOne(targetEntity="Profile", inversedBy="studies")
 * @ORMJoinColumn(name="profile_id", nullable=false, onDelete="CASCADE", referencedColumnName="profile_id")
 */
private $profile;

以及通常的 getter 和 setter.

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的mappedBy属性,OneToMany 或 ManyToMany 映射声明
  • 拥有方必须使用 OneToOne 的 inversedBy 属性,ManyToOne 或 ManyToMany 映射声明
  • 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

所以我在我的第一个回答中搞砸了你的关联.在您的情况下,Profile 必须是相反的一面,而研究应该是拥有的一面.但是您正在处理 Profile,因此您需要在配置文件上使用 cascade 注释来持久化新实体:

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)
     *
     * @ORMOneToMany(targetEntity="Study", mappedBy="profile",
     *     cascade={"persist", "remove"})
     */
    private $studies;
}

class Study
{
    /**
     * Bidirectional (OWNING SIDE - FK)
     * 
     * @ORMManyToOne(targetEntity="Profile", inversedBy="studies")
     * @ORMJoinColumn(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天全站免登陆