Php Symfony实体字段:多个为多个=假的多个-字段填充不正确

Php Symfony实体字段:多个为多个=假的多个-字段填充不正确,php,forms,symfony,doctrine-orm,entity,Php,Forms,Symfony,Doctrine Orm,Entity,我将symfony2与条令2一起使用。 我在两个实体之间存在多对多关系: /** * @ORM\ManyToMany(targetEntity="\AppBundle\Entity\Social\PostCategory", inversedBy="posts") * @ORM\JoinTable( * name="post_postcategory", * joinColumns={@ORM\JoinColumn(name="postId", referencedCol

我将symfony2与条令2一起使用。 我在两个实体之间存在多对多关系:

/**
 * @ORM\ManyToMany(targetEntity="\AppBundle\Entity\Social\PostCategory", inversedBy="posts")
 * @ORM\JoinTable(
 *     name="post_postcategory",
 *     joinColumns={@ORM\JoinColumn(name="postId", referencedColumnName="id", onDelete="CASCADE")},
 *     inverseJoinColumns={@ORM\JoinColumn(name="postCategoryId", referencedColumnName="id", onDelete="CASCADE")}
 * )
 */
private $postCategories;
现在我想让用户只选择一个类别。为此,我在表单中使用选项'multiple'=>false

我的表格:

        ->add('postCategories', 'entity', array(
                'label'=> 'Catégorie',
                'required' => true,
                'empty_data' => false,
                'empty_value' => 'Sélectionnez une catégorie',
                'class' => 'AppBundle\Entity\Social\PostCategory',
                'multiple' => false,
                'by_reference' => false,
                'query_builder' => $queryBuilder,
                'position' => array('before' => 'name'),
                'attr' => array(
                    'data-toggle'=>"tooltip",
                    'data-placement'=>"top",
                    'title'=>"Choisissez la catégorie dans laquelle publier le feedback",
                )))
这首先在保存时给了我错误,我必须更改setter,如下所示:

/**
 * @param \AppBundle\Entity\Social\PostCategory $postCategories
 *
 * @return Post
 */
public function setPostCategories($postCategories)
{
    if (is_array($postCategories) || $postCategories instanceof Collection)
    {
        /** @var PostCategory $postCategory */
        foreach ($postCategories as $postCategory)
        {
            $this->addPostCategory($postCategory);
        }
    }
    else
    {
        $this->addPostCategory($postCategories);
    }

    return $this;
}

/**
 * Add postCategory
 *
 * @param \AppBundle\Entity\Social\PostCategory $postCategory
 *
 * @return Post
 */
public function addPostCategory(\AppBundle\Entity\Social\PostCategory $postCategory)
{
    $postCategory->addPost($this);
    $this->postCategories[] = $postCategory;

    return $this;
}

/**
 * Remove postCategory
 *
 * @param \AppBundle\Entity\Social\PostCategory $postCategory
 */
public function removePostCategory(\AppBundle\Entity\Social\PostCategory $postCategory)
{
    $this->postCategories->removeElement($postCategory);
}

/**
 * Get postCategories
 *
 * @return \Doctrine\Common\Collections\Collection
 */
public function getPostCategories()
{
    return $this->postCategories;
}
/**
 * Constructor
 * @param null $user
 */
public function __construct($user = null)
{
    $this->postCategories = new \Doctrine\Common\Collections\ArrayCollection();
}
现在,当编辑一篇文章时,我也遇到了一个问题,因为它使用了一个getter来输出一个集合,而不是一个实体,并且我的category字段没有正确填充

/**
 * Get postCategories
 *
 * @return \Doctrine\Common\Collections\Collection
 */
public function getPostCategories()
{
    return $this->postCategories;
}
如果我将'multiple'=>设置为true,它就可以工作,但我不希望这样,我希望用户只选择一个类别,我不希望仅用断言来约束这一点

当然,在某些情况下,我希望让用户选择多个字段,因此我希望保留多个字段之间的关系


我能做些什么?

您可以将表单类型设置为不使用
后分类
(将
按引用
选项设置为false)

这将强制symfony表单使用
addPostCategory
removePostCategory
而不是setPostCategories

UPD

1) 您正在混合使用普通数组和ArrayCollection。选择一种策略。Getter将始终输出ArrayCollection,因为它应该这样做。如果要强制它为纯数组,请将
->toArray()
方法添加到getter

2) 我还理解,选择
multiple=false
返回一个实体,而
multiple=true
返回与映射关系无关的数组(*toMany,或*toOne)。所以,只要试着从类中删除setter,如果您希望在不同的情况下有类似的行为,就只使用adder和remover

/** @var ArrayCollection|PostCategory[] */
private $postCategories;

public function __construct()
{
    $this->postCategories = new ArrayCollection();
}

public function addPostCategory(PostCategory $postCategory)
{
   if (!$this->postCategories->contains($postCategory) {
      $postCategory->addPost($this);
      $this->postCategories->add($postCategory);
   }
}

public function removePostCategory(PostCategory $postCategory)
{
   if ($this->postCategories->contains($postCategory) {
      $postCategory->removePost($this);
      $this->postCategories->add($postCategory);
   }
}

/**
 * @return ArrayCollection|PostCategory[]
 */
public function getPostCategories()
{
    return $this->postCategories;
}

如果要在添加到
ManyToMany
集合时将
multiple
选项设置为
false
,可以通过创建两个新的getter和setter并更新表单构建代码,在实体上使用“fake”属性

(有趣的是,我在升级到Symfony 2.7后才在项目中看到这个问题,这迫使我设计了这个解决方案。)

下面是一个使用实体的示例。本例假设您需要验证(因为这有点复杂,所以希望这个答案对其他人更有用!)

将以下内容添加到您的
Post
课程中:

public function setSingleCategory(PostCategory $category = null)
{
    // When binding invalid data, this may be null
    // But it'll be caught later by the constraint set up in the form builder
    // So that's okay!
    if (!$category) {
        return;
    }

    $this->postCategories->add($category);
}

// Which one should it use for pre-filling the form's default data?
// That's defined by this getter.  I think you probably just want the first?
public function getSingleCategory()
{
    return $this->postCategories->first();
}
现在在表单中更改这一行:

->add('postCategories', 'entity', array(
将来


i、 e.我们已经更改了它引用的字段,还添加了一些内联验证-您不能通过注释设置验证,因为在您的类上没有名为
singleCategory
的属性,只有一些方法使用该短语。

在我的情况下,原因是原则没有一对多的关系,单向连接表。在文档中的例子是show haw,我们可以通过manytomy(在第二列中添加标志unique=true)来实现这种关联

这种方式是可以的,但表单组件会自己混合

解决方案是更改实体类中的geter和seter。。。即使是自动生成的

这是我的箱子(我希望有人会需要它)。假设:经典的一对多关系,单向连接表

实体类:

/**
 * @ORM\ManyToMany(targetEntity="B2B\AdminBundle\Entity\DictionaryValues")
 * @ORM\JoinTable(
 *      name="users_responsibility",
 *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="responsibility_id", referencedColumnName="id", unique=true, onDelete="CASCADE")}
 * )
 */
private $responsibility;

/**
 * Constructor
 */
public function __construct()
{
    $this->responsibility = new \Doctrine\Common\Collections\ArrayCollection();
}

/**
 * Add responsibility
 *
 * @param \B2B\AdminBundle\Entity\DictionaryValues $responsibility
 *
 * @return User
 */
public function setResponsibility(\B2B\AdminBundle\Entity\DictionaryValues $responsibility = null)
{

    if(count($this->responsibility) > 0){
        foreach($this->responsibility as $item){
            $this->removeResponsibility($item);
        }
    }

    $this->responsibility[] = $responsibility;

    return $this;
}

/**
 * Remove responsibility
 *
 * @param \B2B\AdminBundle\Entity\DictionaryValues $responsibility
 */
public function removeResponsibility(\B2B\AdminBundle\Entity\DictionaryValues $responsibility)
{
    $this->responsibility->removeElement($responsibility);
}

/**
 * Get responsibility
 *
 * @return \Doctrine\Common\Collections\Collection
 */
public function getResponsibility()
{
    return $this->responsibility->first();
}
表格:


我知道这是一个很老的问题,但这个问题在今天仍然有效。 使用一个简单的内联数据转换器为我做到了这一点

public function buildForm(FormBuilderInterface$builder,array$options):void
{
$builder->add('profileTypes',EntityType::class[
“多个”=>false,
“扩展”=>true,
'class'=>ProfileType::class,
]);
//data transformer,因此ProfileType可以与multiple=>false一起工作
$builder->get('profileTypes')
->addModelTransformer(新CallbackTransformer(
//从集合中返回第一项
fn($data)=>Collection&&$data->count()?$data->first():$data的$data实例,
//将单个ProfileType转换为集合
fn($data)=>ProfileType的$data&$data实例?新的ArrayCollection([$data]):$data
));
}

PS:在PHP7.4及更高版本中可用。

不幸的是,在多对多上使用multiple=>false会强制使用setPostCategories setter。我有by_reference=>false,这个setter仍然在使用。我正在发布我的表单详细信息。然后,这并没有解决getter问题,这正是我感兴趣的问题。是的,正如我看到的,您使用的不是默认的选择字段,而是外部字段。正如我看到的源代码,它需要额外的服务配置,你可以发布它吗?哦,不需要,我看到你的小部件正在配置实体,调查,对不起,我可以使用默认的实体小部件,这将是一样的。试试看(有很多文档由_参考和访问)很高兴这是有用的。谢谢你接受我的回答!
/**
 * @ORM\ManyToMany(targetEntity="B2B\AdminBundle\Entity\DictionaryValues")
 * @ORM\JoinTable(
 *      name="users_responsibility",
 *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="responsibility_id", referencedColumnName="id", unique=true, onDelete="CASCADE")}
 * )
 */
private $responsibility;

/**
 * Constructor
 */
public function __construct()
{
    $this->responsibility = new \Doctrine\Common\Collections\ArrayCollection();
}

/**
 * Add responsibility
 *
 * @param \B2B\AdminBundle\Entity\DictionaryValues $responsibility
 *
 * @return User
 */
public function setResponsibility(\B2B\AdminBundle\Entity\DictionaryValues $responsibility = null)
{

    if(count($this->responsibility) > 0){
        foreach($this->responsibility as $item){
            $this->removeResponsibility($item);
        }
    }

    $this->responsibility[] = $responsibility;

    return $this;
}

/**
 * Remove responsibility
 *
 * @param \B2B\AdminBundle\Entity\DictionaryValues $responsibility
 */
public function removeResponsibility(\B2B\AdminBundle\Entity\DictionaryValues $responsibility)
{
    $this->responsibility->removeElement($responsibility);
}

/**
 * Get responsibility
 *
 * @return \Doctrine\Common\Collections\Collection
 */
public function getResponsibility()
{
    return $this->responsibility->first();
}
->add('responsibility', EntityType::class, 
    array(
        'required' => false,
        'label'    => 'Obszar odpowiedzialności:',
        'class'    => DictionaryValues::class,
        'query_builder' => function (EntityRepository $er) {
            return $er->createQueryBuilder('n')
                ->where('n.parent = 2')
                ->orderBy('n.id', 'ASC');
        },
        'choice_label' => 'value',
        'placeholder'  => 'Wybierz',
        'multiple' => false,
        'constraints' => array(
            new NotBlank()
        )
    )
)