> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Classe Fluent

> Utilisez la classe Illuminate\Support\Fluent pour manipuler des données de tableau comme des objets. Une classe utilitaire discrète présente depuis les débuts de Laravel.

## 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`).

<Tip>
  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.
</Tip>

## Création d'instance

### Constructeur

```php theme={null}
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()

```php theme={null}
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()`.

```php theme={null}
$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

```php theme={null}
$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.

```php theme={null}
$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

```php theme={null}
$user = new Fluent([
    'profile' => [
        'email' => 'user@example.com',
        'phone' => '090-xxxx-xxxx'
    ]
]);

// Récupérer une valeur imbriquée avec la notation par point
$email = $user->get('profile.email'); // 'user@example.com'
$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

```php theme={null}
$fluent = new Fluent();

$fluent->set('user.name', 'Laravel');
$fluent->set('user.email', 'laravel@example.com');

print_r($fluent->toArray());
// Array (
//     [user] => Array (
//         [name] => Laravel
//         [email] => laravel@example.com
//     )
// )
```

### fill() — définir plusieurs propriétés à la fois

```php theme={null}
$fluent = new Fluent(['initial' => 'value']);

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

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

### all() — récupérer toutes les propriétés sous forme de tableau

```php theme={null}
$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

```php theme={null}
$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

```php theme={null}
$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

```php theme={null}
$fluent = fluent(['name' => 'Laravel', 'version' => 13]);

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

print_r($array);
```

### getAttributes() — accès direct aux attributs internes

```php theme={null}
$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.

```php theme={null}
$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.

```php theme={null}
$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

```php theme={null}
$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;
```

### toPrettyJson() — JSON formaté

```php theme={null}
$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.

```php theme={null}
$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()

```php theme={null}
$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.

```php theme={null}
$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.

```php theme={null}
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

```php theme={null}
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

```php theme={null}
$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()]);
```

### Validation et transformation des paramètres de requête

```php theme={null}
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

```php theme={null}
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'
```

### Normalisation des données de formulaire

```php theme={null}
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

```php theme={null}
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.

<Tip>
  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
</Tip>

## Étapes suivantes

<Card title="Classe Collection" icon="arrow-right-arrow-left" href="/fr/advanced/collection-deep-dive">
  Approfondissez la classe Collection qui manipule plusieurs éléments.
</Card>

<Card title="Trait Conditionable" icon="arrow-right-arrow-left" href="/fr/advanced/conditionable">
  Apprenez à écrire des traitements conditionnels fluides avec le trait Conditionable.
</Card>

<Card title="Trait Macroable" icon="arrow-right-arrow-left" href="/fr/advanced/macroable">
  Apprenez à ajouter dynamiquement des méthodes à une classe existante avec le trait Macroable.
</Card>


## Related topics

- [Manipulation de chaînes (classe Str)](/fr/strings.md)
- [Génération d'URL](/fr/urls.md)
- [Trait InteractsWithData](/fr/advanced/interacts-with-data.md)
- [Classe Lottery](/fr/advanced/lottery.md)
- [Tests HTTP](/fr/http-tests.md)
