Skip to content

Commit

Permalink
di-configuration: fixed wrong info about autowiring
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Mar 6, 2018
1 parent 6babe05 commit b8f62c1
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 136 deletions.
146 changes: 78 additions & 68 deletions cs/di-configuration.texy
Expand Up @@ -175,7 +175,7 @@ Pojmenování služeb je vhodné hlavně ve chvíli, kdy na ně chceme odkazovat

/--neon
services:
- PDO('sqlite::memory:)
- PDO('sqlite::memory:')

-
factory: Model\ArticleRepository
Expand Down Expand Up @@ -362,154 +362,164 @@ class ArticleRepository
\--


Omezení autowiringu
-------------------

Aby bylo možné použit autowiring, musí pro každý typ být v kontejneru právě jedna služba. Pokud by jich bylo víc, autowiring by nevěděl, kterou z nich předat a vyhodil by výjimku:

/--neon
services:
mainDatabase: PDO(%dsn%, %user%, %password%)
tempDatabase: PDO('sqlite::memory:)
articles: Model\ArticleRepository # VYHODÍ VÝJIMKU
mainDb: PDO(%dsn%, %user%, %password%)
tempDb: PDO('sqlite::memory:')
articles: Model\ArticleRepository # VYHODÍ VÝJIMKU, vyhovuje mainDb i tempDb
\--

Řešením by bylo buď autowiring obejít a explicitně uvést název služby (tj `articles: Model\ArticleRepository(@mainDatabase)`). Nebo jej u jedné ze služeb můžeme vypnout. Autowiring pak bude fungovat a automaticky bude předávat službu druhou:
Řešením by bylo buď autowiring obejít a explicitně uvést název služby (tj `articles: Model\ArticleRepository(@mainDb)`). Nebo jej u jedné ze služeb můžeme vypnout. Autowiring pak bude fungovat a automaticky bude předávat službu druhou:

/--neon
services:
mainDatabase: PDO(%dsn%, %user%, %password%)
mainDb: PDO(%dsn%, %user%, %password%)

tempDatabase:
factory: PDO('sqlite::memory:)
autowired: no # služba tempDatabase je vyřazena z autowiringu
tempDb:
factory: PDO('sqlite::memory:')
autowired: no # služba tempDb je vyřazena z autowiringu

articles: Model\ArticleRepository # tudíž předá do kontruktoru mainDatabase
articles: Model\ArticleRepository # tudíž předá do kontruktoru mainDb
\--


Omezený autowiring
------------------

Lze také autowiring omezit jen pro určité třídy nebo rozhraní. Ukážeme si to na příkladu:

/--php
interface FooInterface
class ParentClass
{}

class BarClass implements FooInterface
class ChildClass extends ParentClass
{}

class FooDependent
class ParentDependent
{
function __construct(FooInterface $obj)
function __construct(ParentClass $obj)
{}
}

class BarDependent
class ChildDependent
{
function __construct(BarClass $obj)
function __construct(ChildClass $obj)
{}
}
\--

Nejpve příklad bez omezení autowiringu:
Pokud bychom je všechny zaregistrovali jako služby, tak by autowiring selhal:

/--neon
services:
bar: BarClass
fooDep: FooDependent # autowiring předá do konstruktoru službu bar
barDep: BarDependent # autowiring předá do konstruktoru službu bar
parent: ParentClass
child: ChildClass
parentDep: ParentDependent # VYHODÍ VÝJIMKU, vyhovují služby parent i child
childDep: ChildDependent # autowiring předá do konstruktoru službu child
\--

Autowiring v obou případech předá do konstruktoru službu `bar` protože vyhovuje typu `BarClass` i `FooInterface`. Ale teď to omezíme jen na interface:
Služba `parentDep` vyhodí výjimku `Multiple services of type ParentClass found: parent, child`, protože do jejího kontruktoru pasují obě služby `parent` i `child` a autowiring nemůže rozhodnout, kterou z nich zvolit.

U služby `child` můžeme omezit její autowirování jen na třídu `ChildClass`, takže nebude předaná tam, kde se očekává `ParentClass`:

/--neon
services:
bar:
factory: BarClass
autowired: FooInterface
parent: ParentClass
child:
factory: ChildClass
autowired: ChildClass # lze napsat: autowired: self

fooDep: FooDependent # autowiring předá do konstruktoru službu bar
barDep: BarDependent # VYHODÍ VÝJIMKU
parentDep: ParentDependent # autowiring předá do konstruktoru službu parent
childDep: ChildDependent # autowiring předá do konstruktoru službu child
\--

Autowiring u služby `barDep` vyhodí výjimku `Nette\DI\ServiceCreationException`, protože nemá službu, kterou by jí předal do konstruktoru. Služba `bar` je totiž autowirová jen pod `FooInterface` a nikoliv `BarClass`. A obráceně, pokud bychom v klíči uvedli `autowired: BarClass`, vyhodila by výjimku služba `fooDep`.
V případě služby `parentDep` se do konstruktoru předá `parent`, protože teď je to jediný vyhovující objekt kvůli omezení definovaném u služby `child`.

U služby `child` by bylo možné místo `autowired: ChildClass` zapsat `autowired: self`, jelikož `self` představuje třídu služby.

V klíči `autowired` je možné uvést i několik tříd a interfaců jako pole:
V klíči `autowired` je možné uvést i několik tříd nebo interfaců jako pole:

/--neon
autowired: [BarClass, FooInterface]
\--

Tím, že autowiring omezíme pro určitý typ, neříkáme, že bude dostupná čistě jen pro tento typ, ale také pro všechny potomky v dědičné hierarchii. Tj. třeba u služby `child` znamená `autowired: ParentClass` totéž, jako `autowired: [ParentClass, ChildClass]`, protože `ChildClass` je potomkem `ParentClass`.

Příklad:

/--neon
services:
bar:
factory: BarClass
autowired: [BarClass, FooInterface]
child:
factory: ChildClass
autowired: ParentClass

parentDep: ParentDependent # autowiring předá do konstruktoru službu child
childDep: ChildDependent # autowiring předá do konstruktoru službu child
\--

Podobně to bude fungovat i v případě dědičnosti, což si ukážeme na dalším příkladu:
Zkusme příklad doplnit ještě o rozhraní:

/--php
class ParentClass
interface FooInterface
{}

class ChildClass extends ParentClass
interface BarInterface
{}

class ParentDependent
class ParentClass implements FooInterface
{}

class ChildClass extends ParentClass implements BarInterface
{}

class FooDependent
{
function __construct(ParentClass $obj)
function __construct(FooInterface $obj)
{}
}

class ChildDependent
class BarDependent
{
function __construct(ChildClass $obj)
function __construct(BarInterface $obj)
{}
}
\--

Služby vložíme do konfiguračního souboru s tím, že `child` bude mít omezený autowiring jen pro `ChildClass` (a potomky).
Když službu `child` omezíme na `FooInterface`, bude autowirovaná do všech služeb kromě `barDep`:

/--neon
services:
child:
factory: ChildClass
autowired: ChildClass # lze použít 'self'
autowired: FooInterface

parentDep: ParentDependent # VYHODÍ VÝJIMKU, službu child nelze použít
childDep: ChildDependent # autowiring předá do konstruktoru službu child
fooDep: FooDependent # autowiring předá do konstruktoru child
barDep: BarDependent # VYHODÍ VÝJIMKU, žádná služba nevyhovuje
parentDep: ParentDependent # autowiring předá do konstruktoru child
childDep: ChildDependent # autowiring předá do konstruktoru child
\--

V tomto případě by bylo možné místo `autowired: ChildClass` zapsat `autowired: self`, jelikož za `self` znamená třídu služby.


Preference autowiringu
----------------------

V autowiringu můžeme také určité služby pro určité typy preferovat. Pro ukázku použijeme výše uvedené třídy.

Pokud bychom je všechny zaregistrovali jako služby, tak by autowiring selhal:
V autowiringu můžeme také určité služby pro určité typy preferovat. Dělá se to stejně, jako když omezujeme autowiring. Omezením se z ní totiž zároveň stává pro tento typ *preferovaná služba*.

/--neon
services:
parent: ParentClass
child: ChildClass
parentDep: ParentDependent # autowiring předá do konstruktoru službu child
childDep: ChildDependent # VYHODÍ VÝJIMKU
\--

Služba `parentDep` vyhodí výjimku `Multiple services of type ParentClass found: parent, child`, protože do jejího kontruktoru pasují obě služby `parent` i `child` a autowiring nemůže jednoznačně rozhodnout, kterou z nich zvolit.

U služby `child` můžeme omezit její autowirování jen na třídu `ChildClass`, takže nebude předaná tam, kde se očekává `ParentClass`, což jsme si už ukazovali výše. Teď ale navíc přidáme ještě službu `parent`:
mainDb:
factory: PDO(%dsn%, %user%, %password%)
autowired: PDO # stává se preferovanou

/--neon
services:
parent: ParentClass
child:
factory: ChildClass
autowired: ChildClass
tempDb:
factory: PDO('sqlite::memory:')

parentDep: ParentDependent # autowiring předá do konstruktoru službu parent
childDep: ChildDependent # autowiring předá do konstruktoru službu child
articles: Model\ArticleRepository
\--

V případě služby `parentDep` se do konstruktoru předá `parent`, protože teď je to jediný vyhovující objekt kvůli omezení definovaném u služby `child`. Tím omezením se z ní ale zároveň stala pro tento typ *preferovaná služba*. Proto služba `childDep` nevyhodí výjimku, že existují dvě vyhovující služby (tj. `child` a `parent`), které lze do konstruktoru předat, ale použije preferovanou službu, tedy `child`.
Proto služba `articles` nevyhodí výjimku, že existují dvě vyhovující služby (tj. `mainDb` a `tempDb`), které lze do konstruktoru předat, ale použije preferovanou službu, tedy `mainDb`.


Více konfiguračních souborů
Expand Down

0 comments on commit b8f62c1

Please sign in to comment.