This library provides a hydrator for laravel-hal for Doctrine. Instead of manually creating every hydrator for your entities, this library will introspect an entity and generate the HAL for it including links to other entities and collections for one to many relationships and embedding many to one and one to one entities.
A grouped minimal configuration file allows for excluded fields and associations and configures the routes for each entity.
namespace App\HAL;
use ApiSkeletons\Laravel\HAL\HydratorManager as HALHydratorManager;
final class HydratorManager extends HALHydratorManager
{
public function __construct()
{
$this->classHydrators = [
// This wildcard entry is used as an example and may not be exactly what you need
'*' => \ApiSkeletons\Laravel\HAL\Doctrine\DoctrineHydrator::class,
];
}
}
use App\Hal\HydratorManager;
public function fetch(Entity\Artist $artist): array
{
return (new HydratorManager())->extract($artist)->toArray();
}
will result in a HAL response like
{
"_links": {
"self": {
"href": "https://website/artist/1"
},
"albums": {
"href": "https://website/album?filter[artist]=1"
}
},
"id": 1,
"name": "Grateful Dead"
}
A hal-doctrine.php
configuration file is required. Publish the included config to your project:
php artisan vendor:publish --tag=config
$config = [
'default' => [
'entityManager' => EntityManager::class,
'routeNamePatterns' => [
'entity' => 'api.{entityName}::fetch',
'collection' => 'api.{entityName}::fetchAll',
],
// All entities configuration is optional
'entities' => [
\App\ORM\Entity\Artist::class => [
// Override route patterns
'routesNames' => [
'entity' => 'artist::fetch',
'collection' => 'artist::fetchAll',
],
// List of fields and associations to exclude
'exclude' => [
'alias',
],
],
],
],
];
The Laminas Hydrator is used by this library to extract data directly from entities. You must add this configuration
to your doctrine.php
configuration file:
'custom_hydration_modes' = [
'hal-doctrine' => \Doctrine\Laminas\Hydrator\DoctrineObject::class,
],
The default naming strategy uses the Inflector's urlize()
method to change 'associationName' into 'association-name'.
If this is not the way you want to name your relationsihps or routes then create your own naming strategy and assign
it in the config file.
When using the routeNamePatterns
to create a route name, the entity name becomes $namingStrategy->route($entityName)
such as api.short-name::fetch
according to the example configuration.
For extracted related collections, links will be created with filter options compatible with
ApiSkeletons\Doctrine\QueryBuilder\Filter\Applicator
to filter the collection data to just the extracted entity.
For example
"_links": {
...
"album": {
"href": "https://website/album?filter[artist]=1"
}
The link to the Album will be filtered where album.artist = 1
. In order to process these URLs in your application,
implement ApiSkeletons\Doctrine\QueryBuilder\Filter\Applicator
in your controller action:
use ApiSkeletons\Doctrine\QueryBuilder\Filter\Applicator;
public function fetchAll(EntityManager $entityManager): array
{
$applicator = new Applicator($entityManager, Entity\Album::class);
$queryBuilder = $applicator($_REQUEST['filter']);
return (new HydratorManager())
->paginate('albums', collect($queryBuilder->getQuery()->getResult()))->toArray();
}
See the documentation for doctrine-querybuilder-filter for more detailed examples.
To configure a hydrator for other than the default
configuration section, extend the Doctrine Hydrator
class SecondDoctrineHydrator extends ApiSkeletons\Laravel\HAL\Doctrine\DoctrineHydrator
{
protected string $configurationSection = 'secondary';
}
Then in your hal-doctrine.php
configuration file, create a new section titled secondary
and set the
entityManager
to your second Entity Manager.