Ako správne získavať dáta z databáze a zobrazovať ich
- PeterB
- Člen | 12
Ahojte,
trochu som váhal a je mi jasné, že toto je otázka, ktorá Vás rozosmeje,
no moje skúsenosti s Nette a MVC sú minimálne a ak sa neopýtam, nikdy
nebudem vedieť, či je to správne:
Mám databázu firma, telefon, email.
Potrebujem výpis z tabuľky firma a zobraziť z tabuliek telefon a email
relevantne hodnoty (telefónne číslo a e-mail pre danú firmu).
Podla ukážkovej aplikácie som spravil renderDefault() metódu v triede
HomepagePresenter:
<?php
public function renderDefault() {
$this->template->firmy = $this->database->table('firma')
->order('nazov DESC');
}
?>
V template default som teda vytvoril výpis nasledovne:
{foreach $firmy as $firma }
<div class="firma">
<h2 class="nazov">{$firma->nazov }</h2>
<address>Adresa:<br />
{$firma->ulica } {$firma->cislo }<br />
{$firma->psc } {$firma->mesto }<br />
{$firma->stat }
</address>
{foreach $firma->related('telefon') as $telefon }
<span>Telefón: {$telefon->cislo }</span><br />
{/foreach }
{foreach $firma->related('email') as $email }
<span>E-mail: {$email->email }</span><br />
{/foreach }
<div class="popis">{$firma->popis }</div>
</div>
{/foreach }
…je to správne? myslím to, že som získavanie telefónnych čísel a e-mailov riešil v template?
ďakujem Peter
- David Matějka
- Moderator | 6445
Ano, tento zapis je v souladu s filosofii NDBT, ale: https://forum.nette.org/…principy-mvc
- japlavaren
- Člen | 404
ako napisal Azathoth, tento kod funguje. ale je neudrzatelny. osobne pouzivam vsetky vyhodu ndtb v modeloch a presenter je len most pre sablonu ktora to vypise
rozsirit takyto kod je totiz peklo…
- japlavaren
- Člen | 404
no dobry model s ndtb je problem. asi kazdy ma vlastne riesenie (otazka je, co z toho je spravne…)
- Azathoth
- Člen | 495
Pokud je nějaký příklad na správný model, tak se také hlásím.
@Lkopo Ale mohu lehce popsat architekturu svého modelu, který
používám.
Je to vlastně spousta tříd, které injektuji do presenteru jako
závislosti.
A ty třídy dělají aplikační logiku, mají napojení na databázi a tak
podobně.
Mám například v modelu třídu UserHandler, která má veškeré metody
potřebné pro práci s uživateli a každý presenter, který ji chce, ji
dostane přes DI.
malá ukázka:
<?php
private $customOtherDependecyFromModel;
function __construct(dependencies) {
//injecting all dependencies
}
public function authenticate(array $credentials) {
//some magic
return $user;
}
public function getUserFromLoginName($loginName) {
//some magic
return $user;
}
public function registerNewClient($firstName, $lastName, $email, $linkToAccountActivation) {
//some magic, registering to database, sending registration e-mail
}
public function registerNewAdmin($firstName, $lastName, $email, $link) {
//some magic, registering to database, sending registration e-mail
}
public function promoteToLector(Client $client, $commission) {
//some magic, promoting and sending e-mail
}
public function activateAccount($activationParameter) {
//some magic
}
public function setNewPassword($password, $activationParameter) {
//some magic
}
?>
a potom mám podobné třídy na další věci, mám messageHandler, který obstarává samotné odesílání zpráv, několik database handlerů, které mají samotné připojení k databázi (tato třída má pouze database handler jako závislost a sama přímo napojení na databázi nemá), notificationHandler, který generuje novinky o tom, co se děje, fileHandler, který obsluhuje úložiště uživatelů a tak podobně…
Editoval Azathoth (18. 9. 2014 20:35)
- japlavaren
- Člen | 404
ja ndtb chapem asi takto – rozsireny nastroj ktory ma nenuti rozmyslat a skladat joiny…
cize ak vezmem klsicky model spranevy v dibi, toto iste sa da spravit s ndtb a je to jednoduchsie na napisanie (jednoducho napisem co chcem a nestaram sa o joiny a pod)…
cize model by mal nacitat data z tabuliek, zoskupit ich a predat do resenteru. ci pouzije ciste sql, dibi, ndtb, doctrine, nosql db… → odovzda data
- Azathoth
- Člen | 495
@PeterB doporučuji toto: http://www.zdrojak.cz/…neho-navrhu/ dalo mi to hodně.
(pokud melu blbosti, opravujte mne)
U malých projektů je to jedno, ale u velkých už na tom záleží.
Snaž se, aby každá třída měla zodpovědnost pouze za jednu věc. Tedy
presenter má mít na starosti pouze zobrazování a nějaké úkolování
modelu, takže v presenteru by mělo být akorát
$this->template->firmy =
$this->mujDatabazovyModel->dejMiVsechnyFirmy
A třída MujDatabazovyModel by měla poskytnou všechny firmy.
Pokud jsem správně pochopil mvc a single responsibility principle, tak
presenter, tedy to, co poskytuje data uživateli, by vůbec neměl vědět
o tom, co se používá pro připojení k databázi.
- PeterB
- Člen | 12
…hmmm
Je mi úplne jasné, čo znamená MVC, alebo čo by mal znamenať, nikdy som ho
nepoužíval, čiže mám znalosti iba teoretické, myslel som si, že mi
vzorový príklad pomôže pochopiť, ako sa to robí správne v Nette, ale
správne to tam nie-je a teraz nejde o to, či je to malý projekt, alebo
mikro, ide o to, že vzorový príklad by mohol naviesť človeka, ako správne
používať Nette (teda mohlo by tam byť, ako vytvoriť model na vytiahnutie
dát z db, predanie dát do prezentera, ktorý ich spracuje a potom do
template, aby ich vykreslil)
- Oli
- Člen | 1215
@PeterB je a není to špatně. To co jsi udělal ty je VC (template + presenter). Ono to takhle je popsáno i v dokumentaci. Byly tu i nějaké diskuse jestli to je správně nebo špatně. Jde o to aby se začátečník „nezahltil“. Já jsem třeba začínal stejně, po napsání 2 presenterů jsem hodil práci s databází do modelu, ale komponenty vytvářel v presenteru. Pak jsem experimentoval, že jsem třeba formuláře vytvářel jako potomky třídy \Nette\Application\UI\Form, pak jako \Nette\Application\UI\Control až nakonec teď téměř všechny služby generuju pomocí továrničky a pokud je to trochu komplexní tak z toho dělám extension.
Tím chci říct, že to co jsi udělal ty je první krok k tomu ten kód napsat ve správném MVC. K tomu ale musíš dospět postupně. Něco uděláš a řekneš si, sakra tohle bych mohl udělat obecněji. Pokud si to neřekneš, tak to asi nepotřebuješ. Například firemní presentace o 10 stránkách a blogu by klidně šla napsat způsobem jak jsi naznačil. ;-)
- PeterB
- Člen | 12
Azathoth napsal(a):
$this->template->firmy = $this->mujDatabazovyModel->dejMiVsechnyFirmy
…tomuto chápem, mám teda iba jedinú otázku, ako by sa mal volať model ktorý vytvorím (myslím, podľa obecných zásad Nette, alebo modely sú načítané všetky – usudzujem podľa use Nette, App\Model; ), kde potom definujem triedu a tieto metódy?
- Azathoth
- Člen | 495
@PeterB
Ve složce, tedy i v namespace App\Model si vytvoříš PHP Class, nějak si
ji pojmenuješ a začneš tam psát ty metody, dáš tam jako property $database
a tak dále. A potom ji v neon.config přidáš mezi služby (jak je services:
tak tam přidáš nový řádek, kde bude pomlčka a jméno té php class spolus
s celým namespace, takže tam bude:
services:
- App\Model\MyDatabaseHandler (nebo jiný název, který zvolíš pro tu třídu)
) a injectuješ do presenteru, asi takhle:
/**
* @var \App\Model\MyDatabaseHandler
* @inject
*/
public $myDatabaseHandler;
Podívej se na nette sandbox, jakým způsobem je dělaný UserManager. Tak
takhle bys to měl mít, obdobně.
Ale název té třídy je čistě na tobě, prostě to pojmenuj tak, abys tomu
rozuměl a aby název přibližně odpovídal tomu, co to bude dělat.
- Azathoth
- Člen | 495
@Bambulko komponentu ti asi přesně nevysvětlím, na to si
netroufnu, ale továrnička (ta zdrobnělina mne velmi irituje, ale celou
komunitu na továrnu nepřeučím) je metoda, kterou zavoláš a ona ti vrátí
nějaký objekt.
Takže místo $newObject = new Object(); zavoláš $newObject =
$objectFactory->create(); nebo něco podobného.
- Oli
- Člen | 1215
@PeterB Na foru bude urcite nekolik ukazek kodu jak se to ma delat.
Myslenka je ale zhruba takovahle:
Napises nejakou tridu, ktera se stara o praci s databazi, rekneme treba
clanky:
namespace App\Model;
class ArticleRepository extends \Nette\Object
{
private $context;
function __construct(\Nette\Database\Context $context)
{
$this->context = $context;
}
public function findArticleById($id)
{
return $this->context->table('articles')->get($id);
}
Potom tu třídu zaregistruješ jako službu do config.neon (musí být někde, kam „vidí“ Nette Autoloader, typicky řekněme někde v App + podsložky). Protože jsi to zaregistroval jako službu, tak ti tam ten Context Nette injectne automaticky.:
services:
- App\Model\ArticleRepository
# můžeš si tu službu i pojmenovat - mujClanek: App\Model\ArticleRepository
# a potom k tomu přistoupis jako k @mujClanek
To že jsi to zaregistroval jako službu v configu ti dovoluje to načíst pomocí DI v presenteru.
class ArticlePresenter extends BasePresenter
{
public function __construct(\App\Model\ArticleRepository $articleRepository)
{
$this->articleRepository = $articleRepository;
}
public function renderDetail($id)
{
$this->template->article = $this->articleRepository->findArticleById($id);
}
}
@Bambulko komponenty jsou jednoduše řečeno znovupoužitelné kusy kodu. Například paginator (stránkovač) chceš vždycky, když vypisuješ nějakej přehled např. článků. Je blbost to psát pořád dokola, tak si uděláš komponentu, které předáš celkový počet článků a kolik jich chceš zobrazit a ona ti to „rozřeže“. Potom když chceš takhle vypsat novinky, tak už si zavoláš jen tu komponentu a nastavíš ji.
Továrnička není nic jiného než návrhový vzor factory. Prostě metoda, která vytvoří jinou třídu. Její výhoda je, že pokaždé vytvoří novou instanci.
Editoval Oli (18. 9. 2014 22:11)
- PeterB
- Člen | 12
…no, ako tak pozerám, tak s Vašou pomocou to nebolo až také
tápanie…
Nespravil som nič, iba prerobil horný príklad nasledovne:
Model:
DatabaseHandler s rovnakou triedou
<?php
namespace App\Model;
use Nette;
class DatabaseHandler extends Nette\Object {
/** @var \Nette\Database\Context */
private $database;
public function __construct(Nette\Database\Context $database) {
$this->database = $database;
}
public function vsetkyFirmy() {
return $this->database->table('firma')->order('nazov DESC');
}
}
?>
- pridané do services v config.neon
V HompagePresenter:
<?php
/** @var \App\Model\DatabaseHandler @inject */
public $firmy;
public function renderDefault() {
$this->template->firmy = $this->firmy->vsetkyFirmy();
}
?>
…a default.latte ostal rovnaký
…je to takto správne? Robí to presne to isté
Peter
- Lkopo
- Člen | 65
Mám takú otázku ohľadom toho, či záznamy vkládať cez presenter alebo model, keďže takisto musím v presenteri povedať, že tieto $values chcem vložiť/upraviť.
Čiže v modeli mám to asi takto
public function findOneById($id)
{
$post = $this->database->table(self::TABLE_NAME)->get($id);
return $post;
}
A v presenteri mám:
$post = $this->postRepository->findOneById($postId);
$post->update($values);
Je to správne riešenie? Keďže na insert sa v modeli robí metóda (napr. add), ale na update alebo delete si nie som istý.
Editoval Lkopo (18. 9. 2014 23:27)
- Azathoth
- Člen | 495
@PeterB Je to mnohem lepší. Sice to dělá to samé, ale ve chvíli, kdy bys chtěl kód předělávat, tak tohle bude mnohem lepší.
- pokud budeš chtít přejít na jinou knihovnu/framework pro práci s databází nebo budeš přejmenovávat tabulky v sql databázi, tak nebudeš muset měnit presenter.
- pokud budeš chtít použít výpis všech firem ještě v jiném presenteru, tak nebudeš kopírovat table(‚firma‘)->order(‚nazov DESC‘);, ale zavoláš si metodu vsetkyFirmy(); což je praktické, když budeš chtít upravit získání všech firem (například z DESC budeš chtít přejít na ASC), takhle to změníš jenom na 1 místě a ne všude, kde seznam všech firem používáš.
A možná se najdou ještě nějaké výhody.
Editoval Azathoth (18. 9. 2014 23:29)
- japlavaren
- Člen | 404
insert/update/delete, related/ref vsetko v modeli. model by mal predavat polozky (polozka = array/object) a presenter by nemal vediet o strukture db a pod…
- Lkopo
- Člen | 65
No a ako poviem modelu len po odoslaní formulára updatne tabuľky? V návode bolo v presenteri:
$post = $this->database->table('posts')->get($postId);
$post->update($values);
No a ja mám
$post = $this->postRepository->findOneById($postId);
$post->update($values);
Naozaj je lepšie to spraviť týmto štýlom?
$post = $this->postRepository->findOneById($postId);
$this->postRepository->updateData($post, $values);
Editoval Lkopo (19. 9. 2014 0:20)
- japlavaren
- Člen | 404
Lkopo napsal(a):
No a ako poviem modelu len po odoslaní formulára updatne tabuľky? V návode bolo v presenteri:
$post = $this->database->table('posts')->get($postId); $post->update($values);
No a ja mám
$post = $this->postRepository->findOneById($postId); $post->update($values);
Naozaj je lepšie to spraviť týmto štýlom?
$post = $this->postRepository->findOneById($postId); $this->postRepository->updateData($post, $values);
problem tvojho riesenia vznikne, ked potrebujes updatovat viac tabuliek, alebo budes mat vo formulari inputy, ktore nemas v db ako stlpce
ja to robim tak, ze values z formularu predam modelu, ten si vydzobe co potrebuje a spravi update