Skip to content

Instantly share code, notes, and snippets.

@davedevelopment
Created November 13, 2012 16:36
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davedevelopment/4066819 to your computer and use it in GitHub Desktop.
Save davedevelopment/4066819 to your computer and use it in GitHub Desktop.
Tinkering with a Hal resource DTO, separating the framework from the app and content negotiation
<?php
namespace Demo {
require __DIR__."/vendor/autoload.php";
use Nocarrier\Hal;
class Resource extends Hal {}
class ResourceResponse
{
protected $resource;
protected $code;
public function __construct(Resource $resource, $code = 200)
{
$this->resource = $resource;
$this->code = 200;
}
public function getResource()
{
return $this->resource;
}
public function getCode()
{
return $this->code;
}
}
class UserController
{
public function get($id)
{
$resource = new Resource("/user/" . $id, array(
"name" => "Dave", // everybody's called Dave
));
return new ResourceResponse($resource, 200);
}
}
}
namespace {
use Silex\Application;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Exception\HttpException;
use FOS\Rest\Util\FormatNegotiator;
$app = new Application();
$app['debug'] = true;
$app['format_negotiator'] = $app->share(function() {
return new FormatNegotiator;
});
/**
* Content negotiating Resource to Response listener
*/
$app->on(KernelEvents::VIEW, function(GetResponseForControllerResultEvent $event) use ($app) {
$response = $event->getControllerResult();
/**
* If its not a resource, let other listeners handle it
*/
if (!($response instanceof Demo\ResourceResponse)) {
return;
}
$request = $event->getRequest();
/**
* This is an annoying prerequisite
*/
$request->setFormat('json', array("application/hal+json", "application/json"));
$request->setFormat('xml', array("application/hal+xml", "application/xml"));
/**
* Determine which format is best
*
* I think I'd prefer this to have methods to calculate the prefered
* mimetype and best format
*/
$format = $app['format_negotiator']->getBestFormat($request, array(
"json",
"xml",
));
/**
* If we don't know what they want, bail
*/
if (null === $format) {
throw new HttpException(406, "No matching accepted Response format could be determined");
return;
}
/**
* Set the response
*/
if ("xml" == $format) {
$response = new Response(
$response->getResource()->asXml(),
$response->getCode(),
array("Content-Type" => "application/hal+xml")
);
} else {
$response = new Response(
$response->getResource()->asJson(),
$response->getCode(),
array("Content-Type" => "application/hal+json")
);
}
$event->setResponse($response);
});
/**
* Routing
*/
$app->get("/user/{id}", "Demo\UserController::get");
$app->run();
}
@davedevelopment
Copy link
Author

Points to note

  • The URLs need to be specific to the app layer, then transformed in the web layer?
  • Need some way of binding acceptable mimetypes to the routes, so content negotiation can be done pre-cintroller, should check FOSRestBundle

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment