skip to Main Content

I’m a beginner and I’m trying to follow a tutorial to create a blog under Symfony 7, unfortunately I find myself stuck with an error when I want to display the page of an article using his address (slug) I get:

Controller "AppControllerArticleController::show" requires the "$article" argument that could not be resolved. Cannot autowire argument $article of "AppControllerArticleController::show()": it needs an instance of "AppEntityArticle" but this type has been excluded in "config/services.yaml".

It was when I injected my Article entity into the show function of my ArticleController that the problem occurred.

<?php

namespace AppController;

use AppEntityArticle;
use SymfonyBundleFrameworkBundleControllerAbstractController;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentRoutingAttributeRoute;

class ArticleController extends AbstractController
{
    #[Route('/article/{slug}', name: 'article_show')]
    public function show(Article $article): Response
    {     
        return $this->render('article/show.html.twig', [
            'article' => $article,
        ]);
    }
}

When I use the link from the home page, the route displayed is correct (with the slug).

Any idea how I could fix this? (it must be something really simple but I don’t understand)

I tried to check my code, especially the entity:

<?php

namespace AppEntity;

use AppModelTimestampedInterface;
use AppRepositoryArticleRepository;
use DoctrineCommonCollectionsArrayCollection;
use DoctrineCommonCollectionsCollection;
use DoctrineDBALTypesTypes;
use DoctrineORMMapping as ORM;

#[ORMEntity(repositoryClass: ArticleRepository::class)]
class Article implements TimestampedInterface
{
    #[ORMId]
    #[ORMGeneratedValue]
    #[ORMColumn]
    private ?int $id = null;

    #[ORMColumn(length: 255)]
    private ?string $title = null;

    #[ORMColumn(length: 255)]
    private ?string $slug = null;

    #[ORMColumn(type: Types::TEXT, nullable: true)]
    private ?string $content = null;

    #[ORMColumn(length: 100, nullable: true)]
    private ?string $featuredText = null;

    #[ORMColumn(type: Types::DATETIME_MUTABLE)]
    private ?DateTimeInterface $createdAt = null;

    #[ORMColumn(type: Types::DATETIME_MUTABLE, nullable: true)]
    private ?DateTimeInterface $updatedAt = null;

    /**
     * @var Collection<int, Category>
     */
    #[ORMManyToMany(targetEntity: Category::class, mappedBy: 'articles')]
    private Collection $categories;

    /**
     * @var Collection<int, Comment>
     */
    #[ORMOneToMany(targetEntity: Comment::class, mappedBy: 'article', orphanRemoval: true)]
    private Collection $comments;

    #[ORMManyToOne]
    private ?Media $featuredImage = null;

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

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

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

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

        return $this;
    }

    public function getSlug(): ?string
    {
        return $this->slug;
    }

    public function setSlug(string $slug): static
    {
        $this->slug = $slug;

        return $this;
    }

    public function getContent(): ?string
    {
        return $this->content;
    }

    public function setContent(?string $content): static
    {
        $this->content = $content;

        return $this;
    }

    public function getFeaturedText(): ?string
    {
        return $this->featuredText;
    }

    public function setFeaturedText(?string $featuredText): static
    {
        $this->featuredText = $featuredText;

        return $this;
    }

    public function getCreatedAt(): ?DateTimeInterface
    {
        return $this->createdAt;
    }

    public function setCreatedAt(DateTimeInterface $createdAt): static
    {
        $this->createdAt = $createdAt;

        return $this;
    }

    public function getUpdatedAt(): ?DateTimeInterface
    {
        return $this->updatedAt;
    }

    public function setUpdatedAt(?DateTimeInterface $updatedAt): static
    {
        $this->updatedAt = $updatedAt;

        return $this;
    }

    /**
     * @return Collection<int, Category>
     */
    public function getCategories(): Collection
    {
        return $this->categories;
    }

    public function addCategory(Category $category): static
    {
        if (!$this->categories->contains($category)) {
            $this->categories->add($category);
            $category->addArticle($this);
        }

        return $this;
    }

    public function removeCategory(Category $category): static
    {
        if ($this->categories->removeElement($category)) {
            $category->removeArticle($this);
        }

        return $this;
    }

    /**
     * @return Collection<int, Comment>
     */
    public function getComments(): Collection
    {
        return $this->comments;
    }

    public function addComment(Comment $comment): static
    {
        if (!$this->comments->contains($comment)) {
            $this->comments->add($comment);
            $comment->setArticle($this);
        }

        return $this;
    }

    public function removeComment(Comment $comment): static
    {
        if ($this->comments->removeElement($comment)) {
            // set the owning side to null (unless already changed)
            if ($comment->getArticle() === $this) {
                $comment->setArticle(null);
            }
        }

        return $this;
    }

    public function getFeaturedImage(): ?Media
    {
        return $this->featuredImage;
    }

    public function setFeaturedImage(?Media $featuredImage): static
    {
        $this->featuredImage = $featuredImage;

        return $this;
    }
}

You understood it, the goal is therefore to display the article page :

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

{% block title %}{{ article.title }}{% endblock %}

{% block body %}
    <div class="container">
        <h1>{{ article.title }}</h1>
        <hr>
        {{ article.content|raw }}
    </div>
{% endblock %}

Many thanks,

EDIT1: Here is the code of the home page where the link to the article page is generated. But whatever the slug indicated in the url (whether it exists or not in the database), the error message is the same.

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

{% block title %}Accueil{% endblock %}

{% block body %}
        <main class="container">
            <div class="row">
                <div class="col-md-8">
                    <h3 class="pb-4 mb-4 fst-italic border-bottom">Articles récents</h3>
                    <div id="articles-list">
                        {% include 'article/list.html.twig' with { articles } %}
                    </div>
                </div>
                <div class="col-md-4">
                    <div class="position-sticky" style="top: 2rem;">
                        {% include 'widget/about.html.twig' %}
                        {% include 'widget/categories.html.twig' %}
                    </div>
                </div>
            </div>
        </main>
{% endblock %}

The template list.html.twig :

{% for article in articles %}
    {% include 'article/item.html.twig' with { article } %}
{% endfor %}

item.html.twig :

{% set article_show = path('article_show', { 'slug': article.slug }) %}

<article class="mb-5">
    <div class="row">
        <div class="col-md-5">
            {% if article.featuredImage %}
                    <a href="{{ article_show }}">
                        <img src="/uploads/{{ article.featuredImage.filename }}" alt="{{ article.featuredImage.altText }}" loading="lazy" width="350" height="205">
                    </a>
            {% endif %}
        </div>
        <div class="col-md-7">
            <h2>
                <a class="text-decoration-none" href="{{ article_show }}">{{ article.title }}</a>
            </h2>
            <p>
                {{ article.createdAt|date('d M Y') }}
            </p>
            {{ article.featuredText ?: article.content|striptags|slice(0, 130) ~ '...' }}
        </div>
    </div>
</article>

EDIT2, I only have one article in the database and it does have a slug. Normally the slug cannot be null, but if I manage to get past this step, I will add a redirection in case the article cannot be found:

class ArticleController extends AbstractController
{
    #[Route('/article/{slug}', name: 'article_show')]
    public function show(?Article $article): Response
    {
        if (!$article) {
            return $this->redirectToRoute('app_home');
        }
        
        return $this->render('article/show.html.twig', [
            'article' => $article,
        ]);
    }
}

For the moment if I apply this code, the article is not found and we return to the home page (this seems normal considering the error I had before that).

EDIT3, Here is the HomeController with the findAll():

<?php

namespace AppController;

use AppRepositoryArticleRepository;
use SymfonyBundleFrameworkBundleControllerAbstractController;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentRoutingAttributeRoute;

class HomeController extends AbstractController
{
    #[Route('/', name: 'app_home')]
    public function index(ArticleRepository $articleRepo): Response
    {
        return $this->render('home/index.html.twig', [
            'articles' => $articleRepo->findAll()
        ]);
    }
}

And ArticleRepository :

<?php

namespace AppRepository;

use AppEntityArticle;
use DoctrineBundleDoctrineBundleRepositoryServiceEntityRepository;
use DoctrinePersistenceManagerRegistry;

class ArticleRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Article::class);
    }
}

2

Answers


  1. You can use the MapEntity attribute to solve your problem this way :

    <?php
    
    namespace AppController;
    
    use AppEntityArticle;
    use SymfonyBundleFrameworkBundleControllerAbstractController;
    use SymfonyComponentHttpFoundationResponse;
    use SymfonyComponentRoutingAttributeRoute;
    
    class ArticleController extends AbstractController
    {
        #[Route('/article/{slug}', name: 'article_show')]
        public function show(#[MapEntity(mapping:['slug'=>'slug'])]Article $article): Response
        {     
            return $this->render('article/show.html.twig', [
                'article' => $article,
            ]);
        }
    }
    

    The mapEntity attribute will allow Symfony to find the entity with the slug you have in your route parameter.

    Login or Signup to reply.
  2. The error say

    "it needs an instance of "AppEntityArticle" but this type has been
    excluded in "config/services.yaml""

    So I guess there is some

    App:
        resource: '../src/'
        exclude:
            - ...
            - '../src/Entity/'
    

    in this file, so if you removed that line …

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search