我目前正在使用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吗?
我只是想要一些线索,如果我能用这种方式真正实现,如果是的话用什么组件? (配置,路由器等)
如果不是,我该怎么办? :)
谢谢!
答案 0 :(得分:1)
我想在这里展示我的方法。
我已经为API创建了一个基本控制器,并且我坚持使用FOSRest
's rest
routing type生成的路由。因此,控制器看起来像这样:
<?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% ] ]
然后,我只是将控制器包含在routing.yml
中作为FOSRestBundle文档说明,并且已完成。