Pomoc s strukturou aplikace
- pospi
- Člen | 18
Ahoj predem dekuji vsem za pomoct , muj dotaz je tento :
Budu mit aplikaci s pripojenim do vice databazi, napr. basic, user, forum(tzn.
v jednom preseteru muze byt vice modelu z vice databazi), jakym zpusobem mam
udelat strukturu aplikace? Moje predstava je tato :
V database.neon budu mit konfigurace databazi
database:
basic:
dsn: 'xxx'
user: ''
password: ''
autowired: true
forum:
dsn: 'xxx'
user: ''
password: ''
autowired: false
user:
dsn: 'xxx'
user: ''
password: ''
autowired: false
dal bych chtel udelat 3 abstraktni tridy ktere budou prijimat db context, jake services(basic tam neni protoze je autowired)
services:
- App\Models\ForumFacade(@database.forum.context)
- App\Models\UserFacade(@database.user.context)
Z techto abstraktnich trid budou dedit jine tridy ktere budou s timto kontextem a popripade jinyma funkcema pracovat, tedy:
<?php
namespace App\Models;
use Nette;
use Nette\Database\Context;
abstract class BasicContext {
/** @var Context */
protected $database;
public function __construct(Context $dbContext) {
$this->database = $dbContext;
}
}
<?php
namespace App\Models;
use Nette;
use Nette\Database\Context;
final class TestFacade extend App\Models\BasicContext
{
public function getCompanies()
{
return $this->database
->table('companies');
}
}
V tuto chvily mi ale tuto predstavu komplikuje hlaska :
„Nette\DI\ServiceCreationException Service of type App\Models\BasicContext:
Class App\Models\BasicContext is abstract.“
Tedy premyslim jak to udelat jinak. dekuji za odpovedi
- m.brecher
- Generous Backer | 765
Ahoj,
není dobré, aby abstraktní předek modelových tříd byl současně sám finální modelovou třídou, protože jak hláška napověděla, nelze z něj vytvořit instanci – je abstraktní. Udělej to třeba takto:
<?php
declare(strict_types=1); // používej striktní typování !!!
namespace App\Models;
use Nette;
use Nette\Database\Explorer; // používej moderní Explorer
abstract class AbstractModel
{
public function __construct(protected Explorer $database) // moderní syntaxe php 8.0
{}
}
a z AbstractModelu podědíš všechny tři finální modelové třídy BasicModel, ForumModel, UserModel:
<?php
declare(strict_types=1);
namespace App\Models;
use Nette;
use Nette\Database\Table\Selection;
final class BasicModel extends AbstractModel // místo zastaralého <*>Facade používej raději <*>Model
{
public function getCompanies(): Selection
{
return $this->database
->table('companies');
}
}
konfigurace:
https://doc.nette.org/…onfiguration#…
„Autowiring je zapnutý jen u služeb z první sekce.“ proto není autowired potřeba vůbec uvádět.
database:
basic:
dsn: 'xxx'
user: ''
password: ''
forum:
dsn: 'xxx'
user: ''
password: ''
user:
dsn: 'xxx'
user: ''
password: ''
a modelové třídy:
services:
- App\Models\BasicModel # zde je autowiring automaticky zapnutý
- App\Models\ForumModel(@database.forum.explorer)
- App\Models\UserModel(@database.user.explorer)
Kód je za předpokladu PHP 8.0 a nějaké novější verze Nette s Explorerem. Používej důsledně declare(strict_types=1);
Editoval m.brecher (25. 2. 21:32)
- pospi
- Člen | 18
Diky za odpoved urcite me to posunulo o kus dal(nejen v tom na co jsme se
ptal)
Mam jeste dotaz:
Pokud bych se pozdeji rozhodl ze tridy BasicModel, ForumModel a UserModel
nebudou finalni a budu dale z nich dedit.
A zaroven bych chtel mit v service pouze tento zapis :
services:
- App\Models\BasicModel # zde je autowiring automaticky zapnutý
- App\Models\ForumModel(@database.forum.explorer)
- App\Models\UserModel(@database.user.explorer)
Napr. Nova trida Basic1Model by dedila od BasicModel ale uz bych nechtel do
service davat radek
- App\Models\Basic1Model
proste aby se mi ten Database\Explorer predal zkrz to dedeni. Je to mozne nebo
musim kazdy model zapisovat do services?
- Kamil Valenta
- Člen | 762
m.brecher napsal(a):
abstract class AbstractModel { public function __construct(protected Explorer $database) // moderní syntaxe php 8.0 {} }
K čemu by takový abstraktní model měl být dobrý?
// místo zastaralého <>Facade používej raději <>Model
Čím je Facade zastaralé? Proč je srovnávána s modelem?
Model a fasáda má každý jinou funkci, nijak se nealternují, jeden není
modernějším nástupcem druhého…
pospi napsal(a):
Napr. Nova trida Basic1Model by dedila od BasicModel ale uz bych nechtel do service davat radek
- App\Models\Basic1Model
proste aby se mi ten Database\Explorer predal zkrz to dedeni. Je to mozne nebo musim kazdy model zapisovat do services?
Snažíš se „zabít“ smysl a výhody DI. Výsledkem bude netestovatelný, hůře čitelný a udržitelný kód.
Jedna možnost je každé modelové třídě předhodit DB konexi v NEONu. Chápu ale, že těch modelových tříd budou mraky a budou rozděleny do 3 skupin, podle DB… Je to celkem neobvyklé, ale asi bych i tak vše registroval s uvedenou DB. Pokud bys hodně nechtěl, šel bych cestou DB provideru, který se bude hlásit k závislosti na všech 3 DB a podle nějaké vnitřní logiky (třeba namespace modelové třídy) poskytne správnou DB.
- m.brecher
- Generous Backer | 765
@KamilValenta
K čemu by takový abstraktní model měl být dobrý?
Navazoval jsem na zadaný dotaz, kde byla chyba v tom, že abstraktní předek byl současně finálním modelem. Samozřejmě že abstraktní předek, který pouze sosá Explorer z DIContaineru smysl nemá, to ale nebylo tématem dotazu.
Snažíš se „zabít“ smysl a výhody DI
Souhlasím – uvedený příklad zabíjí DI, ale obecně neplatí, že v modelu nesmíme použít dědění, ale pouze DI. Jak DI, tak dědění jsou praxí ověřené koncepty, které oba přinášejí určité benefity a oba lze v modelových třídách kreativně využít.
Já používám abstraktní předky pro modelové třídy a hodně mě zjednodušují kód. V abstraktním předku jsem shromáždil společné funkce všech modelových tříd. Finální modelové třídy přidávají specifické funkce, kód je menší, přehlednější.
Čím je Facade zastaralé?
To jsem špatně formuloval, měl jsem na mysli pouze suffix názvu modelových tříd, nikoliv návrhový vzor Facade. Facade je dle wikipedie obecný návrhový vzor, kdy se zapouzdřuje složitější api do jednoduššího, to nezestárne nikdy. Část komunity ale používá Facade jako suffix názvu modelové třídy – proč? Máme koncepci MVC, kde část aplikace obhospodařující databázi se jmenuje Model, tak mě přijde logické použít pro třídy v této části aplikace suffix Model a kód je čitelnější.
- m.brecher
- Generous Backer | 765
@pospi
Pokud bych se pozdeji rozhodl ze tridy BasicModel, ForumModel a UserModel nebudou finalni a budu dale z nich dedit.
Dědit v modelových třídách chce určité zkušenosti, já jsem první dva roky psal všechny modelové třídy bez dědění a nekomplikoval jsem si život. Cca po 2 letech jsem přešel na určité zobecnění a modelové třídy dědím z abstraktního předka, je to ale součástí složitější koncepce, kdy toto navazuje na presenter a formuláře. Takže jak se obvykle doporučuje v modelových třídách dědění nepoužívat tak bych to Tobě také pro začátek doporučil.
Doporučuji mít všechny modelové třídy s koncovkou <*>Model a registrovat je automaticky jako služby takto:
services.neon:
search:
model:
in: %appDir%/Model
classes:
- *Model
Modelové třídy umísti do adresáře app/Model.
Finální třídy potom mohou autowirovanou databázi získat klasicky konstruktorem.
Chceš mít tři nezávislé databáze. Všeobecně se ale doporučuje používat koncepci jeden projekt – jedna databáze. Použitím několika databází v jednom projektu si zbytečně zkomplikuješ život. Doporučuji všechny tabulky umístit do jedné společné databáze – pokud není nějaký silný důvod mít databází více. Mezi databázemi nelze použít cizí klíče, bude složitější zálohování, složitější předávání závislostí.
- Kamil Valenta
- Člen | 762
m.brecher napsal(a):
Samozřejmě že abstraktní předek, který pouze sosá Explorer z DIContaineru smysl nemá, to ale nebylo tématem dotazu.
Tématem dotazu bylo jak to udělat co nejlépe. Poradit stylem, o kterém sám tvrdíš, že to smysl nemá, ale tazatel se prostě nezeptal dost dobře, je zavádějící :(
Všeobecně se ale doporučuje používat koncepci jeden projekt – jedna databáze.
Nevím kde se to všeobecně doporučuje, ale úplně nesouhlasím. Ačkoliv
je to velmi častý případ, je hodně důvodů, proč to tak být nemusí.
Tazatel, na rozdíl od nás, asi lépe ví, zda to tak potřebuje / musí
mít.
Např. některé ERP mají API ve formě databázových procedur. V takovém
případě se 2 db pracovat musíš. Mnohdy, pokud má aplikace
propracovanější logy, je dobré logovat do jiné databáze, např. zmíněné
zálohování se tím naopak zjednoduší.
Mezi databázemi nelze použít cizí klíče
Proč by to nešlo?
- Šaman
- Člen | 2635
@pospi : To co hledáš je decorator
Umožní ti konfigurovat služby potomků, narozdíl od sekce
services
, která se snaží přímo třídu vytvořit.
Doporučuji závislost přes předka předávat spíš setterem (který se
může jmenovat injectXxx) a nechat potomkům prázdný konstruktor pro jejich
vlastní závislosti.
Editoval Šaman (26. 2. 20:35)