簡體   English   中英

Symfony 2-保持代碼干燥-FOSRestBundle

[英]Symfony 2 - keeping the code DRY - FOSRestBundle

我目前正在使用Symfony2創建(並學習如何)REST API。 我正在使用FOSRestBundle,並使用以下命令創建了一個“ ApiControllerBase.php”:

<?php
namespace Utopya\UtopyaBundle\Controller;


use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\View\View;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormTypeInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * Class ApiControllerBase
 *
 * @package Utopya\UtopyaBundle\Controller
 */
abstract class ApiControllerBase extends FOSRestController
{
    /**
     * @param string $entityName
     * @param string $entityClass
     *
     * @return array
     * @throws NotFoundHttpException
     */
    protected function getObjects($entityName, $entityClass)
    {
        $dataRepository = $this->container->get("doctrine")->getRepository($entityClass);

        $entityName = $entityName."s";
        $data = $dataRepository->findAll();
        foreach ($data as $object) {
            if (!$object instanceof $entityClass) {
                throw new NotFoundHttpException("$entityName not found");
            }
        }

        return array($entityName => $data);
    }

    /**
     * @param string  $entityName
     * @param string  $entityClass
     * @param integer $id
     *
     * @return array
     * @throws NotFoundHttpException
     */
    protected function getObject($entityName, $entityClass, $id)
    {
        $dataRepository = $this->container->get("doctrine")->getRepository($entityClass);

        $data = $dataRepository->find($id);
        if (!$data instanceof $entityClass) {
            throw new NotFoundHttpException("$entityName not found");
        }

        return array($entityClass => $data);
    }

    /**
     * @param FormTypeInterface $objectForm
     * @param mixed             $object
     * @param string            $route
     *
     * @return Response
     */
    protected function processForm(FormTypeInterface $objectForm, $object, $route)
    {
        $statusCode = $object->getId() ? 204 : 201;

        $em = $this->getDoctrine()->getManager();

        $form = $this->createForm($objectForm, $object);
        $form->submit($this->container->get('request_stack')->getCurrentRequest());

        if ($form->isValid()) {
            $em->persist($object);
            $em->flush();

            $response = new Response();
            $response->setStatusCode($statusCode);

            // set the `Location` header only when creating new resources
            if (201 === $statusCode) {
                $response->headers->set('Location',
                    $this->generateUrl(
                        $route, array('id' => $object->getId(), '_format' => 'json'),
                        true // absolute
                    )
                );
            }

            return $response;
        }

        return View::create($form, 400);
    }
}

這處理獲取一個具有給定id的對象,所有對象並處理表單。 但是要使用它,我必須根據需要創建盡可能多的控制器。 例如:GameController。

<?php

namespace Utopya\UtopyaBundle\Controller;

use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\View\View;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Utopya\UtopyaBundle\Entity\Game;
use Utopya\UtopyaBundle\Form\GameType;

/**
 * Class GameController
 *
 * @package Utopya\UtopyaBundle\Controller
 */
class GameController extends ApiControllerBase
{
    private $entityName = "Game";
    private $entityClass = 'Utopya\UtopyaBundle\Entity\Game';

    /**
     * @Rest\View()
     */
    public function getGamesAction()
    {
        return $this->getObjects($this->entityName, $this->entityClass);
    }

    /**
     * @param int $id
     *
     * @return array
     * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
     * @Rest\View()
     */
    public function getGameAction($id)
    {
        return $this->getObject($this->entityName, $this->entityClass, $id);
    }

    /**
     * @return mixed
     */
    public function postGameAction()
    {
        return $this->processForm(new GameType(), new Game(), "get_game");
    }
}

聽起來對我來說還算不錯,但是有一個主要問題:如果我想創建另一個控制器(例如,服務器,用戶或角色),我將不得不執行相同的過程,而且我也不想這樣做。邏輯相同。

另一個“也許”問題可能是我的$ entityName和$ entityClass。

任何想法還是我可以使它更好?

謝謝 !

=====編輯1 =====

我想我下定了決心。 對於那些基本的控制器。 我希望能夠“配置”而不是“重復”。

通過示例,我可以使用以下命令在config.yml中創建一個新節點:

#config.yml
mynode:
    game:
        entity: 'Utopya\UtopyaBundle\Entity\Game'

這是一個非常基本的示例,但是是否可以通過3種方法(getGame,getGames,postGame)將其轉換為我的GameController?

如果我真的可以用這種方法實現,我只是想要一些線索,如果可以,用什么組件可以實現? (配置,路由器等)

如果沒有,我該怎么辦? :)

謝謝 !

我想在這里展示我的方法。

我已經為API創建了基本控制器,並且堅持使用FOSRestrest路由類型生成的路由 因此,控制器看起來像這樣:

<?php

use FOS\RestBundle\Controller\Annotations\View;

use \Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Psr\Log\LoggerInterface;
use Symfony\Component\Form\FormFactoryInterface,
    Symfony\Bridge\Doctrine\RegistryInterface,
    Symfony\Component\Security\Core\SecurityContextInterface,
    Doctrine\Common\Persistence\ObjectRepository;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;

abstract class AbstractRestController 
{

    /**
     * @var \Symfony\Component\Form\FormFactoryInterface
     */
    protected $formFactory;

    /**
     * @var string
     */
    protected $formType;

    /**
     * @var string
     */
    protected $entityClass;

    /**
     * @var SecurityContextInterface
     */
    protected $securityContext;

    /**
     * @var RegistryInterface
     */
    protected $doctrine;

    /**
     * @var EventDispatcherInterface
     */
    protected $dispatcher;

    /**
     * @param FormFactoryInterface     $formFactory
     * @param RegistryInterface        $doctrine
     * @param SecurityContextInterface $securityContext
     * @param LoggerInterface          $logger
     */
    public function __construct(
        FormFactoryInterface $formFactory,
        RegistryInterface $doctrine,
        SecurityContextInterface $securityContext,
        EventDispatcherInterface $dispatcher
    )
    {
        $this->formFactory = $formFactory;
        $this->doctrine = $doctrine;
        $this->securityContext = $securityContext;
        $this->dispatcher = $dispatcher;
    }

    /**
     * @param string $formType
     */
    public function setFormType($formType)
    {
        $this->formType = $formType;
    }

    /**
     * @param string $entityClass
     */
    public function setEntityClass($entityClass)
    {
        $this->entityClass = $entityClass;
    }

    /**
     * @param null  $data
     * @param array $options
     *
     * @return \Symfony\Component\Form\FormInterface
     */
    public function createForm($data = null, $options = array())
    {
        return $this->formFactory->create(new $this->formType(), $data, $options);
    }

    /**
     * @return RegistryInterface
     */
    public function getDoctrine()
    {
        return $this->doctrine;
    }

    /**
     * @return \Doctrine\ORM\EntityRepository
     */
    public function getRepository()
    {
        return $this->doctrine->getRepository($this->entityClass);
    }

    /**
     * @param Request $request
     *
     * @View(serializerGroups={"list"}, serializerEnableMaxDepthChecks=true)
     */
    public function cgetAction(Request $request)
    {
        $this->logger->log('DEBUG', 'CGET ' . $this->entityClass);

        $offset = null;
        $limit  = null;

        if ($range = $request->headers->get('Range')) {
            list($offset, $limit) = explode(',', $range);
        }

        return $this->getRepository()->findBy(
            [],
            null,
            $limit,
            $offset
        );
    }

    /**
     * @param int $id
     *
     * @return object
     *
     * @View(serializerGroups={"show"}, serializerEnableMaxDepthChecks=true)
     */
    public function getAction($id)
    {
        $this->logger->log('DEBUG', 'GET ' . $this->entityClass);

        $object = $this->getRepository()->find($id);

        if (!$object) {
            throw new NotFoundHttpException(sprintf('%s#%s not found', $this->entityClass, $id));
        }

        return $object;
    }

    /**
     * @param Request $request
     *
     * @return \Symfony\Component\Form\Form|\Symfony\Component\Form\FormInterface
     *
     * @View()
     */
    public function postAction(Request $request)
    {
        $object = new $this->entityClass();

        $form = $this->createForm($object);
        $form->submit($request);

        if ($form->isValid()) {
            $this->doctrine->getManager()->persist($object);
            $this->doctrine->getManager()->flush($object);

            return $object;
        }

        return $form;
    }

    /**
     * @param Request $request
     * @param int     $id
     *
     * @return \Symfony\Component\Form\FormInterface
     *
     * @View()
     */
    public function putAction(Request $request, $id)
    {
        $object = $this->getRepository()->find($id);

        if (!$object) {
            throw new NotFoundHttpException(sprintf('%s#%s not found', $this->entityClass, $id));
        }

        $form = $this->createForm($object);
        $form->submit($request);

        if ($form->isValid()) {
            $this->doctrine->getManager()->persist($object);
            $this->doctrine->getManager()->flush($object);

            return $object;
        }

        return $form;
    }

    /**
     * @param Request $request
     * @param         $id
     *
     * @View()
     *
     * @return object|\Symfony\Component\Form\FormInterface
     */
    public function patchAction(Request $request, $id)
    {
        $this->logger->log('DEBUG', 'PATCH ' . $this->entityClass);

        $object = $this->getRepository()->find($id);

        if (!$object) {
            throw new NotFoundHttpException(sprintf('%s#%s not found', $this->entityClass, $id));
        }

        $form = $this->createForm($object);
        $form->submit($request, false);

        if ($form->isValid()) {
            $this->doctrine->getManager()->persist($object);
            $this->doctrine->getManager()->flush($object);

            return $object;
        }

        return $form;
    }

    /**
     * @param int $id
     *
     * @return array
     *
     * @View()
     */
    public function deleteAction($id)
    {
        $this->logger->log('DEBUG', 'DELETE ' . $this->entityClass);

        $object = $this->getRepository()->find($id);

        if (!$object) {
            throw new NotFoundHttpException(sprintf('%s#%s not found', $this->entityClass, $id));
        }

        $this->doctrine->getManager()->remove($object);
        $this->doctrine->getManager()->flush($object);

        return ['success' => true];
    }

} 

它具有用於大多數RESTful操作的方法。 接下來,當我想為實體實施CRUD時,這就是我要做的:

抽象控制器在DI中注冊為抽象服務:

my_api.abstract_controller:
  class: AbstractRestController
  abstract: true
  arguments:
    - @form.factory
    - @doctrine
    - @security.context
    - @logger
    - @event_dispatcher

接下來,當我為新實體實現CRUD時,將為其創建一個空類和一個服務定義:

class SettingController extends AbstractRestController implements ClassResourceInterface {} 

請注意,該類實現ClassResourceInterface 這對於rest路由工作很有必要。

這是此控制器的服務聲明:

api.settings.controller.class: MyBundle\Controller\SettingController
api.settings.form.class: MyBundle\Form\SettingType

my_api.settings.controller:
  class: %api.settings.controller.class%
  parent: my_api.abstract_controller
  calls:
    - [  setEntityClass, [ MyBundle\Entity\Setting ] ]
    - [  setFormType,    [ %my_api.settings.form.class% ] ]

然后,我只將控制器作為FOSRestBundle文檔的狀態包含在routing.yml中,就完成了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM