Cos’è la classe Fluent
La classe Fluent è un’utility che permette di trattare un array come un oggetto. È implementata in Illuminate\Support\Fluent, esiste dalle prime versioni di Laravel ed è quasi assente dalla documentazione ufficiale.
Internamente gestisce l’array in una proprietà e, tramite magic method (__get / __set / __call), consente accessi in stile proprietà.
In Laravel 11 è stato aggiunto l’helper fluent() e la classe Fluent è stata potenziata: è il momento giusto per sfruttarla.
Creare un’istanza
Costruttore
use Illuminate\Support\ Fluent ;
// inizializza con un array
$user = new Fluent ([ 'name' => 'Laravel' , 'type' => 'Framework' ]);
echo $user -> name ; // 'Laravel'
echo $user -> type ; // 'Framework'
Factory method make()
use Illuminate\Support\ Fluent ;
$config = Fluent :: make ([
'host' => 'localhost' ,
'port' => 3306 ,
'database' => 'laravel'
]);
echo $config -> host ; // 'localhost'
Helper fluent()
In Laravel 11 è stato aggiunto l’helper fluent(), equivalente a Fluent::make().
$request = fluent ([
'method' => 'POST' ,
'path' => '/api/users' ,
'status' => 201
]);
echo $request -> method ; // 'POST'
Accesso alle proprietà
Lettura e scrittura di proprietà dinamiche
$fluent = new Fluent ();
// scrittura
$fluent -> name = 'Laravel' ;
$fluent -> version = 13 ;
// lettura
echo $fluent -> name ; // 'Laravel'
echo $fluent -> version ; // 13
Method chaining
Grazie al magic method __call, chiamando un metodo inesistente imposti una proprietà. Il metodo restituisce $this, quindi puoi concatenare.
$config = new Fluent ();
$config
-> host ( 'localhost' )
-> port ( 3306 )
-> database ( 'laravel' )
-> username ( 'root' )
-> password ( 'secret' );
echo $config -> host ; // 'localhost'
echo $config -> password ; // 'secret'
Con questo meccanismo puoi impostare i valori in stile fluent API invece che con un array.
Metodi principali
get() — accesso con dot notation
$user = new Fluent ([
'profile' => [
'email' => '[email protected] ' ,
'phone' => '090-xxxx-xxxx'
]
]);
// accedi ai valori annidati con la dot notation
$email = $user -> get ( 'profile.email' ); // '[email protected] '
$phone = $user -> get ( 'profile.phone' ); // '090-xxxx-xxxx'
// puoi indicare un default
$fax = $user -> get ( 'profile.fax' , 'N/A' ); // 'N/A'
set() — impostazione con 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() — impostare più proprietà in un colpo solo
$fluent = new Fluent ([ 'initial' => 'value' ]);
$fluent -> fill ([
'name' => 'Laravel' ,
'version' => 13 ,
'license' => 'MIT'
]);
echo $fluent -> name ; // 'Laravel'
echo $fluent -> version ; // 13
all() — ottenere tutte le proprietà come array
$fluent = fluent ([
'name' => 'Laravel' ,
'version' => 13 ,
'license' => 'MIT'
]);
// tutte le proprietà
$all = $fluent -> all ();
// ['name' => 'Laravel', 'version' => 13, 'license' => 'MIT']
// solo alcune
$subset = $fluent -> all ([ 'name' , 'license' ]);
// ['name' => 'Laravel', 'license' => 'MIT']
scope() — converte un valore annidato in un nuovo Fluent
$config = fluent ([
'database' => [
'host' => 'localhost' ,
'port' => 3306 ,
'name' => 'laravel'
]
]);
$dbConfig = $config -> scope ( 'database' );
// $dbConfig è una nuova istanza Fluent
echo $dbConfig -> host ; // 'localhost'
echo $dbConfig -> port ; // 3306
Così un array annidato diventa un altro oggetto Fluent.
value() — default dinamico con callback
$user = fluent ([ 'role' => 'admin' ]);
// se la chiave esiste, restituisce il valore
$role = $user -> value ( 'role' ); // 'admin'
// se non esiste, restituisce il default
$status = $user -> value ( 'status' , 'active' );
// 'active'
// default indicato tramite callback
$timestamp = $user -> value ( 'updated_at' , function () {
return now () -> toIso8601String ();
});
Operazioni con array
toArray() — conversione in array
$fluent = fluent ([ 'name' => 'Laravel' , 'version' => 13 ]);
$array = $fluent -> toArray ();
// ['name' => 'Laravel', 'version' => 13]
print_r ( $array );
getAttributes() — accesso diretto agli attributi interni
$fluent = fluent ([ 'a' => 1 , 'b' => 2 ]);
$attributes = $fluent -> getAttributes ();
// ['a' => 1, 'b' => 2]
Interfaccia ArrayAccess
Fluent implementa ArrayAccess, quindi lo usi come un array.
$config = new Fluent ();
// scrittura in stile array
$config [ 'host' ] = 'localhost' ;
$config [ 'port' ] = 3306 ;
// lettura in stile array
echo $config [ 'host' ]; // 'localhost'
// verifica di esistenza
if ( isset ( $config [ 'port' ])) {
echo $config [ 'port' ];
}
// eliminazione
unset ( $config [ 'port' ]);
Interfaccia IteratorAggregate
Fluent è iterabile con foreach.
$settings = fluent ([
'debug' => true ,
'cache' => 'redis' ,
'queue' => 'database'
]);
foreach ( $settings as $key => $value ) {
echo " $key : $value \n " ;
// debug: 1
// cache: redis
// queue: database
}
Gestione JSON
toJson() — conversione in stringa JSON
$response = fluent ([
'success' => true ,
'data' => [ 'id' => 1 , 'name' => 'User' ]
]);
$json = $response -> toJson ();
// {"success":true,"data":{"id":1,"name":"User"}}
// utilizzabile come risposta 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"
// }
// ]
// }
Interfaccia JsonSerializable
Fluent implementa JsonSerializable, quindi lo converti direttamente con 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]
Verifica dello stato
isEmpty() / isNotEmpty()
$empty = new Fluent ();
$filled = fluent ([ 'value' => 1 ]);
$empty -> isEmpty (); // true
$empty -> isNotEmpty (); // false
$filled -> isEmpty (); // false
$filled -> isNotEmpty (); // true
Trait Conditionable
Fluent usa il trait Conditionable per supportare l’elaborazione condizionale.
$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 ;
Con when() / unless() scrivi in modo fluente configurazioni condizionali.
Trait Macroable
Fluent usa anche il trait Macroable: puoi aggiungergli dinamicamente metodi.
use Illuminate\Support\ Fluent ;
// definisci nel boot() di un provider
Fluent :: macro ( 'isProduction' , function () {
/** @var Fluent $this */
return $this -> env === 'production' ;
});
Fluent :: macro ( 'isDevelopment' , function () {
/** @var Fluent $this */
return $this -> env === 'development' ;
});
// utilizzo
$config = fluent ([ 'env' => 'production' ]);
if ( $config -> isProduction ()) {
// logica per la produzione
}
Casi d’uso pratici
Builder di risposte 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 ();
}
}
// utilizzo nel controller
public function store ( Request $request )
{
$user = User :: create ( $request -> validated ());
return response () -> json (
json_decode ( ApiResponse :: success ([ 'id' => $user -> id ]))
);
}
Builder di configurazione
$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' ));
});
// valida e usa la configurazione
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' ];
});
// calcolo della paginazione
$filter -> offset = ( $filter -> page - 1 ) * $filter -> perPage ;
return $filter ;
}
}
// utilizzo
$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 ();
Combinazione tra modello e 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 );
}
}
}
// utilizzo
$post = new Post ();
$post -> metadata = fluent ()
-> title ( 'SEO Title' )
-> description ( 'Meta Description' )
-> keywords ([ 'laravel' , 'fluent' , 'tutorial' ])
-> author ( 'Laravel Community' );
$post -> save ();
// lettura
$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' ,
];
}
// restituisce i dati validati come 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 ?? [])
]);
}
}
// utilizzo nel controller
public function store ( CreateUserRequest $request )
{
$data = $request -> toFluent ();
$user = User :: create ( $data -> all ());
return response () -> json ([ 'success' => true , 'user_id' => $user -> id ]);
}
Confronto con altre classi
Fluent vs Array
Funzionalità Fluent Array Accesso a proprietà $fluent->name$array['name']Method chaining ✓ supportato ✗ no Conversione JSON metodo toJson() funzione json_encode() Dot notation ✓ get('user.name') ✗ da gestire a mano Verifica stato isEmpty()funzione empty() Aggiunta di metodi dinamici Macroable ✗ non possibile
Fluent vs Model
Funzionalità Fluent Model Persistenza DB ✗ no ✓ automatica Efficienza memoria ✓ leggero ✗ pesante Relation ✗ no ✓ supportate Cast ✗ no ✓ supportati Validazione ✗ no ✓ supportata API fluent ✓ sì △ limitata
Dettagli dell’implementazione interna
class Fluent
{
protected $attributes = [];
// magic method: accesso a proprietà inesistente
public function __get ( $key )
{
return $this -> value ( $key );
}
// magic method: set di proprietà inesistente
public function __set ( $key , $value )
{
$this -> offsetSet ( $key , $value );
}
// magic method: chiamata a metodo inesistente
// il nome del metodo diventa direttamente la chiave della proprietà
public function __call ( $method , $parameters )
{
$this -> attributes [ $method ] = count ( $parameters ) > 0
? $parameters [ 0 ]
: true ;
return $this ;
}
}
Il metodo __call, se non c’è una macro registrata, imposta il valore usando il nome del metodo come chiave dell’attributo e restituisce $this: è ciò che rende possibile il method chaining.
Fluent usa i seguenti trait, ognuno per una funzionalità diversa:
Conditionable — elaborazione condizionale con when() / unless()
InteractsWithData — metodi di accesso ai dati come data()
Macroable — aggiunta dinamica di metodi
Prossimi passi
Classe Collection Approfondisci la classe Collection per gestire più elementi.
Trait Conditionable Impara il trait Conditionable per scrivere elaborazioni condizionali in modo fluent.
Trait Macroable Impara il trait Macroable per aggiungere metodi dinamici a classi esistenti.