Databáze – construct aby se nemusel psát do každého presenteru
- Amgis
- Člen | 8
Ahoj, s Nette začínám a jdu podle návodu na https://doc.nette.org/cs/quickstart plus http://www.itnetwork.cz/…-a-tutorialy chtěla jsem se zeptat jestli je nutné stále psát do každého presenteru nastavení k připojení k databázi
<?php
/** @var Nette\Database\Context */
private $database;
public function __construct(Nette\Database\Context $database)
{
$this->database = $database;
}
?>
Někde jsem viděla tuto funkci zařazenou jako medel a pak bylo
v presenteru volání pouze přes tento model bez toho aniž by v presenteru
byla opět nastavována databáze přes construct.
Příklad:
<?php
$this->model->getNews()->insert(array(... // getNews() - "továrnička" v modelu k výběru určité tabulky v DB..
?>
Zkoušela jsem to dát do „BasePresenteru.php“, ale tak to nejde a nevím
vůbec proč. :/
Nicméně už nevím kde to bylo a teď nevím jak to tak nějak podobně
zprovoznit :(
Poradí někdo?
Díky.
Editoval Amgis (8. 5. 2015 20:02)
- Unlink
- Člen | 298
No pokiaľ chceš používať databázu, môžeš ju do BasePresentera
dostať pomocou autowire
https://pla.nette.org/…ect-autowire
(trochu starší článok)
Pokiaľ si jo v base presentery predávaš cez konštruktor, je nutné tento konštruktor volať aj v potomkoch.
Lepšie riešenie je využitie inject metod (ako v tom linku čo som ti dal) alebo sa to dá aj jednoduchšie:
<?php
/**
* @inject
* @var Nette\Database\Context
*/
public $database;
?>
- Amgis
- Člen | 8
Díky za odpověď. Tak jsem to nějak zprovoznila. Sice jinak než podle mé
představy, ale zatím to vypadá, že to funguje.
V basepresenteru pro „front“ mám:
<?php
/** @var Nette\Database\Context */
private $database;
public function __construct(Nette\Database\Context $database) {
$this->database = $database;
}
?>
A v basepresenteru pro admin je to samé, jen místo
private je public.
Zkoušela jsem to dát do hlavního basepresenteru, který byl měl být nad
tím vším, ale tam to nejde. Respektive funguje jen pro „front“ a to pouze
když je private. Pokud tak vstoupím do admin části, kde je
nějaký dotaz na databázi, tak to vyhodí eror:
'Nette\MemberAccessException
Cannot read an undeclared property App\AdminModule\Presenters\AddNewsPresenter::$database.'
EDIT:
Pokud se podívám zpětně na článek http://www.itnetwork.cz/…ework-modely tak je tam to
nastavení databáze provedeno a mělo by se načítat automaticky ne?
Editoval Amgis (9. 5. 2015 8:34)
- Freema
- Člen | 18
Ten constructor je tam myšlen jak to máš dělat v modelu ne v Presenteru. Fakt do Presenteru to injectuj přes annotaci @inject nebo jestli si to chceš při injectováním nějak rozšířit tak přes metodu inject. Asi takhle:
<?php
/**
* @param Mail $service
*/
final function injectMail(Mail $service) {
$this->Mail = $service;
}
?>
Jasně jde to do presenteru tahat přes constructor ale když se ti applikace nafoukne tak to vypadá fakt divně. Hlavně musíš pořád přemýšlet co si předáš přes parent::__construct() do base presenteru.
Editoval Freema (9. 5. 2015 11:48)
- Amgis
- Člen | 8
Děkuji za další nápovědu. Nicméně i tak se mi to moc nedaří.
Chtěla jsem ten můj prozatimní výtvor nahrát na Github, ale i po
přečtení několika návodů včetně tady toho rozsáhlého http://git-scm.com/…%C3%A9mu-Git se mi to nijak nepodařilo
zprovoznit :(
Pak tu nějaký kolega začátečník řeší něco podobného https://forum.nette.org/…-config-neon
tak to třeba snad dáme nějak dohromady.
Jinak když mi nešla ta db tak jsem si aspoň úspěšně aplikovala nástavbu
na textareu – tinyMce :) S tím mým dosavadním nastavením konstruktorů
to dokonce i funguje (zápis a výpis s DB). Nicméně jsem ještě zjistila,
že zápis do DB funguje i bez těch databázových konstruktorů. Jen výpis
se bez nich nedaří. Takže tipuji, že DB bude brána z toho modelu
Base.php
Jen tedy asi spíš špatně píši dotaz na výpis dat z DB.
Soubor Base.php:
<?php
namespace App\Model;
class Base extends \Nette\Object
{
/** @var Nette\Database\Context */
public $database;
/** @var string */
protected $tableName;
/**
* @param Nette\Database\Connection $db
* @throws Nette\InvalidStateException
*/
public function __construct(\Nette\Database\Context $db)
{
$this->database = $db;
if($this->tableName === NULL) {
$class = get_class($this);
throw new Nette\InvalidStateException("Název tabulky musí být definován v $class::\$tableName.");
}
}
protected function getTable() {
return $this->database->table($this->tableName);
}
/**
* Vrací všechny záznamy z databáze
* @return \Nette\Database\Table\Selection
*/
public function findAll() {
return $this->getTable();
}
/**
* Vrací vyfiltrované záznamy na základě vstupního pole
* (pole array('name' => 'David') se převede na část SQL dotazu WHERE name = 'David')
* @param array $by
* @return \Nette\Database\Table\Selection
*/
public function findBy(array $by) {
return $this->getTable()->where($by);
}
/**
* To samé jako findBy akorát vrací vždy jen jeden záznam
* @param array $by
* @return \Nette\Database\Table\ActiveRow|FALSE
*/
public function findOneBy(array $by) {
return $this->findBy($by)->limit(1)->fetch();
}
/**
* Vrací záznam s daným primárním klíčem
* @param int $id
* @return \Nette\Database\Table\ActiveRow|FALSE
*/
public function find($id) {
return $this->getTable()->get($id);
}
/**
* Upraví záznam
* @param array $data
*/
public function update($data) {
$this->findBy(array('id'=>$data['id']))->update($data);
}
/**
* Vloží nový záznam a vrátí jeho ID
* @param array $data
* @return \Nette\Database\Table\ActiveRow
*/
public function insert($data) {
return $this->getTable()->insert($data);
}
public function delete($id) {
$this->getTable()->where('id',$id)->delete();
}
}
?>
Tento soubor je pak „spojen“ se souborem Settings.php:
<?php
/**
* Description of Settings
*
* @author Agmis
*/
namespace App\Model;
class Settings extends Base {
/** @var string */
protected $tableName = 'settings';
public function getAll() {
$rows = $this->findAll();
$settings = array();
foreach ($rows as $row) {
$settings[$row->name] = $row->value;
}
return $settings;
}
}
?>
V config.neon je pak snad správně načten:
<?php
services:
- App\UserManager
- App\RouterFactory
router: @App\RouterFactory::createRouter
database: @Nette\Database\Connection
settings: App\Model\Settings
?>
Jinak tedy například presenter na výpis dat z DB mám takto:
<?php
namespace App\FrontModule\Presenters;
use Nette,
App\Model;
/**
* Description of NewsPresenter
*
* @author Agmis
*/
class NewsPresenter extends BasePresenter {
/** @var Nette\Database\Context */
private $database;
public function __construct(Nette\Database\Context $database) {
$this->database = $database;
}
public function beforeRender() {
parent::beforeRender();
$this->template->pages = $this->database->table('news')->order('id DESC');
}
}
?>
- Amgis
- Člen | 8
Tak se mi podařilo vše nahrát na Github – https://github.com/…tte-Blog.git
Je tam už trochu víc věcí. Jen mě pořád trochu štve to přidávání
konstruktoru na tu databázi. Momentálně to tedy řeším v base presenterech
pro front a public. Podíval by se někdo prosím zda to má cenu pokračovat
v mém projektu založeném na nette a nebo to dělám všechno špatně a
raději se mám učit vařit… :) Hlavně by mě zajímalo jak tedy pořešit
to volání DB aby zůstalo jen v tom modelu „Base.php“ a zároveň jak
používat tamní funkce na výběr dat z DB.
Teoretické použití těch funkcí bych viděla asi tak:
<?php
$this->tempate->vypisDb = $this->model->findAll('jmeno_tabulky_v_db'); // aspoň tak si myslím, že by to mělo (mohlo) nějak fungovat
?>
Díky
- Azathoth
- Člen | 495
Ahoj, jestli můžu poradit pár best practises, tak nepoužívej $this->context. Místo toho je lepší mít
/**
* @var \App\Model\Settings
* @inject
*/
public $settingsModel;
tím se ti do té proměnné dá přesně ten objekt, který potřebuješ, a
nebudeš to muset tahat přes kontext. A to nastavení z databáze si
vytahneš jako $settings = $this->settingsModel->getAll().
Dál si myslím, že je lepší nemít modelovou třídu (settings) v base
presenteru a tedy úplně ve všech presenterech, ale jen tam, kde to skutečně
potřebuješ.
A to samé o tepmlate…do template je dobré si předávat jen to, co
potřebuješ. Nevím, jestli je všude zapotřebí settings, v každém
template.
Jinak až se to trochu rozroste, tak určitě doporučuji mít nějakou
modelovou třídu na stránky i novinky, tu si jen injectuješ do presenteru a
ta ti bude zapouzdřovat logiku za přidáváním/vyhledáváním
článků/stránek.
Správně by presentery měly být odstíněné od toho, kde máš data
uložená.
A jestli to nechceš injectovat tu databázi přes kontruktor, tak si ji
můžeš injectova do presenteru takhle:
/**
* @var Nette\Database\Context
* @inject
*/
public $database;
- Amgis
- Člen | 8
Azathoth: Díky za další nakopnutí, které mě snad poslalo tam kam chci. Již nemám v BasePresenterech ty databázové konstruktory, ale jen jednou injectované dle tvého návodu.
Co se týká toho nastavení, to asi zruším, jelikož to vypisuje jen
název stránek, a to nevím proč by mělo být v DB, pokud to nebude systém
„edited all“ (něco jako WordPress)… Nicméně by to podle mělo jít
nějak použít i pro výpis z jiných tabulek, což jsem na to koukala, ale
nějak moc nepochopila jak by to mělo jít. :(
Výsledek je viditelný na GitHubu.