Рубрики
Uncategorized

Зачем нам нужны контейнеры Laravel IoC?

Автор оригинала: David Wong.

Контейнер IOC – это удобный механизм для внедрения зависимостей-Тейлор Отуэлл

От: https://learnku.com/ларавель/т…

Laravel-одна из самых популярных и часто используемых современных платформ веб-приложений с открытым исходным кодом. Он предоставляет некоторые уникальные функции, такие как красноречивый ORM, конструктор запросов, Усадьба и другие модные функции, которые доступны только в Laravel.

Мне нравится Laravel из-за его уникального дизайна, такого как архитектурный стиль. Laravel использует несколько шаблонов проектирования внизу, таких как синглтон, фабрика, конструктор, фасад, стратегия, поставщик, агент и т.д. По мере того как мои знания растут, я все больше и больше нахожу красоту Ларавеля. Laravel предлагает разработчикам меньше проблем и больше удобства.

Изучение Laravel-это не только изучение того, как использовать различные классы, но и изучение философии и грамматики Laravel. Важной частью философии Laravel является контейнер IoC, также известный как сервисный контейнер. Это основная часть приложений Laravel, поэтому понимание и использование контейнеров IoC-важный навык, которым мы должны овладеть.

Контейнер IoC-это очень мощный инструмент управления классами. Он может автоматически анализировать классы. Далее я попытаюсь объяснить, почему это так важно и как это работает.

Прежде всего, я хотел бы поговорить о принципе отмены зависимости, который поможет нам лучше понять важность контейнеров IoC.

Этот принцип предусматривает, что:

Модули высокого уровня не должны зависеть от модулей низкого уровня, и оба они должны зависеть от абстрактных интерфейсов.

Абстрактные интерфейсы не должны зависеть от конкретных реализаций. Конкретная реализация должна зависеть от абстрактных интерфейсов.

Одним словом, это зависит скорее от абстракции, чем от конкретного.

class MySQLConnection
{

   /**
   * Database Connection
   */
   public function connect()
   {
      var_dump('MYSQL Connection');
   }

}


class PasswordReminder
{    
    /**
     * @var MySQLConnection
     */
     private $dbConnection;

    public function __construct(MySQLConnection $dbConnection) 
    {
      $this->dbConnection = $dbConnection;
    }
}

Часто возникает непонимание того, что обращение зависимости-это просто еще один способ сказать “инъекция зависимости”. Но на самом деле они разные. В приведенном выше примере кода, хотя класс MySqlConnection вводится в класс напоминания пароля, он по-прежнему зависит от класса MySqlConnection.

Однако напоминание пароля модуля высокого уровня не должно полагаться на MySqlConnection модуля низкого уровня.

Если мы хотим изменить MySqlConnection на соединение MongoDB, мы должны вручную изменить зависимости в конструкторе класса PasswordReminder.

Классы напоминания пароля должны полагаться на абстрактные интерфейсы, а не на конкретные классы. Так что же мы будем делать? Смотрите следующие примеры:

interface ConnectionInterface
{
   public function connect();
}

class DbConnection implements ConnectionInterface
{
 /**
  * Database Connection
  */
 public function connect()
 {
   var_dump('MYSQL Connection');
 }
}

class PasswordReminder
{
    /**
    * @var DBConnection
    */
    private $dbConnection;
    public function __construct(ConnectionInterface $dbConnection)
    {
      $this->dbConnection = $dbConnection;
    }
}

С помощью приведенного выше кода, если мы хотим изменить MySqlConnection на соединение MongoDB, нам вообще не нужно изменять зависимости в конструкторе класса PasswordReminder. Потому что теперь классы напоминания паролей зависят от интерфейсов, а не от конкретных классов.

Если вы мало что знаете о концепции интерфейсов, вы можете прочитать эту статью. Это поможет вам понять принцип обращения зависимостей и контейнер IoC.

Теперь я собираюсь рассказать о том, что произошло в контейнере МоК. Мы можем просто рассматривать контейнер IoC как контейнер, содержащий зависимости классов.

Интерфейс управления заказом интерфейс:

namespace App\Repositories;

interface OrderRepositoryInterface 
{
   public function getAll();
}

Класс DbOrderRepository:

namespace App\Repositories;

class DbOrderRepository implements OrderRepositoryInterface
{

  function getAll()
  {
    return 'Getting all from mysql';
  }
}

Класс OrdersController:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests;
use App\Repositories\OrderRepositoryInterface;

class OrdersController extends Controller
{
    protected $order;

   function __construct(OrderRepositoryInterface $order)
   {
     $this->order = $order;
   }

   public function index()
   {
     dd($this->order->getAll());
     return View::make(orders.index);
   }
}

Маршрут:

Route::resource('orders', 'OrdersController');

Теперь введите этот адрес в браузере.

Причина ошибки заключается в том, что контейнер службы пытается создать экземпляр интерфейса, который не может быть создан. Чтобы решить эту проблему, нам нужно только привязать интерфейс к определенному классу:

Добавьте следующую строку кода в файл маршрутизации, чтобы выполнить это:

App::bind('App\Repositories\OrderRepositoryInterface', 'App\Repositories\DbOrderRepository');

Теперь обновите браузер, чтобы увидеть:

Мы можем определить класс контейнера следующим образом:

class SimpleContainer
 {
    protected static $container = [];

    public static function bind($name, Callable $resolver)
    {   
        static::$container[$name] = $resolver;
    }

    public static function make($name)
    {
      if(isset(static::$container[$name])){
        $resolver = static::$container[$name] ;
        return $resolver();
    }

    throw new Exception("Binding does not exist in containeer");

   }
}

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

class LogToDatabase 
{
    public function execute($message)
    {
       var_dump('log the message to a database :'.$message);
    }
}

class UsersController {

    protected $logger;

    public function __construct(LogToDatabase $logger)
    {
        $this->logger = $logger;
    }

    public function show()
    {
      $user = 'JohnDoe';
      $this->logger->execute($user);
    }
}

Зависимости привязки:

SimpleContainer::bind('Foo', function()
 {
   return new UsersController(new LogToDatabase);
 });

$foo = SimpleContainer::make('Foo');

print_r($foo->show());

Выход:

string(36) "Log the messages to a file : JohnDoe"

Исходный код контейнера службы Laravel:

  public function bind($abstract, $concrete = null, $shared = false)
    {
        $abstract = $this->normalize($abstract);

        $concrete = $this->normalize($concrete);

        if (is_array($abstract)) {
           list($abstract, $alias) = $this->extractAlias($abstract);

           $this->alias($abstract, $alias);
        }

        $this->dropStaleInstances($abstract);

        if (is_null($concrete)) {
            $concrete = $abstract;
        }


        if (! $concrete instanceof Closure) {
            $concrete = $this->getClosure($abstract, $concrete);
        }

        $this->bindings[$abstract] = compact('concrete', 'shared');


        if ($this->resolved($abstract)) {
            $this->rebound($abstract);
        }
    }

    public function make($abstract, array $parameters = [])
    {
        $abstract = $this->getAlias($this->normalize($abstract));

        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }

        $concrete = $this->getConcrete($abstract);

        if ($this->isBuildable($concrete, $abstract)) {
            $object = $this->build($concrete, $parameters);
        } else {
            $object = $this->make($concrete, $parameters);
        }


        foreach ($this->getExtenders($abstract) as $extender) {
            $object = $extender($object, $this);
        }

        if ($this->isShared($abstract)) {
            $this->instances[$abstract] = $object;
        }

       $this->fireResolvingCallbacks($abstract, $object);

       $this->resolved[$abstract] = true;

       return $object;
    }

    public function build($concrete, array $parameters = [])
    {

        if ($concrete instanceof Closure) {
            return $concrete($this, $parameters);
        }

       $reflector = new ReflectionClass($concrete);

        if (! $reflector->isInstantiable()) {
            if (! empty($this->buildStack)) {
                $previous = implode(', ', $this->buildStack);
                $message = "Target [$concrete] is not instantiable while building [$previous].";
            } else {
                $message = "Target [$concrete] is not instantiable.";
            }

          throw new BindingResolutionException($message);
        }

         $this->buildStack[] = $concrete;

         $constructor = $reflector->getConstructor();

        if (is_null($constructor)) {
            array_pop($this->buildStack);

           return new $concrete;
        }

        $dependencies = $constructor->getParameters();
        $parameters = $this->keyParametersByArgument(
            $dependencies, $parameters
        );

         $instances = $this->getDependencies($dependencies,$parameters);

         array_pop($this->buildStack);

         return $reflector->newInstanceArgs($instances);
    }

Если вы хотите узнать больше о служебных контейнерах, взгляните. поставщик/laravel/фреймворк/src/Подсветка/Контейнер/Контейнер. php

Простая привязка

$this->app->bind('HelpSpot\API', function ($app) {
    return new HelpSpot\API($app->make('HttpClient'));
});

Привязка одноэлементного шаблона

метод singleton , привязанный к классу или интерфейсу контейнера службы, анализируется только один раз.

$this->app->singleton('HelpSpot\API', function ($app) {
    return new HelpSpot\API($app->make('HttpClient'));
});

Экземпляр привязки

Он также может быть передан через экземпляр Методы Привязки определенных экземпляров к контейнерам служб. После этого экземпляр привязки всегда возвращается:

$api = new HelpSpot\API(new HttpClient);

$this->app->instance('HelpSpot\API', $api);

Без привязок PHP использует механизмы отражения для анализа экземпляров и зависимостей.

Для получения более подробной информации см. официальную документацию

Практический код для сервисного контейнера Laravel можно получить на моем GitHub (Если хотите, пожалуйста, не скупитесь на star) Доступ к складу.

Спасибо, что прочитали.

От: https://learnku.com/laravel/t…

Дополнительные статьи: https://learnku.com/laravel/c…