Databáze – construct aby se nemusel psát do každého presenteru

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Amgis
Člen | 8
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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.