serri/alchemist

The JSON Revolution for Laravel, a simple, fast, and elegant alternative to Laravel JSON Resource.

1.0.3 2025-05-21 13:56 UTC

This package is auto-updated.

Last update: 2025-05-21 14:11:43 UTC


README

Latest Version License Total Downloads

The JSON Revolution for Laravel, a simple, fast, and elegant alternative to Laravel JSON Resource.

๐Ÿ“– Table of Contents

  1. Philosophy
  2. Requirements
  3. Installation
  4. Fundamentals
  5. Quick Start
  6. Usage & Examples
  7. License

๐Ÿ”ฎ 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:

  • Each model deserves its own formula class ModelNameFormula.php

  • The BlankParchment remains your fallback option.

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

  • $fillable / $guarded : are available to use in formulas by default.

  • Decorators: Only #[Relation] and #[Mutagen] methods are exposed to formulas.

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.