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 ;
$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 ()]);
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'
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
Feature Fluent Array Property Access $fluent->name$array['name']Method Chaining ✓ Supported ✗ Not Available JSON Conversion toJson() Methodjson_encode() FunctionDot Notation ✓ get('user.name') ✗ Manual Handling State Checking isEmpty()empty() FunctionDynamic Methods Macroable ✗ Not Possible
Fluent vs Model
Feature Fluent Model 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:
Conditionable — when() / 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.