2014-04-22 31 views
3

我有一個控制器,它處理來自AJAX請求的表單提交。我不想重複自己,所以我把表單處理代碼的方法:使用PUT而不是POST提交表單

// 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請求,如果沒有任何表單錯誤消息,返回表單無效。如果我改變控制器代碼升技,

$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; 
} 

回答

3

只是一對夫婦的(希望)有用的筆記:

首先第一件事情,你可以提交方法從Request對象中,不需要單獨傳遞:

getMethod()

其次,我想我找到了你正在尋找的代碼的一部分。首先,如果您在Symfony Form類中檢查handleRequest呼叫,您可以看到它從位於配置中的RequestHandler類(請參閱FormConfigInterface類)中調用handleRequest。我猜測RequestHandlerInterface的正確實施是NativeRequestHandler。您可以在線路48上看到檢查請求方法是否相同。

現在,爲了處理這個問題,您可以將窗體的FormConfigInterface設置爲自定義值,在該自定義值中將RequestHandler製作爲自己的實現。如果NativeRequestHandler被定義爲服務,那麼你很幸運(目前我無法訪問服務列表)。只需將類切換爲指向您自己的實現。

說了這麼多,我覺得表單類型檢查是有原因的。你應該像現在一樣分開處理你的表單提交類型。另外,使用POST插入編輯是一個很好的解決方案。越簡單越好,引入新bug的機會就越小!

+0

嘿,那裏。昨天我正在執行另一個類似的表單,我突然意識到我可以使用'Request :: getMethod()'!無論如何,非常感謝。 – pikachu0

3

[編輯2014年5月23日]我已經完全修改了我的第一個答案,因爲它是一個「骯髒的黑客」。

我有完全相同的問題(和幾乎相同的代碼)。我在這裏閱讀了答案,並在自己的代碼中發現了一個主要問題,我忘記修改/web/app.php文件以默認啓用HttpMethodParameterOverride參數。 (It's a change introduced in Symfony2.2

現在一切正常使用handleRequest()功能預期:

  • 的創建操作使用POST查詢。
  • 編輯操作使用PUT查詢。
  • 刪除操作使用DELETE查詢。

我不需要修改接受答案中建議的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); 
} 

:表單類型綁定到模型實體。

注2:正如你所看到的,我有一個額外的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); 
} 

注3:響應幫手只是創建與FOSRestBundle定製View響應對象。

更多關於REST和Symfony2中:

+0

對不起,這麼晚回覆。該項目已進入下一步,一旦我有時間重構舊代碼,我將測試您的解決方案。儘量做到儘快!謝謝。 – pikachu0

+0

嗨,你的答案看起來不錯,但我找到了一個更好的方式來處理這個問題。看到明顯的答案。謝謝。 – pikachu0

相關問題