【问题标题】:Symfony2 form and Doctrine2 - update foreign key in assigned entities failsSymfony2 表单和 Doctrine2 - 更新分配实体中的外键失败
【发布时间】:2012-05-22 08:10:19
【问题描述】:

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

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

表单构建器是:

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

和 Twig 模板

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

当我在表单中添加新的 Study 实体并将其发布到服务器时出现错误:通过关系 'Alden\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]: Integrity constraint violation: 1048 Column 'profile_id' cannot be null。

已编辑 我的控制器代码:

    $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 的建议)

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

以及通常的 getter 和 setter。

【问题讨论】:

    标签: forms symfony doctrine-orm symfony-forms


    【解决方案1】:

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

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

    所以我在第一个回答中搞砸了你的联想。在您的情况下,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;
    }
    

    请注意,您的示例与 Doctrine2 文档中的 that 完全相同。

    【讨论】:

    • @koral 抱歉,我搞砸了拥有的东西。我要更新答案。
    • 如您所见,我设法整理了拥有的东西 :) 但问题仍然存在
    • @koral 查看我的编辑。我犯了一个错误 - 在你的具体情况下,应该是拥有/反向方面有一些限制。查看我更新的答案并告诉我(它必须有效)。
    • 也不工作 - 仍然 study.profile_id=null。 D2 示例看起来与我的案例相似,但它使用代码而不是 Sf2 表单进行管理。顺便说一句,在学习课程中,我更改了引用列:* @ORM\JoinColumn(nullable=false, onDelete="CASCADE", referencedColumnName="profile_id")
    【解决方案2】:

    您必须将配置文件设置为实例。在控制器中。您还没有粘贴控制器代码.. 您应该遍历 $profile->getStudy 集合并为每个项目设置 ->setProfile=$profile。

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

    那里发生的事情不适用于表单..他实际上有一个this引用..嗯也许添加这是一种自动修复以避免迭代,不确定它是否有效,我会尝试

    【讨论】:

    • @Parths 基于示例我添加了方法 Profile::addStudy() 但它从未被调用。应该以某种方式激活?
    • 我告诉过你,你应该粘贴控制器代码。你还没有向我们展示控制器代码。它不是自动调用的!
    【解决方案3】:

    您的解决方案对我不起作用。事实上,我已经建立了关系并定义了加法器、移除器、获取器,在看到您的解决方案后,我添加了设置器。

    但是当表单提交时我的设置器没有被调用。

    我的解决方案是另一个。

    在 adder 方法中,我添加了一个设置所有者的调用。

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

    要在提交表单时调用此加法器,您需要将设置为 falseby_reference 属性添加到表单集合字段。

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

    这是文档:

    同样,如果你使用 CollectionType 字段,而你的基础集合数据是一个对象(比如 Doctrine 的 ArrayCollection),那么如果你需要加法器和删除器(例如 addAuthor() 和 removeAuthor( )) 被调用。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-27
      • 1970-01-01
      • 1970-01-01
      • 2013-10-19
      • 1970-01-01
      • 2011-10-31
      • 2012-09-29
      相关资源
      最近更新 更多