Skip to content

Commit

Permalink
Merge branch 'master' into query_param-proof-of-concept
Browse files Browse the repository at this point in the history
  • Loading branch information
lsmith77 committed Mar 24, 2012
2 parents bf75439 + 557833b commit 8e19e8f
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 30 deletions.
1 change: 1 addition & 0 deletions DependencyInjection/Configuration.php
Expand Up @@ -55,6 +55,7 @@ public function getConfigTreeBuilder()
->scalarNode('view_handler')->defaultValue('fos_rest.view_handler.default')->end()
->end()
->end()
->scalarNode('objects_version')->defaultNull()->end()
->end()
->end();

Expand Down
6 changes: 4 additions & 2 deletions DependencyInjection/FOSRestExtension.php
Expand Up @@ -53,6 +53,8 @@ public function load(array $configs, ContainerBuilder $container)
foreach ($config['service'] as $key => $service) {
$container->setAlias($this->getAlias().'.'.$key, $config['service'][$key]);
}
$container->setParameter($this->getAlias().'.objects_version', $config['objects_version']);

$container->setParameter($this->getAlias().'.formats', $formats);
$container->setParameter($this->getAlias().'.default_engine', $config['view']['default_engine']);

Expand Down Expand Up @@ -84,7 +86,7 @@ public function load(array $configs, ContainerBuilder $container)
foreach ($config['exception']['messages'] as $exception => $message) {
$this->testExceptionExists($exception);
}

$container->setParameter($this->getAlias().'.exception.codes', $config['exception']['codes']);
$container->setParameter($this->getAlias().'.exception.messages', $config['exception']['messages']);

Expand Down Expand Up @@ -121,7 +123,7 @@ public function load(array $configs, ContainerBuilder $container)

/**
* Check if an exception is loadable.
*
*
* @param string $exception class to test
* @throws InvalidArgumentException if the class was not found.
*/
Expand Down
1 change: 0 additions & 1 deletion EventListener/FormatListener.php
Expand Up @@ -12,7 +12,6 @@
namespace FOS\RestBundle\EventListener;

use Symfony\Component\HttpKernel\Event\FilterControllerEvent,
Symfony\Component\Serializer\SerializerInterface,
Symfony\Component\HttpKernel\Exception\HttpException,
Symfony\Component\HttpKernel\HttpKernelInterface;

Expand Down
5 changes: 3 additions & 2 deletions Resources/doc/1-setting_up_the_bundle.md
Expand Up @@ -20,7 +20,7 @@ Add the following lines in your `deps` file:
```
[FOSRest]
git=git://github.com/FriendsOfSymfony/FOSRest.git
target=fos/FOS/RestBundle
target=fos/FOS/Rest
[FOSRestBundle]
git=git://github.com/FriendsOfSymfony/FOSRestBundle.git
Expand Down Expand Up @@ -53,7 +53,8 @@ Add the `FOS` namespace to your autoloader:

$loader->registerNamespaces(array(
// ...
'FOS' => array(__DIR__.'/../vendor/bundles', __DIR__.'/../vendor/fos'),
'FOS\\Rest' => __DIR__.'/../vendor/fos',
'FOS' => __DIR__.'/../vendor/bundles',
));
```

Expand Down
Expand Up @@ -31,6 +31,8 @@ users:

This will tell Symfony2 to automatically generate proper REST routes from your ``UsersController`` action names.
Notice ``type: rest`` option. It's required so that the RestBundle can find which routes are supported.
Notice ``name_prefix: my_bundle_`` option. It's useful to prefix the generated controller routes. Take care that
you can use ``name_prefix`` on an import only when the file is imported itself with the type ``rest``.

## Define resource actions

Expand Down Expand Up @@ -63,10 +65,10 @@ class UsersController extends Controller
{} // "patch_user" [PATCH] /users/{slug}

public function lockUserAction($slug)
{} // "lock_user" [PUT] /users/{slug}/lock
{} // "lock_user" [POST] /users/{slug}/lock

public function banUserAction($slug, $id)
{} // "ban_user" [PUT] /users/{slug}/ban
{} // "ban_user" [POST] /users/{slug}/ban

public function removeUserAction($slug)
{} // "remove_user" [GET] /users/{slug}/remove
Expand All @@ -93,7 +95,7 @@ class UsersController extends Controller
{} // "put_user_comment" [PUT] /users/{slug}/comments/{id}

public function voteUserCommentAction($slug, $id)
{} // "vote_user_comment" [PUT] /users/{slug}/comments/{id}/vote
{} // "vote_user_comment" [POST] /users/{slug}/comments/{id}/vote

public function removeUserCommentAction($slug, $id)
{} // "remove_user_comment" [GET] /users/{slug}/comments/{id}/remove
Expand Down Expand Up @@ -137,13 +139,14 @@ to *PUT*, or update, an existing resource. Shown as ``UsersController::editUserA
* **remove** - A hypermedia representation that acts as the engine to *DELETE*. Typically this is a form that allows the
client to *DELETE* an existing resource. Commonly a confirmation form. Shown as ``UsersController::removeUserAction()`` above.

### Custom PUT Actions
### Custom POST Actions

All actions that do not match the ones listed in the sections above will register as a *PUT* action. In the controller
shown above, these actions are ``UsersController::lockUserAction()`` and ``UsersController::banUserAction()``. You could
just as easily create a method called ``UsersController::promoteUserAction()`` which would take a *PUT* request to the url
*/users/{slug}/promote*. This allows for easy updating of aspects of a resource, without having to deal with the
resource as a whole at the standard *PUT* endpoint.
All actions that do not match the ones listed in the sections above will register as a *POST* action. In the controller
shown above, these actions are ``UsersController::lockUserAction()``, ``UsersController::banUserAction()`` and
``UsersController::voteUserCommentAction()``. You could just as easily create a method called
``UsersController::promoteUserAction()`` which would take a *POST* request to the url */users/{slug}/promote*.
This allows for easy updating of aspects of a resource, without having to deal with the resource as a whole at
the standard *POST* endpoint.

### Sub-Resource Actions

Expand Down
Expand Up @@ -60,7 +60,7 @@ auto-generation process and can be any name you like.
class CommentsController extends Controller
{
public function voteCommentAction($slug, $id)
{} // "vote_user_comment" [PUT] /users/{slug}/comments/{id}/vote
{} // "vote_user_comment" [POST] /users/{slug}/comments/{id}/vote

public function getCommentsAction($slug)
{} // "get_user_comments" [GET] /users/{slug}/comments
Expand Down Expand Up @@ -106,7 +106,7 @@ the ``type: rest`` param in your application level routes include.

RestBundle uses REST paths to generate route name. This means, that URL:

[PUT] /users/{slug}/comments/{id}/vote
[POST] /users/{slug}/comments/{id}/vote

will become the route with the name ``vote_user_comment``.

Expand Down
6 changes: 2 additions & 4 deletions Tests/EventListener/ViewResponseListenerTest.php
Expand Up @@ -147,13 +147,11 @@ public function testOnKernelViewFallsBackToFrameworkExtraBundle()
$request = new Request();
$request->attributes->set('_template', $template);

$response = new Response();

$templating = $this->getMock('\Symfony\Bundle\FrameworkBundle\Templating\EngineInterface');
$templating->expects($this->once())
->method('render')
->method('renderResponse')
->with($template, array())
->will($this->returnValue('output'));
->will($this->returnValue(new Response('output')));

$container = $this->getMockBuilder('\Symfony\Component\DependencyInjection\Container')->disableOriginalConstructor()->getMock();
$container->expects($this->once())
Expand Down
10 changes: 8 additions & 2 deletions Tests/View/ViewHandlerTest.php
Expand Up @@ -133,7 +133,7 @@ public function testCreateResponseWithoutLocation($format, $expected, $createVie
{
$viewHandler = new ViewHandler(array('html' => true, 'json' => false));

$container = $this->getMock('\Symfony\Component\DependencyInjection\Container', array('get'));
$container = $this->getMock('\Symfony\Component\DependencyInjection\Container', array('get', 'getParameter'));
if ('html' === $format) {
$templating = $this->getMockBuilder('\Symfony\Bundle\FrameworkBundle\Templating\PhpEngine')
->setMethods(array('render'))
Expand All @@ -150,7 +150,7 @@ public function testCreateResponseWithoutLocation($format, $expected, $createVie
->with('fos_rest.templating')
->will($this->returnValue($templating));
} else {
$serializer = $this->getMock('\stdClass', array('serialize'));
$serializer = $this->getMock('\stdClass', array('serialize', 'setVersion'));
$serializer
->expects($this->once())
->method('serialize')
Expand All @@ -161,6 +161,12 @@ public function testCreateResponseWithoutLocation($format, $expected, $createVie
->method('get')
->with('fos_rest.serializer')
->will($this->returnValue($serializer));

$container
->expects($this->once())
->method('getParameter')
->with('fos_rest.objects_version')
->will($this->returnValue('1.0'));
}

$viewHandler->setContainer($container);
Expand Down
7 changes: 4 additions & 3 deletions Tests/bootstrap.php
Expand Up @@ -9,12 +9,13 @@
* file that was distributed with this source code.
*/

if (file_exists($file = __DIR__.'/../vendor/.composer/autoload.php')) {
$autoload = require_once $file;
} else {
$file = __DIR__.'/../vendor/.composer/autoload.php';
if (!file_exists($file)) {
throw new RuntimeException('Install dependencies to run test suite.');
}

require_once $file;

spl_autoload_register(function($class) {
if (0 === strpos($class, 'FOS\\RestBundle\\')) {
$path = __DIR__.'/../'.implode('/', array_slice(explode('\\', $class), 2)).'.php';
Expand Down
28 changes: 28 additions & 0 deletions View/View.php
Expand Up @@ -66,6 +66,11 @@ class View
*/
private $route;

/**
* @var string
*/
private $objectsVersion;

/**
* Convenience method to allow for a fluent interface.
*
Expand Down Expand Up @@ -146,6 +151,19 @@ public function setStatusCode($code)
return $this;
}

/**
* set the serializer objects version
*
* @param $objectsVersion
* @return View
*/
public function setObjectsVersion($objectsVersion)
{
$this->objectsVersion = $objectsVersion;

return $this;
}

/**
* Sets template to use for the encoding
*
Expand Down Expand Up @@ -318,4 +336,14 @@ public function getRoute()
{
return $this->route;
}

/**
* get the objects version
*
* @return string objects version
*/
public function getObjectsVersion()
{
return $this->objectsVersion;
}
}
26 changes: 21 additions & 5 deletions View/ViewHandler.php
Expand Up @@ -133,7 +133,7 @@ private function getStatusCode(View $view)
* If the given format uses the templating system for rendering
*
* @param string $format
*
*
* @return Boolean
*/
public function isFormatTemplating($format)
Expand All @@ -154,13 +154,25 @@ protected function getRouter()
/**
* Get the serializer service
*
* @return Symfony\Component\Serializer\SerializerInterface
* @return JMS\SerializerBundle\Serializer\SerializerInterface
*/
protected function getSerializer()
{
return $this->container->get('fos_rest.serializer');
}

/**
* Get the serializer objects version
*
* @param View $view
*
* @return string|null "Objects versioning" version
*/
protected function getObjectsVersion(View $view)
{
return $view->getObjectsVersion() ?: $this->container->getParameter('fos_rest.objects_version');
}

/**
* Get the templating service
*
Expand Down Expand Up @@ -307,9 +319,13 @@ public function createResponse(View $view, Request $request, $format)
return $this->createRedirectResponse($view, $location, $format);
}

$content = $this->isFormatTemplating($format)
? $this->renderTemplate($view, $format)
: $this->getSerializer()->serialize($view->getData(), $format);
if ($this->isFormatTemplating($format)) {
$content = $this->renderTemplate($view, $format);
} else {
$serializer = $this->getSerializer();
$serializer->setVersion($this->getObjectsVersion($view));
$content = $serializer->serialize($view->getData(), $format);
}

return new Response($content, $this->getStatusCode($view), $view->getHeaders());
}
Expand Down

0 comments on commit 8e19e8f

Please sign in to comment.