Eloquent: Mutators & 캐스팅
소개
접근자, 변경자 및 속성 캐스팅을 사용하면 모델 인스턴스에서 Eloquent 속성 값을 검색하거나 설정할 때 변환할 수 있습니다. 예를 들어, 데이터베이스에 저장하는 동안 Laravel 암호화기를 사용하여 값을 암호화한 다음 Eloquent 모델에서 접근할 때 속성을 자동으로 해독할 수 있습니다. 또는 데이터베이스에 저장된 JSON 문자열을 Eloquent 모델을 통해 접근할 때 배열로 변환할 수 있습니다.
접근자 및 변경자
접근자 정의
접근자는 Eloquent 속성 값에 접근할 때 해당 값을 변환합니다. 접근자를 정의하려면 접근 가능한 속성을 나타내기 위해 모델에 protected 메서드를 생성하세요. 이 메서드 이름은 해당되는 모델 속성/데이터베이스 열의 "카멜 케이스" 표현과 일치해야 합니다.
이 예제에서는 first_name 속성에 대한 접근자를 정의합니다. 접근자는 first_name 속성 값을 검색하려고 할 때 Eloquent에 의해 자동으로 호출됩니다. 모든 속성 접근자/변경자 메서드는 Illuminate\Database\Eloquent\Casts\Attribute의 반환 타입 힌트를 선언해야 합니다.
<?php namespace App\Models; use Illuminate\Database\Eloquent\Casts\Attribute;use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * 사용자 이름을 가져옵니다. */ protected function firstName(): Attribute { return Attribute::make( get: fn (string $value) => ucfirst($value), ); }}
모든 접근자 메서드는 속성에 접근하고 선택적으로 변경하는 방법을 정의하는 Attribute 인스턴스를 반환합니다. 이 예제에서는 속성에 접근하는 방법만 정의합니다. 이렇게 하려면 Attribute 클래스 생성자에 get 인수를 제공합니다.
보시다시피 열의 원래 값이 접근자로 전달되어 값을 조작하고 반환할 수 있습니다. 접근자 값에 접근하려면 모델 인스턴스에서 first_name 속성에 접근하면 됩니다.
use App\Models\User; $user = User::find(1); $firstName = $user->first_name;
이러한 계산된 값을 모델의 배열/JSON 표현에 추가하려면 추가해야 합니다.
여러 속성에서 값 객체 빌드
때로는 접근자가 여러 모델 속성을 단일 "값 객체"로 변환해야 할 수도 있습니다. 이렇게 하려면 get 클로저가 두 번째 인수로 $attributes를 받을 수 있습니다. 이 인수는 클로저에 자동으로 제공되며 모든 모델의 현재 속성 배열을 포함합니다.
use App\Support\Address;use Illuminate\Database\Eloquent\Casts\Attribute; /** * 사용자의 주소와 상호 작용합니다. */protected function address(): Attribute{ return Attribute::make( get: fn (mixed $value, array $attributes) => new Address( $attributes['address_line_one'], $attributes['address_line_two'], ), );}
Accessor 캐싱
액세서에서 값 객체를 반환할 때, 값 객체에 가해진 모든 변경 사항은 모델이 저장되기 전에 자동으로 모델에 다시 동기화됩니다. 이는 Eloquent가 액세서에 의해 반환된 인스턴스를 유지하여 액세서가 호출될 때마다 동일한 인스턴스를 반환할 수 있기 때문에 가능합니다.
use App\Models\User; $user = User::find(1); $user->address->lineOne = '업데이트된 주소 1행 값';$user->address->lineTwo = '업데이트된 주소 2행 값'; $user->save();
그러나, 때로는 특히 계산량이 많은 경우 문자열 및 부울과 같은 기본 값에 대한 캐싱을 활성화하고 싶을 수도 있습니다. 이를 수행하려면 액세서를 정의할 때 shouldCache 메서드를 호출하면 됩니다.
protected function hash(): Attribute{ return Attribute::make( get: fn (string $value) => bcrypt(gzuncompress($value)), )->shouldCache();}
속성의 객체 캐싱 동작을 비활성화하려면 속성을 정의할 때 withoutObjectCaching 메서드를 호출하면 됩니다.
/** * 사용자의 주소와 상호 작용합니다. */protected function address(): Attribute{ return Attribute::make( get: fn (mixed $value, array $attributes) => new Address( $attributes['address_line_one'], $attributes['address_line_two'], ), )->withoutObjectCaching();}
Mutator 정의하기
Mutator는 Eloquent 속성 값이 설정될 때 해당 값을 변환합니다. Mutator를 정의하려면 속성을 정의할 때 set 인수를 제공하면 됩니다. first_name 속성에 대한 mutator를 정의해 보겠습니다. 이 mutator는 모델에서 first_name 속성 값을 설정하려고 할 때 자동으로 호출됩니다.
<?php namespace App\Models; use Illuminate\Database\Eloquent\Casts\Attribute;use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * 사용자의 이름과 상호 작용합니다. */ protected function firstName(): Attribute { return Attribute::make( get: fn (string $value) => ucfirst($value), set: fn (string $value) => strtolower($value), ); }}
mutator 클로저는 속성에 설정되는 값을 받아서 값을 조작하고 조작된 값을 반환할 수 있습니다. mutator를 사용하려면 Eloquent 모델에서 first_name 속성을 설정하기만 하면 됩니다.
use App\Models\User; $user = User::find(1); $user->first_name = 'Sally';
이 예시에서 set 콜백은 값 Sally로 호출됩니다. 그런 다음 mutator는 strtolower 함수를 이름에 적용하고 결과 값을 모델의 내부 $attributes 배열에 설정합니다.
여러 속성 변형하기
때로는 mutator가 기본 모델에서 여러 속성을 설정해야 할 수 있습니다. 이렇게 하려면 set 클로저에서 배열을 반환할 수 있습니다. 배열의 각 키는 모델과 연결된 기본 속성 / 데이터베이스 열과 일치해야 합니다.
use App\Support\Address;use Illuminate\Database\Eloquent\Casts\Attribute; /** * 사용자 주소와 상호 작용합니다. */protected function address(): Attribute{ return Attribute::make( get: fn (mixed $value, array $attributes) => new Address( $attributes['address_line_one'], $attributes['address_line_two'], ), set: fn (Address $value) => [ 'address_line_one' => $value->lineOne, 'address_line_two' => $value->lineTwo, ], );}
속성 캐스팅
속성 캐스팅은 모델에 추가적인 메서드를 정의할 필요 없이 접근자와 변경자와 유사한 기능을 제공합니다. 대신, 모델의 casts 메서드는 속성을 일반적인 데이터 유형으로 변환하는 편리한 방법을 제공합니다.
casts 메서드는 캐스팅할 속성의 이름을 키로, 컬럼을 캐스팅하려는 유형을 값으로 하는 배열을 반환해야 합니다. 지원되는 캐스트 유형은 다음과 같습니다:
-
array -
AsStringable::class -
boolean -
collection -
date -
datetime -
immutable_date -
immutable_datetime -
decimal:<정밀도> -
double -
encrypted -
encrypted:array -
encrypted:collection -
encrypted:object -
float -
hashed -
integer -
object -
real -
string -
timestamp
속성 캐스팅을 설명하기 위해 데이터베이스에 정수(0 또는 1)로 저장된 is_admin 속성을 boolean 값으로 캐스팅해 보겠습니다:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * 캐스팅해야 하는 속성을 가져옵니다. * * @return array<string, string> */ protected function casts(): array { return [ 'is_admin' => 'boolean', ]; }}
캐스트를 정의한 후, 기본 값이 데이터베이스에 정수로 저장되어 있더라도 is_admin 속성에 접근할 때마다 항상 boolean으로 캐스팅됩니다:
$user = App\Models\User::find(1); if ($user->is_admin) { // ...}
런타임에 새 임시 캐스트를 추가해야 하는 경우 mergeCasts 메서드를 사용할 수 있습니다. 이러한 캐스트 정의는 모델에 이미 정의된 모든 캐스트에 추가됩니다:
$user->mergeCasts([ 'is_admin' => 'integer', 'options' => 'object',]);
null인 속성은 캐스팅되지 않습니다. 또한 관계와 이름이 같거나 모델의 기본 키에 캐스트를 할당하는 캐스트(또는 속성)를 정의해서는 안 됩니다.
Stringable 캐스팅
Illuminate\Database\Eloquent\Casts\AsStringable 캐스트 클래스를 사용하여 모델 속성을 fluent Illuminate\Support\Stringable 객체로 캐스팅할 수 있습니다:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Casts\AsStringable;use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * 캐스팅해야 하는 속성을 가져옵니다. * * @return array<string, string> */ protected function casts(): array { return [ 'directory' => AsStringable::class, ]; }}
배열 및 JSON 캐스팅
array 캐스트는 직렬화된 JSON으로 저장된 컬럼으로 작업할 때 특히 유용합니다. 예를 들어, 데이터베이스에 직렬화된 JSON을 포함하는 JSON 또는 TEXT 필드 유형이 있는 경우, 해당 속성에 array 캐스트를 추가하면 Eloquent 모델에서 해당 속성에 접근할 때 자동으로 PHP 배열로 역직렬화됩니다:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * 캐스팅해야 하는 속성을 가져옵니다. * * @return array<string, string> */ protected function casts(): array { return [ 'options' => 'array', ]; }}
캐스트가 정의되면 options 속성에 접근할 수 있으며 자동으로 JSON에서 PHP 배열로 역직렬화됩니다. options 속성 값을 설정하면 주어진 배열이 스토리지용으로 자동으로 JSON으로 다시 직렬화됩니다:
use App\Models\User; $user = User::find(1); $options = $user->options; $options['key'] = 'value'; $user->options = $options; $user->save();
더 간결한 구문으로 JSON 속성의 단일 필드를 업데이트하려면 속성을 대량 할당 가능하게 만들고 update 메서드를 호출할 때 -> 연산자를 사용할 수 있습니다:
$user = User::find(1); $user->update(['options->key' => 'value']);
배열 객체 및 컬렉션 캐스팅
표준 array 캐스트는 많은 애플리케이션에 충분하지만 몇 가지 단점이 있습니다. array 캐스트는 기본 유형을 반환하므로 배열의 오프셋을 직접 변경할 수 없습니다. 예를 들어, 다음 코드는 PHP 오류를 트리거합니다:
$user = User::find(1); $user->options['key'] = $value;
이 문제를 해결하기 위해 Laravel은 JSON 속성을 ArrayObject 클래스로 캐스팅하는 AsArrayObject 캐스트를 제공합니다. 이 기능은 Laravel의 사용자 지정 캐스트 구현을 사용하여 구현되며, 이를 통해 Laravel은 PHP 오류를 트리거하지 않고 개별 오프셋을 수정할 수 있도록 변경된 객체를 지능적으로 캐시하고 변환할 수 있습니다. AsArrayObject 캐스트를 사용하려면 속성에 할당하기만 하면 됩니다:
use Illuminate\Database\Eloquent\Casts\AsArrayObject; /** * 캐스팅해야 하는 속성을 가져옵니다. * * @return array<string, string> */protected function casts(): array{ return [ 'options' => AsArrayObject::class, ];}
마찬가지로 Laravel은 JSON 속성을 Laravel 컬렉션 인스턴스로 캐스팅하는 AsCollection 캐스트를 제공합니다:
use Illuminate\Database\Eloquent\Casts\AsCollection; /** * 캐스팅해야 하는 속성을 가져옵니다. * * @return array<string, string> */protected function casts(): array{ return [ 'options' => AsCollection::class, ];}
AsCollection 캐스트가 Laravel의 기본 컬렉션 클래스 대신 사용자 지정 컬렉션 클래스를 인스턴스화하도록 하려면 컬렉션 클래스 이름을 캐스트 인수로 제공할 수 있습니다:
use App\Collections\OptionCollection;use Illuminate\Database\Eloquent\Casts\AsCollection; /** * 캐스팅해야 하는 속성을 가져옵니다. * * @return array<string, string> */protected function casts(): array{ return [ 'options' => AsCollection::using(OptionCollection::class), ];}
날짜 캐스팅
기본적으로 Eloquent는 created_at 및 updated_at 컬럼을 PHP DateTime 클래스를 확장하고 다양한 유용한 메서드를 제공하는 Carbon 인스턴스로 캐스팅합니다. 모델의 casts 메서드 내에서 추가 날짜 캐스트를 정의하여 추가 날짜 속성을 캐스팅할 수 있습니다. 일반적으로 날짜는 datetime 또는 immutable_datetime 캐스트 유형을 사용하여 캐스팅해야 합니다.
date 또는 datetime 캐스트를 정의할 때 날짜 형식을 지정할 수도 있습니다. 이 형식은 모델이 배열 또는 JSON으로 직렬화될 때 사용됩니다:
/** * 캐스팅해야 하는 속성을 가져옵니다. * * @return array<string, string> */protected function casts(): array{ return [ 'created_at' => 'datetime:Y-m-d', ];}
컬럼이 날짜로 캐스팅되면 해당 모델 속성 값을 UNIX 타임스탬프, 날짜 문자열(Y-m-d), 날짜-시간 문자열 또는 DateTime / Carbon 인스턴스로 설정할 수 있습니다. 날짜 값은 올바르게 변환되어 데이터베이스에 저장됩니다.
모델에 serializeDate 메서드를 정의하여 모델의 모든 날짜에 대한 기본 직렬화 형식을 사용자 지정할 수 있습니다. 이 메서드는 데이터베이스에 저장하기 위해 날짜 형식이 지정되는 방식에 영향을 미치지 않습니다:
/** * 배열 / JSON 직렬화를 위해 날짜를 준비합니다. */protected function serializeDate(DateTimeInterface $date): string{ return $date->format('Y-m-d');}
모델의 날짜를 실제로 데이터베이스 내에 저장할 때 사용해야 하는 형식을 지정하려면 모델에 $dateFormat 속성을 정의해야 합니다:
/** * 모델의 날짜 컬럼의 저장 형식입니다. * * @var string */protected $dateFormat = 'U';
날짜 캐스팅, 직렬화 및 시간대
기본적으로 date 및 datetime 캐스트는 응용 프로그램의 timezone 구성 옵션에 지정된 시간대에 관계없이 날짜를 UTC ISO-8601 날짜 문자열(YYYY-MM-DDTHH:MM:SS.uuuuuuZ)로 직렬화합니다. 이 직렬화 형식을 항상 사용하고 응용 프로그램의 timezone 구성 옵션을 기본 UTC 값에서 변경하지 않고 응용 프로그램의 날짜를 UTC 시간대로 저장하는 것이 좋습니다. 응용 프로그램 전체에서 UTC 시간대를 일관되게 사용하면 PHP와 JavaScript로 작성된 다른 날짜 조작 라이브러리와의 상호 운용성이 최대화됩니다.
datetime:Y-m-d H:i:s와 같이 사용자 정의 형식이 date 또는 datetime 캐스트에 적용된 경우 날짜 직렬화 중에 Carbon 인스턴스의 내부 시간대가 사용됩니다. 일반적으로 이는 응용 프로그램의 timezone 구성 옵션에 지정된 시간대입니다. 그러나 created_at 및 updated_at와 같은 timestamp 컬럼은 이 동작에서 제외되며 응용 프로그램의 시간대 설정에 관계없이 항상 UTC로 형식이 지정됩니다.
Enum 캐스팅
Eloquent를 사용하면 속성 값을 PHP Enum으로 캐스팅할 수도 있습니다. 이를 수행하려면 모델의 casts 메서드에서 캐스팅하려는 속성과 Enum을 지정할 수 있습니다:
use App\Enums\ServerStatus; /** * 캐스팅해야 하는 속성을 가져옵니다. * * @return array<string, string> */protected function casts(): array{ return [ 'status' => ServerStatus::class, ];}
모델에 캐스트를 정의하면 지정된 속성은 속성과 상호 작용할 때 자동으로 enum으로 캐스팅되고 enum에서 캐스팅됩니다:
if ($server->status == ServerStatus::Provisioned) { $server->status = ServerStatus::Ready; $server->save();}
Enum 배열 캐스팅
때로는 모델이 단일 컬럼 내에 enum 값의 배열을 저장해야 할 수도 있습니다. 이를 수행하려면 Laravel에서 제공하는 AsEnumArrayObject 또는 AsEnumCollection 캐스트를 사용할 수 있습니다:
use App\Enums\ServerStatus;use Illuminate\Database\Eloquent\Casts\AsEnumCollection; /** * 캐스팅해야 하는 속성을 가져옵니다. * * @return array<string, string> */protected function casts(): array{ return [ 'statuses' => AsEnumCollection::of(ServerStatus::class), ];}
암호화된 캐스팅
encrypted 캐스트는 Laravel의 내장 암호화 기능을 사용하여 모델의 속성 값을 암호화합니다. 또한 encrypted:array, encrypted:collection, encrypted:object, AsEncryptedArrayObject 및 AsEncryptedCollection 캐스트는 암호화되지 않은 캐스트와 유사하게 작동합니다. 그러나 예상대로 기본값은 데이터베이스에 저장될 때 암호화됩니다.
암호화된 텍스트의 최종 길이는 예측할 수 없으며 일반 텍스트보다 길기 때문에 연결된 데이터베이스 컬럼이 TEXT 유형 이상인지 확인하세요. 또한 값은 데이터베이스에서 암호화되므로 암호화된 속성 값을 쿼리하거나 검색할 수 없습니다.
키 순환
아시다시피 Laravel은 응용 프로그램의 app 구성 파일에 지정된 key 구성 값을 사용하여 문자열을 암호화합니다. 일반적으로 이 값은 APP_KEY 환경 변수의 값에 해당합니다. 응용 프로그램의 암호화 키를 순환해야 하는 경우 새 키를 사용하여 암호화된 속성을 수동으로 다시 암호화해야 합니다.
쿼리 시간 캐스팅
테이블에서 원시 값을 선택하는 경우와 같이 쿼리를 실행하는 동안 캐스트를 적용해야 할 수 있습니다. 예를 들어, 다음 쿼리를 고려해 보세요:
use App\Models\Post;use App\Models\User; $users = User::select([ 'users.*', 'last_posted_at' => Post::selectRaw('MAX(created_at)') ->whereColumn('user_id', 'users.id')])->get();
이 쿼리 결과의 last_posted_at 속성은 단순 문자열입니다. 쿼리를 실행할 때 이 속성에 datetime 캐스트를 적용할 수 있다면 좋을 것입니다. 다행히 withCasts 메서드를 사용하여 이를 수행할 수 있습니다:
$users = User::select([ 'users.*', 'last_posted_at' => Post::selectRaw('MAX(created_at)') ->whereColumn('user_id', 'users.id')])->withCasts([ 'last_posted_at' => 'datetime'])->get();
사용자 지정 캐스트
Laravel에는 다양한 내장된 유용한 캐스트 유형이 있지만, 때로는 고유한 캐스트 유형을 정의해야 할 수도 있습니다. 캐스트를 생성하려면 make:cast Artisan 명령을 실행합니다. 새 캐스트 클래스가 app/Casts 디렉터리에 배치됩니다:
php artisan make:cast Json
php artisan make:cast Json
모든 사용자 정의 캐스트 클래스는 CastsAttributes 인터페이스를 구현합니다. 이 인터페이스를 구현하는 클래스는 get 및 set 메서드를 정의해야 합니다. get 메서드는 데이터베이스의 원시 값을 캐스트 값으로 변환하는 역할을 담당하며, set 메서드는 캐스트 값을 데이터베이스에 저장할 수 있는 원시 값으로 변환해야 합니다. 예를 들어, 내장된 json 캐스트 유형을 사용자 정의 캐스트 유형으로 다시 구현해 보겠습니다.
<?php namespace App\Casts; use Illuminate\Contracts\Database\Eloquent\CastsAttributes;use Illuminate\Database\Eloquent\Model; class Json implements CastsAttributes{ /** * 주어진 값을 캐스팅합니다. * * @param array<string, mixed> $attributes * @return array<string, mixed> */ public function get(Model $model, string $key, mixed $value, array $attributes): array { return json_decode($value, true); } /** * 저장을 위해 주어진 값을 준비합니다. * * @param array<string, mixed> $attributes */ public function set(Model $model, string $key, mixed $value, array $attributes): string { return json_encode($value); }}
사용자 정의 캐스트 유형을 정의했으면 해당 클래스 이름을 사용하여 모델 속성에 연결할 수 있습니다.
<?php namespace App\Models; use App\Casts\Json;use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * 캐스팅해야 하는 속성을 가져옵니다. * * @return array<string, string> */ protected function casts(): array { return [ 'options' => Json::class, ]; }}
값 객체 캐스팅
값을 기본 유형으로 캐스팅하는 데 국한되지 않습니다. 값을 객체로 캐스팅할 수도 있습니다. 값을 객체로 캐스팅하는 사용자 정의 캐스트를 정의하는 것은 기본 유형으로 캐스팅하는 것과 매우 유사합니다. 그러나 set 메서드는 모델에 원시 저장 가능 값을 설정하는 데 사용될 키 / 값 쌍 배열을 반환해야 합니다.
예를 들어, 여러 모델 값을 단일 Address 값 객체로 캐스팅하는 사용자 정의 캐스트 클래스를 정의합니다. Address 값에 lineOne 및 lineTwo의 두 가지 공용 속성이 있다고 가정합니다.
<?php namespace App\Casts; use App\ValueObjects\Address as AddressValueObject;use Illuminate\Contracts\Database\Eloquent\CastsAttributes;use Illuminate\Database\Eloquent\Model;use InvalidArgumentException; class Address implements CastsAttributes{ /** * 주어진 값을 캐스팅합니다. * * @param array<string, mixed> $attributes */ public function get(Model $model, string $key, mixed $value, array $attributes): AddressValueObject { return new AddressValueObject( $attributes['address_line_one'], $attributes['address_line_two'] ); } /** * 저장을 위해 주어진 값을 준비합니다. * * @param array<string, mixed> $attributes * @return array<string, string> */ public function set(Model $model, string $key, mixed $value, array $attributes): array { if (! $value instanceof AddressValueObject) { throw new InvalidArgumentException('The given value is not an Address instance.'); } return [ 'address_line_one' => $value->lineOne, 'address_line_two' => $value->lineTwo, ]; }}
값 객체로 캐스팅할 때 값 객체에 대한 모든 변경 사항은 모델이 저장되기 전에 자동으로 모델과 동기화됩니다.
use App\Models\User; $user = User::find(1); $user->address->lineOne = '업데이트된 주소 값'; $user->save();
값 객체를 포함하는 Eloquent 모델을 JSON 또는 배열로 직렬화하려는 경우, 값 객체에 Illuminate\Contracts\Support\Arrayable 및 JsonSerializable 인터페이스를 구현해야 합니다.
값 객체 캐싱
값 객체로 캐스팅된 속성이 확인되면 Eloquent에 의해 캐시됩니다. 따라서 속성에 다시 접근하면 동일한 객체 인스턴스가 반환됩니다.
사용자 정의 캐스트 클래스의 객체 캐싱 동작을 비활성화하려면 사용자 정의 캐스트 클래스에 공용 withoutObjectCaching 속성을 선언하면 됩니다.
class Address implements CastsAttributes{ public bool $withoutObjectCaching = true; // ...}
배열 / JSON 직렬화
Eloquent 모델이 toArray 및 toJson 메서드를 사용하여 배열 또는 JSON으로 변환될 때, 커스텀 캐스트 값 객체는 일반적으로 Illuminate\Contracts\Support\Arrayable 및 JsonSerializable 인터페이스를 구현하는 한 직렬화됩니다. 그러나 타사 라이브러리에서 제공하는 값 객체를 사용하는 경우, 해당 객체에 이러한 인터페이스를 추가할 수 없을 수도 있습니다.
따라서 커스텀 캐스트 클래스가 값 객체를 직렬화하는 역할을 하도록 지정할 수 있습니다. 그렇게 하려면 커스텀 캐스트 클래스가 Illuminate\Contracts\Database\Eloquent\SerializesCastableAttributes 인터페이스를 구현해야 합니다. 이 인터페이스는 클래스에 값 객체의 직렬화된 형태를 반환해야 하는 serialize 메서드가 포함되어야 함을 나타냅니다.
/** * 값의 직렬화된 표현을 가져옵니다. * * @param array<string, mixed> $attributes */public function serialize(Model $model, string $key, mixed $value, array $attributes): string{ return (string) $value;}
인바운드 캐스팅
경우에 따라 모델에 설정되는 값만 변환하고 모델에서 속성을 검색할 때는 아무런 작업을 수행하지 않는 커스텀 캐스트 클래스를 작성해야 할 수도 있습니다.
인바운드 전용 커스텀 캐스트는 set 메서드만 정의하면 되는 CastsInboundAttributes 인터페이스를 구현해야 합니다. make:cast Artisan 명령은 --inbound 옵션과 함께 호출하여 인바운드 전용 캐스트 클래스를 생성할 수 있습니다.
php artisan make:cast Hash --inbound
인바운드 전용 캐스트의 고전적인 예는 "해싱" 캐스트입니다. 예를 들어, 주어진 알고리즘을 통해 인바운드 값을 해시하는 캐스트를 정의할 수 있습니다.
<?php namespace App\Casts; use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;use Illuminate\Database\Eloquent\Model; class Hash implements CastsInboundAttributes{ /** * 새 캐스트 클래스 인스턴스를 만듭니다. */ public function __construct( protected string|null $algorithm = null, ) {} /** * 저장할 값을 준비합니다. * * @param array<string, mixed> $attributes */ public function set(Model $model, string $key, mixed $value, array $attributes): string { return is_null($this->algorithm) ? bcrypt($value) : hash($this->algorithm, $value); }}
캐스트 매개변수
모델에 사용자 지정 캐스트를 연결할 때, 클래스 이름과 : 문자를 사용하여 구분하고 여러 매개변수를 쉼표로 구분하여 캐스트 매개변수를 지정할 수 있습니다. 매개변수는 캐스트 클래스의 생성자로 전달됩니다.
/** * 캐스팅해야 하는 속성을 가져옵니다. * * @return array<string, string> */protected function casts(): array{ return [ 'secret' => Hash::class.':sha256', ];}
Castable (캐스팅 가능)
애플리케이션의 값 객체가 자체 사용자 정의 캐스트 클래스를 정의하도록 허용할 수 있습니다. 사용자 지정 캐스트 클래스를 모델에 연결하는 대신 Illuminate\Contracts\Database\Eloquent\Castable 인터페이스를 구현하는 값 객체 클래스를 대신 연결할 수 있습니다.
use App\ValueObjects\Address; protected function casts(): array{ return [ 'address' => Address::class, ];}
Castable 인터페이스를 구현하는 객체는 Castable 클래스로부터 캐스팅하고 캐스팅하는 데 책임이 있는 사용자 정의 캐스터 클래스의 이름을 반환하는 castUsing 메서드를 정의해야 합니다.
<?php namespace App\ValueObjects; use Illuminate\Contracts\Database\Eloquent\Castable;use App\Casts\Address as AddressCast; class Address implements Castable{ /** * 이 캐스트 대상에서 캐스팅할 때 사용할 캐스터 클래스의 이름을 가져옵니다. * * @param array<string, mixed> $arguments */ public static function castUsing(array $arguments): string { return AddressCast::class; }}
Castable 클래스를 사용할 때, casts 메서드 정의에서 인수를 제공할 수 있습니다. 인수는 castUsing 메서드로 전달됩니다.
use App\ValueObjects\Address; protected function casts(): array{ return [ 'address' => Address::class.':argument', ];}
Castable 및 익명 캐스트 클래스
"캐스팅 가능"과 PHP의 익명 클래스를 결합하여 값 객체와 해당 캐스팅 로직을 단일 캐스팅 가능한 객체로 정의할 수 있습니다. 이를 위해 값 객체의 castUsing 메서드에서 익명 클래스를 반환합니다. 익명 클래스는 CastsAttributes 인터페이스를 구현해야 합니다.
<?php namespace App\ValueObjects; use Illuminate\Contracts\Database\Eloquent\Castable;use Illuminate\Contracts\Database\Eloquent\CastsAttributes; class Address implements Castable{ // ... /** * 이 캐스트 대상에서 캐스팅할 때 사용할 캐스터 클래스를 가져옵니다. * * @param array<string, mixed> $arguments */ public static function castUsing(array $arguments): CastsAttributes { return new class implements CastsAttributes { public function get(Model $model, string $key, mixed $value, array $attributes): Address { return new Address( $attributes['address_line_one'], $attributes['address_line_two'] ); } public function set(Model $model, string $key, mixed $value, array $attributes): array { return [ 'address_line_one' => $value->lineOne, 'address_line_two' => $value->lineTwo, ]; } }; }}