Qu’est-ce que la classe Fluent
La classe Fluent est une classe utilitaire générique qui permet de traiter un tableau comme un objet. Implémentée dans Illuminate\Support\Fluent, elle existe depuis les premières versions de Laravel mais n’est presque pas mentionnée dans la documentation officielle.
En interne, elle gère un tableau via des propriétés et permet l’accès en lecture/écriture façon propriétés via les méthodes magiques (__get / __set / __call).
Laravel 11 a ajouté le helper fluent() et la classe Fluent a été enrichie. C’est le bon moment pour tirer parti de cette excellente classe.
Création d’instance
Constructeur
use Illuminate\Support\ Fluent ;
// Initialisation à partir d'un tableau
$user = new Fluent ([ 'name' => 'Laravel' , 'type' => 'Framework' ]);
echo $user -> name ; // 'Laravel'
echo $user -> type ; // 'Framework'
Méthode fabrique make()
use Illuminate\Support\ Fluent ;
$config = Fluent :: make ([
'host' => 'localhost' ,
'port' => 3306 ,
'database' => 'laravel'
]);
echo $config -> host ; // 'localhost'
Fonction helper fluent()
Laravel 11 a ajouté le helper fluent(). Il est équivalent à Fluent::make().
$request = fluent ([
'method' => 'POST' ,
'path' => '/api/users' ,
'status' => 201
]);
echo $request -> method ; // 'POST'
Accès aux propriétés
Lecture/écriture dynamique des propriétés
$fluent = new Fluent ();
// Écriture
$fluent -> name = 'Laravel' ;
$fluent -> version = 13 ;
// Lecture
echo $fluent -> name ; // 'Laravel'
echo $fluent -> version ; // 13
Chaînage de méthodes
Grâce à la méthode magique __call, l’appel d’une méthode inexistante définit une propriété. La méthode retourne $this, ce qui permet le chaînage.
$config = new Fluent ();
$config
-> host ( 'localhost' )
-> port ( 3306 )
-> database ( 'laravel' )
-> username ( 'root' )
-> password ( 'secret' );
echo $config -> host ; // 'localhost'
echo $config -> password ; // 'secret'
Ce mécanisme permet de définir des valeurs via une API fluide (Fluent Interface) au lieu d’un tableau.
Principales méthodes
get() — accès avec la notation par point
$user = new Fluent ([
'profile' => [
'email' => '[email protected] ' ,
'phone' => '090-xxxx-xxxx'
]
]);
// Récupérer une valeur imbriquée avec la notation par point
$email = $user -> get ( 'profile.email' ); // '[email protected] '
$phone = $user -> get ( 'profile.phone' ); // '090-xxxx-xxxx'
// On peut préciser une valeur par défaut
$fax = $user -> get ( 'profile.fax' , 'N/A' ); // 'N/A'
set() — écriture avec la notation par point
$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() — définir plusieurs propriétés à la fois
$fluent = new Fluent ([ 'initial' => 'value' ]);
$fluent -> fill ([
'name' => 'Laravel' ,
'version' => 13 ,
'license' => 'MIT'
]);
echo $fluent -> name ; // 'Laravel'
echo $fluent -> version ; // 13
$fluent = fluent ([
'name' => 'Laravel' ,
'version' => 13 ,
'license' => 'MIT'
]);
// Toutes les propriétés
$all = $fluent -> all ();
// ['name' => 'Laravel', 'version' => 13, 'license' => 'MIT']
// Seulement certaines
$subset = $fluent -> all ([ 'name' , 'license' ]);
// ['name' => 'Laravel', 'license' => 'MIT']
scope() — convertir une valeur imbriquée en un nouveau Fluent
$config = fluent ([
'database' => [
'host' => 'localhost' ,
'port' => 3306 ,
'name' => 'laravel'
]
]);
$dbConfig = $config -> scope ( 'database' );
// $dbConfig est une nouvelle instance Fluent
echo $dbConfig -> host ; // 'localhost'
echo $dbConfig -> port ; // 3306
Cela permet de manipuler un tableau imbriqué comme un autre objet Fluent.
value() — valeur par défaut dynamique via un callback
$user = fluent ([ 'role' => 'admin' ]);
// Si la clé existe, la valeur est retournée
$role = $user -> value ( 'role' ); // 'admin'
// Si la clé n'existe pas, la valeur par défaut est retournée
$status = $user -> value ( 'status' , 'active' );
// 'active'
// Valeur par défaut spécifiée via callback
$timestamp = $user -> value ( 'updated_at' , function () {
return now () -> toIso8601String ();
});
Manipulations de tableau
toArray() — conversion en tableau
$fluent = fluent ([ 'name' => 'Laravel' , 'version' => 13 ]);
$array = $fluent -> toArray ();
// ['name' => 'Laravel', 'version' => 13]
print_r ( $array );
getAttributes() — accès direct aux attributs internes
$fluent = fluent ([ 'a' => 1 , 'b' => 2 ]);
$attributes = $fluent -> getAttributes ();
// ['a' => 1, 'b' => 2]
Interface ArrayAccess
Fluent implémente ArrayAccess, donc il se manipule comme un tableau.
$config = new Fluent ();
// Affectation type tableau
$config [ 'host' ] = 'localhost' ;
$config [ 'port' ] = 3306 ;
// Lecture type tableau
echo $config [ 'host' ]; // 'localhost'
// Vérification d'existence
if ( isset ( $config [ 'port' ])) {
echo $config [ 'port' ];
}
// Suppression
unset ( $config [ 'port' ]);
Interface IteratorAggregate
Fluent peut être itéré via foreach.
$settings = fluent ([
'debug' => true ,
'cache' => 'redis' ,
'queue' => 'database'
]);
foreach ( $settings as $key => $value ) {
echo " $key : $value \n " ;
// debug: 1
// cache: redis
// queue: database
}
Traitement JSON
toJson() — conversion en chaîne JSON
$response = fluent ([
'success' => true ,
'data' => [ 'id' => 1 , 'name' => 'User' ]
]);
$json = $response -> toJson ();
// {"success":true,"data":{"id":1,"name":"User"}}
// Utilisable en réponse API
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"
// }
// ]
// }
Interface JsonSerializable
Fluent implémente JsonSerializable : json_encode() peut donc le convertir directement.
$fluent = fluent ([ 'status' => 'ok' , 'code' => 200 ]);
$json = json_encode ( $fluent );
// {"status":"ok","code":200}
$data = json_decode ( $json , true );
// ['status' => 'ok', 'code' => 200]
Vérification d’état
isEmpty() / isNotEmpty()
$empty = new Fluent ();
$filled = fluent ([ 'value' => 1 ]);
$empty -> isEmpty (); // true
$empty -> isNotEmpty (); // false
$filled -> isEmpty (); // false
$filled -> isNotEmpty (); // true
Trait Conditionable
Fluent utilise le trait Conditionable et prend en charge le traitement conditionnel.
$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 ;
Les méthodes when() / unless() permettent d’écrire de manière fluide une configuration conditionnelle.
Trait Macroable
Fluent utilise également le trait Macroable, ce qui permet d’ajouter dynamiquement des méthodes.
use Illuminate\Support\ Fluent ;
// À définir dans la méthode boot() d'un provider
Fluent :: macro ( 'isProduction' , function () {
/** @var Fluent $this */
return $this -> env === 'production' ;
});
Fluent :: macro ( 'isDevelopment' , function () {
/** @var Fluent $this */
return $this -> env === 'development' ;
});
// Utilisation
$config = fluent ([ 'env' => 'production' ]);
if ( $config -> isProduction ()) {
// Traitement production
}
Cas d’usage pratiques
Constructeur de réponse API
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 ();
}
}
// Utilisation dans un contrôleur
public function store ( Request $request )
{
$user = User :: create ( $request -> validated ());
return response () -> json (
json_decode ( ApiResponse :: success ([ 'id' => $user -> id ]))
);
}
Constructeur de configuration
$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' ));
});
// Vérifier et utiliser la config
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' ];
});
// Calcul de la pagination
$filter -> offset = ( $filter -> page - 1 ) * $filter -> perPage ;
return $filter ;
}
}
// Utilisation
$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 ();
Combinaison Model et Fluent
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 );
}
}
}
// Utilisation
$post = new Post ();
$post -> metadata = fluent ()
-> title ( 'SEO Title' )
-> description ( 'Meta Description' )
-> keywords ([ 'laravel' , 'fluent' , 'tutorial' ])
-> author ( 'Laravel Community' );
$post -> save ();
// Lecture
$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' ,
];
}
// Retourne les données validées sous forme de 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 ?? [])
]);
}
}
// Utilisation dans un contrôleur
public function store ( CreateUserRequest $request )
{
$data = $request -> toFluent ();
$user = User :: create ( $data -> all ());
return response () -> json ([ 'success' => true , 'user_id' => $user -> id ]);
}
Comparaison avec d’autres classes
Fluent vs Array
Fonctionnalité Fluent Array Accès aux propriétés $fluent->name$array['name']Chaînage de méthodes ✓ Pris en charge ✗ Non Conversion JSON Méthode toJson() Fonction json_encode() Notation par point ✓ get('user.name') ✗ À gérer manuellement Vérification d’état isEmpty()Fonction empty() Ajout dynamique de méthodes Macroable ✗ Impossible
Fluent vs Model
Fonctionnalité Fluent Model Persistance BDD ✗ Non ✓ Automatique Efficacité mémoire ✓ Léger ✗ Lourd Relations ✗ Non ✓ Prises en charge Casts ✗ Non ✓ Pris en charge Validation ✗ Non ✓ Prise en charge API fluide ✓ Oui △ Limitée
Détails d’implémentation interne
class Fluent
{
protected $attributes = [];
// Méthode magique : accès à une propriété inexistante
public function __get ( $key )
{
return $this -> value ( $key );
}
// Méthode magique : affectation d'une propriété inexistante
public function __set ( $key , $value )
{
$this -> offsetSet ( $key , $value );
}
// Méthode magique : appel d'une méthode inexistante
// Le nom de la méthode devient la clé de la propriété
public function __call ( $method , $parameters )
{
$this -> attributes [ $method ] = count ( $parameters ) > 0
? $parameters [ 0 ]
: true ;
return $this ;
}
}
Si aucune macro n’est enregistrée, la méthode __call utilise le nom de la méthode comme clé et définit la valeur, puis retourne $this. C’est ce qui rend possible le chaînage de méthodes.
Fluent utilise les traits suivants, chacun fournissant des fonctionnalités différentes :
Conditionable — traitement conditionnel avec when() / unless()
InteractsWithData — méthodes de manipulation de données comme data()
Macroable — ajout dynamique de méthodes
Étapes suivantes
Classe Collection Approfondissez la classe Collection qui manipule plusieurs éléments.
Trait Conditionable Apprenez à écrire des traitements conditionnels fluides avec le trait Conditionable.
Trait Macroable Apprenez à ajouter dynamiquement des méthodes à une classe existante avec le trait Macroable.