Използване на Traits

public function boot()
{
$this->container = new Container();
$this->container->set('kernel', $this); // Ако имаме нужда от достъп до kernel-а някъде
}
Така не се ли нарушава самостоятелността на обектите? Добра практика ли е всички да имат достъп до (и по-важното - да разчитат на) Kernel класа?
В какви случаи това е наложително?
 
Какво имаш на предвид под самостоятелност на обектите?

Идеята kernel-a да е в контейнера е, че това ти позволява изпълнението на под завки или команди (заявки през командният ред) в приложението ти. Това се използва масово при функционални тестове.

Реално приложението би могло да симулира заявка вътре в себе си за да получи желаният резултат, като използва същият kernel. Като запазваш kernel-a в контейнера не ти се налага да го boot-ваш всеки път.

Реално до контейнера не би трябвало да има достъп всеки сервиз. Той трябва да получава необходимите зависимости като параметри в конструктора. До контейнера трябва да имат достъп само класове с множество зависимости, които не се използват винаги.

Всички останали зависимости на една услуга трябва да бъдат зададени при генерирането услугата.

Пример: За да криптирам информация достъпна само за текущата сесията на потребителя, съм си създал един прост Encrypter клас, който приема методът за криптиране и SessionInterface:

PHP:
<?php

namespace UtilitiesBundle\Util\Encryption;

use Symfony\Component\HttpFoundation\Session\SessionInterface;

class Encrypter
{
	const ENCRYPTER_SESSION_KEY = 'util.encrypter.secret';

	/**
	 * @var string
	 */
	private $method;

	/**
	 * @var string
	 */
	private $secret;

	/**
	 * Encrypter constructor.
	 * @param string $method
	 */
	public function __construct(string $method, SessionInterface $session)
	{
		$this->method = $method;

		if ($session->has(static::ENCRYPTER_SESSION_KEY))
		{
			$this->secret = $session->get(static::ENCRYPTER_SESSION_KEY);
		}
		else
		{
			$this->secret = base64_encode(random_bytes(128));
			$session->set(static::ENCRYPTER_SESSION_KEY, $this->secret);
		}
	}

	/**
	 * @param string $string
	 * @return string
	 */
	public function encrypt(string $string): string
	{
		... encrypting using $this->secret
	}

	/**
	 * @param string $encrypted
	 * @return string
	 * 
	 * @throws \InvalidArgumentException
	 */
	public function decrypt(string $encrypted): string
	{
		... decrypting using $this->secret
	}
}

Дефиницията на услугата в контейнера ми е следната:

Код:
services:
  util.encrypter:
    class: UtilitiesBundle\Util\Encryption\Encrypter
    arguments:
      - "aes-256-cbc"
      - "@session"

По този начин, ако искам да криптирам дадена информация в контролера, мога да го направя само като направя:

PHP:
...
$encrypter = $this->container->get('util.encrypter');
$enctypted = $encrypter->encrypt("Some secret text");

По този начин полученият текст в $encrypted ще може да бъде декриптиран само в текущата сесия посредством $encrypter->decrypt("alabala#encryptedvalue");

И сега плюсът от контейнера:
В тестовете няма нужда да се притеснявам каква сесия се подава на енкриптера, защото kernel-a може да ми генерира различни Mock-ове за сесия. Освен това, ако след 1 година реша, че няма да ползвам $_SESSION, а ще използвам базата данни или Redis, няма да се налага да ходя да пренаписвам и encryptera, а само услугата @session, която просто пак ще трябва да имплементира SessionInterface.

Мисля, че трябва да се направят доста уроци по темата, тъй като забелязвам, че много хора не разбират как да използват тези благинки.
 
Какво имаш на предвид под самостоятелност на обектите?
Абе... превод на encapsulation

Идеята kernel-a да е в контейнера е, че това ти позволява изпълнението на под завки или команди (заявки през командният ред) в приложението ти. Това се използва масово при функционални тестове.
Тук имаш предвид shell команди? За това не служи ли exec() & co?
А дори и в такъв случай, не е ли по-добре shell-обработващата част да стои отделно от kernel-а?

Реално приложението би могло да симулира заявка вътре в себе си за да получи желаният резултат, като използва същият kernel. Като запазваш kernel-a в контейнера не ти се налага да го boot-ваш всеки път.
Под "симулира заявка" имаш предвид извикване на $kernel->handle($req) с различен Request? Гледам Kernel-а на Symfony и доколкото виждам има __constructor и boot, както и shutdown и terminate... като shutdown извиква terminate...?? (в документацията не открих нищо...

Реално до контейнера не би трябвало да има достъп всеки сервиз. Той трябва да получава необходимите зависимости като параметри в конструктора. До контейнера трябва да имат достъп само класове с множество зависимости, които не се използват винаги.
Това е малко условно - повече или по-малко зависимости?
Контейнерът се ползва, за да бръкнеш и да си вземеш каквото ти трябва без да изискваш референции при извикването на методите, т.е. динамично да можеш да ползваш всичко.

Всички останали зависимости на една услуга трябва да бъдат зададени при генерирането услугата.
Предполагам това означава зависимостите да стоят в параметрите. Което е обратното на идеята на контейнера. Едното (container), другото (parameters) или и от двете според ситуацията?

От примера с class Encrypter се вижда, че не е необходим контейнер (в случая).

Тогава защо при използването му е необходим контейнер? Защо трябва да вземеш $encrypter от контейнера, вместо да го получиш от аргументите?

И сега плюсът от контейнера:
В тестовете няма нужда да се притеснявам каква сесия се подава на енкриптера, защото kernel-a може да ми генерира различни Mock-ове за сесия.
Това по какъв начин е плюс за контейнера? Т.е. тук контейнера с какво помага? Доколкото виждам в yml-то задаваш аргументи, с които да се извика. Тук койтейнерът ти върши работата на инициализиране - полезно.

Освен това, ако след 1 година реша, че няма да ползвам $_SESSION, а ще използвам базата данни или Redis, няма да се налага да ходя да пренаписвам и encryptera, а само услугата @session, която просто пак ще трябва да имплементира SessionInterface.
Съгласен съм. Но това (?май?) няма връзка с контейнера.

---
Преди известно време в едно приложение използвах ръчно писан, съвсем прост контейнер, който слагах общо взето всичко - БД, информация за юзера, информация към viewтата...
Беше удобно, наистина, но стана пълна каша. Всеки модел и всеки контролер държеше връзка към контейнера и си взимаше бд / кой каквото.
И отделно, при създаването на всеки обект (контролери/модели) трябваше да правя
$obj = new Class();
$obj->setContainer($this->container);
И така навсякъде, където ми трябваше нещо от контейнера.

След това потърсих за готови контейнери. Мислех си за нещо по-"истинско" (symfony-style), но накрая се спрях на dice - четох, че е бърз, а и откъм функционалност ми се виждаше страшно удобен за ползване. И направих следното:
Във всеки контролер слагам нужните ми модели в аргументите.
Във всеки модел слагам нужните ми зависимости в аргументите.

Когато си намеря контролера (routerи и т.н.), връчвам го на dice, и... всичко тръгва. Няма контейнери, няма дявол. (то dice си е контейнер, де, но е изцяло изолиран. Всъщност не е изолиран, а "скрит"). Вече няма $this->c->get.... и $obj->setContainer()... - изчистил съм всичко, което не е непосредствено необходимо на дадения клас.
 
Абе... превод на encapsulation

Не съм сигурен. Реално погледнато има логика. Явно Symfony правят тази грешка.

Тук имаш предвид shell команди? За това не служи ли exec() & co?
А дори и в такъв случай, не е ли по-добре shell-обработващата част да стои отделно от kernel-а?

Имам на предвид команди към твоето приложение, които да се викат от командният ред.

Пример:
За да не се налага да правя промени по crontab, тъй като използвам един код за много сайтове, на много сървъри, съм създал една команда bin/console crontab:exec, която се извиква от crontab всяка минута. Тази команда посредством конфигурации (услиги в контейнера с таг crontab.job), си създава отделни нишки (thread-ове). След това всяка нишка извиква друга команда (примерно data:update, cache:warmup...). Всички подкоманди използват същият kernel защото при създаването им се използва kernel-а от контейнера.


Под "симулира заявка" имаш предвид извикване на $kernel->handle($req) с различен Request? Гледам Kernel-а на Symfony и доколкото виждам има __constructor и boot, както и shutdown и terminate... като shutdown извиква terminate...?? (в документацията не открих нищо...

Именно. Разликата между shutdown и terminate е следната: Ако стартираш shutdown на всички бъндъли (в симфони се приложението е разделено на самостоятелни преизползваеми части на име бъндъл) се извиква метода shutdown и се нулира контейнерът им. Terminate пуска събитие kernel.terminate в event.dispatcher-а на symfony, към което можеш да закачиш различни слушатели (listeners) (примерно да се отвърже от БД).

Това е малко условно - повече или по-малко зависимости?
Контейнерът се ползва, за да бръкнеш и да си вземеш каквото ти трябва без да изискваш референции при извикването на методите, т.е. динамично да можеш да ползваш всичко.

Логиката е следната: Ако даден код не е специфичен за приложението и може да се използва в множество приложения (като Encrypter-a), то е логично да не зависи от контейнер, а да получава зависимостите си като параметри. По този начин, когато го дефинираш пред контейнера, можеш да му зададеш необходимите dependencies. Ако кодът ти е специфичен за приложението и не е приложим извън него (примерно контролер), то той трябва да използва контейнера. Като все пак всички жизнени за него (като контролер, а не за конкретен контролер) зависимости трябва да се подават като параметри на конструктора.

Общо взето логиката е проста: Ако нещо е жизнено важно то е параметър. Ако не е, се задава с метод. Един контролер може да се изпълни и без контейнера - може да върне просто някакъв Response. В symfony контролера може да бъде клас, който не наследява нищо. Наследяването се прави за не пишеш всеки път ContainerAwareInterface alabala....

PHP:
class TestController
{
    public function someAction()
    {
       return new Response('alabala');
    }
}

Това е напълно валиден контролер в Symfony (тествано и работи).


От примера с class Encrypter се вижда, че не е необходим контейнер (в случая).

Тогава защо при използването му е необходим контейнер? Защо трябва да вземеш $encrypter от контейнера, вместо да го получиш от аргументите?

Както вече казах - Encrypter е код, който може да се използва във всяко приложение. В него няма код, който да е специфичен за приложението. Можеш спокойно да си напишеш $encrypter = new Encrypter('alabala', $sessionStorage);

Идеята обаче този Encrypter да е в контейнера е, че 1123123 контролера, мениджъра и т.н. го ползват. Ако след 1 година реша, че този Encrypter не ми върши работа и той не е в контейнера, ще трябва да променя кода на 123123 места. Ако е в контейнера обаче, ще трябва да сменя само дефиницията в контейнера. Грешката в примера, който съм дал е, че не съм създал EncryterInterface, на който да разчитат нещата които го ползват.

Когато си в тестов режим (test environment), нямам $_SESSION. Когато прескачам от тест в тест с phpunit няма да ми се помни сесията. За тази цел кернела дефинира друг клас като @session в контейнера (MockArraySessionStorage). Реално Encryptera си няма идея, че работи с MockArraySessionStorage, а не с реалния SessionStorage, който използва $_SESSION. По този начин кодът ми стои изолиран.



Съгласен съм. Но това (?май?) няма връзка с контейнера.
Мися, че обясних логиката с промяната на Encrypter.

В симфони контейнера се нарича DependencyInjectionContainer. Това не е случайно.

Ако има услига, която иска да ползва контейнера, мога просто в дефиницията и да добава calls с информация за контейнера. Същото важи и за други нежизнено важни dependencies.

Код:
some_event_subscriber:
    class: MyBundle\EventSubscriber\SomeEventSubscriber
    arguments:
      - "%kernel.root_dir%"
    calls:
      - [setContainer, ["@service_container"]]
    tags:
      - {name: kernel.event_subscriber}

Сега контейнера на симфони ще направи следното:
$some_event_subscriber = new MyBundle\EventSubscriber\SomeEventSubscriber('/vat/www/..../app');
$some_event_subscriber->setContainer($this);

По време на генерирането на контейнера (компилиране), че бъде прочетен и тага kernel.event_subscriber.

Услугата event_dispatcher е добавила един CompilerPass, който проверява всички услуги с този таг и си ги записва като в дефиницията си добавя addMethodCall към addListenerService (Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass - за справка).

По този начин контейнера знае как да инициализира всяка услуга, но не го прави, докато не я поискаш.

Трябва да гледаш на нещата абстрактно. :)

ПП. Двата ми коментара събраха материал за урок :D
 
Интересна дискусия, но може ли да споделите малко повече информация, какво точно се очаква да прави един контейнер. Навсякъде има информация как се ползва този или онзи контейнер, но има много малко за това как да си напишем свой. Намерих едно видео, в което се почва от нулата, но е на френски и доста неща не разбирам. Когато го допиша ще споделя кода тук. Другия който видях беше в krasimirtsonev.com но не ми хареса как е написан, а и обясненията са пак какво е DI а не за самия контейнер.
 
Наистина интересна дискусия се отвори по темата. И все пак кажете ми в случаят ми как да преразпределям настройките или прочие в кода на класът. Ок ще ги подавам на места през конструктора, но ако класовете са сингълтон ще ми трябва метод да приема кода отвън. Хубаво, но трейтовете с използване на външна инстанция в тях не е ли вид разновидност на DI? Все пак кодът ми е на едно място а се използва в много класове, тоест ако направя промяна в трейта ще се отрази навсякъде където се използва той. Вярно е, че се нарушава самостоятелността на класовете, но в случаят аз така или иначе ще трябва да правя инстанция към контейнер, защо трябва д асе обръщам към него като мога да се обърна директно към контролера и да ми върне инстанцията на класът (От AppКеrnel към класовете където е нужно) В кернела по интерфейс винаги мога да сетна и да сменя за класът рутера или конфиг папката или изгледа и прочие само с обръщане към ядрото и сетване на новият обект. Все пак тук идва една логическа грешка в парадигмата ни. Все някъде ще трябва да инициализирам обекта, а контейнера реално съдържа инстанции на обектите. Затова е контейнер да задържа, не да ги управлява. Защо трябва да задържам инстанции в контейнера, правени в конттролера (AppKernel) и после да наследявам контейнера и неговите масиви с данни, като в моят случай се обръщам към контролера на инстанциите и ги викиам само при нужда, а ако имам контейнер за да ги извикам ще $container->get('Configuration') ще трябва преди това да съм направил или наследил контейнера, и ако няма инстанцията да върне грешка. Преди това трябва пък да съм създал инстанцията в Ядрото и реално погледнато контейнера ми е само посредник на инстанциите, но като се замисля всеки път при викането му той ще бави защото ще трябва да обработва голям масив. Не ми ли е по-добре да се обръщам къвм контролера на контейнера към неговите методи и те да ми връщат резултата. Закакво ми е да създавам инстанцията някъде по дефаут ако не съм сетнал друг обект от интерфейса да я препредавам на контейнера под някакъв ключ и накрая да я викам някъде си, като Кернела ми директно в момента прави същото и управлява инстанциите и прочие. Наистина харакирито с тези контейнери е преувеличено, все пак това е просто шаблон на писане, но има и един термин преоптимизация, който често води до прекомерно претрупване. Изчистеното ядро и сведените до минимум брой класове, води до по-добро представяне.
 
Не съм много компетентен по въпроса, но според мен трейтовете не могат да са разновидност на DI, защото ти не премахваш зависимостите а създаваш такива. Един вид ако решиш даден клас да го извадиш от приложението като библиотека, то трябва и трейтовете да ги извадиш с него.
 
teroristd каза:
Не съм много компетентен по въпроса, но според мен трейтовете не могат да са разновидност на DI, защото ти не премахваш зависимостите а създаваш такива. Един вид ако решиш даден клас да го извадиш от приложението като библиотека, то трябва и трейтовете да ги извадиш с него.

Вярно е и това.
 
@gamena007 - тези конфиги за DI ме "плашат", защо трябва да се пишат? Symfony не успява ли само да разбере кой клас се иска(ако е клас де, не интерфейс)
 
Не знам какво да ви кажа повече. Имам чувството, че @dakata__92 въобще не е прочел това което съм написал в предишните два поста, както и в няколко други дискусии по темата, започнати имено от него. Не пиемай това което пиша за абсолютна истина (най-вероятно не е такава), но поне се старай да го прочетеш, след като питаш! Мога да ти гарантирам, че така структуриран кодът ти ще бъде много по-бавен от този с контейнера. Да не говоря, че няма да бъде четим. Ако трябва някой да наследи работата ти, ще трябва да се опитва да ти разбере идеята ти първите няколко седмици.
Не мисля, че има нужда да се повтарям 12312312 пъти, каква е идеята на контейнера.

@teroristd, не е добра идея да си пишеш сам контейнер. Нямаш нужда от това. Единствената смислена причина е, за да се "тренираш". Не ти трябва урок как да си го напишеш. Отдели два дни, хвани един качествен контейнер (примерно този на Symfony, но той е доста сложен, тъй като е изключително функционален и оптимизиран) и разбери как работи из основи. След като знаеш как работи, можеш да погледнеш и някой друг. Да натрупаш добри идеи и практики и чак след това да пишеш каквото и да е. Няма как да си напишеш нещо, което не знаеш как работи.

@lam3r4370, според теб как може един код сам да отгатва кой клас искаш да ползваш ако не му е зададено? Идеята е, че или пишеш конфигурации, или бъхтиш като гламав, когато нанасяш корекции (примерно сменяш SMTP клиента). В повечето случаи това бъхтене води само до едно - недокоментирани функционалности (бъгове).
 
gamena007 каза:
@teroristd, не е добра идея да си пишеш сам контейнер. Нямаш нужда от това. Единствената смислена причина е, за да се "тренираш". Не ти трябва урок как да си го напишеш. Отдели два дни, хвани един качествен контейнер (примерно този на Symfony, но той е доста сложен, тъй като е изключително функционален и оптимизиран) и разбери как работи из основи. След като знаеш как работи, можеш да погледнеш и някой друг. Да натрупаш добри идеи и практики и чак след това да пишеш каквото и да е. Няма как да си напишеш нещо, което не знаеш как работи.

Идеята ми е точно такава, да разбера как работят. При фреймуорците като Symfony нещата са преплетени едно с друго и не мога да им хвана нишката на логиката. Затова се опитвам да намеря нещо по-просто което да почва от нулата.
 
На видеото човекът обяснява много подробно, показва защо се правят нещата, но проблемът е че е на френски :) .
gamena007 ако имаш желание бих се радвал да направиш някое уроче подобно на това на български :) .

Ето и самият клас, и примери за ползването му.
Ето и видеото https://www.youtube.com/watch?v=22d4_KIqBNc
Код:
class DIC {

    private $registry = [];
    private $factories = [];
    private $instances = [];

    public function set($key, callable $resolver) {
        $this -> registry[$key] = $resolver;
    }





    public function setFactory($key, callable $resolver) {
        $this -> factories[$key] = $resolver;
    }





    public function setInstance($instance) {
        $reflection = new ReflectionClass($instance);
        $this -> instances[$reflection -> getName()] = $instance;
    }





    public function get($key) {

        if (isset($this -> factories[$key])) {

            return $this -> factories[$key]();
        }

        if (!isset($this -> instances[$key])) {

            if (isset($this -> registry[$key])) {

                $this -> instances[$key] = $this -> registry[$key]();
            }
            else {

                $reflected_class = new ReflectionClass($key);

                if ($reflected_class -> isInstantiable()) {

                    $constructor = $reflected_class -> getConstructor();

                    if ($constructor) {
                        
                        $parameters = $constructor -> getParameters();

                        $constructor_parameters = [];

                        foreach ($parameters as $parameter) {

                            if ($parameter -> getClass()) {

                                $constructor_parameters[] = $this -> get($parameter -> getClass() -> getName());
                            }
                            else {
                                $constructor_parameters[] = $parameter -> getDefaultValue();
                            }
                        }

                        $this -> instances[$key] = $reflected_class -> newInstanceArgs($constructor_parameters);
                    }
                    else {
                        $this -> instances[$key] = $reflected_class -> newInstance();
                    }
                }
                else {

                    throw new Exception($key . ' Is not instanciable class!');
                }
            }
        }

        return $this -> instances[$key];
    }

}

















require 'DIC.php';
require 'Database/Connection.php';



namespace Database;

class Connection {

    private $db;
    private $pass;
    private $user;
    private $uniqid;

    public function __construct($db, $pass, $user) {

        $this -> db = $db;
        $this -> pass = $pass;
        $this -> user = $user;
    }

}







class Model {

    private $connection;
    private $uniqid;

    public function __construct(Database\Connection $connection) {

        $this -> connection = $connection;
    }


}








$dic = new DIC();

$dic -> set('Database\Connection', function() {
    return new Database\Connection('db_name', 'pass', 'user');
});

$dic -> setFactory('Model', function () use($dic) {
    return new Model($dic -> get('Database\Connection'));
});

$dic -> get('Model');
 
Имам едно питане. Да кажем имам си контейнер и прилежащите му класове, примерно Repository, Factory, алабала, имам си файл където записвам имена, пътища, аргументи, например json или някаккъв друг, сетвам си гетвам си и т.н. и всичко е ок. Стигаме до там когато трябва да инжектирам обекти. Примерно имам някакви класове в които трябва да инжектирам различни обекти. Трябва да напиша примерно:

Код:
$container = new Container();

$container->set('obj_1', 'alabala');
$container->set('obj_2', 'drundrun');

$user = $container->get('obj1');
$mail = $container->get('obj2');

Въпросът ми е къде трябва да ги пиша тези работи за да мога после да ги използвам?
Ако се ориентирам правилно, трябва да си направя един клас примерно ContainerBuilder, и в него да сетвам и да гетвам, след което класовете в които искам да инжектирам да го наследяват, за да имам достъп до променливите. Прав ли съм или греша?
 
Забрави за всякакво наследяване, когато говорим за Dependency Injection!

Имаш контейнер с конфигурация
Код:
Service1:
  class: My\FirstService
  calls:
     - [setContainer, ["@service_container"]] # ако е ContainerAware

Service2:
  class: My\SecondService
  arguments:
     -  "@Service1"

В този момент, когато му кажеш: $container->get('Service2"), контейнера изпълнява


1. Контейнера вижда, че трябва да се създаде услуга Service2, която е от клас My\SecondService
2. Контейнера вижда, че при създаването на инстанция от My\SecondService, трябва да пусне като аргумент услуга Service1
$Service2 = new My\SecondService($this->get('Service1'));
3. Контейнера се опитва да вземе Service1. Вижда, че тя е от клас My\FirstService. Няма зададени аргументи.
$Service1 = new My\FirstService();
4. Контейнера вижда, че при създаването на услугата, трябва да извика метод setContainer, с аргумент услугата service_container
$Service1->setContainer($this->get('service_container')); // service_container=$this това е заложено в get()

В крайна сметка нещата стават в този ред:
PHP:
$Service1 = new My\FirstService();
$Service1->setContainer($this);
$Service2 = new My\SecondService($Servcice1);

Всичко това го прави контейнера.

Този контейнер се намира в kernel-а
При $kernel->handle($request), след създаването на контролера, ако той имплементира ContainerAwareInterface, му се пуска setContainer($this/*Kernel*/->container).

От тук нататък вече всяко нещо което искаш да правиш, може да бъде инициализирано в контейнера (задължително е да бъде инициализирано в контейнера).

Но как да обясним тези дефиниции на контейнера контейнера?
Всеки си има предпочитания. По-смотаните контейнери използват Closure. По-умните правят следното:
PHP:
$container
    ->register('Service1', 'My\FirstService')
    ->addMethodCall('setContainer', [new Reference('service_container')]);

$container
    ->register('Service2', 'My\SecondService')
    ->addArgument(new Reference('Service1'));
 
Нещо не мога да схвана как става самото инжектиране на обект в конструктора. Имам следната ситуация. Най грубо казано имам клас App който ми е ядрото и в него ми е инициализиран контейнера, един модел и един контролер.

В модела имам един конструктор с едно echo.
Код:
class Model {
    public function __construct() {
        echo 'This is Model';
    }
}

По този начин написан контролера, получавам 'This is Model', както и се очаква.
Код:
class Controller {
    public function__construct() {
        $this -> _app = App::getInstance();
        $this -> _app -> _container -> make('Model');
    }
}

Не мога да разбера как трябва да стане за да инжектирам в конструктора. Написан по горния начин кода се обезмисля, защото то е все едно да кажа направо new Model(); Написан по долния начин кода не работи. Навсякъде където се говори за DI, е даден подобен на долния пример, но реално за контролера този параметър не значи нищо. Разбирате ли объркването ми? Ако може някой да даде прост, ясен и работещ пример за DI ще съм му много благодарен.
Код:
class Controller {
    public $_model;
    public function__construct(Model $model) {
        $this->_model = $model;
    }
}
 

Back
Горе