Vai al contenuto principale

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;

toPrettyJson() — conversione in JSON formattato

$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()]);

Validazione e trasformazione dei parametri di richiesta

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'

Normalizzazione dei dati di form

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àFluentArray
Accesso a proprietà$fluent->name$array['name']
Method chaining✓ supportato✗ no
Conversione JSONmetodo toJson()funzione json_encode()
Dot notationget('user.name')✗ da gestire a mano
Verifica statoisEmpty()funzione empty()
Aggiunta di metodi dinamiciMacroable✗ non possibile

Fluent vs Model

FunzionalitàFluentModel
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.
Ultima modifica il 13 luglio 2026