Skip to content

Commit

Permalink
Merge pull request zendframework#3482 from weierophinney/feature/rest…
Browse files Browse the repository at this point in the history
…-delete-replace-collection

Feature/rest delete replace collection
  • Loading branch information
Freeaqingme committed Jan 18, 2013
2 parents 0bb9585 + 56e347c commit 21f4850
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 34 deletions.
95 changes: 61 additions & 34 deletions library/Zend/Mvc/Controller/AbstractRestfulController.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ abstract public function create($data);
*/
abstract public function delete($id);

/**
* Delete the entire resource collection
*
* Not marked as abstract, as that would introduce a BC break
* (introduced in 2.1.0); instead, raises an exception if not implemented.
*
* @return mixed
* @throws Exception\RuntimeException
*/
public function deleteList()
{
throw new Exception\RuntimeException(sprintf(
'%s is unimplemented', __METHOD__
));
}

/**
* Return single resource
*
Expand Down Expand Up @@ -138,6 +154,23 @@ public function patch($id, $data)
));
}

/**
* Replace an entire resource collection
*
* Not marked as abstract, as that would introduce a BC break
* (introduced in 2.1.0); instead, raises an exception if not implemented.
*
* @param mixed $data
* @return mixed
* @throws Exception\RuntimeException
*/
public function replaceList($data)
{
throw new Exception\RuntimeException(sprintf(
'%s is unimplemented', __METHOD__
));
}

/**
* Update an existing resource
*
Expand Down Expand Up @@ -229,19 +262,20 @@ public function onDispatch(MvcEvent $e)
break;
// DELETE
case 'delete':
if (null === $id = $routeMatch->getParam('id')) {
if (! ($id = $request->getQuery()->get('id', false))) {
throw new Exception\DomainException(
'Missing identifier');
}
$id = $this->getIdentifier($routeMatch, $request);
if ($id !== false) {
$action = 'delete';
$return = $this->delete($id);
break;
}
$action = 'delete';
$return = $this->delete($id);

$action = 'deleteList';
$return = $this->deleteList();
break;
// GET
case 'get':
$id = $this->getIdentifier($routeMatch, $request);
if ($id) {
if ($id !== false) {
$action = 'get';
$return = $this->get($id);
break;
Expand All @@ -252,7 +286,7 @@ public function onDispatch(MvcEvent $e)
// HEAD
case 'head':
$id = $this->getIdentifier($routeMatch, $request);
if (!$id) {
if ($id !== false) {
$id = null;
}
$action = 'head';
Expand All @@ -270,8 +304,10 @@ public function onDispatch(MvcEvent $e)
// PATCH
case 'patch':
$id = $this->getIdentifier($routeMatch, $request);
if (!$id) {
throw new Exception\DomainException('Missing identifier');
if ($id === false) {
$response = $e->getResponse();
$response->setStatusCode(405);
return $response;
}
$data = $this->processBodyContent($request);
$action = 'patch';
Expand All @@ -284,12 +320,23 @@ public function onDispatch(MvcEvent $e)
break;
// PUT
case 'put':
$action = 'update';
$return = $this->processPutData($request, $routeMatch);
$id = $this->getIdentifier($routeMatch, $request);
$data = $this->processBodyContent($request);

if ($id !== false) {
$action = 'update';
$return = $this->update($id, $data);
break;
}

$action = 'replaceList';
$return = $this->replaceList($data);
break;
// All others...
default:
throw new Exception\DomainException('Invalid HTTP method!');
$response = $e->getResponse();
$response->setStatusCode(405);
return $response;
}

$routeMatch->setParam('action', $action);
Expand All @@ -314,26 +361,6 @@ public function processPostData(Request $request)
return $this->create($data);
}

/**
* Process put data and call update
*
* @param Request $request
* @param $routeMatch
* @return mixed
* @throws Exception\DomainException
*/
public function processPutData(Request $request, $routeMatch)
{
$id = $this->getIdentifier($routeMatch, $request);
if (!$id) {
throw new Exception\DomainException('Missing identifier');
}

$data = $this->processBodyContent($request);

return $this->update($id, $data);
}

/**
* Check if request has certain content type
*
Expand Down
48 changes: 48 additions & 0 deletions tests/ZendTest/Mvc/Controller/RestfulControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,21 @@ public function testDispatchInvokesUpdateMethodWhenNoActionPresentAndPutInvokedW
$this->assertEquals('update', $this->routeMatch->getParam('action'));
}

public function testDispatchInvokesReplaceListMethodWhenNoActionPresentAndPutInvokedWithoutIdentifier()
{
$entities = array(
array('id' => uniqid(), 'name' => __FUNCTION__),
array('id' => uniqid(), 'name' => __FUNCTION__),
array('id' => uniqid(), 'name' => __FUNCTION__),
);
$string = http_build_query($entities);
$this->request->setMethod('PUT')
->setContent($string);
$result = $this->controller->dispatch($this->request, $this->response);
$this->assertEquals($entities, $result);
$this->assertEquals('replaceList', $this->routeMatch->getParam('action'));
}

public function testDispatchInvokesDeleteMethodWhenNoActionPresentAndDeleteInvokedWithIdentifier()
{
$entity = array('id' => 1, 'name' => __FUNCTION__);
Expand All @@ -102,6 +117,16 @@ public function testDispatchInvokesDeleteMethodWhenNoActionPresentAndDeleteInvok
$this->assertEquals('delete', $this->routeMatch->getParam('action'));
}

public function testDispatchInvokesDeleteListMethodWhenNoActionPresentAndDeleteInvokedWithoutIdentifier()
{
$this->request->setMethod('DELETE');
$result = $this->controller->dispatch($this->request, $this->response);
$this->assertSame($this->response, $result);
$this->assertEquals(204, $result->getStatusCode());
$this->assertTrue($result->getHeaders()->has('X-Deleted'));
$this->assertEquals('deleteList', $this->routeMatch->getParam('action'));
}

public function testDispatchInvokesOptionsMethodWhenNoActionPresentAndOptionsInvoked()
{
$this->request->setMethod('OPTIONS');
Expand Down Expand Up @@ -341,4 +366,27 @@ public function testRequestingContentTypeReturnsFalseForInvalidMatches($contentT
$this->request->getHeaders()->addHeaderLine('Content-Type', $contentType);
$this->assertFalse($this->controller->requestHasContentType($this->request, TestAsset\RestfulTestController::CONTENT_TYPE_JSON));
}

public function testDispatchViaPatchWithoutIdentifierReturns405Response()
{
$entity = new stdClass;
$entity->name = 'foo';
$entity->type = 'standard';
$this->controller->entity = $entity;
$entity = array('name' => __FUNCTION__);
$string = http_build_query($entity);
$this->request->setMethod('PATCH')
->setContent($string);
$result = $this->controller->dispatch($this->request, $this->response);
$this->assertInstanceOf('Zend\Http\Response', $result);
$this->assertEquals(405, $result->getStatusCode());
}

public function testDispatchWithUnrecognizedMethodReturns405Response()
{
$this->request->setMethod('PROPFIND');
$result = $this->controller->dispatch($this->request, $this->response);
$this->assertInstanceOf('Zend\Http\Response', $result);
$this->assertEquals(405, $result->getStatusCode());
}
}
24 changes: 24 additions & 0 deletions tests/ZendTest/Mvc/Controller/TestAsset/RestfulTestController.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ public function delete($id)
return array();
}

/**
* Delete the collection
*
* @return \Zend\Http\Response
*/
public function deleteList()
{
$response = $this->getResponse();
$response->setStatusCode(204);
$response->getHeaders()->addHeaderLine('X-Deleted', 'true');
return $response;
}

/**
* Return single resource
*
Expand Down Expand Up @@ -98,6 +111,17 @@ public function patch($id, $data)
return array('entity' => $updated);
}

/**
* Replace the entire resource collection
*
* @param array|\Traversable $items
* @return array|\Traversable
*/
public function replaceList($items)
{
return $items;
}

/**
* Update an existing resource
*
Expand Down

0 comments on commit 21f4850

Please sign in to comment.