Может войдёшь?
Черновики Написать статью Профиль

Сценарии в Laravel (шестиугольный шаблон проектирования)

перевод

Laravel традиционно является MVC-фреймворком, но MVC не слишком хорошо масштабируется для крупных проектов. Обычно, в конечном итоге получается, что логика содержится во всех секциях: моделях, представлениях и контроллерах, и по мере развития приложения его становится почти невозможно тестировать. В книге Тейлора Отвелла упоминается шаблон репозитория, но даже он не способен решить эту проблему масштабируемости. После того, как коллега показал мне статью в блоге о шестиугольном шаблоне проектирования в Rails, я решил попробовать применить его в Laravel — результаты были потрясающие.

/packages/proger/habravel/uploads/13-hexagonal-pattern.png

Эта диаграмма взята из блога и показывает разделение логики доставки и применения.

Основная идея заключается в том, что маршруты и контроллеры предназначены только для доставки. В контроллере нет никакой логики, и точка.

Контроллер похож на вашего почтальона. Он получает адрес (маршрут) и доставляет коробку (модель + представление) по вашему адресу. Ему не нужно беспокоиться о содержимом коробки. Но получается, что почтальон всё равно принимает решения. Например, если сейчас идет дождь, коробка может оказаться либо в вашем автомобиле, иначе, вероятно, на вашем крыльце.

Итак, где вы поместите вашу логику? Я называю её сценарием (scenario), но она еще называется примером использования (use cases).

Так давайте применим нашу концепцию сценария к нашему придуманному примеру о почтальоне. Вместо того, чтобы почтальон пытался выяснить, что делать с коробкой, он просто будет следовать строгому набору руководящих принципов заранее определенного сценария. Он не делает никаких предположений и делает именно то, что ему сказано. Таким образом, в сценарии, в котором идет дождь, согласно правилам, он должен оставить записку, объяснив ситуацию, и не оставлять коробку под дождём. Это означает, что почтальон не несет ответственности за любые решения. Он просто механизм доставки заказов в соответствии с данными ему инструкциями.

Реализация

Ниже приведены два класса: PHPUsersController и PHPUserScenario. Это примеры, показывающие, каким образом можно создать в Laravel пользователя с помощью нашего шестиугольного шаблона со сценариями.

PHP
<?php

class UserController extends BaseController
{

  public function 
__construct(UserScenario $scenario)
  {
    
$this->scenario $scenario;
    
$scenario->controller $this;
  }

  public function 
create()
  {
    
// здесь не нужна логика, мы всегда показываем emptyUser представлению user.create
    
$user $this->scenario->emptyUser();
    
$this->layout->nest('content''user.create'compact('user'));
  }

  public function 
store()
  {
    
// возвращаем createUserSuccess или createUserInvalid в зависимости от результата сценария
    
return $this->scenario->createUser(Input::get());
  }

  protected function 
createUserInvalid($validator$input)
  {
    return 
Redirect::action('UserController@create')->withErrors($validator)->withInput($input);
  }

  protected function 
createUserSuccess($user)
  {
    
Auth::loginUsingId($user->id);
    return 
Redirect::action('HomeController@dashboard');
  }
}
PHP
<?php

class UserScenario extends BaseScenario
{

  
/**
  * Я мог бы запросто использовать здесь репозиторий, но для этого
  * примера я собираюсь использовать обычную Eloquent-модель. Я думаю,
  * что репозитории - пример предварительной оптимизации и не должны
  * использоваться, если нет достаточно логики в самих моделях...
  */
  
public function __construct(User $user)
  {
    
parent::__construct();
    
$this->user $user;
  }

  
/**
  * просто вернем нового пользователя при создании сценария
  */
  
public function emptyUser()
  {
    return 
$this->user;
  }

  
/**
  * сценарий, в котором происходит создание нового пользователя...
  * мы проверяем поля ввода и создаем нового пользователя
  */
  
public function createUser($input)
  {
    
$validation $this->validator($input$this->user->creationRules);
    if (
$validation->fails()) {
      return 
$this->invoke('createUserInvalid', [$validation$input]);
    }
    
$this->user->create($input);

    return 
$this->invoke('createUserSuccess', [$user]);
  }

}

Вы удивлены тем, что здесь делает функция PHPinvoke()? Она реализована в моем классе BaseScenario и позволяет мне вызывать защищенные функции контроллера. Рефлексия позволяет мне видеть действия, которые не должны иметь маршрутов, привязанных к ним, то есть PHPcreateUserInvalid не имеет маршрута и является защищенной функцией. Так что вот, у нас есть контроллер фактически без логики, использующий сценарий. В моей следующей статье, я буду рассказывать о том, как держать шаблоны Laravel простыми и понятными с помощью макросов.

Как вы считаете, полезен ли этот материал? Да Нет

Комментарии (1)

Toratoda

Поздравляю, вы изобрели сервисный слой. Но до гексагональной архитектуры ещё не близко.
Валидация должна быть в классе Request, за одно это исключило бы ваше применение рефлексии которое тут не очень уместно.

Написать комментарий

Разметка: ? ?

Авторизуйся, чтобы прокомментировать.