Problém při dědění presenterů – registrace služeb v config
- Jarek92
- Člen | 91
Dobrý den,
při injektování jsem narazil na následující problém. Mám model Base, kde
je připojení k databázi a pár SQL dotazů, které se mají dostat do
společné šablony. Z tohoto moelu dědí modely SezonniAkce a Cenik. Pak mám
BasePresenter, který injectuje služby z Base modelu. A teď chci dědit
z BasePresenteru pro další Presentery (SezonniAkce a CenikPresenter), jenže
při zaregistrování služeb v configu mi to hlásí výjimku:
Nette\DI\MissingServiceException → Multiple services of type \App\Base found:
22_App_Cenik, 24_App_SezonniAkce.
A teď jedna zvláštnost: Když z configu odmažu registraci jedné ze služeb, tak to funguje, ale tím pádem nemohu používat daný presenter. Proč můžu z BasePresenteru dědit „pouze jednou“?
Base model:
abstract class Base extends Nette\Object
{
/** @var Nette\Database\Connection */
protected $connection;
public function __construct(Nette\Database\Connection $connection)
{
$this->connection = $connection;
}
public function vratEmaily(){
return $this->connection->query('SELECT kontakty.hodnota FROM kontakty NATURAL JOIN typy_kontaktu WHERE typy_kontaktu.nazev = "E-mail"');
}
public function vratTelefony(){
return $this->connection->query('SELECT kontakty.hodnota FROM kontakty NATURAL JOIN typy_kontaktu WHERE typy_kontaktu.nazev = "Telefon"');
}
public function vratSouradnice(){
return $this->connection->query('SELECT kontakty.hodnota FROM kontakty NATURAL JOIN typy_kontaktu WHERE typy_kontaktu.nazev = "GPS souřadnice"');
}
}
BasePresenter:
abstract class BasePresenter extends Nette\Application\UI\Presenter
{
/** @var \App\Base @inject */
public $base;
public function beforeRender()
{
$this->template->emaily = $this->base->vratEmaily()->fetchAll();
$this->template->telefony = $this->base->vratTelefony()->fetchAll();
$this->template->souradnice = $this->base->vratSouradnice()->fetchAll();
}
}
SezonniAkcePresenter
final class SezonniAkcePresenter extends BasePresenter
{
/** @var \App\SezonniAkce @inject */
public $sezonniAkce;
public function renderDefault()
{
$this->template->akce = $this->sezonniAkce->vratAkce();
}
}
config.neon: Config.neon
- Vojtěch Dobeš
- Gold Partner | 1316
Celá ta architektura je taková pofidérní. Nelze autowirovat abstraktní třídu, stejně jako ji nelze registrovat jako službu. Abstraktní třída má ze své podstaty více implementací, a tudíž logicky nedává smysl ji autowirovat – kterou implementaci?
Udělej si pro ty ošklivě pojmenované vrat*
metody
separátní třídy, a ty si naautowiruj. Tohle mi přijde jako typické
zneužití dědičnosti oproti kompozici.
- Vojtěch Dobeš
- Gold Partner | 1316
Klidně děď, ale ne ze špatných důvodů :). autowired: off
nic nevyřeší, pak si ti zase nenainjectují ty reálně zaregistrované
služby. Prostě bych pro každou z těch metod vytvořil samostatnou službu a
tu injectnul.
On koneckonců i BasePresenter
je prý antipattern :).
- Jarek92
- Člen | 91
Jasně máš pravdu, ale já potřebuju dostat ty výsledky ze třech metod do každého Presenteru který budu vytvářet, tzn. SezonniAkcePresenter, CenikPresenter… atd, protože ty věci se vykreslují v šabloně @layout.latte. Čili bych potřeboval nějak vysvětlit, jak udělat, abych měl dejme tomu Model, který bude vykreslovat základní věci a z něho budou dědit další modely a tím pádem i presentery pro vnitřek stránky (další layouty). Díky.
- David Matějka
- Moderator | 6445
do konkretniho presenteru si injectnes konkretni modelove tridy a base presenter si injectne vse, co potrebuje na vsech strankach – zadna dedicnost modelovych trid neni potreba.
- MartinitCZ
- Člen | 580
Viz můj přepis podpisu:
V presenterch zepomentě na __constructor(), ale používejte pouze anotace
@inject nebo třídu inject<Name>(Name $a) {…}
- Jarek92
- Člen | 91
Ano, přesně o to se snažím. V presenterech nemám db připojení, to mám v Base modelu. Jenže pak mám model např. Cenik a ten opět potřebuje připojení k databázi. No a když nebude potomkem třídy Base, tak nezbývá nic jiného než ten konstruktor s vytvořením připojení zavolat znovu. Nicméně předpokládám, že existuje nohem lepší řešení.
- Etch
- Člen | 403
Jarek92 napsal(a):
A připojení k databázi se pak řeší jak? V každém modelu?
Třeba, ale tohle zrovna není ten problém. Podědit si nějaký
BaseModel
kvůli databázi je v podstatě v pořádku, ale ty si
do toho abstraktního BaseModelu
dáváš i věci, které tam
nemají naprosto co dělat. K čemu by byla například metoda
vratSouradnice()
v nějakém ActionModelu
??
Nic ti nebrání udělat si BaseModel
abstract class Base extends Nette\Object
{
/** @var Nette\Database\Connection */
protected $connection;
public function __construct(Nette\Database\Connection $connection)
{
$this->connection = $connection;
}
}
a pak jednotlivé služby:
class ContactModel extends Base
{
public function vratEmaily(){
return $this->connection->query('SELECT kontakty.hodnota FROM kontakty NATURAL JOIN typy_kontaktu WHERE typy_kontaktu.nazev = "E-mail"');
}
public function vratTelefony(){
return $this->connection->query('SELECT kontakty.hodnota FROM kontakty NATURAL JOIN typy_kontaktu WHERE typy_kontaktu.nazev = "Telefon"');
}
public function vratSouradnice(){
return $this->connection->query('SELECT kontakty.hodnota FROM kontakty NATURAL JOIN typy_kontaktu WHERE typy_kontaktu.nazev = "GPS souřadnice"');
}
}
class ActionModel extends Base
{
public function vratAkce(){
return ...;
}
}
a do presenteru si pak injectnout jen ty služby které potřebuješ. To o co se snažíš ty, v tom prvním příspěvku, je prostě znásilnění návrhu.
Editoval Etch (19. 2. 2014 19:56)
- Jarek92
- Člen | 91
Dobrý nápad, ale asi se nechápeme. To samozřejmě můžu udělat, taky sem tu udělal už, ale nezmizí tím problém. Když registruju služby v configu, tak nesmím zapisovat dvě a více služeb, které dědí ze stejné třídy. Takže stejně, pokud nebudu připojení k db vytvářet v každém modelu, tak musím dědit z Base modelu. Nebo existuje ještě jiné řešení?
- David Matějka
- Moderator | 6445
problem tim zmizi, nebudes vyzadovat tridu Base, ale ContactModel, ActionModel apod.
a pripojeni se nebude v kazdem modelu vytvaret, pouze ho vyzadas a do vsech modelu se preda stejna instance
- Jarek92
- Člen | 91
Nette\DI\MissingServiceException → Multiple services of type \App\Base found: 22_App_Cenik, 23_App_Contact, 25_App_SezonniAkce.
Výjimka při následujícím kódu:
Base:
<?php
namespace App;
use Nette;
class Base extends Nette\Object
{
/** @var Nette\Database\Connection */
protected $connection;
public function __construct(Nette\Database\Connection $connection)
{
$this->connection = $connection;
}
}
Contact:
<?php
namespace App;
use Nette;
class Contact extends Base
{
public function vratEmaily(){
return $this->connection->query('SELECT kontakty.hodnota FROM kontakty NATURAL JOIN typy_kontaktu WHERE typy_kontaktu.nazev = "E-mail"');
}
public function vratTelefony(){
return $this->connection->query('SELECT kontakty.hodnota FROM kontakty NATURAL JOIN typy_kontaktu WHERE typy_kontaktu.nazev = "Telefon"');
}
public function vratSouradnice(){
return $this->connection->query('SELECT kontakty.hodnota FROM kontakty NATURAL JOIN typy_kontaktu WHERE typy_kontaktu.nazev = "GPS souřadnice"');
}
}
SezonniAkce:
<?php
namespace App;
use Nette;
class SezonniAkce extends Base
{
public function vratAkce(){
return $this->connection->query('SELECT obsah, datum, cas FROM sezonni_akce WHERE zobrazovat = true ORDER BY datum DESC, cas DESC');
}
}