首页 > 解决方案 > 该属性和方法之一都不存在并且在类“Symfony\Component\Form\FormView”中具有公共访问权限

问题描述

尝试创建新的“类别”时出现此错误,它可以正常工作,但现在不行。修改“食谱”表单以添加其他关系 ManyToMany 后出现错误

Blockquote 属性 "category" 和方法 "category()"、"getcategory()"/"iscategory()"/"hascategory()" 或 "__call()" 都不存在并且在类 "Symfony" 中具有公共访问权限\组件\窗体\窗体视图”。

分类.php

<?php

namespace App\Entity;

use App\Repository\CategoryRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\CategoryRepository", repositoryClass=CategoryRepository::class)
 */
class Category
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

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

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

    /**
     * Bidirectional (INVERSE SIDE)
     *
     * @ORM\ManyToMany(targetEntity=Recipe::class, mappedBy="category")
     */
    private $recipes;

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

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

    public function getTitle(): ?string
    {
        return $this->title;
    }

    public function setTitle(string $title): self
    {
        $this->title = $title;

        return $this;
    }

    public function getDescription(): ?string
    {
        return $this->description;
    }

    public function setDescription(?string $description): self
    {
        $this->description = $description;

        return $this;
    }

    /**
     * @return Collection|Recipe[]
     */
    public function getRecipes(): Collection
    {
        return $this->recipes;
    }

    public function addRecipe(Recipe $recipe): self
    {
        if (!$this->recipes->contains($recipe)) {
            $this->recipes[] = $recipe;
            $recipe->addCategory($this);
        }

        return $this;
    }

    public function removeRecipe(Recipe $recipe): self
    {
        if ($this->recipes->removeElement($recipe)) {
            $recipe->removeCategory($this);
        }

        return $this;
    }
    
    public function __toString()
    {
        return $this->title;
    }

}

食谱.php

<?php

namespace App\Entity;

use App\Repository\RecipeRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=RecipeRepository::class)
 */
class Recipe {

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

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

    /**
     * @ORM\Column(type="string", length=1000)
     */
    private $description;

    /**
     * @ORM\Column(type="datetime")
     */
    private $date;

    /**
     * Bidirectional - Many recipes have Many categories (OWNING SIDE)
     *
     * @ORM\ManyToMany(targetEntity=Category::class, inversedBy="recipes")
     */
    private $category;

    /**
     * @ORM\OneToMany(targetEntity=Step::class, mappedBy="recipe", cascade={"persist"})
     */
    private $steps;

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

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

    public function getTitle(): ?string {
        return $this->title;
    }

    public function setTitle(string $title): self {
        $this->title = $title;

        return $this;
    }

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

    public function setDate(\DateTimeInterface $date): self {
        $this->date = $date;

        return $this;
    }

    public function getDescription(): ?string {
        return $this->description;
    }

    public function setDescription(string $description): self {
        $this->description = $description;

        return $this;
    }

    /**
     * @return Collection|Category[]
     */
    public function getCategory(): Collection {
        return $this->category;
    }

    public function addCategory(Category $category): self {
        if (!$this->category->contains($category)) {
            $this->category[] = $category;
        }

        return $this;
    }

    public function removeCategory(Category $category): self {
        $this->category->removeElement($category);

        return $this;
    }
    
    public function hasCategory() {
        if($this->category->isEmpty()){
            return true;
        }
        return false;
    }

    /**
     * @return Collection|Step[]
     */
    public function getSteps(): Collection {
        return $this->steps;
    }

    public function addStep(Step $step): self {
        if (!$this->steps->contains($step)) {
            $this->steps[] = $step;
            $step->setRecipe($this);
        }

        return $this;
    }

    public function removeStep(Step $step): self {
        if ($this->steps->removeElement($step)) {
            // set the owning side to null (unless already changed)
            if ($step->getRecipe() === $this) {
                $step->setRecipe(null);
            }
        }

        return $this;
    } 
    
    public function __toString() {
        return $this->title;
    }

}

new.html.twig(用于新类别)

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

{% block title %}New Category{% endblock %}

{% block body %}
    <h1>New Category</h1>
    {{ form(form) }}
{% endblock %}

new.html.twig(用于新配方)

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

{% block title %}New Recipe{% endblock %}

{% block body %}
    <h1>New Recipe</h1>
    {{ form_start(form) }}
    {{ form_row(form.title) }}
    {{ form_row(form.description) }}
    {{ form_widget(form.category) }}
    
    <ul class="steps list-unstyled"  data-prototype="{{ form_widget(form.steps.vars.prototype)|e }}">
        
        {{ form_end(form) }}
    {% endblock %}
    {% block javascripts %}

        <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
        <script>

            var $collectionHolder;

            // setup an "add a tag" link
            var $saveButton = $('#recipe_save');
            var $addStepButton = $('<button type="button" class="add_step_link btn btn-secondary btn-sm mb-2 mt-2">Add Step</button>');
            var $newLinkLi = $('<li></li>');

            jQuery(document).ready(function () {
                
                // Get the ul that holds the collection of tags
                $collectionHolder = $('ul.steps');
                $collectionHolder.before($addStepButton);

                // add the "add a tag" anchor and li to the tags ul
                $collectionHolder.append($newLinkLi);

                // count the current form inputs we have (e.g. 2), use that as the new
                // index when inserting a new item (e.g. 2)
                $collectionHolder.data('index', $collectionHolder.find('input').length);

                $addStepButton.on('click', function (e) {
                    // add a new tag form (see next code block)
                    addStepForm($collectionHolder, $newLinkLi);
                    jQuery('form').append($saveButton);
                });
            });
            function addStepForm($collectionHolder, $newLinkLi) {
                // Get the data-prototype explained earlier
                var prototype = $collectionHolder.data('prototype');

                // get the new index
                var index = $collectionHolder.data('index');

                var newForm = prototype;
                // You need this only if you didn't set 'label' => false in your tags field in TaskType
                // Replace '__name__label__' in the prototype's HTML to
                // instead be a number based on how many items we have
                // newForm = newForm.replace(/__name__label__/g, index);

                // Replace '__name__' in the prototype's HTML to
                // instead be a number based on how many items we have
                newForm = newForm.replace(/__name__/g, index);

                // increase the index with one for the next item
                $collectionHolder.data('index', index + 1);

                // Display the form in the page in an li, before the "Add a tag" link li
                var $newFormLi = $('<div></div>').append(newForm);
                // also add a remove button, just for this example
                $newFormLi.prepend('<a href="#" class="remove-tag btn btn-outline-danger btn-sm float-right mb-1">X</a>');
                $newLinkLi.before($newFormLi);

                // handle the removal, just for this example
                $('.remove-tag').click(function (e) {
                    e.preventDefault();

                    $(this).parent().remove();

                    return false;
                });
            }
        </script>
    {% endblock %}

类别类型.php

<?php

namespace App\Form;

use App\Entity\Category;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;

class CategoryType extends AbstractType {

    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder
                ->add('title', TextType::class, [
                    'label' => 'Category Title:',
                    'attr' => [
                        'class' => 'form-control',
                        'placeholder' => 'Category Title'
                    ]
                ])
                ->add('description', TextareaType::class, [
                    'label' => 'Category Description:',
                    'attr' => [
                        'class' => 'form-control',
                        'placeholder' => 'Category Description'
                    ]
                ])
                ->add('save', SubmitType::class, [
                    'attr' => [
                        'class' => 'btn btn-primary btn-block'
                    ]
                ])
        ;
    }

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

}

食谱类型.php

<?php

namespace App\Form;

use App\Entity\Recipe;
use App\Entity\Category;
use App\Form\StepType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;

class RecipeType extends AbstractType {

    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder
                ->add('title', TextType::class, [
                    'attr' => [
                        'class' => 'form-control',
                        'placeholder' => 'Recipe Title'
                    ]
                ])
                ->add('description', TextareaType::class, [
                    'attr' => [
                        'class' => 'form-control',
                        'placeholder' => 'Recipe Description'
                    ]
                ])
                ->add('category', EntityType::class, [
                    'class' => Category::class,
                    'multiple' => true,
                    'mapped' => true,
                    'required' => false,
                    'attr' => [
                        'class' => 'form-control',
                    ]
                ])
                ->add('steps', CollectionType::class, [
                    'label' => false,
                    'entry_type' => StepType::class,
                    'entry_options' => ['label' => false],
                    'allow_add' => true,
                    'by_reference' => false,
                ])
                ->add('save', SubmitType::class, [
                    'attr' => [
                        'id' => 'save',
                        'class' => 'btn btn-success btn-block'
                    ]
                ])
        ;
    }

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

}

运行时错误引用来自 new.html.twig 的 {{ form_widget(form.category) }}(用于食谱)。

标签: phpsymfonyormtwighttp-status-code-500

解决方案


我的怀疑是错误与控制器或路由器结合在一起。您确定在 Controller 操作中创建一个新类别,您正在渲染类别页面(类似于

        return $this->render('category/new.html.twig');

而不是错误的食谱页面?所以它正在尝试查找form.category,但该类别本身没有类别。


推荐阅读