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

# Fluent 클래스

> Illuminate\Support\Fluent 클래스를 사용해 배열 데이터를 오브젝트처럼 다루는 방법을 해설합니다. Laravel 초기부터 있던 숨은 편리 클래스입니다.

## Fluent 클래스란

`Fluent` 클래스는 배열을 오브젝트처럼 다룰 수 있는 범용 유틸리티 클래스입니다. `Illuminate\Support\Fluent`에 구현되어 있으며, Laravel 초기 버전부터 존재하지만 공식 문서에는 거의 기재되어 있지 않습니다.

내부적으로는 배열을 프로퍼티로 관리하고, 매직 메서드(`__get` / `__set` / `__call`)를 통해 프로퍼티와 같은 읽고쓰기를 실현하고 있습니다.

<Tip>
  Laravel 11에서 `fluent()` 헬퍼가 추가되었고, Fluent 클래스도 기능이 강화되어, 지금이야말로 활용해야 할 훌륭한 클래스입니다.
</Tip>

## 인스턴스 생성

### 컨스트럭터

```php theme={null}
use Illuminate\Support\Fluent;

// 配列で初期化
$user = new Fluent(['name' => 'Laravel', 'type' => 'Framework']);

echo $user->name; // 'Laravel'
echo $user->type; // 'Framework'
```

### make() 팩토리 메서드

```php theme={null}
use Illuminate\Support\Fluent;

$config = Fluent::make([
    'host' => 'localhost',
    'port' => 3306,
    'database' => 'laravel'
]);

echo $config->host; // 'localhost'
```

### fluent() 헬퍼 함수

Laravel 11에서 `fluent()` 헬퍼가 추가되었습니다. `Fluent::make()`와 동등합니다.

```php theme={null}
$request = fluent([
    'method' => 'POST',
    'path' => '/api/users',
    'status' => 201
]);

echo $request->method; // 'POST'
```

## 프로퍼티 접근

### 동적 프로퍼티의 읽고쓰기

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

// 書き込み
$fluent->name = 'Laravel';
$fluent->version = 13;

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

### 메서드 체인

`__call` 매직 메서드에 의해, 존재하지 않는 메서드를 호출하면 프로퍼티가 설정됩니다. 메서드는 `$this`를 반환하므로 체이닝할 수 있습니다.

```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'
```

이 구조에 의해, 배열 대신 유창한 API(Fluent Interface)로 값을 설정할 수 있습니다.

## 주요 메서드

### get() — 도트 표기법으로 접근

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

// ドット記法でネストされた値を取得
$email = $user->get('profile.email'); // 'user@example.com'
$phone = $user->get('profile.phone'); // '090-xxxx-xxxx'

// デフォルト値を指定可能
$fax = $user->get('profile.fax', 'N/A'); // 'N/A'
```

### set() — 도트 표기법으로 세팅

```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() — 여러 프로퍼티를 한 번에 설정

```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() — 모든 프로퍼티를 배열로 취득

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

// すべてのプロパティ
$all = $fluent->all();
// ['name' => 'Laravel', 'version' => 13, 'license' => 'MIT']

// 特定のプロパティのみ
$subset = $fluent->all(['name', 'license']);
// ['name' => 'Laravel', 'license' => 'MIT']
```

### scope() — 중첩된 값을 새 Fluent로 변환

```php theme={null}
$config = fluent([
    'database' => [
        'host' => 'localhost',
        'port' => 3306,
        'name' => 'laravel'
    ]
]);

$dbConfig = $config->scope('database');
// $dbConfig は新しい Fluent インスタンス

echo $dbConfig->host; // 'localhost'
echo $dbConfig->port; // 3306
```

이를 통해, 중첩된 배열을 별도의 Fluent 오브젝트로 다룰 수 있습니다.

### value() — 기본값을 콜백으로 동적 지정

```php theme={null}
$user = fluent(['role' => 'admin']);

// キーが存在する場合は値を返す
$role = $user->value('role'); // 'admin'

// キーが存在しない場合はデフォルト値を返す
$status = $user->value('status', 'active');
// 'active'

// デフォルト値をコールバックで指定
$timestamp = $user->value('updated_at', function () {
    return now()->toIso8601String();
});
```

## 배열 조작

### toArray() — 배열로 변환

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

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

print_r($array);
```

### getAttributes() — 내부 속성에 직접 접근

```php theme={null}
$fluent = fluent(['a' => 1, 'b' => 2]);

$attributes = $fluent->getAttributes();
// ['a' => 1, 'b' => 2]
```

### ArrayAccess 인터페이스

Fluent는 `ArrayAccess`를 구현하고 있으므로, 배열처럼 조작할 수 있습니다.

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

// 配列のようにセット
$config['host'] = 'localhost';
$config['port'] = 3306;

// 配列のように読み込み
echo $config['host']; // 'localhost'

// 存在確認
if (isset($config['port'])) {
    echo $config['port'];
}

// 削除
unset($config['port']);
```

### IteratorAggregate 인터페이스

Fluent는 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
}
```

## JSON 처리

### toJson() — JSON 문자열로 변환

```php theme={null}
$response = fluent([
    'success' => true,
    'data' => ['id' => 1, 'name' => 'User']
]);

$json = $response->toJson();
// {"success":true,"data":{"id":1,"name":"User"}}

// APIレスポンスに使用可能
return $json;
```

### toPrettyJson() — 정형된 JSON으로 변환

```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"
//         }
//     ]
// }
```

### JsonSerializable 인터페이스

Fluent는 `JsonSerializable`을 구현하고 있으므로, `json_encode()`로 직접 변환할 수 있습니다.

```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]
```

## 상태 확인

### isEmpty() / isNotEmpty()

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

$empty->isEmpty();      // true
$empty->isNotEmpty();   // false

$filled->isEmpty();     // false
$filled->isNotEmpty();  // true
```

## Conditionable 트레이트

Fluent는 `Conditionable` 트레이트를 사용하고 있으며, 조건부 처리를 지원합니다.

```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;
```

`when()` / `unless()` 메서드를 사용함으로써, 조건에 기반한 설정을 유창하게 기술할 수 있습니다.

## Macroable 트레이트

Fluent는 `Macroable` 트레이트도 사용하고 있으며, 동적으로 메서드를 추가할 수 있습니다.

```php theme={null}
use Illuminate\Support\Fluent;

// プロバイダーの boot() メソッドで定義
Fluent::macro('isProduction', function () {
    /** @var Fluent $this */
    return $this->env === 'production';
});

Fluent::macro('isDevelopment', function () {
    /** @var Fluent $this */
    return $this->env === 'development';
});

// 使用
$config = fluent(['env' => 'production']);

if ($config->isProduction()) {
    // 本番環境の処理
}
```

## 실전적인 유스케이스

### 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();
    }
}

// コントローラーで使用
public function store(Request $request)
{
    $user = User::create($request->validated());

    return response()->json(
        json_decode(ApiResponse::success(['id' => $user->id]))
    );
}
```

### Config 빌더

```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'));
    });

// 設定値を検証して使用
config(['database.connections.mysql' => $dbConfig->toArray()]);
```

### 요청 파라미터의 검증·변환

```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'];
            });

        // ページネーション計算
        $filter->offset = ($filter->page - 1) * $filter->perPage;

        return $filter;
    }
}

// 使用
$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();
```

### 모델과 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);
        }
    }
}

// 使用
$post = new Post();

$post->metadata = fluent()
    ->title('SEO Title')
    ->description('Meta Description')
    ->keywords(['laravel', 'fluent', 'tutorial'])
    ->author('Laravel Community');

$post->save();

// 読み込み
$post = Post::first();
echo $post->metadata->title; // 'SEO Title'
echo $post->metadata->author; // 'Laravel Community'
```

### 폼 데이터의 정규화

```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',
        ];
    }

    // バリデーション済みデータを 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 ?? [])
        ]);
    }
}

// コントローラーで使用
public function store(CreateUserRequest $request)
{
    $data = $request->toFluent();

    $user = User::create($data->all());

    return response()->json(['success' => true, 'user_id' => $user->id]);
}
```

## 다른 클래스와의 비교

### Fluent vs Array

| 기능        | Fluent               | Array              |
| --------- | -------------------- | ------------------ |
| 프로퍼티 접근   | `$fluent->name`      | `$array['name']`   |
| 메서드 체인    | ✓ 지원                 | ✗ 없음               |
| JSON 변환   | `toJson()` 메서드       | `json_encode()` 함수 |
| 도트 표기법    | ✓ `get('user.name')` | ✗ 수동 처리            |
| 상태 확인     | `isEmpty()`          | `empty()` 함수       |
| 동적 메서드 추가 | Macroable            | ✗ 불가               |

### Fluent vs Model

| 기능      | Fluent | Model |
| ------- | ------ | ----- |
| DB 영속화  | ✗ 없음   | ✓ 자동  |
| 메모리 효율  | ✓ 경량   | ✗ 무거움 |
| 릴레이션    | ✗ 없음   | ✓ 지원  |
| 캐스트     | ✗ 없음   | ✓ 지원  |
| 검증      | ✗ 없음   | ✓ 지원  |
| 유창한 API | ✓ 있음   | △ 제한적 |

## 내부 구현의 상세

```php theme={null}
class Fluent
{
    protected $attributes = [];

    // マジックメソッド：存在しないプロパティアクセス
    public function __get($key)
    {
        return $this->value($key);
    }

    // マジックメソッド：存在しないプロパティセット
    public function __set($key, $value)
    {
        $this->offsetSet($key, $value);
    }

    // マジックメソッド：存在しないメソッド呼び出し
    // メソッド名がそのままプロパティキーになる
    public function __call($method, $parameters)
    {
        $this->attributes[$method] = count($parameters) > 0
            ? $parameters[0]
            : true;

        return $this;
    }
}
```

`__call` 메서드는, 매크로가 등록되어 있지 않은 경우, 메서드명을 프로퍼티 키로 하여 속성에 값을 설정하고, `$this`를 반환합니다. 이것이 메서드 체인을 가능하게 하고 있습니다.

<Tip>
  Fluent는 다음의 트레이트를 사용하고 있으며, 각각 다른 기능을 제공합니다:

  * **Conditionable** — `when()` / `unless()`로 조건부 처리
  * **InteractsWithData** — `data()` 등의 데이터 조작 메서드
  * **Macroable** — 동적 메서드 추가
</Tip>

## 다음 단계

<Card title="Collection 클래스" icon="arrow-right-arrow-left" href="/ko/advanced/collection-deep-dive">
  여러 요소를 다루는 Collection 클래스를 심층 분석합니다.
</Card>

<Card title="Conditionable 트레이트" icon="arrow-right-arrow-left" href="/ko/advanced/conditionable">
  조건부 처리를 유창하게 기술하는 Conditionable 트레이트를 배웁니다.
</Card>

<Card title="Macroable 트레이트" icon="arrow-right-arrow-left" href="/ko/advanced/macroable">
  기존 클래스에 동적 메서드를 추가하는 Macroable 트레이트를 배웁니다.
</Card>


## Related topics

- [문자열 조작 (Str 클래스)](/ko/strings.md)
- [Lottery 클래스](/ko/advanced/lottery.md)
- [InteractsWithData 트레이트](/ko/advanced/interacts-with-data.md)
- [커스텀 검증 규칙](/ko/advanced/custom-validation-rules.md)
- [알림(Notifications)](/ko/notifications.md)
