Bonjour à tous,

Cela fait quelques heures que je cherche en vain... j'espère que vous pourrez m'aider !
Alors voilà, je suis en train de créer un back office sur Symfony 4 afin de gérer un Blog & un Forum. Étant donné que ces 2 modules diposent d'une méthode new(), edit() et delete() j'ai voulu créer une classe générique pour éviter de me répéter, voici la classe en question:

<?php
namespace App\Controller\Admin;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class Crud extends AbstractController {
    /**
     * @var string
     */
    protected $entity;

    /**
     * @var string
     */
    protected $repository;

    /**
     * @var string
     */
    protected $formType;

    /**
     * @var string
     */
    protected $csrfTokenName;

    /**
     * @var string
     */
    protected $redirectRoute;

    /**
     * @var string[]
     */
    protected $messages = [
        "item_added" => "L'élément %d a bien été ajouté avec succès",
        "item_edited" => "L'élément %d a bien été modifié avec succès",
        "item_removed" => "L'élément %d a bien été supprimé avec succès"
    ];

    /**
     * @param Request $request
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
     */
    protected function new(Request $request): Response {
        $item = new $this->entity();
        $form = $this->createForm($this->formType, $item);
        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $em->persist($item);
            $em->flush();
            $this->addFlash('success', $this->getFormatMessage("item_added", $item->getId()));
            return $this->redirectToRoute($this->redirectRoute);
        }
        return $this->render("admin/forum/category/new.html.twig", ['form' => $form->createView()]);
    }

    /**
     * @param Request $request
     * @param int $id
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
     */
    protected function edit(Request $request,  int $id): Response {
        $item = $this->getRepository()->find($id);
        if (is_null($item)) {
            return $this->redirectToRoute("home.index", [], 301);
        }
        $form = $this->createForm($this->formType, $item);
        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $em->flush();
            $this->addFlash('success', $this->getFormatMessage("item_edited", $item->getId()));
            return $this->redirectToRoute($this->redirectRoute);
        }
        return $this->render("admin/forum/category/edit.html.twig", [
            'form' => $form->createView(),
            'item' => $item
        ]);
    }

    /**
     * @param Request $request
     * @param int $id
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    protected function delete(Request $request, int $id): RedirectResponse {
        $item = $this->getRepository()->find($id);
        if (is_null($item) || !$this->isCsrfTokenValid($this->csrfTokenName, $request->get("_csrf_token"))) {
            return $this->redirectToRoute("home.index", [], 301);
        } else {
            $em = $this->getDoctrine()->getManager();
            $em->remove($item);
            $em->flush();
            return $this->redirectToRoute($this->redirectRoute);
        }
    }

    private function getRepository() {
        $repository = str_replace("Entity", "Repository", $this->entity) . "Repository";
        return $this->getDoctrine()->getRepository($repository);
    }

    /**
     * @param string $type
     * @param int $value
     * @return string
     */
    private function getFormatMessage(string $type, int $value): string {
        return sprintf($this->messages[$type], $value);
    }
}

Et le but c'est que les classes représentant par exemple les catégories du Forum héritent de celle-ci donc voilà ce que je fais:

<?php
namespace App\Controller\Admin\Forum;

use App\Controller\Admin\Crud;
use App\Entity\ForumCategory;
use App\Form\ForumCategoryType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Annotation\Route;

/**
 * Class AdminForumCategoryController
 * @package App\Controller\Admin\Forum
 * @Route("/admin/gestion-forum/categories")
 */
class AdminForumCategoryController extends Crud {
    /**
     * @var string
     */
    protected $entity = ForumCategory::class;

    /**
     * @var string
     */
    protected $formType = ForumCategoryType::class;

    /**
     * @var string
     */
    protected $csrfTokenName = "delete-forum-category";

    /**
     * @var string
     */
    protected $redirectRoute = "admin.forum.index";

    /**
     * @Route("/new", name="admin.forum.category.new")
     * @param Request $request
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
     */
    public function new(Request $request): Response {
        return parent::new($request);
    }

    /**
     * @Route("/edit/{id}", name="admin.forum.category.edit", requirements={"id": "\d+"})
     * @param Request $request
     * @param int $id
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
     */
    public function edit(Request $request,  int $id): Response {
        return parent::edit($request, $id);
    }

    /**
     * @Route("/delete/{id}", name="admin.forum.category.delete", requirements={"id": "\d+"})
     * @param Request $request
     * @param int $id
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function delete(Request $request, int $id): RedirectResponse {
        return parent::delete($request, $id);
    }
}

Et donc on en arrive à mon problème, lorsque j'actualise ma page je tombe sur cette erreur:

The class 'App\Repository\ForumCategoryRepository' was not found in the chain configured namespaces App\Entity

Sachant qu'avant que je crée la classe générique Crud.php ça fonctionnait très bien je doute que ça vienne de mes entités.
Merci d'avance.

EDIT: la méthode new() fonctionne, c'est uniquement les méthodes edit() et delete() qui elles, nécessitent le repository.

3 réponses


Digivia
Réponse acceptée

Petite remarque au passage, utilises plutôt des getters et setters pour tes propriétés. C'est le principe de ne pas rendre ta classe modifiable, mais plutôt extensible...

private $entity;
public function setEntity($entity)
{
    $this->entity = $entity;
    return $this; // pour chainer les setters
}
public function getEntity()
{
    return $this->entity;
}

Dans ton constructeur

$this->setEntity($entity);

Et dans edit :

$item = $this->getRepository($this->getEntity())->find($id);

Ainsi ta propriété ne peut pas être accessible ou modifiée par une classe héritée autrement que par les getters et setters. Et en cas de modification de $entity (par exemple pour passer le nom d'un repository au lieu du nom de l'entité), la modif ne se fait que dans la classe mère...
Enfin, je mettrais la classe Crud en abstract, comme ça elle ne peut pas être instanciée, juste étendue (ce qui devrait être le cas)...

Salut,
Dans la fonction edit et delete, tu démarres par :

$item = $this->getRepository()->find($id);

Tu ne précises pas le repository (l'entité concernée).

$item = $this->getRepository($this->entity)->find($id);

Serait plus correct dans ton cas, non?

Pour faire du CRUD, il y a un super bundle, easyadmin, créé par Javier Eguiluz, qui bosse chez SensioLabs. Ce bundle est top car il couvre tous les besoins classiques en CRUD, et est super simple à implémenter... A toi de voir, mais pourquoi ré-inventer la roue?

Warzik
Auteur

Salut,
Merci pour ta réponse ça fonctionne, je vais me renseigner à propos du Bundle, je n'y avais pas réellement pensé.
Bonne soirée en tout cas et merci !