Symfony:如何为与 Voryx REST Bundle 的一对多关系创建 POST 请求
Posted
技术标签:
【中文标题】Symfony:如何为与 Voryx REST Bundle 的一对多关系创建 POST 请求【英文标题】:Symfony: How to create a POST request for one-to-many relationship with Voryx REST Bundle 【发布时间】:2016-03-06 13:00:14 【问题描述】:鉴于以下Category
实体...
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Category
*
* @ORM\Table(name="category", uniqueConstraints = @ORM\UniqueConstraint(name="unique_categoryName", columns="name"))
* @ORM\Entity(repositoryClass="AppBundle\Repository\CategoryRepository")
*/
class Category
/**
* @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=64)
*/
private $name;
/**
* @var string
*
* @ORM\Column(name="description", type="string", length=256, nullable=true)
*/
private $description;
/**
* @ORM\OneToMany(targetEntity="AppBundle\Entity\Subcategory", mappedBy="category", cascade="remove")
* @ORM\OrderBy("name" = "ASC")
*/
private $subcategories;
...
...以及以下Subcategory
实体:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Subcategory
*
* @ORM\Table(name="subcategory")
* @ORM\Entity(repositoryClass="AppBundle\Repository\SubcategoryRepository")
*/
class Subcategory
/**
* @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=64)
*/
private $name;
/**
* @var string
*
* @ORM\Column(name="description", type="string", length=256, nullable=true)
*/
private $description;
/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Category", inversedBy="subcategories")
*/
private $category;
...
我正在使用https://github.com/voryx/restgeneratorbundle
来生成 REST 控制器...下面是我的 src/AppBundle/Form/SubcategoryType
的样子(src/AppBundle/Form/CategoryType
非常相似):
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class SucategoryType extends AbstractType
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
$builder
->add('name')
->add('description')
->add('category', 'voryx_entity', array('class' => 'AppBundle\Entity\Category'))
;
/**
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Sucategory'
));
/**
* @return string
*/
public function getName()
return 'appbundle_subcategory';
最后是我的src/AppBundle/Controller/CategoryRESTController
:
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\Category;
use AppBundle\Form\CategoryType;
use FOS\RestBundle\Controller\Annotations\QueryParam;
use FOS\RestBundle\Controller\Annotations\RouteResource;
use FOS\RestBundle\Controller\Annotations\View;
use FOS\RestBundle\Request\ParamFetcherInterface;
use FOS\RestBundle\Util\Codes;
use FOS\RestBundle\View\View as FOSView;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Voryx\RESTGeneratorBundle\Controller\VoryxController;
/**
* Category controller.
* @RouteResource("Category")
*/
class CategoryRESTController extends VoryxController
/**
* Get a Category entity
*
* @View(serializerEnableMaxDepthChecks=true)
*
* @return Response
*
*/
public function getAction(Category $entity)
return $entity;
/**
* Get all Category entities.
*
* @View(serializerEnableMaxDepthChecks=true)
*
* @param ParamFetcherInterface $paramFetcher
*
* @return Response
*
* @QueryParam(name="offset", requirements="\d+", nullable=true, description="Offset from which to start listing notes.")
* @QueryParam(name="limit", requirements="\d+", default="20", description="How many notes to return.")
* @QueryParam(name="order_by", nullable=true, array=true, description="Order by fields. Must be an array ie. &order_by[name]=ASC&order_by[description]=DESC")
* @QueryParam(name="filters", nullable=true, array=true, description="Filter by fields. Must be an array ie. &filters[id]=3")
*/
public function cgetAction(ParamFetcherInterface $paramFetcher)
try
$offset = $paramFetcher->get('offset');
$limit = $paramFetcher->get('limit');
$order_by = $paramFetcher->get('order_by');
$filters = !is_null($paramFetcher->get('filters')) ? $paramFetcher->get('filters') : array();
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('AppBundle:Category')->findBy($filters, $order_by, $limit, $offset);
if ($entities)
return $entities;
return FOSView::create('Not Found', Codes::HTTP_NO_CONTENT);
catch (\Exception $e)
return FOSView::create($e->getMessage(), Codes::HTTP_INTERNAL_SERVER_ERROR);
/**
* Create a Category entity.
*
* @View(statusCode=201, serializerEnableMaxDepthChecks=true)
*
* @param Request $request
*
* @return Response
*
*/
public function postAction(Request $request)
$entity = new Category();
$form = $this->createForm(new CategoryType(), $entity, array("method" => $request->getMethod()));
$this->removeExtraFields($request, $form);
$form->handleRequest($request);
if ($form->isValid())
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $entity;
return FOSView::create(array('errors' => $form->getErrors()), Codes::HTTP_INTERNAL_SERVER_ERROR);
/**
* Update a Category entity.
*
* @View(serializerEnableMaxDepthChecks=true)
*
* @param Request $request
* @param $entity
*
* @return Response
*/
public function putAction(Request $request, Category $entity)
try
$em = $this->getDoctrine()->getManager();
$request->setMethod('PATCH'); //Treat all PUTs as PATCH
$form = $this->createForm(new CategoryType(), $entity, array("method" => $request->getMethod()));
$this->removeExtraFields($request, $form);
$form->handleRequest($request);
if ($form->isValid())
$em->flush();
return $entity;
return FOSView::create(array('errors' => $form->getErrors()), Codes::HTTP_INTERNAL_SERVER_ERROR);
catch (\Exception $e)
return FOSView::create($e->getMessage(), Codes::HTTP_INTERNAL_SERVER_ERROR);
/**
* Partial Update to a Category entity.
*
* @View(serializerEnableMaxDepthChecks=true)
*
* @param Request $request
* @param $entity
*
* @return Response
*/
public function patchAction(Request $request, Category $entity)
return $this->putAction($request, $entity);
/**
* Delete a Category entity.
*
* @View(statusCode=204)
*
* @param Request $request
* @param $entity
*
* @return Response
*/
public function deleteAction(Request $request, Category $entity)
try
$em = $this->getDoctrine()->getManager();
$em->remove($entity);
$em->flush();
return null;
catch (\Exception $e)
return FOSView::create($e->getMessage(), Codes::HTTP_INTERNAL_SERVER_ERROR);
像这样发布添加单个类别的请求非常有效:
curl -i -H "Content-Type: application/json" -X POST -d '"name" : "Sport", "description" : "Sport category"' http://localhost:8000/api/categories
...结果如下:
+----+-------+---------------------------+
| id | name | description |
+----+-------+---------------------------+
| 1 | Sport | Sport category |
+----+-------+---------------------------+
但是我如何发布请求以向上面创建的类别添加子类别?我们的想法是添加这样的子类别:
curl -i -H "Content-Type: application/json" -X POST -d '"name" : "Football", "description" : "Groups ranks about football"' http://localhost:8000/api/categories/1/subcategories
【问题讨论】:
我的回答解决了你的问题吗? 【参考方案1】:是的,为类别子类别资源创建 POST 请求是完全可以的。
我还认为(基于您的实体的外观)您不需要SubcategoryType
。因为它将与CategoryType
相同。
您可以查看 Gedmo Tree extension
: https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/tree.md 并将其全部保存在一个实体上,而不是两个实体。
【讨论】:
以上是关于Symfony:如何为与 Voryx REST Bundle 的一对多关系创建 POST 请求的主要内容,如果未能解决你的问题,请参考以下文章
Django Rest API,如何为 2 个模型条目创建 post api,并具有与模型关联的外键