Skip to main content

What is the Fluent Class?

The Fluent class is a versatile utility class that allows you to work with arrays as if they were objects. Implemented in Illuminate\Support\Fluent, it has existed since Laravel’s early versions but is rarely documented in the official guides. Internally, it manages array data as properties and uses magic methods (__get, __set, __call) to provide property-like read/write access.
With the addition of the fluent() helper function in Laravel 11 and enhancements to the Fluent class itself, now is the perfect time to leverage this powerful utility.

Creating Instances

Constructor

use Illuminate\Support\Fluent;

// Initialize with an array
$user = new Fluent(['name' => 'Laravel', 'type' => 'Framework']);

echo $user->name; // 'Laravel'
echo $user->type; // 'Framework'

make() Factory Method

use Illuminate\Support\Fluent;

$config = Fluent::make([
    'host' => 'localhost',
    'port' => 3306,
    'database' => 'laravel'
]);

echo $config->host; // 'localhost'

fluent() Helper Function

Laravel 11 introduces the fluent() helper function, equivalent to Fluent::make().
$request = fluent([
    'method' => 'POST',
    'path' => '/api/users',
    'status' => 201
]);

echo $request->method; // 'POST'

Property Access

Dynamic Property Read/Write

$fluent = new Fluent();

// Write
$fluent->name = 'Laravel';
$fluent->version = 13;

// Read
echo $fluent->name;    // 'Laravel'
echo $fluent->version; // 13

Method Chaining

The __call magic method allows you to call non-existent methods to set properties. Each method returns $this, enabling chaining.
$config = new Fluent();

$config
    ->host('localhost')
    ->port(3306)
    ->database('laravel')
    ->username('root')
    ->password('secret');

echo $config->host;     // 'localhost'
echo $config->password; // 'secret'
This enables a fluent interface for setting values, providing a more elegant alternative to array syntax.

Key Methods

get() — Access Using Dot Notation

$user = new Fluent([
    'profile' => [
        'email' => '[email protected]',
        'phone' => '090-xxxx-xxxx'
    ]
]);

// Access nested values using dot notation
$email = $user->get('profile.email'); // '[email protected]'
$phone = $user->get('profile.phone'); // '090-xxxx-xxxx'

// Specify a default value
$fax = $user->get('profile.fax', 'N/A'); // 'N/A'

set() — Set Values Using Dot Notation

$fluent = new Fluent();

$fluent->set('user.name', 'Laravel');
$fluent->set('user.email', '[email protected]');

print_r($fluent->toArray());
// Array (
//     [user] => Array (
//         [name] => Laravel
//         [email] => [email protected]
//     )
// )

fill() — Set Multiple Properties at Once

$fluent = new Fluent(['initial' => 'value']);

$fluent->fill([
    'name' => 'Laravel',
    'version' => 13,
    'license' => 'MIT'
]);

echo $fluent->name;    // 'Laravel'
echo $fluent->version; // 13

all() — Get All Properties as Array

$fluent = fluent([
    'name' => 'Laravel',
    'version' => 13,
    'license' => 'MIT'
]);

// All properties
$all = $fluent->all();
// ['name' => 'Laravel', 'version' => 13, 'license' => 'MIT']

// Specific properties only
$subset = $fluent->all(['name', 'license']);
// ['name' => 'Laravel', 'license' => 'MIT']

scope() — Convert Nested Values to New Fluent Instance

$config = fluent([
    'database' => [
        'host' => 'localhost',
        'port' => 3306,
        'name' => 'laravel'
    ]
]);

$dbConfig = $config->scope('database');
// $dbConfig is a new Fluent instance

echo $dbConfig->host; // 'localhost'
echo $dbConfig->port; // 3306
This allows you to work with nested arrays as separate Fluent objects.

value() — Set Default Values Using Callbacks

$user = fluent(['role' => 'admin']);

// Return value if key exists
$role = $user->value('role'); // 'admin'

// Return default if key doesn't exist
$status = $user->value('status', 'active');
// 'active'

// Specify default using callback
$timestamp = $user->value('updated_at', function () {
    return now()->toIso8601String();
});

Array Operations

toArray() — Convert to Array

$fluent = fluent(['name' => 'Laravel', 'version' => 13]);

$array = $fluent->toArray();
// ['name' => 'Laravel', 'version' => 13]

print_r($array);

getAttributes() — Direct Access to Internal Attributes

$fluent = fluent(['a' => 1, 'b' => 2]);

$attributes = $fluent->getAttributes();
// ['a' => 1, 'b' => 2]

ArrayAccess Interface

Fluent implements ArrayAccess, allowing array-like operations.
$config = new Fluent();

// Set like array
$config['host'] = 'localhost';
$config['port'] = 3306;

// Read like array
echo $config['host']; // 'localhost'

// Check existence
if (isset($config['port'])) {
    echo $config['port'];
}

// Delete
unset($config['port']);

IteratorAggregate Interface

Fluent can be looped using foreach.
$settings = fluent([
    'debug' => true,
    'cache' => 'redis',
    'queue' => 'database'
]);

foreach ($settings as $key => $value) {
    echo "$key: $value\n";
    // debug: 1
    // cache: redis
    // queue: database
}

JSON Processing

toJson() — Convert to JSON String

$response = fluent([
    'success' => true,
    'data' => ['id' => 1, 'name' => 'User']
]);

$json = $response->toJson();
// {"success":true,"data":{"id":1,"name":"User"}}

// Can be used directly in API responses
return $json;

toPrettyJson() — Convert to Formatted JSON

$data = fluent([
    'users' => [
        ['id' => 1, 'name' => 'Alice'],
        ['id' => 2, 'name' => 'Bob']
    ]
]);

echo $data->toPrettyJson();
// {
//     "users": [
//         {
//             "id": 1,
//             "name": "Alice"
//         },
//         {
//             "id": 2,
//             "name": "Bob"
//         }
//     ]
// }

JsonSerializable Interface

Fluent implements JsonSerializable, allowing direct use with json_encode().
$fluent = fluent(['status' => 'ok', 'code' => 200]);

$json = json_encode($fluent);
// {"status":"ok","code":200}

$data = json_decode($json, true);
// ['status' => 'ok', 'code' => 200]

State Checking

isEmpty() / isNotEmpty()

$empty = new Fluent();
$filled = fluent(['value' => 1]);

$empty->isEmpty();      // true
$empty->isNotEmpty();   // false

$filled->isEmpty();     // false
$filled->isNotEmpty();  // true

Conditionable Trait

Fluent uses the Conditionable trait, supporting conditional operations.
$config = fluent(['env' => 'production']);

$config
    ->when($config->env === 'production', function ($fluent) {
        $fluent->debug = false;
        $fluent->cache = 'redis';
    })
    ->when($config->env === 'local', function ($fluent) {
        $fluent->debug = true;
        $fluent->cache = 'array';
    });

echo $config->debug;
echo $config->cache;
Using when() / unless() methods enables fluent configuration based on conditions.

Macroable Trait

Fluent also uses the Macroable trait, allowing dynamic method addition.
use Illuminate\Support\Fluent;

// Define in your service provider's boot() method
Fluent::macro('isProduction', function () {
    /** @var Fluent $this */
    return $this->env === 'production';
});

Fluent::macro('isDevelopment', function () {
    /** @var Fluent $this */
    return $this->env === 'development';
});

// Usage
$config = fluent(['env' => 'production']);

if ($config->isProduction()) {
    // Production-specific logic
}

Practical Use Cases

API Response Builder

namespace App\Support;

use Illuminate\Support\Fluent;

class ApiResponse
{
    public static function success($data = null, string $message = 'Success'): string
    {
        return fluent([
            'success' => true,
            'message' => $message,
            'data' => $data,
            'timestamp' => now()->toIso8601String()
        ])->toJson();
    }

    public static function error(string $message, int $code = 400): string
    {
        return fluent([
            'success' => false,
            'message' => $message,
            'code' => $code,
            'timestamp' => now()->toIso8601String()
        ])->toJson();
    }
}

// Use in controllers
public function store(Request $request)
{
    $user = User::create($request->validated());

    return response()->json(
        json_decode(ApiResponse::success(['id' => $user->id]))
    );
}

Configuration Builder

$dbConfig = fluent()
    ->host(env('DB_HOST', 'localhost'))
    ->port(env('DB_PORT', 3306))
    ->database(env('DB_DATABASE', 'laravel'))
    ->username(env('DB_USERNAME', 'root'))
    ->password(env('DB_PASSWORD', ''))
    ->charset('utf8mb4')
    ->collation('utf8mb4_unicode_ci')
    ->when(env('APP_ENV') === 'production', function ($config) {
        $config->sslmode('require');
        $config->sslcert(env('DB_SSL_CERT'));
    });

// Use configuration
config(['database.connections.mysql' => $dbConfig->toArray()]);

Request Parameter Validation and Transformation

namespace App\Services;

use Illuminate\Support\Fluent;

class SearchFilter
{
    public function apply(array $params): Fluent
    {
        $filter = fluent()
            ->page($params['page'] ?? 1)
            ->perPage($params['per_page'] ?? 15)
            ->sort($params['sort'] ?? 'created_at')
            ->order($params['order'] ?? 'desc')
            ->when(isset($params['search']), function ($f) use ($params) {
                $f->search = $params['search'];
            })
            ->when(isset($params['status']), function ($f) use ($params) {
                $f->status = $params['status'];
            });

        // Calculate pagination
        $filter->offset = ($filter->page - 1) * $filter->perPage;

        return $filter;
    }
}

// Usage
$filter = app(SearchFilter::class)->apply(request()->all());

$users = User::query()
    ->when($filter->has('search'), fn ($q) => $q->search($filter->search))
    ->when($filter->has('status'), fn ($q) => $q->where('status', $filter->status))
    ->orderBy($filter->sort, $filter->order)
    ->offset($filter->offset)
    ->limit($filter->perPage)
    ->get();

Model and Fluent Integration

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Fluent;

class Post extends Model
{
    protected $casts = [
        'metadata' => 'json'
    ];

    public function getMetadataAttribute($value): Fluent
    {
        return new Fluent($value ?? []);
    }

    public function setMetadataAttribute($value): void
    {
        if ($value instanceof Fluent) {
            $this->attributes['metadata'] = $value->toJson();
        } else {
            $this->attributes['metadata'] = json_encode($value);
        }
    }
}

// Usage
$post = new Post();

$post->metadata = fluent()
    ->title('SEO Title')
    ->description('Meta Description')
    ->keywords(['laravel', 'fluent', 'tutorial'])
    ->author('Laravel Community');

$post->save();

// Reading
$post = Post::first();
echo $post->metadata->title; // 'SEO Title'
echo $post->metadata->author; // 'Laravel Community'

Form Data Normalization

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Fluent;

class CreateUserRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name' => 'required|string',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:8|confirmed',
            'role' => 'in:user,admin',
            'preferences' => 'json',
        ];
    }

    // Return validated data as Fluent
    public function toFluent(): Fluent
    {
        $preferences = is_string($this->preferences)
            ? json_decode($this->preferences, true)
            : $this->preferences;

        return fluent([
            'name' => $this->name,
            'email' => $this->email,
            'password' => bcrypt($this->password),
            'role' => $this->role ?? 'user',
            'preferences' => new Fluent($preferences ?? [])
        ]);
    }
}

// Use in controller
public function store(CreateUserRequest $request)
{
    $data = $request->toFluent();

    $user = User::create($data->all());

    return response()->json(['success' => true, 'user_id' => $user->id]);
}

Comparison with Other Classes

Fluent vs Array

FeatureFluentArray
Property Access$fluent->name$array['name']
Method Chaining✓ Supported✗ Not Available
JSON ConversiontoJson() Methodjson_encode() Function
Dot Notationget('user.name')✗ Manual Handling
State CheckingisEmpty()empty() Function
Dynamic MethodsMacroable✗ Not Possible

Fluent vs Model

FeatureFluentModel
DB Persistence✗ None✓ Automatic
Memory Efficiency✓ Lightweight✗ Heavy
Relationships✗ None✓ Supported
Casting✗ None✓ Supported
Validation✗ None✓ Supported
Fluent API✓ Yes△ Limited

Internal Implementation Details

class Fluent
{
    protected $attributes = [];

    // Magic method: non-existent property access
    public function __get($key)
    {
        return $this->value($key);
    }

    // Magic method: non-existent property set
    public function __set($key, $value)
    {
        $this->offsetSet($key, $value);
    }

    // Magic method: non-existent method call
    // Method name becomes property key
    public function __call($method, $parameters)
    {
        $this->attributes[$method] = count($parameters) > 0
            ? $parameters[0]
            : true;

        return $this;
    }
}
The __call method, when no macro is registered, sets the method name as the property key with the provided value and returns $this, enabling method chaining.
Fluent uses the following traits, each providing different capabilities:
  • Conditionablewhen() / unless() for conditional operations
  • InteractsWithData — Data manipulation methods like data()
  • Macroable — Dynamic method addition

Next Steps

Collection Class

Deep dive into the Collection class for working with multiple elements.

Conditionable Trait

Learn to write conditional logic fluently with the Conditionable trait.

Macroable Trait

Learn to dynamically add methods to existing classes with the Macroable trait.
Last modified on June 15, 2026