symfony3:为具有多对一关系的列的实体创建窗体


symfony3: create form for entity that has a column with many to one relationship

我有一个"Tasks"表,它引用了一个带有外键的"Estados"表(从Tasks.taskestado列到Estados.estado.)

以下是任务的相关XML映射:

<entity name="AppBundle'Entity'Tasks" table="TASKS" repository-class="AppBundle'Repository'TasksRepository">
<id name="taskid" type="bigint" column="TaskID">
  <generator strategy="IDENTITY"/>
</id>
...
<many-to-one field="taskestado" target-entity="Estados" fetch="LAZY">
  <join-columns>
    <join-column name="TaskEstado" referenced-column-name="Estado"/>
  </join-columns>
</many-to-one>
...

Estados:

<entity name="AppBundle'Entity'Estados" table="ESTADOS">
<id name="estado" type="string" column="Estado" length="15">
  <generator strategy="IDENTITY"/>
</id>
<field name="estadodescricao" type="string" column="EstadoDescricao" length="50" nullable="true">
  <options>
    <option name="fixed"/>
  </options>
</field>
...

考虑到这一点,我正在尝试执行一个操作(novaAction())来创建任务。这是控制器代码:

public function novaAction(Request $request)
{
    $task = new Tasks();
    $em = $this->getDoctrine()->getManager();
    dump($task);
    #$task->setTaskEstado(new Estados());
    $form = $this->createForm(TasksType::class, $task);
    $form->handleRequest($request);
    if ($form->isSubmitted()) {
        if ($form->isValid()) {
            // Criar a tarefa na BD
            $em->persist($form->getData());
            $em->flush();
            $this->addFlash('notice', 'app.nova_tarefa.mensagem_sucesso');
            return $this->redirectToRoute('nova_tarefa');
        }
        $this->addFlash('error', 'app.nova_tarefa.mensagem_erro');
    }

以及相关任务类型代码:

class TasksType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('taskid', HiddenType::class)
        ...
        #->add('taskestado')
        ->add('taskestado', EntityType::class, [ 'class' => 'AppBundle:Estados' ])
        ...
}
/**
 * @param OptionsResolver $resolver
 */
public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'AppBundle'Entity'Tasks',
        #'empty_data' => function ('Symfony'Component'Form'FormInterface $form) {
        #    #return new Tasks($form->get('tasks')->getData());
        #    return new Tasks();
        #},
    ));
}
}

"empty_data"选项试图在不向表单传递Tasks实例的情况下创建表单。此外,当我添加带有注释的代码的taskestado时,也就是说,没有参数时,我会得到同样的结果。

这是相关的任务实体:

namespace AppBundle'Entity;
use Doctrine'Common'Collections'ArrayCollection;
/**
 * Tasks
 */
class Tasks
{
    /**
     * @var integer
     */
    protected $taskid;
    /**
     * @var 'AppBundle'Entity'Estados
     */
    protected $taskestado;
    ...
    /**
     * Get taskid
     *
     * @return integer
     */
    public function getTaskid()
    {
        return $this->taskid;
    }
    /**
     * Set taskestado
     *
     * @param 'AppBundle'Entity'Estados $taskestado
     *
     * @return Tasks
     */
    public function setTaskestado('AppBundle'Entity'Estados $taskestado = null)
    {
        $this->taskestado = $taskestado;
        return $this;
    }
    /**
     * Get taskestado
     *
     * @return 'AppBundle'Entity'Estados
     */
    public function getTaskestado()
    {
        return $this->taskestado;
    }
    ...
}

当我打开页面时,我得到以下错误:

传递给选择字段的实体必须进行管理。也许在实体经理中坚持它们?

我还生成了CRUD(bin/console doctrine:generate:crud --filter=Tasks),这样我就可以检查它是如何完成的,但代码和结果是相似的(在修复了一些日期时间列的TasksType中的一些问题之后)。

我做错了什么?

正如错误所说,您需要在Tasks的映射中设置cascade=persistent。例如:

<many-to-one field="taskestado" target-entity="Estados" fetch="LAZY">
  <cascade>
     <cascade-persist/>
  </cascade>
  <join-columns>
    <join-column name="TaskEstado" referenced-column-name="Estado"/>
  </join-columns>
</many-to-one>

也不用做$em->persist($form->getData());使用$em->persist($task);

看看这是否有效。。。

我终于得到了答案,这还远远不够明显。

正如我在问题中所说的,从Tasks.taskestado到Estados.estado有一种多对一的关系。Estados.estado是一个字符串,可以为null,也可以是空字符串或(通常)非空字符串。虽然映射中没有指示,但空字符串是taskestado的默认值。

我已经在表上有了数据,Estados.estado的一个条目正是空字符串。

我没有在问题中发帖,但我在这个异常的堆栈跟踪中看到了以下信息:

[1] Symfony'Component'Form'Exception'RuntimeException: Entities passed to the choice field must be managed. Maybe persist them in the entity manager?
    at n/a
        in /var/www/simpletask2/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php line 119
    at Symfony'Bridge'Doctrine'Form'ChoiceList'IdReader->getIdValue(object(Estados))
        in  line 
    at call_user_func(array(object(IdReader), 'getIdValue'), object(Estados))
        in /var/www/simpletask2/vendor/symfony/symfony/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php line 204
    at Symfony'Component'Form'ChoiceList'ArrayChoiceList->flatten(array(object(Estados), object(Estados), object(Estados), object(Estados), object(Estados), object(Estados), object(Estados), object(Estados)), array(object(IdReader), 'getIdValue'), array(), array(), array())
        in /var/www/simpletask2/vendor/symfony/symfony/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php line 91

因此,EntityType从数据库中检索了这些值,并试图呈现它们。换句话说,它们已经被持久化了,问题不可能真正存在于我如何初始化实体。

因此,我想起了Estados实体的空字符串值,并试图将其从数据库中删除:问题解决了,TaskEstado实体按预期进行了初始化和呈现。

现在我需要一种方法来解决这个问题,但解决方案已经找到了。

感谢大家的回答。

确保您有

$this->taskestado = new ArrayCollection();

Tasks类的__construct()中。

然后2个通知:

尽量只在代码中使用英语。这样你会得到更多的回应。作为"最佳实践",为实体类名使用Singulars,如TaskTaskEstado