cyvelnet/laravel5-fractal
Laravel 5 Fractal offers a simple Laravel-friendly wrapper around League Fractal for transforming Eloquent models and collections into clean API responses, with reusable transformers, includes, serializers, and pagination support.
composer require cyvelnet/laravel5-fractal
php artisan vendor:publish --provider="Cyvelnet\Laravel5Fractal\FractalServiceProvider"
Laravel 5.5+ auto-discovers the provider and Fractal facade. On older versions, register Cyvelnet\Laravel5Fractal\FractalServiceProvider and alias 'Fractal' => Cyvelnet\Laravel5Fractal\Facades\Fractal::class in config/app.php.
Look first at config/fractal.php — the serializer key is the single setting that most affects your JSON shape. Default behavior uses League\Fractal\Serializer\ArraySerializer; switching to DataArraySerializer gives you the conventional {"data": ...} envelope.
Generate a transformer with the bundled command (the main convenience this package adds over raw Fractal):
php artisan make:transformer BookTransformer
It lands in app/Transformers. Fill in transform():
public function transform(Book $book)
{
return [
'id' => (int) $book->id,
'title' => $book->title,
'author' => [
'id' => (int) $book->author->id,
'name' => $book->author->name,
],
];
}
First end-to-end use — a controller returning a single item and a collection:
use Cyvelnet\Laravel5Fractal\Facades\Fractal;
return response()->json(
Fractal::item($book)->transformWith(new BookTransformer)->toArray()
);
return response()->json(
Fractal::collection($books)->transformWith(new BookTransformer)->toArray()
);
There is no response macro in this package (unlike spatie/laravel-fractal), so you always wrap in response()->json() yourself.
Item vs. collection. Fractal::item() and Fractal::collection() map to Fractal's Item/Collection resources. Everything else (transformWith, parseIncludes, paginateWith, setMeta, addMeta, toArray) is the underlying League Fractal API you already know — the facade is a thin proxy over a Manager instance.
Includes via query string. This is the standard REST pattern and works out of the box:
$manager = Fractal::parseIncludes(request('include', ''));
return response()->json(
$manager->collection($books)->transformWith(new BookTransformer)->toArray()
);
The include parameter must match relationship names in the transformer ($this->item($book->author, new AuthorTransformer)). Eager-load them yourself — Fractal does not protect you from N+1:
$books = Book::with($this->parseIncludes(request('include')))->paginate();
Pagination with metadata. Use IlluminatePaginatorAdapter and paginateWith — otherwise the paginator is serialized as a plain array and you lose meta.pagination:
$paginator = Book::paginate(20);
$adapter = new \League\Fractal\Pagination\IlluminatePaginatorAdapter($paginator);
return response()->json(
Fractal::collection($paginator->getCollection())
->paginateWith($adapter)
->transformWith(new BookTransformer)
->toArray()
);
Serializer strategy. DataArraySerializer is the sane default for JSON APIs. Set it once in the config, and use setSerializer() only when you need a per-endpoint override (e.g. a flat payload for an internal endpoint).
Nested transformers. Keep transformers small and compose them:
public function includeAuthor(Book $book)
{
return $this->item($book->author, new AuthorTransformer);
}
This is what makes ?include=author work and keeps payloads flexible.
Response shape in one place. Since there is no macro, add a small base controller helper or a JsonResponse macro of your own; do not scatter response()->json(Fractal::...) calls across controllers — you will regret it when the envelope changes.
Caching. Fractal has no cache. Wrap the transformed array with Cache::remember() keyed on the include string, page, and model version/updated_at. Transform after fetching, cache the array, not the Eloquent models.
ArraySerializer and DataArraySerializer changes your payload from {...} to {"data": {...}}. Clients that expect one will break on the other. Pick once, test the shape.paginateWith(new IlluminatePaginatorAdapter($p)) you get a plain Collection resource with no meta.pagination. That is expected, not a bug.parseIncludes(); otherwise a typo or malicious value causes ModelNotFoundException or unexpected eager loads. Sanitize with explode(',', request('include')) and intersect.Fractal resolves from the container; includes and serializers set during a request can persist into the next on long-running workers. Reset with Fractal::setSerializer(...) and Fractal::parseIncludes([]) (or re-resolve the manager) per request.config/fractal.php fire on every transform. If a relationship is not eager-loaded, you get N+1 silently. Keep the default list tiny or empty, and eager-load explicitly.toArray() returns an array, toJson() returns a JSON string. If you then response()->json($string), Laravel double-encodes it. Use toArray() with response()->json().app/Transformers namespace in your autoloader as usual (composer autoload picks it up if it's PSR-4 under App\).spatie/laravel-fractal. APIs differ (Fractal::item() here vs fractal()->item() there). Do not copy-paste snippets from spatie docs; response()->fractal() does not exist in this package.league/fractal is in maintenance mode. This wrapper is thin enough that migrating off it later means replacing Fractal::item() with (new Manager())->createData(new Item(...)). For new projects consider native Laravel API Resources or spatie/laravel-fractal, which has more activity and a response macro.league/fractal version constraints (^0.20-era) can conflict with other packages.$request->include is actually populated, the includeXxx method name matches the camelized key, and the transformer extends League\Fractal\TransformerAbstract (not a plain class).extend or bind a custom Fractal class via the container if you need a macro-style response helper of your own.How can I help you explore Laravel packages today?