{{TOC}} **Контейнер IoC** - неочевидная на первый взгляд возможность Laravel: его описание в ((док3:ioc документации)) сбивает с толку многих начинающих программистов и какое-то короткое время я сам был одним из них. Однако после копания этой темы и при поддержке замечательного сообщества Laravel на IRC-канале **FreeNode #laravel** эта тема полностью прояснилась. Надеюсь, я смогу пролить немного света на этот таинственный аспект Laravel в этой статье. //IoC// означает "обратный контроль" ("Inversion of Control"). Я не буду усложнять изложение детальным описанием этой проблемы, есть множество статей на эту тему. Вместо этого будем относиться к //IoC// как к "выполнение наоборот" или "возврат контроля выполнения кода в Laravel" для того, чтобы создать нужный объект. На самом деле это всё, зачем нужен данный //контейнер// - создание объектов. Если не считать его применения для //определения зависимостей// для юнит-тестов (мы поговорим об этом позже) вы можете просто думать о нём, как о короткой форме создания сложных объектов или получения //((ВП:singleton объекта-одиночки))// ("singleton") без прямого вызова методов самого класса. Об //одиночках// мы поговорим чуть ниже, а пока посмотрим, как происходит регистрация объектов в //контейнере IoC//. \ == Регистрация объектов == Используем нашу фантазию: представим класс %%Discoball%%, который мы будем использовать во всех примерах ниже для всякого рода трюков. К сожалению, наш %%Discoball%% требует установить множество конфигурационных опций перед тем, как он может быть использован. Посмотрим, как это происходит: %% $db = new Discoball(Discoball::SHINY); $db->configure_shinyness('max'); $db->spin_speed('8900rpm'); %% Ого, как много настроек! Ещё несколько таких вызовов и нам надоест каждый раз создавать и настраивать этот //дискотечный шар// ("discoball"). Давайте попросим //IoC// делать это для нас, а сами перейдём непосредственно к коду. Я обычно помещаю этот код в **application/start.php**, но вы можете делать это где угодно до тех пор, пока ваши объекты вызываются уже после того, как они зарегистрированы в //контейнере IoC//. %% IoC::register('discoball', function () { // создаём экземпляр объекта, как в примере выше: $db = new Discoball(Discoball::SHINY); $db->configure_shinyness('max'); $db->spin_speed('8900rpm'); // возвращаем созданный объект: return $db; }); %% Мы вызываем метод %%IoC::register()%% для регистрации нашего объекта в //контейнере//. **Первый параметр** - строка, которая будет использована для создания объекта - я использовал слово "discoball", потому что это выглядит самым логичным. **Второй параметр** - //анонимная функция// ("closure"), которая и создаёт сам объект. Внутри функции вы видите уже знакомый код настройки //шара//. Функция возвращает новый экземпляр объекта. Отлично! Объект зарегистрирован и это всё, что делает //IoC//... шучу. Давайте посмотрим, как нам теперь получить новый объект. == Получение объектов == Теперь когда у нас есть зарегистрированный объект %%Discoball%% нужно понять, как нам получить его обратно. Вот код для этого: %% $db = IoC::resolve('discoball'); %% Вот и всё! Вместо создания и настройки нового экземпляра объекта в каждом месте нашего кода мы вызываем метод %%IoC::resolve()%%, передаём ему строку-идентификатор, которая была использована при ((#регистраци+я))и и //IoC// вызовет //анонимную функцию//, привязанную к нему, которая вернёт нам новый экземпляр объекта %%Discoball%%. == Одиночки (singletons) == ((#Получение)) объекта - это хорошо, но что делать, если наш //дискотечный шар// требует много ресурсов для поддержания каждого экземпляра и нам нужно сконструировать его только один раз? В этом случае метод %%IoC::register%% не будет нам полезен, так как он создаёт новый объект при каждом запросе. Здесь в дело вступают //((ВП:singleton одиночки))//. Шаблон проектирования "одиночка" (//((WP:Singleton pattern==))//) означает, что вы пишите свой класс таким образом, что их экземпляр создаётся неким //статическим методом//, который будет возвращать один и тот же //экземпляр объекта// при каждом последующем вызове. Таким образом, класс создаётся только один раз на время выполнения запроса. Вы можете легко найти больше информации по этой теме - например, в ((http://php.net/manual/ru/language.oop5.patterns.php#language.oop5.patterns.singleton документации по PHP)). //Одиночки// полезны, но они требуют определённой структуры класса, чтобы работать. //Контейнер IoC// имеет метод %%signleton()%%, который решает эту проблему, так как с ним не требуется специальный //статический метод// класса для создания экземпляра объекта. Давайте зарегистрируем наш %%Discoball%% в качестве //одиночки//: %% IoC::singleton('discoball', function () { // создаём экземпляр объекта, как в примерах выше: $db = new Discoball(Discoball::SHINY); $db->configure_shinyness('max'); $db->spin_speed('8900rpm'); // возвращаем созданный объект: return $db; }); %% Как вы видите, код ничем не отличается от кода ((#регистраци+я))и, если не считать того, что вместо метода %%IoC::register()%% используется %%IoC::singleton()%% с теми же параметрами. Когда мы попытаемся ((#получение получить)) экземпляр //дискотечного шара// с помощью %%IoC::resolve()%% //анонимная функция// будет вызвана только один раз, после чего возвращённый объект будет сохранён в //контейнере// и все последующие вызовы %%resolve()%% будут возвращаеть именно его. Например: %% // здесь анонимная функция вызывается и создаёт новый экземпляр объекта: $db = IoC::resolve('discoball'); // на этот раз функция не вызывается и мы получаем тот же объект, что и строкой выше: $another = IoC::resolve('discoball'); %% Замечательно! Нужно отметить, что в качестве **второго параметра** вы можете передать методу %%IoC::singleton()%% уже созданный объект и он будет возвращаться всеми последующими вызовами %%resolve()%%. Например: %% $db = new Discoball(Discoball::SHINY); IoC::singleton('discoball', $db); // получить наш дискотечный шар: $ball = IoC::resolve('discoball'); %% В следующий раз я расскажу, как внедрять зависимости в //контейнер IoC// для проведения юнит-тестов.