Stuart Brown
Stuart Brown

Reputation: 987

Unable to guess how to get a Doctrine instance

I have been through the process of updating my project from Symfony 2.8 to Symfony 3 and am now reworking one of my controllers but am having some trouble.

I have a Child entity and controller. The controller has a base route

/**
* Child controller.
*
* @Route("/profile/{parentName}/{childName}")
*/

with an action

/**
 * Finds and displays a Child entity.
 *
 * @Route("/{id}", name="child_show")
 * @Method("GET")
 * @Template()
 */
public function showAction(Child $child)
{
    $deleteForm = $this->createDeleteForm($child);

    return array(
        'child' => $child,
        'delete_form' => $deleteForm->createView(),
    );
}

However I don't want the page URL to be domain.com/parentname/childname/id I want it to be domain.com/parentname/childname

In 2.8 my controller was

/**
* Child controller.
*
* @Route("/profile/{parentName}/{childName}")
*/


/**
 * Finds and displays a Child entity.
 *
 * @Route("/", name="child_show")
 * @Method("GET")
 * @Template()
 */
public function showAction($childName)
{

    $em = $this->getDoctrine()->getManager();
    $entity = $em->getRepository('AppBundle:Child')->findOneByName($childName);
    if (!$entity) {
        throw $this->createNotFoundException('Unable to find Child entity.');
    }

    $deleteForm = $this->createDeleteForm($childName);

    return array(
        'entity'      => $entity,
        'delete_form' => $deleteForm->createView(),
    );
}

and it worked as I wanted.

However if in my updated controller I modify the route annotation on showAction to

/**
* @Route("/", name="child_show")
* @Method("GET")
* @Template()
*/

I get an error Unable to guess how to get a Doctrine instance from the request information. I guess because the Id is needed to get the correct instance of Child? However childName ($name) in the Child entity is also unique.

I'm a bit stuck with this. Can anyone advise what I am doing wrong? I want to be able to have a route for the child profile page that doesn't contain the ID but uses the child's name to return the info required.

Update in response to some comments / questions - below is my Child entity

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use AppBundle\Entity\User;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

/**
* Child
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="AppBundle\Entity\ChildRepository")
* @UniqueEntity("name")
*/

class Child
{

/**
 * @var integer
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

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

/*
* Todo - figure out how to use a date type for dateofbirth below and as well in users.yml fixtures file
*/

/**
 * @var \DateTime
 *
 * @ORM\Column(name="date_of_birth", type="datetime")
 */
private $dateOfBirth;

//todo find ot why the parent variable here and Id variable in AppBundle:User are not mapped correctly
/**
 * @ORM\ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="id")
 * @ORM\JoinColumn(onDelete="CASCADE")
 */

private $parent;

/**
 * @return User
 */
public function getParent()
{
    return $this->parent;
}

/**
 * @param User $parent
 */
public function setParent(User $parent)
{
    $this->parent = $parent;
}


/**
 * Get id
 *
 * @return integer
 */
public function getId()
{
    return $this->id;
}

/**
 * Set name
 *
 * @param string $name
 *
 * @return Child
 */
public function setName($name)
{
    $this->name = $name;

    return $this;
}

/**
 * Get name
 *
 * @return string
 */
public function getName()
{
    return $this->name;
}


/**
 * Set dateOfBirth
 *
 * @param \DateTime $dateOfBirth
 *
 * @return Child
 */
public function setDateOfBirth($dateOfBirth)
{
    $this->dateOfBirth = $dateOfBirth;

    return $this;
}

/**
 * Get dateOfBirth
 *
 * @return \DateTime
 */
public function getDateOfBirth()
{
    return $this->dateOfBirth;
}
}

and modified the showAction to

/**
* Child controller.
*
* @Route("/profile/{parentName}/{name}")
*/

/**
 * Finds and displays a Child entity.
 *
 * @Route("/", name="child_show")
 * @Method("GET")
 * @Template()
 */
public function showAction(Child $name)
{
    $deleteForm = $this->createDeleteForm($name);

    return array(
        'child' => $name,
        'delete_form' => $deleteForm->createView(),
    );
}

but I now get an error AppBundle\Entity\Child object not found.

ERROR - Uncaught PHP Exception  

Symfony\Component\HttpKernel\Exception\NotFoundHttpException:     "AppBundle\Entity\Child object not found." at /Library/WebServer/Documents/story-project/app/cache/dev/classes.php line 7183 

Upvotes: 1

Views: 8865

Answers (4)

Sonia Ch
Sonia Ch

Reputation: 1

@Dan Costinel

I got the same error message, but I guess, in a little bit different way than yours.

"Unable to generate a URL for the named route "users_delete" as such route does not exist"

I had forgotten to add the requested {variable} in the route in the routing.yml

users_delete:
    path:     /{id}/modifier
    defaults: { _controller: ReservationBundle:Users:modifier }

Thats why my controller couldn't access the information expected in the $request variable:

public function modifierAction(Request $request, Users $user) {}

But that's your anwer that put me in the right track:

That {name} in the @Route changed everything. I mean that was causing the error. So I needed to make sure that instead of {name} I should've have {title} as in the 'title' part of my anchor element ({ 'title': article.title|lower }).

Upvotes: 0

Dan Costinel
Dan Costinel

Reputation: 1736

I got the same error message, but I guess, in a bit different situation than yours.

I needed to render a particular article in its own twig template. So I had:

<a href="{{ path('article_show', { 'title': article.title|lower }) }}" class="pull-right">Read More</a>, where 'title' was the name of a column in a table.

But in my controller class I had:

/**
 * @Route("/article/{name}", name="article_show")
 * @Method("GET")
 */
public function showAction(Article $article)
{
    return $this->render('AppBundle:Article:show.html.twig', ['article'=>$article]);
}

That {name} in the @Route changed everything. I mean that was causing the error. So I needed to make sure that instead of {name} I should've have {title} as in the 'title' part of my anchor element ({ 'title': article.title|lower }).

Upvotes: 0

Terenoth
Terenoth

Reputation: 2588

The entity is not found, because Doctrine can't find a Child with name {name} and parentName parentName... Which seems legit, since parentName does not exist in the Child entity.

Two solutions :

1 - Never tried, but might work : create getParentName() in Child and make it return $this->parent->getName()

2 - Add a ParamConverter annotation on the top of your Controller Action:

/**
 * Finds and displays a Child entity.
 *
 * @Route("/", name="child_show")
 * @Method("GET")
 * @Template()
 * @ParamConverter("child", class="AppBundle:Child", options={"name" = "name"})
 */
 public function showAction(Child $child)

This way, the Converter will only consider the argument "name" when attempting to retrieve your Child entity... (is that really what you want by the way?) Don't forget to use ParamConverter in your Controller.

Upvotes: 1

Wilt
Wilt

Reputation: 44316

If you have unique fields you should mark them as unique in your entity definitions:

In the @column annotation:

/**
 * @Column(type="string", length=32, unique=true, nullable=false)
 */
protected $name;

Or for other unique constraints:

/**
 * @entity
 * @Table(name="child", uniqueConstraints{   
 *     @UniqueConstraint(name="name_something_unique", columns = {"name", "something"})
 * })
 */

If you add the @column unique constraint for name then your findOneByName will probably work as expected.

You can for now also make it work like this:

$childs = $em->getRepository('AppBundle:Child')->findBy( array('name' => $childName ));

Then you get a Collection and if you are sure it is unique you can simply get the first element if the collection is not empty...

if( ! $childs->isEmpty() ){

    $first = $childs->first();

}

Upvotes: 0

Related Questions