【问题标题】:Symfony2, Doctrine2 Found entity of type Doctrine\Common\Collections\ArrayCollection on association sth#category, but expecting sthSymfony2,Doctrine2 在关联 sth#category 上找到类型为 Doctrine\Common\Collections\ArrayCollection 的实体,但期待 sth
【发布时间】:2013-09-29 14:30:06
【问题描述】:

现在我在表单中提交帖子数据时遇到问题(我的表单看起来像:

Task: <input text>
Category: <multiple select category>
DueDate: <date>
<submit>

) 提交表单后,我会收到此错误:

Found entity of type Doctrine\Common\Collections\ArrayCollection on association Acme\TaskBundle\Entity\Task#category, but expecting Acme\TaskBundle\Entity\Category

我的来源:

任务对象 Task.php

<?php

namespace Acme\TaskBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity
 * @ORM\Table(name="tasks") 
 */
class Task
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;              

    /**
     * @ORM\Column(type="string", length=200)    
     * @Assert\NotBlank(
     *      message = "Task cannot be empty"      
     * )    
     * @Assert\Length(
     *      min = "3",
     *      minMessage = "Task is too short"         
     * )     
     */     
    protected $task;

    /**
     * @ORM\Column(type="datetime")    
     * @Assert\NotBlank()
     * @Assert\Type("\DateTime")
     */
    protected $dueDate;

    /**
     * @Assert\True(message = "You have to agree")    
     */         
    protected $accepted;

    /**
     * @ORM\ManyToMany(targetEntity="Category", inversedBy="tasks")                       
     */
    protected $category;

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

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set task
     *
     * @param string $task
     * @return Task
     */
    public function setTask($task)
    {
        $this->task = $task;

        return $this;
    }

    /**
     * Get task
     *
     * @return string 
     */
    public function getTask()
    {
        return $this->task;
    }

    /**
     * Set dueDate
     *
     * @param \DateTime $dueDate
     * @return Task
     */
    public function setDueDate($dueDate)
    {
        $this->dueDate = $dueDate;

        return $this;
    }

    /**
     * Get dueDate
     *
     * @return \DateTime 
     */
    public function getDueDate()
    {
        return $this->dueDate;
    }

    /**
     * Add category
     *
     * @param \Acme\TaskBundle\Entity\Category $category
     * @return Task
     */
    public function addCategory(\Acme\TaskBundle\Entity\Category $category)
    {
        $this->category[] = $category;

        return $this;
    }

    /**
     * Remove category
     *
     * @param \Acme\TaskBundle\Entity\Category $category
     */
    public function removeCategory(\Acme\TaskBundle\Entity\Category $category)
    {
        $this->category->removeElement($category);
    }

    /**
     * Get category
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getCategory()
    {
        return $this->category;
    }
}

类别对象 Category.php

<?php

namespace Acme\TaskBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity
 * @ORM\Table(name="categories") 
 */
class Category
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")             
     */
    protected $id; 

    /**
     * @ORM\Column(type="string", length=200, unique=true)   
     * @Assert\NotNull(message="Choose a category", groups = {"adding"})                   
     */         
    protected $name;  

    /**
     * @ORM\ManyToMany(targetEntity="Task", mappedBy="category")
     */
    private $tasks;            


    public function __toString()
    {
        return strval($this->name);
    }
    /**
     * Constructor
     */
    public function __construct()
    {
        $this->tasks = new \Doctrine\Common\Collections\ArrayCollection();
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Category
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Add tasks
     *
     * @param \Acme\TaskBundle\Entity\Task $tasks
     * @return Category
     */
    public function addTask(\Acme\TaskBundle\Entity\Task $tasks)
    {
        $this->tasks[] = $tasks;

        return $this;
    }

    /**
     * Remove tasks
     *
     * @param \Acme\TaskBundle\Entity\Task $tasks
     */
    public function removeTask(\Acme\TaskBundle\Entity\Task $tasks)
    {
        $this->tasks->removeElement($tasks);
    }

    /**
     * Get tasks
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getTasks()
    {
        return $this->tasks;
    }
}

TaskType TaskType.php

<?php

namespace Acme\TaskBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Acme\TaskBundle\Form\Type\Category;

class TaskType extends AbstractType
{
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Acme\TaskBundle\Entity\Task',
            'cascade_validation' => true,
        ));
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('task', 'text', array('label' => 'Task'))
                ->add('dueDate', 'date', array('label' => 'Due Date'))
                ->add('category', new CategoryType(), array('validation_groups' => array('adding')))
                //->add('accepted', 'checkbox')
                ->add('save', 'submit', array('label' => 'Submit'));
    }

    public function getName()
    {
        return 'task';
    }
}

CategoryType CategoryType.php

<?php

namespace Acme\TaskBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class CategoryType extends AbstractType
{
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => null,
        ));
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name', 'entity', array(
                      'class' => 'AcmeTaskBundle:Category',
                      'query_builder' => function($repository) { return $repository->createQueryBuilder('c')->orderBy('c.id', 'ASC'); },
                      'property' => 'name',
                      'multiple' => true,
                      'label' => 'Categories',
                      ));
    }

    public function getName()
    {
        return 'category';
    }
}

还有我的控制器 DefaultController.php

public function newAction(Request $request)
{
    $task = new Task();
    $task->setTask('Write name here...');
    $task->setDueDate(new \DateTime('tomorrow'));

    $form = $this->createForm('task', $task);                   
    $form->handleRequest($request);

    if($form->isValid())
    {
        $this->get('session')->getFlashBag()->add(
            'success',
            'Task was successfuly created'
        );
        $em = $this->getDoctrine()->getManager();
        /*
        $category = $this->getDoctrine()->getManager()->getRepository('AcmeTaskBundle:Category')->findOneByName($form->get('category')->getData());
        $task->setCategory($category);
        */
        $em->persist($task);
        try {
            $em->flush();
        } catch (\PDOException $e) {
            // sth
        }

        //$nextAction = $form->get('saveAndAdd')->isClicked() ? 'task_new' : 'task_success';

        //return $this->redirect($this->generateUrl($nextAction));
    } 

    return $this->render('AcmeTaskBundle:Default:new.html.twig', array('form' => $form->createView()));
}

所以,我在 google 上查看了这个问题,但有不同类型的问题。有什么想法吗?

更新 完整的错误信息:

[2013-09-30 14:43:55] request.CRITICAL: Uncaught PHP Exception Doctrine\ORM\ORMException: "Found entity of type Doctrine\Common\Collections\ArrayCollection on association Acme\TaskBundle\Entity\Task#category, but expecting Acme\TaskBundle\Entity\Category" at /var/www/Symfony/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php line 762 {"exception":"[object] (Doctrine\\ORM\\ORMException: Found entity of type Doctrine\\Common\\Collections\\ArrayCollection on association Acme\\TaskBundle\\Entity\\Task#category, but expecting Acme\\TaskBundle\\Entity\\Category at /var/www/Symfony/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:762)"} []

更新 2

我的 TWIG 模板 new.html.twig

<html>
<head>
<title>Task create</title>
</head>
<body>
{% for flashMessage in app.session.flashbag.get('success') %}
<div style="display: block; padding: 15px; border: 1px solid green; margin: 15px; width: 450px;">
{{ flashMessage }}
</div>
{% endfor %}

{{ form_start(form, {'action': path ('task_new'), 'method': 'POST', 'attr': {'novalidate': 'novalidate' }}) }}
  {{ form_errors(form) }}

  <div>
    {{ form_label(form.task) }}:<br>  
    {{ form_widget(form.task) }} {{ form_errors(form.task) }}<br>
  </div>
  <div>
    {{ form_label(form.category) }}:<br>
    {{ form_widget(form.category) }} {{ form_errors(form.category) }}   
  </div>
  <div>
    {{ form_label(form.dueDate) }}:<br>
    {{ form_widget(form.dueDate) }} {{ form_errors(form.dueDate) }}<br>
  </div>

{{ form_end(form) }}
</body>
</html>

【问题讨论】:

  • 能否请您添加一个堆栈跟踪,此错误来自何处?
  • 查看我更新的问题(底部)
  • 你能在你坚持之前检查一下你的 $task->getCategory() 中有哪些类型的对象/集合吗?如果表单组件使用 Task::addCategory 或 Task::setCategory 方法,则可能在您的任务中进行调试。
  • 如果表单被验证,它会在之前抛出异常

标签: php symfony doctrine-orm


【解决方案1】:

您可以通过更改实体中的设置器来改进控制器代码:

Task:

public function addCategory(Category $category)
{
    if (!$this->categories->contains($category)) {
        $this->categories->add($category);
        $category->addTask($this); // Fix this
    }
}

Category:

public function addTask(Task $task)
{
    if (!this->tasks->contains($task)) {
        $this->tasks->add($task);
        $task->addCategory($this);
    }
}

这将使ArrayCollection 中的元素保持唯一性,这样您就不必检查代码并自动设置反面。

【讨论】:

    【解决方案2】:

    所以我找到了解决方案!此代码有效:

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('task', 'text', array('label' => 'Task'))
                ->add('dueDate', 'date', array('label' => 'Date', 'format' => 'ddMMMMyyyy'))
                ->add('category', 'entity', array('required' => true, 'multiple' => true, 'class' => 'AcmeTaskBundle:Category', 'query_builder' => function($repository) { return $repository->createQueryBuilder('c')->orderBy('c.id', 'ASC'); },))
                ->add('save', 'submit', array('label' => 'Send'));
    }
    

    我的控制器正在以这种形式工作:

    if($form->isValid())
    {
        $this->get('session')->getFlashBag()->add(
            'success',
            'Task successfuly added'
        );
        $em = $this->getDoctrine()->getManager();
        foreach($form->get('category')->getData() as $cat)
        {
            $task->removeCategory($cat);
            $task->addCategory($cat);
        }
        $em->persist($task);
        try {
            $em->flush();
        } catch (\PDOException $e) {
            // sth
        }
    }
    

    我不得不使用$task-&gt;removeCategory($cat),因为如果它不在这里,我会得到重复主索引的错误。

    现在我正在尝试解决嵌入式和非嵌入式表单的问题 -> Symfony2, validation embedded and non-embedded forms with same parameters and different results?

    我相信我的问题会对遇到同样问题的 Symfony2 初学者有所帮助。

    【讨论】:

      【解决方案3】:

      您正在定义从任务到类别的多对多关系,这意味着每个任务可以有多个类别。如果您尝试在一个任务中包含多个类别,您可以尝试更改此行

      ->add('category', new CategoryType(), array('validation_groups' => array('adding')))
      

      类似的东西

      ->add('category', 'collection', array('type' => new CategoryType()))
      

      查看 symfony 在 form collectionthis cookbook 上的文档

      【讨论】:

      • 您好,如果我这样做,我的类别表单小部件会出现空白。 &lt;div&gt; &lt;label class="required"&gt;Category&lt;/label&gt;:&lt;br&gt; &lt;div id="task_category"&gt;&lt;/div&gt; &lt;/div&gt;
      • 你能添加你的树枝模板吗?您应该在 twig 文件中添加一些内容以添加您的类别。您应该将原型添加到集合容器中...
      • 尝试类似:&lt;div&gt; {{ form_label(form.category) }}:&lt;br&gt; {% for category in form.category %} &lt;li&gt;{{ form_row(category.name) }}&lt;/li&gt; {% endfor %} &lt;/div&gt;
      • 我的表单被渲染后有 0 个数据库查询。从 DB 中选择类别的预期为 1。
      【解决方案4】:

      我不确定,但尝试将 setCategory 方法添加到您的 Task 实体,因为目前您只有 add 函数,这就是表单组件调用 addFunction 而不是整个数组集合的集合的原因。

      【讨论】:

      • 我应该给这个方法写什么?
      • public function setCategory($categories){ $this-&gt;category = $categories }
      • 我认为您的意思是public function setCategory(\Acme\TaskBundle\Entity\Category $category) { $this-&gt;category = $category; },因为我不使用正确的 $categories 但它没有帮助。
      • 不,我的意思是我写的。只需尝试从函数中删除 Type,因为 $this->category 不是单个类别,而是类别的集合。
      • 是的,我称它为 $category 而不是 $categories,我知道它不是一个单一的。
      【解决方案5】:

      我认为你的表格有问题。

      您的任务与类别具有多对多关系并存储在字段category(逻辑上它是一个类别)。但是在你设置的表单中,'->add('category', new CategoryType())' 这意味着你添加了一个新类型,它是一个数组或其他东西(data_class 未定义),并且这个类型包含另一个字段。

      解决方法是直接在你的表单中定义字段(不使用独立表单类型)或从实体类型扩展分类表单类型(默认类型扩展表单类型)。

      【讨论】:

      • 如果我使用 data_class 我会在开始渲染我的页面时收到错误消息。错误消息就像当前的 - ArrayCollection got, expected Category entity。你能告诉我一个例子如何扩展表单类型(在我的例子中 - 类别)?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-22
      • 1970-01-01
      • 2021-04-22
      • 1970-01-01
      • 2018-07-06
      相关资源
      最近更新 更多