I have an issue with an Entity.
The goal is to display a Form which the user can use to change his personal information like the Email
or password
. I created a form for that, but when I created the /Edit
Route I get the following error:
“AppEntityUsers object not found by the @ParamConverter annotation.”
Here’s my Controller:
<?php namespace AppController; use AppEntityUsers; use AppFormEditProfileType; use AppFormUsersType; use AppRepositoryUsersRepository; use SymfonyBundleFrameworkBundleControllerAbstractController; use SymfonyComponentHttpFoundationRequest; use SymfonyComponentHttpFoundationResponse; use SymfonyComponentRoutingAnnotationRoute; /** * @Route("/users") */ class UsersController extends AbstractController { /** * @Route("/", name="users_index", methods={"GET"}) */ public function index(): Response { return $this->render('users/index.html.twig'); } /** * @Route("/{id}", name="users_show", methods={"GET"}) */ public function show(Users $user): Response { return $this->render('users/show.html.twig', [ 'user' => $user, ]); } /** * @Route("/edit", name="users_edit") */ public function editProfile(Request $request): Response { $user = $this->getUser(); $form = $this->createForm(EditProfileType::class, $user); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->persist($user); $em->flush(); $this->addflash('message', 'Profile mis à jour'); return $this->redirectToRoute('users'); } return $this->render('users/editprofile.html.twig', [ 'form' => $form->createView(), ]); }
Here’s the form:
<?php namespace AppForm; use AppEntityUsers; use SymfonyComponentFormAbstractType; use SymfonyComponentFormExtensionCoreTypeSubmitType; use SymfonyComponentFormFormBuilderInterface; use SymfonyComponentOptionsResolverOptionsResolver; class EditProfileType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('email') ->add('pseudo', TextType::class) ->add('Valider', SubmitType::class) ; } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => Users::class, ]); } }
Here’s the Entity Users:
<?php namespace AppEntity; use AppRepositoryUsersRepository; use DoctrineCommonCollectionsArrayCollection; use DoctrineCommonCollectionsCollection; use DoctrineORMMapping as ORM; use SymfonyBridgeDoctrineValidatorConstraintsUniqueEntity; use SymfonyComponentSecurityCoreUserUserInterface; /** * @ORMEntity(repositoryClass=UsersRepository::class) * @UniqueEntity(fields={"email"}, message="There is already an account with this email") */ class Users implements UserInterface { /** * @ORMId() * @ORMGeneratedValue() * @ORMColumn(type="integer") */ private $id; /** * @ORMColumn(type="string", length=180, unique=true) */ private $email; /** * @ORMColumn(type="json") */ private $roles = []; /** * @var string The hashed password * @ORMColumn(type="string") */ private $password; /** * @ORMOneToMany(targetEntity=Commentaires::class, mappedBy="auteur", orphanRemoval=true) */ private $commentaires; /** * @ORMOneToMany(targetEntity=Notes::class, mappedBy="user", orphanRemoval=true) */ private $note; /** * @ORMColumn(type="string", length=100) */ private $pseudo; public function __construct() { $this->commentaires = new ArrayCollection(); $this->note = new ArrayCollection(); } public function getId(): ?int { return $this->id; } public function getEmail(): ?string { return $this->email; } public function setEmail(string $email): self { $this->email = $email; return $this; } /** * A visual identifier that represents this user. * * @see UserInterface */ public function getUsername(): string { return (string) $this->email; } /** * @see UserInterface */ public function getRoles(): array { $roles = $this->roles; // guarantee every user at least has ROLE_USER $roles[] = 'ROLE_USER'; return array_unique($roles); } public function setRoles(array $roles): self { $this->roles = $roles; return $this; } /** * @see UserInterface */ public function getPassword(): string { return (string) $this->password; } public function setPassword(string $password): self { $this->password = $password; return $this; } /** * @see UserInterface */ public function getSalt() { // not needed when using the "bcrypt" algorithm in security.yaml } /** * @see UserInterface */ public function eraseCredentials() { // If you store any temporary, sensitive data on the user, clear it here // $this->plainPassword = null; } /** * @return Collection|Commentaires[] */ public function getCommentaires(): Collection { return $this->commentaires; } public function addCommentaire(Commentaires $commentaire): self { if (!$this->commentaires->contains($commentaire)) { $this->commentaires[] = $commentaire; $commentaire->setAuteur($this); } return $this; } public function removeCommentaire(Commentaires $commentaire): self { if ($this->commentaires->contains($commentaire)) { $this->commentaires->removeElement($commentaire); // set the owning side to null (unless already changed) if ($commentaire->getAuteur() === $this) { $commentaire->setAuteur(null); } } return $this; } /** * @return Collection|Notes[] */ public function getNote(): Collection { return $this->note; } public function addNote(Notes $note): self { if (!$this->note->contains($note)) { $this->note[] = $note; $note->setUser($this); } return $this; } public function removeNote(Notes $note): self { if ($this->note->contains($note)) { $this->note->removeElement($note); // set the owning side to null (unless already changed) if ($note->getUser() === $this) { $note->setUser(null); } } return $this; } public function getPseudo(): ?string { return $this->pseudo; } public function setPseudo(string $pseudo): self { $this->pseudo = $pseudo; return $this; } }
Here’s the View Editprofile.html.twig:
{% extends 'basefront.html.twig' %} {% block title %} Profile de {{ app.user.pseudo }} {% endblock %} {% block body %} <h1 class="h1 text-center">Modification du profile de {{ app.user.pseudo }}</h1> {{ form(form) }} {% endblock %}
Advertisement
Answer
order matters. especially when routing without requirements. you have the following routes (update: added prefix, thanks @Cerad!):
/users/ users_index /users/{id} users_show /users/edit users_edit
now, when you request the uri /users/edit
the UrlMatcher will iterate over your routes to see if and which matches, stopping on the first match.
/users/
obviously doesn’t match
/users/{id}
does match, since {id}
is allowed to be any alpha-numeric string (+ few extra chars), and edit
matches that, leading to the users_show
route, on which the User
object cannot be determined by the id “edit`. And thus, the error message appears. One way to fix this would be to add requirements to the route matching – assuming the id is numerical only:
/** * @Route("/{id}", ..., requirements={"id":"d+"}) */
I also agree with @Cerad that Users
is a very bad name for an entity. They’re usually in singular form.