-
Notifications
You must be signed in to change notification settings - Fork 66
Extending Encoder
Entry point of the application is Encoder
which has methods for encoding data, meta, errors. Encoder
is a relatively thin layer that uses the following components
-
Parser
is a relatively small class but it's a core of the application. Its aim is iterate through all required levels of input data and return found result as a list of attributes. To make things clear it also returnsStack
which indicates where those attributes were found (e.g. stack (type: sites, id: 1) -(post)-> (type: posts, id: 5) -(author)-> (type: people, id: 3)). WhenParser
finishes parsing objects it also sends a message about it. Thus parser receiver has all required information about input data tree (attributes of each leaf and information when leafs start and finish). As parsing could be constrained by input filters it usesParserManager
to decide whether it should parse particular branch of input data tree. -
ReplyInterpreter
interprets results fromParser
and has knowledge about JSON-API document structure. Depending on the found attributes and its depth level it decides where those data should be put (maindata
orincluded
sections). It sends commands toDocument
which builds resulting JSON-API document. -
Document
is used byEncoder
directly for such trivial tasks as set top level links and meta.
It's also should be mentioned that Parser
uses data schemas to get attributes and relationships from particular input data objects. Schemas extend/inherit SchemaProvider
class.
Apart from core JSON-API encoding functionality this package has additional classes for
- Parsing input parameters (field sets, filters, sort, etc) and HTTP headers (a set of classes in
Neomerx\JsonApi\Http\Parameters
namespace). - Auxiliary class
RenderContainer
for storing exception renders that help implement error reporting. -
CodecMatcher
matches registered encoders and decoders against input HTTP headers (Content-Type
andAccept
)
This package is designed to be highly modular. Package parts are connected to each other with interfaces
so you can easily replace those parts with your implementations.
Let's illustrate the approach by example. Suppose your schema returns an object with a subclass of the expected class.
class PostSchema extends SchemaProvider
{
...
public function getRelationships($post)
{
return [
'author' => [
// will not return an Author object but
// AuthorProxy which extends Author
self::DATA => $post->getAuthor()
]
];
}
...
}
For this case there is a quick fix (below) however let's see how Container
class could be extended
// quick fix
$encoder = Encoder::instance([
Author::class => AuthorSchema::class,
AuthorProxy::class => AuthorSchema::class,
]);
- Inherit
Container
and override required method (getResourceType
in our case) - Inherit
Factory
and overridecreateContainer
method - Inherit
Encoder
and overridegetFactory
and return the new schema factory instance
use \Doctrine\Common\Util\ClassUtils;
use \Neomerx\JsonApi\Encoder\Encoder;
use \Neomerx\JsonApi\Schema\Container;
use \Neomerx\JsonApi\Factories\Factory;
class YourContainer extends Container
{
protected function getResourceType($resource)
{
return ClassUtils::getRealClass(get_class($resource));
}
}
class YourFactory extends Factory
{
public function createContainer(array $providers = [])
{
return new YourContainer($this, $providers);
}
}
class YourEncoder extends Encoder
{
protected static function getFactory()
{
return new YourFactory();
}
}