Afelip
Afelip

Reputation: 61

"Unable to guess how to get a Doctrine instance from the request information for parameter "person_id" while adding this parameter to arguments

I have an error from symfony 4 "Unable to guess how to get a Doctrine instance from the request information for parameter "person_id", already tried options for related questions which I found on stackoverflow, but all of them suggest to solve this with @paramconverter, but this method has something to do with @route, I don't think thats what I need.

Here is the code in controller:

/**
 * @Route("/skill/new/", name="new_skill")
 * Method({"GET", "POST"})
 * @param Request $request
 * @param Person $person_id
 * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
 */
public function new(Request $request, Person $person_id) {
    $skill = new Skill();
    $form = $this->createFormBuilder($skill)
        ->add('name', TextType::class, array('attr' => array('class' => 'form-control')))
        ->add('level', TextareaType::class, array(
            'attr' => array('class' => 'form-control')
        ))
        ->add('save', SubmitType::class, array(
            'label' => 'Create',
            'attr' => array('class' => 'btn btn-primary mt-3')
        ))
        ->getForm();
    $form->handleRequest($request);
    if($form->isSubmitted() && $form->isValid()) {
        $skill = $form->getData();
        $entityManager = $this->getDoctrine()->getManager();
        $person = $entityManager->getRepository(Person::class)->find($person_id);
        $person->addSkill($skill);
        $entityManager->persist($skill);
        $entityManager->persist($person);
        $entityManager->flush();
        return $this->redirectToRoute('skill_list');
    }
    return $this->render('main/new.html.twig', array(
        'form' => $form->createView()
    ));
}

and from Person entity

class Person
{
/**
 * @ORM\Id()
 * @ORM\GeneratedValue()
 * @ORM\Column(type="integer")
 */
private $id;

/**
 * @ORM\Column(type="string", length=255)
 */
private $name;

/**
 * @ORM\OneToMany(targetEntity="App\Entity\Skill", mappedBy="person")
 */
private $skills;

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

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

public function getName(): ?string
{
    return $this->name;
}

public function setName(string $name): self
{
    $this->name = $name;

    return $this;
}

/**
 * @return Collection|Skill[]
 */
public function getSkills(): Collection
{
    return $this->skills;
}

public function addSkill(Skill $skill): self
{
    if (!$this->skills->contains($skill)) {
        $this->skills[] = $skill;
        $skill->setPerson($this);
    }

    return $this;
}

public function removeSkill(Skill $skill): self
{
    if ($this->skills->contains($skill)) {
        $this->skills->removeElement($skill);
        // set the owning side to null (unless already changed)
        if ($skill->getPerson() === $this) {
            $skill->setPerson(null);
        }
    }

    return $this;
}

}

With @paramconverter I wrote id param in route like " * @Route("/skill/new/{id}", name="new_skill", but he is giving another error "No route found for "GET /skill/new"

What I am trying to achieve is that when I create new skill it binds to specific person with specific id, so I made ManyToOne assosiation. So, while Im on route "/person/{{ person.id }}", I need to add skill to this specific id, not everyone.

I guess I made a mistake writing person_id on function arguments, but otherwise it cant find this param in entitymanager. How can I solve this?

Upvotes: 0

Views: 4666

Answers (1)

dbrumann
dbrumann

Reputation: 17166

The problem is inside the Route definition and method signature. Symfony has no way to infer which Person $person_id it should fetch. If you want this to be an entity you should assign a url parameter for the id, e.g.

@Route("/skill/new/{person_id}", name="new_skill")

This will change the URL from http://example.com/skill/new to http://example.com/skill/new/123 where 123 is the id you want to fetch the Person-object for. Now you MUST have a person id in your URL otherwise the route will not match (as you have already noticed). You can make this optional by changing the method signature:

/**
 * @Route("/skill/new/{person_id}", name="new_skill")
 * Method({"GET", "POST"})
 * @param Request $request
 * @param Person $person_id
 * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
 */
public function new(Request $request, Person $person_id = null) {

By allowing null for $person_id the url parameter should be optional, so you should be able to use http://example.com/skill/skill/new or http://example.com/skill/skill/new/123.

If you don't want the entity and only want a way to optionally fetch it from the URL without explicitly specifying route parameter you could just change the code a bit:

/**
 * @Route("/skill/new", name="new_skill")
 * Method({"GET", "POST"})
 * @param Request $request
 * @param Person $person_id
 * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
 */
public function new(Request $request) {
    $person_id = $request->query->get('person_id');
    ...

If you now take your existing URL and add a URL parameter it will be read in your action, e.g. http://example.com/skill/new?person_id=1234 will set $person_id to 1234. When you don't specify the parameter it will be null.

Symfony also has debug commands that help you check what routes there are and whether they match:

bin/console debug:router

bin/console router:match /skill/new

Upvotes: 3

Related Questions