Symfony 7 — аргумент, который не удалось разрешить

Я новичок и пытаюсь следовать руководству по созданию блога под Symfony 7, к сожалению, у меня возникает ошибка, когда я хочу отобразить страницу статьи, используя его адрес (слаг). Я получаю:

Контроллеру «App\Controller\ArticleController::show» требуется аргумент «$article», который не удалось разрешить. Невозможно автоматически подключить аргумент $article «App\Controller\ArticleController::show()»: ему нужен экземпляр «App\Entity\Article», но этот тип исключен из «config/services.yaml».

Проблема возникла, когда я внедрил свою сущность Article в функцию show моего ArticleController.

<?php

namespace App\Controller;

use App\Entity\Article;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

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

Когда я использую ссылку с домашней страницы, отображаемый маршрут правильный (с меткой).

Есть идеи, как я могу это исправить? (это должно быть что-то очень простое, но я не понимаю)

Я попытался проверить свой код, особенно объект:

<?php

namespace App\Entity;

use App\Model\TimestampedInterface;
use App\Repository\ArticleRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: ArticleRepository::class)]
class Article implements TimestampedInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

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

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

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

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

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

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

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

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

    #[ORM\ManyToOne]
    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;
    }
}

Вы это поняли, поэтому цель состоит в том, чтобы отобразить страницу статьи:

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

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

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

Большое спасибо,

EDIT1: Вот код домашней страницы, на которой создается ссылка на страницу статьи. Но какой бы пул ни был указан в URL-адресе (существует он или нет в базе данных), сообщение об ошибке одно и то же.

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

Шаблон list.html.twig:

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

элемент.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, у меня есть только одна статья в базе данных, и в ней есть пуля. Обычно пул не может быть нулевым, но если мне удастся пройти этот шаг, я добавлю перенаправление на случай, если статья не будет найдена:

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

На данный момент, если я применю этот код, статья не будет найдена, и мы вернемся на домашнюю страницу (это кажется нормальным, учитывая ошибку, которая у меня была до этого).

EDIT3. Вот HomeController с findAll():

<?php

namespace App\Controller;

use App\Repository\ArticleRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

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

И репозиторий статей:

<?php

namespace App\Repository;

use App\Entity\Article;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

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

EDIT4, я только что проверил, заменив пул на идентификатор статьи в ArticleController:

<?php
  
namespace App\Controller;
  
use App\Entity\Article;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
  
class ArticleController extends AbstractController
{
    #[Route('/article/{id}', name: 'article_show')]
    public function show(Article $article): Response
    {
        return $this->render('article/show.html.twig', [
            'article' => $article,
        ]);
    }
}

Это прекрасно работает с использованием URL-адреса, содержащего идентификатор статьи, что заставляет меня думать, что мой код хорош. Однако до сих пор нет решения, позволяющего заставить его работать с пулей.

покажите код, в котором вы генерируете ссылку на страницу статьи.

craigh 03.09.2024 13:41

Код добавлен в мой исходный пост, спасибо.

Roch 03.09.2024 14:13

Итак, вы предполагаете, что slug не имеет значения null при создании ссылки, но slug имеет значение null в вашей сущности. Я предполагаю, что в вашей базе данных есть хотя бы одна запись статьи, где slug имеет значение null, и в этом проблема. Убедитесь, что все ваши пули не являются нулевыми и уникальными.

craigh 03.09.2024 14:16

Когда вы выгружаете статьи, у вас есть слаг для всех ваших статей?

MoWiz 03.09.2024 14:31

У меня есть только одна статья в базе данных, и в ней есть пул. Обычно пул не может быть нулевым. (Я отредактировал свое сообщение для получения дополнительной информации)

Roch 03.09.2024 14:58

в вашем домашнем шаблоне, я думаю, ваши переменные статьи взяты из метода findAll() вашего репозитория статей?

MoWiz 03.09.2024 15:13

Да, абсолютно. Пост отредактирован с кодами.

Roch 03.09.2024 15:40

В вашем EDIT2: у вас есть общедоступный метод show(), и вы вводите объектную статью... Каков маршрут этого метода? Используете ли вы параметр маршрута для определения объекта?

MoWiz 03.09.2024 15:56

Это был просто ответ Крейгу, чтобы проиллюстрировать, что я буду делать (после того, как проблема будет решена), чтобы перенаправить в случае, если статья не будет найдена. На данный момент используемый код является самым первым в моем сообщении. (Я полностью отредактировал EDIT2 с функцией, чтобы было понятнее)

Roch 03.09.2024 16:17

И когда вы заменяете метод show кодом, который я предоставил в своем ответе, у вас все равно возникает первоначальная ошибка (невозможно автоматически подключить $article...)?

MoWiz 03.09.2024 16:43

Я только что перепроверил, та же проблема. Я создал вторую статью, та же проблема. Какой бы пул не указан в URL-адресе (существует он или нет в базе данных), проблема та же.

Roch 03.09.2024 16:53

Извините, но со всеми этими элементами я действительно не понимаю, как у вас все еще может быть та же ошибка :(

MoWiz 03.09.2024 17:42

Я тоже, но большое спасибо за вашу помощь и время.

Roch 03.09.2024 17:52

К вашему сведению, это отлично работает, если заменить пул на идентификатор статьи (см. EDIT4 в моем посте).

Roch 03.09.2024 20:26
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
14
82
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете использовать атрибут MapEntity для решения вашей проблемы следующим образом:

<?php

namespace App\Controller;

use App\Entity\Article;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

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

Атрибут mapEntity позволит Symfony найти объект с пулем, который у вас есть в параметре маршрута.

Спасибо за ваш ответ. К сожалению, я получаю ту же ошибку. Что бы ни указывал пуля (существует ли она в базе данных или нет), та же ошибка.

Roch 03.09.2024 13:28

Как сказал @craig, покажите нам код, в котором вы генерируете ссылку, потому что я не понимаю, как у вас все еще может быть та же ошибка.

MoWiz 03.09.2024 14:03

Я только что отредактировал свой первый пост, спасибо.

Roch 03.09.2024 14:16

Ошибка говорит

«нужен экземпляр «App\Entity\Article», но этот тип был исключен в "config/services.yaml""

Так что я думаю, что есть некоторые

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

в этом файле, поэтому, если вы удалили эту строку...

Сущности не являются службами и не должны автоматически подключаться.

craigh 03.09.2024 17:31

Спасибо за ваш ответ. Я уже исследовал этот путь. Действительно, если я изменю файл Services.yaml, я выдаю ошибку, но моя статья не появляется. На самом деле это не решает проблему, и (даже если я новичок) я согласен в этом с Крейгом.

Roch 03.09.2024 17:49
Ответ принят как подходящий

После нескольких часов борьбы и изучения я нашел простое решение: просто установите значение «true» для «auto_mapping» в файле Doctor.yaml:

orm:
        auto_generate_proxy_classes: true
        enable_lazy_ghost_objects: true
        report_fields_where_declared: true
        validate_xml_mapping: true
        naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
        auto_mapping: true

рад слышать, что вы нашли свое решение. Я не учел этого, потому что предполагал, что вы использовали бы композитор и flex для установки доктрины, которая бы установила это значение в своем рецепте. В будущем подумайте о композиторе и flex, вам будет легче добиться успеха.

craigh 03.09.2024 23:13

Спасибо, кажется, я установил композитор (насчет flex не помню). Но так как я новичок, есть некоторые очевидные вещи, которые не сразу приходят в голову.

Roch 03.09.2024 23:25

Другие вопросы по теме