Написан ли Eloquent в соответствии с SOLID

«Написан ли Eloquent в соответствии с SOLID» — вопрос из категории Архитектура, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Eloquent, будучи реализацией паттерна Active Record, сознательно жертвует строгим соблюдением некоторых принципов SOLID ради простоты, скорости разработки и удобства для типичных CRUD-задач в Laravel. Вот мой анализ, основанный на опыте работы с ним:

S (Single Responsibility): Нарушается. Модель Eloquent объединяет несколько ответственностей:

  1. Представление строки таблицы (данные).
  2. Логику доступа к БД (поиск, сохранение).
  3. Часто содержит бизнес-правила, валидацию и отношения.

    class User extends Model
    {
    // Ответственность 1: Данные (свойства)
    protected $fillable = ['name', 'email'];
    
    // Ответственность 2: Доступ к БД (отношения - тоже запросы)
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
    
    // Ответственность 3: Бизнес-логика/валидация (часто здесь же)
    public function activate(): void
    {
        $this->is_active = true;
        $this->activated_at = now();
        $this->save(); // Смешивание логики и персистентности
    }
    }
    // Это быстро и удобно для небольших моделей, но усложняет поддержку больших.

O (Open/Closed): Соблюдается через механизмы расширения. Можно использовать трейты, макросы (Macroable), глобальные скоупы и наблюдатели (Observers), чтобы добавлять функциональность, не редактируя сам класс модели.

L (Liskov Substitution): В основном соблюдается. Наследники модели (например, Admin extends User) могут использоваться везде, где ожидается родительский тип, включая отношения.

I (Interface Segregation): Частично нарушается. Класс Model очень «толстый» (более 100 методов). Однако на практике это редко мешает, так как вы используете только нужные методы, а IDE помогает с автодополнением.

D (Dependency Inversion): Нарушается. Модель тесно связана (tightly coupled) с реализацией базы данных через статические вызовы фасада DB внутри и прямые SQL-запросы. Её сложно тестировать в изоляции без базы данных или использовать с другим источником данных.

// Тест для Eloquent-модели почти всегда требует базы данных (интеграционный, а не unit)
$user = User::create(['name' => 'Test']); // Вызывает реальный INSERT
$this->assertDatabaseHas('users', ['name' => 'Test']);

Вывод: Eloquent не является строго SOLID-совместимым, особенно по принципам Single Responsibility и Dependency Inversion. Это осознанный компромисс. Для быстрого прототипирования, админ-панелей или приложений с простой бизнес-логикой его продуктивность перевешивает недостатки. Для сложных доменных моделей с богатой логикой в Laravel-экосистеме часто используют гибридный подход (Eloquent для простого CRUD + отдельные сервис-классы/действия для сложной логики) или подключают Doctrine как альтернативный, более SOLID-ориентированный ORM.