Install & Enable
composer require dmytrof/fractal-bundle
Register in config/bundles.php (Symfony 4/5) or AppKernel.php (Symfony 3):
Dmytrof\FractalBundle\DmytrofFractalBundle::class => ['all' => true],
Configure Fractal
Add to config/packages/fractal.yaml (Symfony 4/5) or app/config/config.yml (Symfony 3):
fractal:
default_format: 'jsonapi' # or 'hal', 'rest'
default_includes: ['author'] # optional
First Transformation
Inject FractalManager into a controller/service:
use Dmytrof\FractalBundle\Manager\FractalManager;
public function __construct(private FractalManager $fractalManager) {}
public function showArticle(Article $article): JsonResponse
{
$transformer = new ArticleTransformer();
$resource = new Item($article, $transformer);
return $this->fractalManager->createResponse($resource);
}
Key Files to Review
config/packages/fractal.yaml (or config.yml)src/Transformer/ (custom transformers)src/Service/ (custom manager extensions)Model → Transformer
Create a transformer for your entity (e.g., ArticleTransformer):
class ArticleTransformer extends AbstractTransformer
{
public function transform(Article $article)
{
return [
'id' => $article->getId(),
'title' => $article->getTitle(),
'published_at' => $article->getPublishedAt()->format('Y-m-d'),
];
}
public function includeAuthor(Article $article)
{
return $this->item($article->getAuthor(), new AuthorTransformer());
}
}
Resource Creation
Use Item (single) or Collection (multiple) resources:
$resource = new Item($article, new ArticleTransformer());
// OR for collections:
$resource = new Collection($articles, new ArticleTransformer());
Response Handling
Leverage the bundle’s FractalManager for consistent responses:
$response = $this->fractalManager->createResponse($resource, 200, [], [
'include' => ['author'], // eager-load includes
'exclude' => ['body'], // exclude fields
]);
API Versioning Override the manager per version:
# config/packages/fractal_v1.yaml
fractal:
default_format: 'jsonapi'
default_includes: ['author']
// In a controller
$this->fractalManager->setFormat('hal'); // Override per request
Symfony Serializer Integration
Use Fractal’s DataArray with Symfony’s Serializer for hybrid APIs:
use League\Fractal\Serializer\DataArraySerializer;
$serializer = new DataArraySerializer();
$this->fractalManager->setSerializer($serializer);
Event Listeners Attach listeners to modify responses globally:
// src/EventListener/FractalListener.php
public function onKernelResponse(GetResponseForControllerResultEvent $event)
{
$result = $event->getControllerResult();
if ($result instanceof Item) {
$event->setResponse($this->fractalManager->createResponse($result));
}
}
Custom Error Handling Extend the manager to handle exceptions:
$this->fractalManager->addErrorHandler(function (Throwable $e) {
return new ErrorResponse($e->getMessage(), 500);
});
Testing
Mock FractalManager in tests:
$fractalManager = $this->createMock(FractalManager::class);
$fractalManager->method('createResponse')->willReturn(new JsonResponse([]));
$this->app->instance(FractalManager::class, $fractalManager);
Format Mismatches
jsonapi format but sending hal headers.$this->fractalManager->setFormat('hal');
Circular References
User ↔ Post).include paths carefully or implement lazy-loading:
public function includePosts(User $user)
{
return $this->collection($user->getPosts(), new PostTransformer(), 'posts');
}
Deprecated Symfony Versions
Configuration Overrides
config/packages/ is ignored if not loaded.Inspect Transformed Data
Use Fractal’s DataArray serializer to debug output:
$serializer = new DataArraySerializer();
$this->fractalManager->setSerializer($serializer);
// Response will now show raw arrays for inspection.
Enable Fractal Logging
Add to config/packages/monolog.yaml:
handlers:
fractal:
type: stream
path: "%kernel.logs_dir%/fractal.log"
level: debug
Then enable logging in the manager:
$this->fractalManager->setLogger($this->container->get('logger'));
Common Errors
jsonapi, hal) is supported by the installed league/fractal version.Custom Serializers Replace the default serializer:
$this->fractalManager->setSerializer(new CustomSerializer());
Dynamic Transformer Selection Use a factory to resolve transformers dynamically:
$transformer = $this->transformerFactory->create($entity->getClass());
Middleware Integration Add Fractal-specific middleware for request/response manipulation:
// src/Middleware/FractalMiddleware.php
public function handle(Request $request, Closure $next)
{
$response = $next($request);
if ($response instanceof JsonResponse) {
$response->headers->set('X-Data-Format', 'jsonapi');
}
return $response;
}
Database Abstraction Combine with Doctrine for ORM-aware transformers:
use Doctrine\ORM\EntityManagerInterface;
public function __construct(private EntityManagerInterface $em) {}
public function transform(Article $article)
{
$this->em->refresh($article); // Ensure fresh data
return [...];
}
How can I help you explore Laravel packages today?