serri / alchemist
The JSON Revolution for Laravel, a simple, fast, and elegant alternative to Laravel JSON Resource.
Requires
- php: ^8.2
Requires (Dev)
- orchestra/testbench: ^10.2.1
README
The JSON Revolution for Laravel, a simple, fast, and elegant alternative to Laravel JSON Resource.
๐ Table of Contents
๐ฎ Philosophy - The Problem with Traditional Laravel Resources
We've all been there:
- Creating endless resource classes that mostly repeat the same boilerplate.
- Duplicating code across multiple API responses.
- Drowning in maintenance when frontend requirements change.
- Wrestling with nested relationships that bloat your codebase.
The breaking point comes when:
- Your API evolves and resources multiply.
- Frontend devs request constant field changes.
- Your models grow, but your resource classes don't scale.
- Nested relationships turn into unmaintainable spaghetti.
The Solution: Laravel Alchemist - Formula Approach
One File to Rule Them All
Each model gets a single SomeModelFormula.php
where you:
โ
Define all fields as simple strings in arrays.
โ
Manage every API variation in one place.
โ
Update database changes with a single edit.
Relationship Handling Made Simple
- Reference nested resources by their name only.
- Each relation maintains its own
Formula::class
. - No more recursive resource nightmares.
Frontend-Friendly Flexibility
- Instantly modify fields without resource class hopping.
- Track all API variations through clear formula methods.
- Never miss a field update again.
Why This Works
- Less Code: Eliminates 80%+ of resource boilerplate.
- True Maintainability: All changes flow through controlled formulas.
- Team Friendly: Frontend can request changes without breaking your flow.
โLaravel Resources grant you the illusion of control โ meticulous yet maddening. Laravel Alchemist surrenders this false dominion... and in its place conjures true magic.โ
๐ Requirements
- PHP โฅ 8.2
- Laravel โฅ 11.x
๐ง Installation
You may install Alchemist using the Composer package manager:
composer require serri/alchemist
You can publish the Alchemist configuration file config/alchemist.php
and the default Formulas/Formula.php
using
vendor:publish
Artisan command:
php artisan vendor:publish --provider="Serri\Alchemist\AlchemistServiceProvider"
Or for configuration file using:
php artisan vendor:publish --tag=alchemist-config
For default formula class using:
php artisan vendor:publish --tag=alchemist-formula
๐ Fundamentals
To wield this package's magic effectively, you must understand these arcane principles:
The Formulas Directory
- Your sacred workshop where all model formulas reside
- Created automatically at
app/Formulas/Formula.php
when you publish the default formula class as we did in the Installtion Section:
namespace App\Formulas; class Formula { const BlankParchment = ['id']; # Default formula. }
Crafting Your Formulas
Begin your alchemy by creating formula classes in app/Formulas/
like so:
namespace App\Formulas; class UserFormula extends Formula { # Define your transformations here. # ex: const UserLogin = ['id', 'username', /*...etc.*/] // ... other formulas. }
Key Laws:
Using package default Formula
If you did not publish the app/Formulas/Formula.php
, you can still use the default Formula.php
provided by the package
like this:
namespace App\Formulas; use Serri\Alchemist\Formulas\Formula class UserFormula extends Formula { // Define your transformations here. }
Relationships must be explicitly marked with the #[Relation]
attribute to be available in formulas:
๐ช Quick Start
1. Model Configuration
To enable formula support, models must use HasAlchemyFormulas
Concern.
use Serri\Alchemist\Concerns\HasAlchemyFormulas; use Illuminate\Database\Eloquent\Model; class Post extends Model { use HasAlchemyFormulas; // }
2. Exposing Fields
By default, everything included in $fillable
array and $guarded
array are automatically loaded in formulas.
use Serri\Alchemist\Concerns\HasAlchemyFormulas; use Illuminate\Database\Eloquent\Model; class Post extends Model { use HasAlchemyFormulas; # Automatically exposed to formulas. protected $guarded = ['id']; # Automatically exposed to formulas. protected $fillable = [ 'title', 'description', 'published_at' ] }
3. Exposing Relationships
Relationships must be explicitly marked with the #[Relation]
decorator to be available in formulas:
use Serri\Alchemist\Decorators\Relation; #[Relation] # Exposed to formulas as 'comments' public function comments(): HasMany { return $this->hasMany(Comment::class); } #[Relation(name: 'author_profile')] # Exposed to formulas as 'author_profile' public function profile(): HasOne { return $this->hasOne(Profile::class); }
4. Exposing Custom Methods
Model methods require the #[Mutagen]
decorator to be accessible in formulas:
use Serri\Alchemist\Decorators\Mutagen; #[Mutagen] # Exposed to formulas as 'fullName' public function fullName(): string { return "{$this->first_name} {$this->last_name}"; } #[Mutagen(name: 'is_verified')] # Exposed to formulas as 'is_verified' public function isVerified(): bool { return $this->email_verified_at !== null; }
Keynotes
5. Crafting Formulas
Once your models are properly configured, you can define formulas to transform your data. Formulas are defined in
classes within the app/Formulas/
directory.
Here is an example:
namespace App\Formulas; class PostFormula extends Formula { const Author = ['id', 'title', 'author_profile']; const WithComments = ['id', 'title', 'comments'] const Detailed = ['id', 'title', 'description', 'comments', 'author_profile'] }
For profile formula:
namespace App\Formulas; class ProfileFormula extends Formula { const OnlyName = ['fullName']; const AnyOther = ['id', 'username', 'fullName'] }
๐ ๏ธ Usage & Examples
Basic Data Transformation
To transform model data using your formulas:
use App\Models\Post; use App\Formulas\PostFormula; use Serri\Alchemist\Facades\Alchemist; // 1. Fetch your models $posts = Post::with('author.profile')->get(); // 2. Specify which formulas to use Post::setFormula(PostFormula::Author); Profile::setFormula(ProfileFormula::OnlyName); // 3. Process through Alchemist $transformedData = Alchemist::brew($posts);
Results:
[ [ 'id' => 1, 'title' => "Post 1", 'author_profile' => [ 'fullName' => "some author name" ] ], [ 'id' => 2, 'title' => 'Post 2', 'author_profile' => [ 'fullName' => "some author name" ] ], [ 'id' => 3, 'title' => 'Post 3', 'author_profile' => [ 'fullName' => "some author name" ] ] ]
Key Methods
Method | Purpose | Example |
---|---|---|
setFormula() |
Assigns formula variant | Post::setFormula(PostFormula::DetailedView) |
brew() |
Executes transformation | Alchemist::brew($collection|$model) |
brewBatch() |
Executes transformation | Alchemist::brewBatch($paginator) |
Patterns
1. Context-Aware Formulas
$formula = auth()->user()->isAdmin() ? PostFormula::AdminView : PostFormula::PublicView; Post::setFormula($formula);
2. Direct Model Transformation
$post = Post::find(1); return Alchemist::brew($post); // Auto-detects single model
3. Pagination Support
$paginated = Post::paginate(15); return Alchemist::brewBatch($paginated); // Preserves pagination structure
Syntax Variations
1. Helper Function (Simplest)
$posts = Post::all(); $transformed = alchemist()->brew($posts);
2. Facade (For static contexts)
use Serri\Alchemist\Facades\Alchemist; $data = Alchemist::brew($models);
3. Dependency Injection (Recommended for controllers)
use Serri\Alchemist\Services\Alchemist; class PostController { public function __construct( protected Alchemist $alchemist ) {} public function index() { return $this->alchemist->brew(Post::all()); } }
๐ License
This project is open-source and available under the MIT License.