首页 > 解决方案 > Symfony 4 Collections 表单错误:无法猜测

问题描述

我按照文档http://symfony.com/doc/current/form/form_collections.html操作,我需要添加一个通用页面来编辑一个实体的所有记录,并且还建议使用“表单集合” . 有类似“ add use ORM”之类的错误,现在遇到了ERROR. Unable to guess how to get a Doctrine instance from the request information for parameter "abilityHolder".谷歌什么也没说,它只显示来自 symfony2 的旧信息,我使用 flex,下面我将附上与我的任务相关的文件

src/Ability.php - 我获取和编写能力的主要实体

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\AbilityRepository")
 * @ORM\Table(name="Ability")
 */
class Ability
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="integer", name="idItem")
     */
    private $idItem;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $code;

    /**
     * @ORM\Column(type="float", name="reloadTime", nullable=true)
     */
    private $reloadTime;

    /**
     * @ORM\Column(type="float", name="durationTime", nullable=true)
     */
    private $durationTime;

    /**
     * @ORM\Column(type="integer", name="idAbilityType")
     */
    private $idAbilityType;

    /**
     * @ORM\Column(type="datetime", nullable=true, name="dateCreated")
     */
    private $dateCreated;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $author;

    /**
     * @ORM\Column(type="datetime", nullable=true, name="dateChange")
     */
    private $dateChange;

    /**
     * @ORM\Column(type="string", length=255, nullable=true, name="lastAuthor")
     */
    private $lastAuthor;

    public function getId()
    {
        return $this->id;
    }

    public function setId(?int $id): self
    {
        $this->id = $id;

        return $this;
    }

    public function getIdItem(): ?int
    {
        return $this->idItem;
    }

    public function setIdItem(int $idItem): self
    {
        $this->idItem = $idItem;

        return $this;
    }

    public function getCode(): ?string
    {
        return $this->code;
    }

    public function setCode(?string $code): self
    {
        $this->code = $code;

        return $this;
    }

    public function getReloadTime(): ?float
    {
        return $this->reloadTime;
    }

    public function setReloadTime(?float $reloadTime): self
    {
        $this->reloadTime = $reloadTime;

        return $this;
    }

    public function getDurationTime(): ?float
    {
        return $this->durationTime;
    }

    public function setDurationTime(?float $durationTime): self
    {
        $this->durationTime = $durationTime;

        return $this;
    }

    public function getDateCreated(): ?\DateTimeInterface
    {
        return $this->dateCreated;
    }

    public function setDateCreated(\DateTimeInterface $dateCreated): self
    {
        $this->dateCreated = $dateCreated;

        return $this;
    }

    public function getAuthor(): ?string
    {
        return $this->author;
    }

    public function setAuthor(string $author): self
    {
        $this->author = $author;

        return $this;
    }

    public function getDateChange(): ?\DateTimeInterface
    {
        return $this->dateChange;
    }

    public function setDateChange(\DateTimeInterface $dateChange): self
    {
        $this->dateChange = $dateChange;

        return $this;
    }

    public function getLastAuthor(): ?string
    {
        return $this->lastAuthor;
    }

    public function setLastAuthor(string $lastAuthor): self
    {
        $this->lastAuthor = $lastAuthor;

        return $this;
    }

    public function getIdAbilityType(): ?int
    {
        return $this->idAbilityType;
    }

    public function setIdAbilityType(int $idAbilityType): self
    {
        $this->idAbilityType = $idAbilityType;

        return $this;
    }
}

src/AbilityHolder.php - 集合表单实体

<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="AbilityHolder")
 */

class AbilityHolder
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    protected $abilitys;

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

    public function getAbilitys()
    {
        return $this->abilitys;
    }

    public function getId()
    {
        return $this->id;
    }
}

来自Ability 的Form/AbilityType.php表单

<?php

namespace App\Form;

use App\Entity\Ability;
use App\Entity\Item;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use App\Repository\AbilityTypeRepository;
use App\Repository\ItemRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;

class AbilityType extends AbstractType
{
    protected $abilityTypeRepository;
    protected $itemRepository;

    public function __construct(AbilityTypeRepository $abilityTypeRepository, ItemRepository $itemRepository){
        $this->abilityTypeRepository = $abilityTypeRepository;
        $this->itemRepository = $itemRepository;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $abilitys = $this->abilityTypeRepository->findAll();
        $items = $this->itemRepository->findAll();
        foreach ($abilitys as $ability) {
            $abilityCode[] = $ability->getCode();
            $abilityId[] = $ability->getId();
        }
        foreach ($items as $item) {
            $itemCode[] = $item->getCode();
            $itemId[] = $item->getId();
        }
        $abilityCodeId = array_combine($abilityCode, $abilityId); //склеивание ассоциативного массива из двух массивов
        $itemCodeId = array_combine($itemCode, $itemId); //склеивание ассоциативного массива из двух массивов
        ksort($abilityCodeId);
        ksort($itemCodeId);

        $builder
            ->add('idItem', ChoiceType::class, array( 'choices'  => $itemCodeId ))
            //->add('idItem', EntityType::class, ['class' => Item::class,'choice_label' => 'code','choice_value' => 'id',])
            ->add('code')
            ->add('reloadTime')
            ->add('durationTime')
            ->add('idAbilityType', ChoiceType::class, array( 'choices'  => $abilityCodeId ))
            // ->add('dateCreated',DateType::class)
            // ->add('author')
            // ->add('dateChange',DateType::class)
            // ->add('lastAuthor')
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Ability::class,
        ]);
    }
}

Form/AbilityHolderType.php - 来自 Ability 的表单

<?php

namespace App\Form;

use App\Entity\AbilityHolder;
use App\Form\AbilityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;

class AbilityHolderType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
        ->add('abilitys', CollectionType::class, array(
            'entry_type' => AbilityType::class,
            'entry_options' => array('label' => false),
            'allow_add' => true,
        ))
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => AbilityHolder::class,
        ]);
    }
}

Controller/AbilityHolderController.php - AbilityHolder-collections 表单中的表单

<?php
namespace App\Controller;

use App\Entity\AbilityHolder;
use App\Entity\Ability;
use App\Form\AbilityHolderType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Doctrine\Common\Collections\ArrayCollection;
use App\Controller\EntityManagerInterface;
use App\Repository\AbilityRepository;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;


/**
 * @Route("/ability-holder")
 * @ParamConverter (name ="abilityHolder", Class ="App\Entity\AbilityHolder") 
 */
class AbilityHolderController extends Controller
{

    /**
     * @Route("/edit", name="ability_holder_edit", methods="GET|POST")
     */
    public function edit(Request $request, AbilityHolder $abilityHolder)
    {
        $entityManager = $this->getDoctrine()->getManager();

        $originalAbilitys = new ArrayCollection();

        // Create an ArrayCollection of the current Tag objects in the database
        foreach ($abilityHolder->getAbilitys() as $ability) {
            $originalAbilitys->add($ability);
        }

        $form = $this->createForm(AbilityHolderType::class, $abilityHolder);

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            // remove the relationship between the tag and the Task
            foreach ($originalAbilitys as $ability) {
                if (false === $abilityHolder->getAbilitys()->contains($ability)) {
                    // remove the Task from the Tag
                    $tag->getTasks()->removeElement($abilityHolder);

                    // if it was a many-to-one relationship, remove the relationship like this
                    // $tag->setTask(null);

                    $entityManager->persist($ability);

                    // if you wanted to delete the Tag entirely, you can also do that
                    // $entityManager->remove($tag);
                }
            }

            $entityManager->persist($abilityHolder);
            $entityManager->flush();
            return $this->redirectToRoute('ability_holder_edit');
        }
        var_dump($abilityHolder);

        return $this->render('ability/edit.holder.html.twig', [
            'form' => $form->createView(),
        ]);
    }

    /**
     * @Route("/new", name="ability_new", methods="GET|POST")
     */
    /*public function new(Request $request)
    {
        $abilitys = new AbilityHolder();
        $ability1 = new Ability();
        $ability1->setIdItem('1');
        $ability1->setCode('code');
        $ability1->setReloadTime('1');
        $ability1->setDurationTime('1');
        $ability1->setIdAbilityType('1');

        // dummy code - this is here just so that the Ability has some tags
        // otherwise, this isn't an interesting example
        $abilitys->getAbilitys()->add($ability1);
        // end dummy code

        $form = $this->createForm(AbilityHolderType::class, $abilitys);

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            // ... maybe do some form processing, like saving the Ability and Tag objects
        }

        return $this->render('ability/new-holder.html.twig', array(
            'form' => $form->createView(),
        ));
    }

    /**
     * @Route("/edit", name="ability_edit", methods="GET|POST")
     */
    /*public function edit(Request $request)
{
    $entityManager = $this->getDoctrine()->getManager();

    $originalAbilitys = new ArrayCollection();
    $abilityHolder = new AbilityHolder();

    // Create an ArrayCollection of the current Tag objects in the database
    foreach ($abilityHolder->getAbilitys() as $ability) {
        $originalAbilitys->add($ability);
    }

    $editForm = $this->createForm(AbilityHolderType::class, $abilityHolder);

    $editForm->handleRequest($request);

    if ($editForm->isSubmitted() && $editForm->isValid()) {
        // remove the relationship between the ability and the Task
        foreach ($originalAbilitys as $ability) {
            if (false === $abilityHolder->getAbilitys()->contains($ability)) {
                // remove the Task from the Tag
                $ability->getAbilitys()->removeElement($abilityHolder);

                // if it was a many-to-one relationship, remove the relationship like this
                // $ability->setTask(null);

                $entityManager->persist($ability);

                // if you wanted to delete the Tag entirely, you can also do that
                // $entityManager->remove($ability);
            }
        }

        $entityManager->persist($abilityHolder);
        $entityManager->flush();
    }

        return $this->render('ability/new-holder.html.twig', array(
            'form' => $editForm->createView(),
        ));

    // render some form template
}*/
}

树枝模板

{% extends 'base.html.twig' %}

{% block body %}
    {{ form_start(form) }}

    {% for ability in form.abilitys %}
        <li>{{ form_row(ability.idItem) }}</li>
        <li>{{ form_row(ability.code) }}</li>
        <li>{{ form_row(ability.reloadTime) }}</li>
        <li>{{ form_row(ability.durationTime) }}</li>
        <li>{{ form_row(ability.idAbilityType) }}</li>
    {% endfor %}

    {{ form_end(form) }}
{% endblock %}

错误

将 / {id} 添加到路由后

将 / {id} 添加到路由后

[Semantical Error] The annotation "@Entity" in class App\Entity\AbilityHolder was never imported. Did you maybe forget to add a "use" statement for this annotation? [语义错误] 注释

标签: phpsymfonysymfony4

解决方案


我相信这与表格几乎没有关系。实际上,如果我没记错的话,即使您将整个动作都注释掉,它仍然会中断。

原因是您设置控制器操作的方式:

/**
 * @Route("/ability-holder")
 * @ParamConverter (name ="abilityHolder", Class ="App\Entity\AbilityHolder") 
 */
class AbilityHolderController extends Controller
{

    /**
     * @Route("/edit", name="ability_holder_edit", methods="GET|POST")
     */
    public function edit(Request $request, AbilityHolder $abilityHolder)
    {   

默认情况下,Symfony 将尝试使用类名类型提示来注入服务,但是,由于您指定了$abilityHolder对应于参数转换器的参数,它确实会尝试在您的 URL 中查找标识符(默认情况下是id)。我没有看到id上面的内容,所以你需要指定它。

例如:

@Route("/edit/{id}", name="ability_holder_edit", methods="GET|POST")

上述方法有效吗?

更新:

所以,正如我在下面的评论部分所说,Doctrine不使用构造函数来创建对象的新实例。这会使您的对象(就非托管成员而言)处于半初始化状态。

处理此问题的一种方法是定义一个postLoad侦听器:

/** 
 * @ORM\Entity 
 * @ORM\HasLifecycleCallbacks 
 */
class AbilityHolder
{
...

    /** 
     * @ORM\PostLoad() 
     */
    public function onPostLoad()
    {
        $this->abilitys = new ArrayCollection();
    }    

...
}

推荐阅读