Skip to content

Instantly share code, notes, and snippets.

@winzou
Created March 21, 2012 09:12
Show Gist options
  • Save winzou/2145734 to your computer and use it in GitHub Desktop.
Save winzou/2145734 to your computer and use it in GitHub Desktop.
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\DoctrineBundle\Registry;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\Mapping\MappingException;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* DoctrineConverter.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DoctrineParamConverter implements ParamConverterInterface
{
protected $registry;
public function __construct(Registry $registry = null)
{
$this->registry = $registry;
}
public function apply(Request $request, ConfigurationInterface $configuration)
{
$class = $configuration->getClass();
$options = $this->getOptions($configuration);
$repository = $this->registry->getRepository($class, $options['entity_manager']);
$metadata = $this->registry->getEntityManager($options['entity_manager'])->getClassMetadata($class);
// name of the parameter in the route
if (isset($options['parameter'])) {
$parameter = $options['parameter'];
} elseif (isset($options['attribute'])) {
$parameter = $options['attribute']; // if we use an attribute say "name" in the objet, we may expect a route parameter called "name"
} else {
$parameter = 'id'; // default
}
if (!$request->attributes->has($parameter)) {
throw new \LogicException(sprintf('Parameter "%s" undefined in the request attributes, unable to guess how to get a Doctrine instance.', $parameter));
}
// method to use to retrieve object from the repository
if (isset($options['method'])) {
$method = $options['method'];
// we check if the defined method exists
$rc = new \ReflectionClass($repository);
if (false === $rc->hasMethod($method)) {
throw new \LogicException(sprintf('Method "%s" undefined in the repository %s, unable to guess how to get a Doctrine instance.', $method, $rc->getName()));
}
// we call the method
$object = call_user_func(array($repository, $method), $request->attributes->get($parameter), $options);
if (get_class($object) !== $metadata->getName()) {
throw new \LogicException(sprintf('Method %s::%s must return a instance of %s, unable to guess how to get a Doctrine instance.', $rc->getName(), $method, $class));
}
} else {
// name of the attribute that should match the route parameter
if (isset($options['attribute'])) {
$attribute = $options['attribute'];
if (false === $metadata->hasField($attribute)) {
throw new \LogicException(sprintf('Attribute "%s" undefined in %s class, unable to guess how to get a Doctrine instance', $attribute, $class));
}
$object = $repository->findOneBy(array($attribute => $request->attributes->get($parameter)));
} else {
// option "attribute" not defined, we try to consider the parameter as the id
$object = $repository->find($request->attributes->get($parameter));
}
}
if (null === $object && false === $configuration->isOptional()) {
throw new NotFoundHttpException(sprintf('%s object not found.', $class));
}
$request->attributes->set($configuration->getName(), $object);
}
public function supports(ConfigurationInterface $configuration)
{
if (null === $this->registry) {
return false;
}
if (null === $configuration->getClass()) {
return false;
}
$options = $this->getOptions($configuration);
// Doctrine Entity?
try {
$this->registry->getEntityManager($options['entity_manager'])->getClassMetadata($configuration->getClass());
return true;
} catch (MappingException $e) {
return false;
}
}
protected function getOptions(ConfigurationInterface $configuration)
{
return array_replace(array(
'entity_manager' => null,
), $configuration->getOptions());
}
}

Usage:

  1. Simple usage, without annotation:

    /**
     * @Route("/blog/{id}")
     * -- This annotation is optionnal, this is the default value:
     * @ParamConverter("post", class="SensioBlogBundle:Post")
     */
    public function showAction(Post $post)
    {
    }
    
    ... in the code:
    $id = $request->attributes->get("id");
    $this->registry->getRepository($class, $options['entity_manager'])->find($id);
    
  2. A bit more advanced, if the name of the route parameter isn't "id" (usefull for multiple converter in the same route):

    /**
     * @Route("/blog/{id}/{id_tag}")
     * @ParamConverter("post", class="SensioBlogBundle:Post")
     * @ParamConverter("tag", class="SensioBlogBundle:Tag", options={"parameter" = "id_tag"})
     */
    public function showAction(Post $post, Tag $tag)
    {
    }
    
    ... in the code:
    $id = $request->attributes->get("id");
    $this->registry->getRepository($class, $options['entity_manager'])->find($id);
    // then
    $id = $request->attributes->get("id_tag");
    $this->registry->getRepository($class, $options['entity_manager'])->find($id);
    
  3. If you want to use an explicit attribute to map the object you can use the attribute option:

    /**
     * @Route("/blog/{title}")
     * @ParamConverter("post", class="SensioBlogBundle:Post", options={"attribute" = "title"})
     */
    public function showAction(Post $post)
    {
    }
    
    ... in the code:
    $attribute = "title"; // this is the attribute of the object
    $parameter = $attribute; // this is the name of the route parameter
    $value = $request->attributes->get($attribute);
    $critera = array($attribute => $value);
    $this->registry->getRepository($class, $options['entity_manager'])->findOneBy($criteria);
    
  1. You can use you own method to retrieve the object:

    /**
     * @Route("/blog/{title}")
     * @ParamConverter("post", class="SensioBlogBundle:Post", options={"method" = "myOwnFind", "parameter" = "title"})
     */
    public function showAction(Post $post)
    {
    }
    
    ... in the code:
    $parameter = "title";
    $value = $request->attributes->get($parameter);
    $repository = $this->registry->getRepository($class, $options['entity_manager']);
    call_user_func(array($repository, $method), $value, $options);
    
    // you can optionnaly add some values in the options array in the annotation, it will be passed to your method through the second argument $options
    
  1. Finaly, you can use them all:

    /**
     * @Route("/blog/{post_title}/{tag_title}")
     * @ParamConverter("post", class="SensioBlogBundle:Post", options={"parameter" = "post_title", "method" = "myOwnFind", "myOwnOption" = "myOwnValue"})
     * @ParamConverter("tag", class="SensioBlogBundle:Tag", options={"parameter" = "tag_title", "attribute" = "title_canonical")
     */
    public function showAction(Post $post, Tag $tag)
    {
    }
    
    ... in the code:
    $parameter = "post_title";
    $value = $request->attributes->get($parameter);
    $repository = $this->registry->getRepository($class, $options['entity_manager']);
    call_user_func(array($repository, $method), $value, $options);
    // then
    $attribute = "title_canonical";
    $parameter = "tag_title";
    $value = $request->attributes->get($parameter);
    $critera = array($attribute => $value);
    $this->registry->getRepository($class, $options['entity_manager'])->findOneBy($criteria);
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment