Model | První aplikace
S tím, jak aplikace roste, brzy zjistíme, že na různých místech, v různých presenterech, potřebujeme provádět podobné operace s databází. Například získávat nejnovější publikované články. Když aplikaci vylepšíme třeba tím, že u článků přidáme příznak, zda je rozepsaný, musíme potom projít i všechna místa v aplikaci, kde se články z databáze získávají a doplnit podmínku where, aby se vybíraly jen články nerozepsané.
V ten moment se přímá práce s databází stává nedostatečnou a bude šikovnější si vypomoci novou funkcí, která nám publikované články bude vracet. A když později přidáme další podmínku, například že se nemají zobrazovat články s budoucím datem, upravíme kód jen na jednom místě.
Funkci umístíme třeba do třídy ArticleManager
a nazveme ji getPublicArticles()
.
V adresáři app/Model/
vytvoříme naši modelovou třídu ArticleManager
, která se nám bude
starat o články. Umístíme ji například do souboru ArticleManager.php
.
namespace App\Model;
use Nette;
class ArticleManager
{
use Nette\SmartObject;
private Nette\Database\Explorer $database;
public function __construct(Nette\Database\Explorer $database)
{
$this->database = $database;
}
public function getPublicArticles()
{
return $this->database->table('posts')
->where('created_at < ', new \DateTime)
->order('created_at DESC');
}
}
Třídu jsme vytvořili pomocí konstruktoru, ve kterém si necháme předat databázový Explorer.
Pokud bychom nevyužívali frameworku a síly DI containeru, museli bychom v našem případě nejprve vytvořit instanci
Nette\Database\Connection
, Nette\Database\Structure
, Nette\Database\Conventions
a
Nette\Caching\Storages\FileStorage
(které pro své instancování také potřebují nějaké závislosti, třeba
jméno a heslo k databázi). To vše pouze pro vytvoření instance Nette\Database\Explorer
, kterou bychom
následně použili pro vytvoření instance ArticleManager
.
...
// vyrobíme vše, co je třeba pro Explorer
$dbConnection = new Nette\Database\Connection(...);
$dbStructure = new Nette\Database\Structure(...);
$dbConventions = new Nette\Database\Conventions\DiscoveredConventions(...);
$dbCache = new Nette\Caching\Storages\FileStorage(...);
// vyrobíme Explorer
$dbExplorer = new Nette\Database\Explorer($dbConnection, $dbStructure, $dbConventions, $dbCache);
// vyrobíme ArticleManager
$articleManager = new App\Model\ArticleManager($dbExplorer)
...
Tolik psaní pro to, abychom dostali fungující ArticleManager? To asi nechceme. Proto použijeme DI container, který berme jako skřínku, která generuje tyto výrobní kódy a nám vrátí jen požadovaný objekt.
Jediné co potřebujeme, je říci DI kontejneru, aby nám vrátil požadovaný objekt. To můžeme zařídit v konfiguraci
aplikace. V souboru config/common.neon
v sekci services přidáme řádek s celým názvem třídy.
services:
- App\Router\RouterFactory::createRouter
- App\Model\ArticleManager
Tím Nette a jeho DI containeru říkáme: „Kdyby někdo chtěl instanci této třídy, tak víš kde ji hledat. Předej této instanci vše, co potřebuje k životu a připrav nám ji pro použití v dalších třídách.“ Takto registrovaná třída se pak nazývá služba.
Přepneme se do HomepagePresenter.php
, který upravíme tak, že se zbavíme závislosti na
Nette\Database\Explorer
a nahradíme za novou závislost na naší nové třídě.
namespace App\Presenters;
use Nette;
use App\Model\ArticleManager;
class HomepagePresenter extends Nette\Application\UI\Presenter
{
private ArticleManager $articleManager;
public function __construct(ArticleManager $articleManager)
{
$this->articleManager = $articleManager;
}
public function renderDefault(): void
{
$this->template->posts = $this->articleManager
->getPublicArticles()
->limit(5);
}
}
V sekci use máme App\Model\ArticleManager
, tak si můžeme zápis v PHP kódu zkrátit na ArticleManager
(nebojte, i v komentářích to funguje a vaše chytré IDE by si s tím mělo poradit).
V konstruktoru si požádáme o ArticleManager
, který si přiřadíme do vlastnosti $articleManager
a v metodě renderDefault zavoláme metodu getPublicArticles()
. Nad výsledkem této funkce zavoláme ještě metodu
limit(5)
;
Třída ArticleManager
si v konstruktoru řekne o předání Nette\Database\Explorer
a jelikož je
tato třída v DI containeru zaregistrovaná, kontejner tuto instanci vytvoří a předá ji. DI za nás takto vytvoří instanci
ArticleManagera a předá ho v konstruktoru třídě HomepagePresenter, který si o něj požádal. Taková matrjoška. :)
Všichni si jen říkají co chtějí a nezajímají se o to, kde se co a jak vytváří. O vytvoření se postará Nette DI container.