На конференції Laravel Live Japan 2026 я презентував Aegis for Laravel. Цей пакет автоматизує патерн, до якого я постійно повертаюся — Value Objects. Вони ефективно усувають цілий клас багів, спричинених примітивами, але писати їх вручну надто довго. Aegis бере цю рутину на себе.
# Проблема
Рядок не стає імейлом без валідації. int не перетворюється на гроші, поки до нього не прив’язана валюта. Ми часто передаємо сирі примітиви через увесь додаток, сподіваючись на їхню коректність, а потім витрачаємо години на пошук помилок там, де ці дані виявилися хибними.
Value Object розв'язує цю проблему. Його конструктор приймає вхідні дані й або створює валідний екземпляр, або видає помилку. Якщо у вас у руках Email — це гарантовано справжній імейл. Некоректні значення просто не потраплять у систему.
Головна перешкода — обсяг коду. Правильний Value Object займає близько 70 рядків PHP: клас final readonly, валідація в конструкторі, нормалізація, метод equals() та блок Castable з методами get, set і compare для збереження в Eloquent. Писати це для кожного рядка, який ви хочете захистити, надто витратно. Тому розробники часто ігнорують цей патерн і залишають примітиви.
Aegis генерує цей код за вас. Одна команда Artisan створює клас із усіма налаштуваннями та заготовку для тесту. Вам залишається лише додати логіку вашого домену.
# Встановлення
composer require harrisrafto/laravel-aegis
Сервіс-провайдер реєструється автоматично через package auto-discovery.
Якщо ви хочете змінити namespace для генерованих Value Objects, опублікуйте конфіг:
php artisan vendor:publish --tag=aegis-config
# Створення Value Object
Ось команда для створення Email:
php artisan make:value-object Email \
--rule=email \
--normalize=lower \
--method=domain:string \
--cast=Order.email
Вона виконує три дії:
По-перше, створює app/Domain/ValueObjects/Email.php. Це final readonly клас із валідацією та нормалізацією. Він реалізує Stringable та JsonSerializable для зручної роботи на «краях» додатку, а також містить блок Castable для Eloquent. Запитаний метод domain(): string додається як пуста заготовка.
По-друге, створює tests/Unit/EmailTest.php. Якщо у вашому проєкті є Pest, Aegis напише it()->todo(). Якщо ні — ви отримаєте markTestIncomplete() для PHPUnit. У будь-якому разі тест уже чекає на заповнення.
По-третє, пакет оновлює app/Models/Order.php, додаючи 'email' => Email::class у метод casts(). Команда зберігає ваше форматування та безпечна для повторного запуску.
# Валідація за допомогою Value Object
Value Object вже «знає», що таке валідний імейл. Тому ви можете використовувати його безпосередньо у валідації замість дублювання правил у FormRequest:
use Illuminate\Validation\Rule;
public function rules(): array
{
return [
'email' => ['required', Rule::valueObject(Email::class)],
];
}
Aegis реєструє valueObject як макрос для Illuminate\Validation\Rule. Це виглядає так само природно, як і вбудовані правила на кшталт Rule::unique().
# Отримання валідного екземпляра
Після успішної валідації вам зазвичай потрібен об’єкт, а не просто рядок. Додайте trait ResolvesValueObjects до вашого FormRequest:
use HarrisRafto\Aegis\Concerns\ResolvesValueObjects;
class StoreUserRequest extends FormRequest
{
use ResolvesValueObjects;
public function rules(): array
{
return [
'email' => ['required', Rule::valueObject(Email::class)],
];
}
}
Тепер у контролері можна отримати готовий об'єкт:
$email = $request->valueObject('email'); // екземпляр Email, вже валідований
# Пошук кандидатів у вашому коді
Якщо ви додаєте Value Objects у вже існуючий проєкт, скористайтеся сканером:
php artisan vo:scan
Aegis проаналізує моделі Eloquent та міграції, знайде назви колонок, що відповідають паттернам (email, url, uuid, country_code, slug тощо), і запропонує готові команди для генерації:
app/Models/Customer.php
· billing_email → php artisan make:value-object Email --rule=email --normalize=lower --cast=Customer.billing_email
· country_code → php artisan make:value-object CountryCode --rule=regex:/^[A-Z]{2}$/ --normalize=upper --cast=Customer.country_code
· monthly_amount_cents candidate — Money column, see cknow/laravel-money
Scanned 3 models, 16 columns total.
7 commands ready, 2 candidates need your input, 1 already wrapped.
Value Object coverage: 6%.
Інструмент аналізує $fillable, $casts та блоки Schema::create у міграціях, не звертаючись до бази даних. Показник coverage (покриття) допомагає перетворити абстрактне «нам варто частіше використовувати Value Objects» на конкретну метрику, яку можна покращувати.
# Прапорці (Flags)
Команда make:value-object підтримує такі параметри:
| Прапорець | Призначення |
|---|---|
--rule=NAME[:ARGS] |
Правило валідації (email, url, ip, uuid, regex:PATTERN тощо). |
--normalize=FN[,FN] |
Нормалізація (lower, upper, trim). Можна комбінувати через кому. |
--type=PHP_TYPE |
Тип властивості. За замовчуванням string. Також: int, float, bool або назва класу. |
--method=NAME[:RETURN_TYPE] |
Додає заготовку методу. Можна використовувати кілька разів. |
--cast=Model.column |
Прописує cast у модель app/Models/Model.php. |
--namespace=NS |
Змінює стандартний namespace. |
--no-test |
Не створювати файл тесту. |
--dry-run |
Показує зміни без запису на диск. |
--force |
Перезаписує існуючі файли. |
# Вимоги
- PHP 8.3+
- Laravel 13
# Посилання
- GitHub: harris21/laravel-aegis
- Приклади використання: harris21/laravel-value-objects-examples
# Про назву
У грецькій міфології «Егіда» (aegis) — це щит Афіни, який захищав її у бою. Саме це робить конструктор Value Object: він приймає лише валідне й відсікає все інше.
Цей патерн бере початок із робіт Еріка Еванса (Domain-Driven Design) та Мартіна Фаулера (Patterns of Enterprise Application Architecture). Aegis просто позбавляє вас необхідності писати зайвий код, щоб ви частіше обирали надійні типи замість сирих рядків.