【问题标题】:Symfony @ParamConverter get entity through user relationshipSymfony @ParamConverter 通过用户关系获取实体
【发布时间】:2016-05-03 19:17:06
【问题描述】:

我有几条以/experiment/{id}/... 开头的路由,我厌倦了重写相同的逻辑来检索已登录用户的实验。我想我可以重构我的代码,但我猜@ParamConverter 会是一个更好的解决方案。

如何重写以下代码以利用 Symfony 的 @ParamConverter 功能?

/**
 * Displays details about an Experiment entity, including stats.
 *
 * @Route("/experiment/{id}/report", requirements={"id" = "\d+"}, name="experiment_report")
 * @Method("GET")
 * @Template()
 * @Security("has_role('ROLE_USER')")
 */
public function reportAction(Request $request, $id)
{
    $em = $this->getDoctrine()->getManager();

    $experiment = $em->getRepository('AppBundle:Experiment')
        ->findOneBy(array(
            'id'   => $id,
            'user' => $this->getUser(),
        ));

    if (!$experiment) {
        throw $this->createNotFoundException('Unable to find Experiment entity.');
    }

    // ...
}

实验实体有composite primary keys如下:

class Experiment
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     */
    protected $id;

    /**
     * @var integer
     *
     * @ORM\Column(name="user_id", type="integer")
     * @ORM\Id
     */
    protected $userId;

    /**
     * @ORM\ManyToOne(targetEntity="UserBundle\Entity\User", inversedBy="experiments", cascade={"persist"})
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     */
    protected $user;

    // ..

}

我想在路由中使用他们的用户 id 和一个实验 id 检索已登录用户的实验。

【问题讨论】:

  • 自定义参数转换器就足够了,只需将security.token_storagedoctrine 作为参数注入您的自定义参数转换器服务。 stfalcon.com/en/blog/post/symfony2-custom-paramconverter
  • 非常感谢@malcolm 我已经接受了 jkucharovic 的回答,其中详细说明了您的建议:)

标签: php symfony doctrine-orm doctrine


【解决方案1】:

您可以通过使用custom ParamConverter 来实现。比如这样的:

namespace AppBundle\Request\ParamConverter;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use AppBundle\Entity\Experiment;

class ExperimentConverter implements ParamConverterInterface
{
    protected $em;
    protected $user;

    public function __construct(EntityManager $em, TokenStorage $tokenStorage)
    {
        $this->em = $em;
        $this->user = $tokenStorage->getToken()->getUser();
    }

    public function apply(Request $request, ParamConverter $configuration)
    {
        $object = $this->em->getRepository(Experiment::class)->findOneBy([
            'id'   => $request->attributes->get('id'),
            'user' => $this->user
        ]);

        if (null === $object) {
            throw new NotFoundHttpException(
                sprintf('%s object not found.', $configuration->getClass())
            );
        }

        $request->attributes->set($configuration->getName(), $object);

        return true;
    }

    public function supports(ParamConverter $configuration)
    {
        return Experiment::class === $configuration->getClass();
    }
}

您需要注册您的转换器服务并为其添加标签:

# app/config/config.yml
services:
    experiment_converter:
        class: AppBundle\Request\ParamConverter\ExperimentConverter
        arguments:
            - "@doctrine.orm.default_entity_manager"
            - "@security.token_storage"
        tags:
            - { name: request.param_converter, priority: 1, converter: experiment_converter }

【讨论】:

  • 非常感谢@jkucharovic 这很好:) 我不得不稍微更改supports() 方法以确保类名始终采用正确的格式。我已更新您的答案以反映我的更改
  • 对于将来遇到此问题的任何人,这都是完美的。如果你忘记将结果对象设置到请求属性中(就在 return true 之前),你会得到一个错误,symfony 认为你忘记注册一个新服务。我将此示例扩展为更通用的转换器,以便我可以使用一个转换器来统治它们。
  • @jkucharovic 当您在转换器类中对单个实体进行硬编码时,这将如何适用于多个实体?您能否举一个 ParamConverter 注释的示例,该注释将应用于想要使用它的控制器方法?
【解决方案2】:

很遗憾,您无法将当前登录的用户 id 注入到参数转换器中,除非您实际上将其作为 url 中的参数传递。

您可以创建自己的转换器,但我认为最好的办法是创建一个受保护的方法来获取实验。它将像注释一样易于使用和维护:

protected function getCurrentUsersExperiment($experimentId)
{
    return $this->getDoctrine()->getManager()->getRepository('AppBundle:Experiment')
        ->findOneBy(array(
            'id' => $experimentId,
            'user' => $this->getUser()
        ));
}

public function reportAction(Request $request, $id)
{
    $experiment = $this->getCurrentUsersExperiment($id);

    ...
}

正如symfony best practices 中所述:在适当的时候使用ParamFetcher,但不要想太多。

【讨论】:

    猜你喜欢
    • 2018-08-17
    • 2014-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多