Neither the property nor one of the methods exist and have public access in class "SymfonyComponentFormFormView"

I have this error when trying to create new "Category", it works befare, but not now.
Error gives me after modify "Recipes" form for add other relation ManyToMany

Blockquote Neither the property "category" nor one of the methods "category()", "getcategory()"/"iscategory()"/"hascategory()" or "__call()" exist and have public access in class "SymfonyComponentFormFormView".

Category.php

<?php

namespace AppEntity;

use AppRepositoryCategoryRepository;
use DoctrineCommonCollectionsArrayCollection;
use DoctrineCommonCollectionsCollection;
use DoctrineORMMapping as ORM;

/**
 * @ORMEntity(repositoryClass="AppRepositoryCategoryRepository", repositoryClass=CategoryRepository::class)
 */
class Category
{
    /**
     * @ORMId
     * @ORMGeneratedValue
     * @ORMColumn(type="integer")
     */
    private $id;

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

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

    /**
     * Bidirectional (INVERSE SIDE)
     *
     * @ORMManyToMany(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;
    }

}

Recipe.php

<?php

namespace AppEntity;

use AppRepositoryRecipeRepository;
use DoctrineCommonCollectionsArrayCollection;
use DoctrineCommonCollectionsCollection;
use DoctrineORMMapping as ORM;

/**
 * @ORMEntity(repositoryClass=RecipeRepository::class)
 */
class Recipe {

    /**
     * @ORMId
     * @ORMGeneratedValue
     * @ORMColumn(type="integer")
     */
    private $id;

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

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

    /**
     * @ORMColumn(type="datetime")
     */
    private $date;

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

    /**
     * @ORMOneToMany(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 (for new category)

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

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

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

new.html.twig (for new recipe)

{% 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 %}

CategoryType.php

<?php

namespace AppForm;

use AppEntityCategory;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
use SymfonyComponentFormExtensionCoreTypeTextType;
use SymfonyComponentFormExtensionCoreTypeSubmitType;
use SymfonyComponentFormExtensionCoreTypeTextareaType;

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,
        ]);
    }

}

RecipeType.php

<?php

namespace AppForm;

use AppEntityRecipe;
use AppEntityCategory;
use AppFormStepType;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
use SymfonyBridgeDoctrineFormTypeEntityType;
use SymfonyComponentFormExtensionCoreTypeTextType;
use SymfonyComponentFormExtensionCoreTypeSubmitType;
use SymfonyComponentFormExtensionCoreTypeTextareaType;
use SymfonyComponentFormExtensionCoreTypeCollectionType;

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,
        ]);
    }

}

The runtime error references {{ form_widget(form.category) }} from new.html.twig (for Recipes).

Source: Symfony Questions

Was this helpful?

0 / 0

Leave a Reply 0

Your email address will not be published. Required fields are marked *