编程语言
首页 > 编程语言> > PHP-Symfony2坚持使用Doctrine UNIQUE INDEX和ORDER BY的表单集合?

PHP-Symfony2坚持使用Doctrine UNIQUE INDEX和ORDER BY的表单集合?

作者:互联网

我有一个用户实体,该实体具有与之关联的列表项的集合.

每个列表项实体还引用另一个实体,主题.我在List Item表上设置了UNIQUE约束,该约束仅允许User和Topic外键的唯一组合.每个用户都不允许使用重复引用Topic实体的列表项.我也按“ completion_week”排序结果.

有时,我将尝试保留表单集合,但由于违反完整性约束而失败.由于某些原因,Symfony似乎认为正在对表单进行更新,并且错误地尝试更新集合项-但是正在某些似乎是随机的更新实体上切换外键-由于上述限制,这导致了错误.

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '6-1' for key 'list_item_user_topic'

用户实体:

<?php
/**
 * @ORM\Entity(repositoryClass="App\MyBundle\Repository\UserRepository")
 * @ORM\Table(name="users")
 * @Gedmo\SoftDeleteable(fieldName="deleted_at", timeAware=true)
 */
class User implements UserInterface, EquatableInterface
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=30, nullable=false)
     */
    private $first_name;

    /**
     * @ORM\Column(type="string", length=30, nullable=false)
     */
    private $last_name;

    /**
     * @ORM\Column(type="string", unique=true, length=100, nullable=true)
     */
    private $email;

    /**
     * @ORM\OneToMany(
     *     targetEntity="App\MyBundle\Entity\ListItem",
     *     mappedBy="user",
     *     orphanRemoval=true,
     *     fetch="EAGER",
     *     cascade={"all"}
     * )
     * @ORM\OrderBy({"completion_week"="ASC"})
     * 
     */
    private $listItems;

    ...

列表项实体:

<?php
/**
 * @ORM\Entity(repositoryClass="App\MyBundle\Repository\ListItemRepository")
 * @ORM\Table(
 *     name="list_items",
 *     uniqueConstraints={@ORM\UniqueConstraint(name="list_item_user_topic", columns={"user_id","topic_id"})}
 * )
 *
 * @Gedmo\SoftDeleteable(fieldName="deleted_at", timeAware=true)
 */
class ListItem
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="App\MyBundle\Entity\User", inversedBy="listItems", fetch="EAGER")
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     */
    private $user;

    /**
     * @ORM\Column(type="integer", length=11, nullable=true)
     */
    private $completion_week;

    /**
     * @ORM\ManyToOne(targetEntity="App\MyBundle\Entity\Topic", inversedBy="listItems", fetch="EAGER")
     * @ORM\JoinColumn(name="topic_id", referencedColumnName="id")
     */
    private $topic;

    ...

我正在使用Symfony2表单生成器来构建表单.这很好.我已经为前端的添加/删除按钮添加了javascript.总的来说-我能够保存并持久保存表单集合,而不会出现任何问题.

用户表格类型:

<?php
/**
 * Class UserType
 * @package App\MyBundle\Form\Type
 */
class UserType extends AbstractType
{
    /**
     * @param OptionsResolverInterface $resolver]
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class'            => 'App\MyBundle\Entity\User',
            'method'                => 'POST',
            'cascade_validation'    => true
        ));
    }

    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // Set User data
        $user = $builder->getData();

        // Generate form
        $builder
            ->add('listItems', 'collection', array(
                'options'  => array(
                    'required' => false,
                    'attr' => array('class'=>'col-sm-12')
                ),
                'type'              => new ListItemType(),
                'label'             => false,
                'allow_add'         => true,
                'allow_delete'      => true,
                'delete_empty'      => true,
                'prototype'         => true,
                'by_reference'      => false
            ))
            ->add('first_name')
            ->add('last_name')
            ->add('email');
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'user';
    }
}

清单项目表单类型:

<?php
/**
 * Class ListItemType
 * @package App\MyBundle\Form\Type
 */
class ListItemType extends AbstractType
{
    /**
     * @param OptionsResolverInterface $resolver]
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class'                => 'App\MyBundle\Entity\ListItem',
            'method'                    => 'POST',
        ));
    }

    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // Generate form
        $builder
            ->add('topic', 'entity', array(
                'attr'          => array('class' => 'form-control chosen-select-10'),
                'class'         => 'AppMyBundle:Topic',
                'empty_value'   => 'Choose a Topic',
                'label'         => false,
                'property'      => 'name',
                'expanded'      => false,
                'multiple'      => false
            ))
            ->add('completion_week', 'integer', array(
                'attr'          => array('class' => 'form-control'),
                'label'         => false,
            ));
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'list_item';
    }
}

我发现的是,在处理表单时-handleRequest()方法中正在发生某些情况,该方法交换出集合中不同列表项上的外键引用.在某些情况下-无需对前端的表单集合进行任何更改.像这样:

用户列表项的原始集合:

handleRequest()之后的用户列表项集合:

然后,当Doctrine尝试写入第一条记录时,这会导致完整性约束冲突,因为它违反了List Items表上的唯一约束.我不明白的是,为什么/为什么handleRequest()方法会在更新时交换外键.

而且-在许多情况下-表单对于用户而言将持续存在.我不喜欢在这里使用“随机”一词,但是除了仅与实体合作一段时间并对其执行CRUD操作外,我无法找到一种复制问题的方法.很多时候,表格仍然可以正常工作-有时候,外键引用被交换,由于UNIQUE约束,我无法提交表格来更新实体.

有没有人遇到过类似的问题或对为什么会发生这种现象有一些见识?这是handleRequest()方法中的错误吗?即使我没有对列表项集合进行任何更改,也会发生这种情况.就像-如果我编辑用户并仅提交表单而不进行任何更改-这种行为仍然会发生.

有一个更好的方法吗?

解决方法:

解决的方法是在用户实体的$listItems属性中添加一个Doctrine“ IndexBy”注释.通过在此处指定一列,返回的结果将按其值编制索引.这必须是唯一值.在这种情况下,我使用了主键.

/**
 * @ORM\OneToMany(
 *     targetEntity="App\MyBundle\Entity\ListItem",
 *     mappedBy="user",
 *     orphanRemoval=true,
 *     fetch="EAGER",
 *     indexBy="id",
 *     cascade={"all"}
 * )
 * @ORM\OrderBy({"completion_week"="ASC"})
 * 
 */
private $listItems;

然后,这更改了每个收集项目在前端建立索引的方式.

由此:

<div class="row" data-content="user[listItems][0]">...</div>
<div class="row" data-content="user[listItems][1]">...</div>
<div class="row" data-content="user[listItems][2]">...</div>
<div class="row" data-content="user[listItems][3]">...</div>
<div class="row" data-content="user[listItems][4]">...</div>

对此:

<div class="row" data-content="user[listItems][1950]">...</div>
<div class="row" data-content="user[listItems][1951]">...</div>
<div class="row" data-content="user[listItems][1955]">...</div>
<div class="row" data-content="user[listItems][1953]">...</div>
<div class="row" data-content="user[listItems][1948]">...</div>

现在,提交表单时,每个集合项都由其唯一ID引用-确保在表单绑定后,前端中输入的数据可以正确保存.

它表现得有些随机的原因是因为我通过“ completion_week”列对结果进行排序.记录有可能以不同的顺序返回,它们共享相同的ORDER BY值.如果您有三个记录的“ completion_week”具有相同的值,并且您通过“ completion_week”进行ORDER-MySQL负责确定结果的顺序.

当Symfony收到POST结果时,控制器必须再次调用数据库以获取User实体并构建表单.如果结果以不同的顺序返回,则从前端捕获的数组键将不匹配-并且唯一约束错误在onFlush上产生.

标签:doctrine-orm,symfony,php
来源: https://codeday.me/bug/20191120/2045947.html