我有一个控制器,用于处理来自AJAX请求的表单提交。我不想重复自己,所以我把表单处理代码放在一个方法中:
// Should process POST request
public function create(Request $request)
{
return $this->processEdit($request);
}
// Should process PUT request
public function update($id, Request $request)
{
$entity = $this->findEntity($id); // custom method
if (!$entity)
return $this->myCustomErrorResponse();
return $this->processEdit($request, $entity);
}
private function processEdit(Request $request, Entity $entity = null)
{
$form = $this->createForm('my_entity', $entity);
$form->handleRequest($request);
if ($form->isValid()) {
// Do something
} else {
// Handle invalid form
}
return $response;
}
我有以下两条路线:
ajax_create:
pattern: /
defaults: { _controller: 'MyBundle:Ajax:create' }
methods: [ POST ]
ajax_update:
pattern: /{id}
defaults: { _controller: 'MyBundle:Ajax:update' }
methods: [ PUT ]
requirements:
id: \d+
但是,当我通过AJAX提交表单时,如果没有任何表单错误消息,它将不接受PUT
请求并且返回表单无效。如果我改变控制器代码abit,
$form = $this->createForm('my_entity', $entity, array(
'method' => 'PUT',
));
...它会处理PUT
个请求但不处理POST
请求。
我想知道Symfony2的哪一部分HTTP方法检查表单,所以我试图在源代码中寻找答案,但我找不到线索。你们中的任何人都可以分享你的知识吗?
另一个问题是,有没有办法绕过HTTP方法检查?我目前正在将$method
传递给上面显示的方法。
非常感谢。
更新
为了使我的问题更清楚,我的Symfony2应用程序将请求(POST和PUT)路由到正确的控制器方法。
我提到了上面改变的代码,这里是:
// Should process POST request
public function create(Request $request)
{
return $this->processEdit($request);
}
// Should process PUT request
public function update($id, Request $request)
{
$entity = $this->findEntity($id); // custom method
if (!$entity)
return $this->myCustomErrorResponse();
return $this->processEdit($request, 'PUT', $entity);
}
private function processEdit(Request $request, $method = 'POST', Entity $entity = null)
{
$form = $this->createForm('my_entity', $entity, array(
'method' => $method,
));
$form->handleRequest($request);
if ($form->isValid()) {
// Do something
} else {
// Handle invalid form
}
return $response;
}
答案 0 :(得分:3)
[编辑2014-05-23] 我完全修改了我的第一个答案,因为它是一个"肮脏的黑客"。
我有完全相同的问题(和几乎相同的代码)。我在这里已经阅读了答案,并且在我自己的代码中发现了一个主要问题,我忘了修改/web/app.php
文件以默认启用HttpMethodParameterOverride
参数。 (It's a change introduced in Symfony2.2)
现在一切都按预期使用handleRequest()
函数:
我不需要按照接受的答案中的建议修改RequestHandler
配置。
现在代码如下:
/**
* Fruits CRUD service controller.
*
* @Route("/services/fruits")
*/
class FruitsController extends Controller
{
// ...
/**
* Create a fruit.
*
* @param Request $request
*
* @Rest\Post("", name="backend_fruits_create")
*
* @return View|array
*/
public function createAction(Request $request)
{
return $this->processForm($request, new Fruit());
}
/**
* Edit a fruit.
*
* @param Request $request
* @param Fruit $fruit
*
* @Rest\Put("/{id}", name="backend_fruits_edit", requirements={"id" = "\d+"})
* @throws HttpException
*
* ## DEV FORM ##
* @Rest\Get("/edit/{id}", name="backend_fruits_edit_dev", requirements={"id" = "\d+"})
* @Rest\View
* ## DEV FORM ##
*
* @return View|array
*/
public function editAction(Request $request, Fruit $fruit)
{
return $this->processForm($request, $fruit);
}
/**
* Delete a fruit.
*
* @param Fruit $fruit
*
* @Rest\Delete("/{id}", name="backend_fruits_delete")
* @throws HttpException
*
* @return View
*/
public function deleteAction(Fruit $fruit)
{
$fruit->delete();
return $this->responseHelper->createSuccessResponse(
$fruit->getTree()->getFruits(),
Response::HTTP_ACCEPTED
);
}
/**
* Form handling.
*
* @param Request $request
* @param Fruit $fruit
*
* @return View|array
*/
protected function processForm(Request $request, Fruit $fruit)
{
list($statusCode, $httpMethod, $action) = $this->getActionParameters($fruit);
$form = $this->createForm(
new FruitType(), $fruit,
array('action' => $action, 'method' => $httpMethod)
);
if (in_array($request->getMethod(), array('POST', 'PUT'))) {
if (!$form->handleRequest($request)->isValid()) {
return $this->responseHelper->createErrorResponse($form);
}
$form->getData()->save();
return $this->responseHelper->createSuccessResponse($form->getData(), $statusCode);
}
return compact('form');
}
/**
* Set the form and action parameters depending on the REST action.
*
* @param Fruit $fruit
*
* @return array
*/
protected function getActionParameters(Fruit $fruit)
{
if ($fruit->isNew()) {
$statusCode = Response::HTTP_CREATED;
$httpMethod = 'POST';
$action = $this->generateUrl('backend_fruits_create');
} else {
$statusCode = Response::HTTP_OK;
$httpMethod = 'PUT';
$action = $this->generateUrl('backend_fruits_edit', array('id' => $fruit->getId()));
}
return array($statusCode, $httpMethod, $action);
}
注意:表单类型绑定到模型实体。
Note2 :正如您所看到的,我有一个额外的GET路线。它在开发时很有用,因为我可以在浏览器中调试我的表单。作为服务委托人,我将在完成后删除相关代码;路线和processForm
函数,不需要测试方法并再返回表单。
/**
* Form handling.
*
* @param Request $request
* @param Fruit $fruit
*
* @return mixed
*/
protected function processForm(Request $request, Fruit $fruit)
{
list($statusCode, $httpMethod, $action) = $this->getActionParameters($fruit);
$form = $this->createForm(
new FruitType(), $fruit,
array('action' => $action, 'method' => $httpMethod)
);
if (!$form->handleRequest($request)->isValid()) {
return $this->responseHelper->createErrorResponse($form);
}
$form->getData()->save();
return $this->responseHelper->createSuccessResponse($form->getData(), $statusCode);
}
Note3 :响应帮助器只是使用FOSRestBundle创建自定义View
响应对象。
有关REST和Symfony2的更多信息:
答案 1 :(得分:3)
只是一些(希望)有用的说明:
首先,您可以从Request对象获取submit方法,不需要单独传递它:
其次,我想我找到了你要找的代码部分。首先,如果您检查Symfony Form类中的handleRequest
调用,您可以看到它从位于配置中的handleRequest
类调用RequestHandler
(check {{ 1}}类)。我猜测FormConfigInterface
的正确实现是NativeRequestHandler。您可以在48行看到检查请求方法是否相等。
现在,为了处理这个问题,您可以将表单的FormConfigInterface设置为自定义值,您可以将RequestHandler设置为您自己的实现。如果将RequestHandlerInterface
定义为服务,那么您很幸运(目前我无法访问服务列表)。只需将类切换为指向您自己的实现。
说完这一切之后,我认为表格类型检查是有原因的。您应该像现在一样单独处理表单提交类型。此外,使用POST插入和编辑是一个非常好的解决方案。越简单越好,引入新bug的机会就越少!