Nette Dependency Injection – Konečně zde! Opravdu!
- Filip Procházka
- Moderator | 4668
Nette Dependency Injection
Repozitář: https://github.com/…Lan/Nette-DI
Ukázka ke stažení: http://di.kdyby.org/…-example.zip
Zdravím,
inspirován Honzovým
článkem, jsem se rozhodl implementovat to o čem píše. Koukl jsem do
Nette\Application\Application
a zjistil, že by to šlo pěkně
napsat, bez jediného zásahu do Nette.
Nakolik jsem uspěl a nakolik je to čisté posuďte sami. Jakékoliv nápady a bugfixy uvítám.
PS: Honzo, kdyby mi tam někde chyběli @author
tak dej
vědět :)
Instalace:
Stáhnout a nakopírovat https://github.com/…Lan/Nette-DI
do LIBS_DIR
nastavit config.ini
service.Nette-Application-Application.option.class = NetteDI\Application\Application
service.Nette-Application-IPresenterLoader = NetteDI\Application\PresenterLoader
Vytvořím si třídu Configurator
, která bude vycházet z
Nette\Configurator
a bude přidávat
metodu createServiceContainer
class Configurator extends Nette\Configurator
{
/**
* @return \NetteDI\Service\IServiceContainer
*/
public static function createServiceContainer()
{
$services = array(
'ISmartService' => array(
'class' => 'SmartService',
'methodCall' => array(
'setTicklish' => array(TRUE)
),
'aliases' => array('Smart'),
'autowire' => TRUE,
),
'IDummyService' => array(
'factory' => "DummyService::createDummyService",
'arguments' => array(
'ticklish' => TRUE
),
'aliases' => array('Dummy'),
)
);
return NetteDI\Configurator::createServiceContainer($services);
}
}
Teď ji musím v bootstrap.php
vnutit do
Nette\Environment
require_once APP_DIR . '/models/Configurator.php';
Environment::setConfigurator(new Configurator());
Environment::loadConfig();
Třídy DummyService a SmartService jsou přibalené v ukázce
Použití?
V Configurator
je ukázka „sexy“ konfigrace služeb, je
možné jim vnutit argumenty, je možné jim pomocí autowire
předat automaticky služby, které vyžadují v konstruktoru, je možné volat
jejich metody, hned po inicializaci.
Argumenty, které NetteDI\Service\ServiceLoader
rozpozná
%nazev\sluzby
– argument s prefixem % se přeloží na instanci konkrétní službyE$wwwDir
– tohle, se přiznávám, jsem moc netestovat, ale snad by to mělo prohnat přesNette\Environment::getVariable('wwwDir')
C$database
– tohle, se přiznávám, jsem taky moc netestovat, ale snad by to mělo prohnat přesNette\Environment::getConfig('database')
Co se týče služeb, není to nic nového, Honza Marek už to implementoval,
já mu to jenom trochu překroutil. Ale co tohle přidává je DI do
Presenterů. Takže pokud váš BasePresenter
bude vycházet
z NetteDI\Application\Presenter
abstract class BasePresenter extends \NetteDI\Application\Presenter
{
// ...
budou mu AUTOMATICKY předávány v konstruktoru služby
class HomepagePresenter extends BasePresenter
{
/** @var DummyService */
protected $dummy;
/** @var SmartService */
protected $smart;
/**
* @param IDummyService $dummy
* @param ISmartService $smart
*/
public function __construct(IDummyService $dummy, ISmartService $smart)
{
$this->dummy = $dummy;
$this->smart = $smart;
}
// ...
Zatím to vyžaduje, aby se název služby a název implementovaného
interface a vyžadovaného interface v konstrutoru shodovali. Jestli je to
špatně, to nevím, ale přemýšlel jsem o automatickém aliasování, pro
případ kdy bych volal službu, jejíž třída se jmenuje jinak, než jak je
pojmenovaná v ServiceContainer
, ale zase když jsou tu ty
Interfaces, tak s nimi mi to přijde čistší.
Samozřejmě startup
ani žádné další funkce by to ovlivnit
nemělo.
Otázkou je, kde se to ještě hodí? Má význam to dělat pro
Control
?
PS: zítra zkusím doplnit více informací
Editoval HosipLan (22. 1. 2011 20:45)
- Honza Marek
- Člen | 1664
Super. To, že DI půjde implementovat do Nette bezzásahově, je výborná
zpráva. Přiznám se, že zneužít interface
Nette\Application\IPresenter
pro to, čemu říkám
PresenterFactory
, mě nenapadlo.
Přimlouval bych se ale za podrobnější popis toho tvého řešení. Koukal
jsem se do zdrojáku a občas trochu tápu, co se týče rozdělení
odpovědností jednotlivých třídy. Třeba mi není jasná role třídy
ServiceLoader
ve vztah ke třídě
ServiceContainer
.
Pokud jsem to dobře pochopil, tak po službách je také vyžadováno
rozhranní NetteDI\Service\IService
. To by byl hrubý omyl z toho
důvodu, že službám přece nemůžeš cpát jak mají vypadat. Zejména
službám, které nevyrábíš sám, např. Nette\Web\IUser
a
další. Pokud je NetteDI\Service\IService
zamýšleno jen pro
objekty, kterým je možné předat kontejner, tak mi to také přijde
zbytečné. Je přece jedno, jestli se kontejner dostane do objektu přes metodu
setContainer
nebo třeba přes konstruktor.
- Filip Procházka
- Moderator | 4668
V tom případě se pustím do úprav, abych pravdu řekl, moc jsem tomu navrhování nedal, chtěl jsem hlavně dokázat, že to funguje :) teď se to může poladit :)
- jantichy
- Člen | 24
Tohle drbání se pravou rukou za levým uchem s vytvářením presenteru se mi vůbec nelíbí, takže jsem Davidovi poslal pull request, který zavádí regulérní oficiální tovární třídu pro vytváření instance presenteru. Takže pak stačí jen implementovat vlastní IPresenterFactory. Viz https://github.com/…tte/pull/191