DATABÁZOVÝ MODEL \ PROPOJENÍ S DATÁBÁZÍ
- HosekPetr
- Člen | 31
Dobrý den všem,
jsem začátečník v nette, v MVP i objektovém programování a potřebuji moc pomoct, nenašel jsem nic na fóru ani v dokumentaci, jak by to mělo vypadat.
Snažím se o znovupoužitelnost kódu, abych vše mohl kdykoli a kdekoli použít znovu. Proto jsem si v adresářové struktuře ve složce app vytvořil složku nazvanou components a v ní složku forms, kde budou třídy formů, které budu dále někde využívat, k tomuto postupu mě přimělo několik témat zde na fóru.
Hlavní třída je BaseForm od které budou všechny ostatní třídy dědit a BaseForm bude dědit od Nette\Application\UI\Form.
Níže jsou uvedeny nějaké kódy.
V součané době to hází chybu „Fatal Error Call to a member function getByType() on a non-object“, ta bude jistě hned v BasePresenteru. Řekl bych, že v BaseForm i BasePresenter nemají být ty construkty, i když v authenticatoru to funguje jak má! :(
Poraďte mi prosím, jak udělat databázový model, který budu moct použít uplně všude! V presenterech i v komponentách, které si vytvořím pro znovupoužití. Předpoklad je, že komponenty se budou stavět na všem možném na UI\Control nebo UI\Form.
Perfektní by bylo, kdybych mohl v jakékoli třídě použít např. $this->database->user() a to už by reprezentovalo tabulku v databázi, jde mi o to aby bylo poznat, že jde o spojení s databází. někde jsem viděl něco jako $this->context->user() za použítí továren, ale to se mi moc nelíbí. Navíc se v prezenterech musí použít getServices a jinde to nejde. :(
Všem předem velice děkuji za pomoc!
app/config/config.neon
common:
parameters:
database:
driver: mysql
host: localhost
dbname: test
user: test
password: test
php:
date.timezone: Europe/Prague
nette:
session:
autoStart: smart
database:
default:
dsn: '%database.driver%:host=%database.host%;dbname=%database.dbname%'
user: %database.user%
password: %database.password%
services:
database: @Nette\Database\Connection
authenticator: Authenticator( @database )
factories:
production < common:
development < common:
soubor app/presenters/BasePresenter.php
use Nette\Database\Connection,
Nette\Application\UI\Presenter;
abstract class BasePresenter extends Presenter
{
public $database;
public function __construct(Connection $database)
{
$this->database = $database;
}
public function handleOdhlaseni()
{
$this->getUser()->logout();
$this->flashMessage('Odhlášení bylo úspěšné.', 'success login');
$this->redirect('Prihlaseni:');
}
}
soubor components/forms/BaseForm.php:
use Nette\Application\Connection;
use Nette\Application\UI\Form;
abstract class BaseForm extends Form
{
public $database;
public function __construct(Connection $database)
{
$this->database = $database;
}
}
soubor components/forms/SignUpForm.php:
use Nette\Diagnostics\Debugger;
use Nette\Database\Connection;
class SignUpForm extends BaseForm
{
public function __construct()
{
parent::__construct();
$this->addText('email', 'E-mail:')
->setRequired('Vyplňte prosím email!')
->setAttribute('class', 'signUpInput')
->addRule(self::EMAIL, 'Email nemá správný formát!');
$this->addPassword('password', 'Heslo:')
->setRequired('Vyplňte prosím heslo!')
->setAttribute('class', 'signUpInput')
->addRule(self::MIN_LENGTH, 'Heslo musí mít alespoň %d znaků!', 6)
->addRule(self::PATTERN, 'Heslo musí obsahovat číslici!', '.*[0-9].*');
$this->addPassword('passwordVerify', 'Ověření hesla:')
->setRequired('Vyplňte prosím ověření hesla!')
->setAttribute('class', 'signUpInput')
->addRule(self::MIN_LENGTH, 'Ověření hesla musí mít alespoň %d znaků!', 6)
->addRule(self::PATTERN, 'Ověření hesla musí obsahovat číslici!', '.*[0-9].*')
->addRule(self::EQUAL, 'Hesla se neshodují!', $this['password']);
$this->addText('username', 'Uživatelské jméno:')
->setRequired('Vyplňte prosím uživatelské jméno!')
->setAttribute('class', 'signUpInput');
$this->addSubmit('register', 'Registrovat')
->setAttribute('class', 'submit');
}
public function handleSignUp($form)
{
$formValues = $form->getValues();
$insertValues = array(
'email' => $formValues['email'],
'username' => $formValues['username'],
'password' => $formValues['password']
);
$userExist = $this->database->table('user')->where('email', $formValues['email'])->count('*');
if ($userExist === 0) {
$this->database->table('user')->insert($insertValues);
$this->flashMessage('Registrace proběhla úspěšně!', 'success');
}
else {
$this->flashMessage('Uživatel pod tímto emailem již existuje!', 'error');
}
}
}
soubor models/Authenticator.php
use Nette\Security as NS,
Nette\Database\Connection,
Nette\Object;
class Authenticator extends Object implements NS\IAuthenticator
{
public $database;
public function __construct(Connection $database)
{
$this->database = $database;
}
public function authenticate(array $credentials)
{
list($email, $password) = $credentials;
$row = $this->database->table('user')->where('email', $email)->fetch();
if (!$row) {
throw new NS\AuthenticationException("User '$email' not found.", self::IDENTITY_NOT_FOUND);
}
if ($row->password !== $this->calculateHash($password)) {
throw new NS\AuthenticationException("Invalid password.", self::INVALID_CREDENTIAL);
}
unset($row->password);
return new NS\Identity($row->id, null, $row->toArray());
}
public function calculateHash($password)
{
return $password; // doplnit zabezpečení hesla
}
}
- thorn
- Člen | 14
No komponenty jsou tu od toho, aby mohli byt znovupouzitelne a nemelo by se v nich vyuzivat globalniho pristupu (napr. v kterekoliv komponente pristupovat rovnou jako prihlaseny uzivatel, rovnou vyuzivat databaze, atp.) jinak receno, spravny pristup by asi mel byt takovy, ze pokud chces pristupovat ke komponente, melo by to fungovat tak, ze v konstruktoru komponenty vstoupis s napr. databazovym parametrem pro vyber, neco jako construct(\Selection $table,..), a abys mohl v techto komponentach manipulovat s databazi, musis vyuzivat napr. use Nette\Database\Table\Selection;, samotnou komponentu pak jen volas tak, ze v prislusnem presenteru vytvoris novy objekt a vstoupis tam s ni se svoji databazi.
Ale abych odpovedel na tvoji otazku, zrejme to chces globalne resit a mit to
tak jednodussi.
Jeste pred rozdelenim QuickStartu na rozdeleni db do faktorek to fungovalo tak,
ze sis v config.neon zaregistroval novou sluzbu do casti Services pod database
sis dal neco takoveho:
<?php
services:
model: Model(@database)
authenticator: Authenticator( @model::getQuery(user) )
?>
Tim sis zaregistroval model a ve slozce models si vytvoril pristup k databazi v souboru Model.php, vytvoril ses tam nejake metody, ktere pak volali nejake tabulky, a kdekoliv, at uz v presenteru nebo komponente kdyz pouzijes napr.
<?php
use \Model;
?>
a nacteni teto sluzby pomoci (to nize muzes dat do napr do BasePresenteru):
<?php
public function startup() {
parent::startup();
$this->model = $this->getService('model');
}
?>
muzes z vesela pristupovat k DB napriklad timto zpusobem:
<?php
$this->model->getQuery('tableName')->where()->order();
?>
Editoval thorn (17. 3. 2012 17:44)
- Michal Vyšinský
- Člen | 608
Ahoj,
takže začnu tím, kde osobně vidím největší problémy:
- BasePresenter:
- presenteru NIKDY nepřepisuj contructor. Nikde ani nevidím
parent::__construct()
– ta aplikace ti podle mého nemůže vůbec jet
- presenteru NIKDY nepřepisuj contructor. Nikde ani nevidím
- Property BasePresenteru database by podle mého měla být private
handleOdhlaseni()
– když pominu hrozné míchání angličtiny a češtiny tak menší chybu vidím tady:
$this->flashMessage('Odhlášení bylo úspěšné.', 'success **login**');
Sice se nejedná o chybu, která by zastavila aplikaci, ale je to podle mě nelogické – odhlášení == logout a ne login
Omlouvám se za klamnou informaci – díky @duke$this->redirect('Prihlaseni:');
– ten redirect myslím taky nikdy nebude fungovat – redirectuje se na presenter nebo action ale rozhodně ne pomocí takového stringu.- BaseForm – stejné, jako 1 a 2. Na database si můžeš udělat
setter – ale nepředával bych to v constructoru – a opět
nevoláš
parent::__construct()
- SignUpForm – také 1 a 2 (kromě
parent::__construct
)
Editoval CherryBoss (17. 3. 2012 18:38)
- HosekPetr
- Člen | 31
Zdravím všechny,
předem všem velice děkuji za odpovědi! Všechno hodlám využít!
@CherryBoss: AJ A ČJ míchám proto, že chci mít adresy kompletně české, ale asi se na to vykašlu a dám to do AJ kompletně, protože i doména bude v AJ.
Ale stále mám problém s modelem!
S funkce startup(), ve které volám $this->getService, lze použít pouze v presenterech a nejde využít například v BaseForm kde používám extends Nette\Application\UI\Form.
Otázka zní… Mám pro BaseForm také pužít extends Nette\Application\UI\Presenter nebo to lze vyřešit vše jinak? V současné době model Database.php využívá přípojení z config.neon že? Nebráním se tomu předělat model a jinak řešit přístup k databázím v projektu!
Moc prosím o rady.
Předem všem děkuji!!!
Soubor BaseForm
use Nette\Application\Connection,
Nette\Application\UI\Form;
use \Database;
abstract class BaseForm extends Form
{
/**
* Spojení s databází
*/
protected $database;
public function startup()
{
parent::startup();
$this->database = $this->getService('databaseModel');
}
}
soubor SignUpForm.php
use Nette\Diagnostics\Debugger;
use \Database;
class SignUpForm extends BaseForm
{
public function __construct()
{
parent::__construct();
$this->addText('email', 'E-mail:')
->setRequired('Vyplňte prosím email!')
->setAttribute('class', 'signUpInput')
->addRule(self::EMAIL, 'Email nemá správný formát!');
$this->addPassword('password', 'Heslo:')
->setRequired('Vyplňte prosím heslo!')
->setAttribute('class', 'signUpInput')
->addRule(self::MIN_LENGTH, 'Heslo musí mít alespoň %d znaků!', 6)
->addRule(self::PATTERN, 'Heslo musí obsahovat číslici!', '.*[0-9].*');
$this->addPassword('passwordVerify', 'Ověření hesla:')
->setRequired('Vyplňte prosím ověření hesla!')
->setAttribute('class', 'signUpInput')
->addRule(self::MIN_LENGTH, 'Ověření hesla musí mít alespoň %d znaků!', 6)
->addRule(self::PATTERN, 'Ověření hesla musí obsahovat číslici!', '.*[0-9].*')
->addRule(self::EQUAL, 'Hesla se neshodují!', $this['password']);
$this->addText('username', 'Uživatelské jméno:')
->setRequired('Vyplňte prosím uživatelské jméno!')
->setAttribute('class', 'signUpInput');
$this->addSubmit('register', 'Registrovat')
->setAttribute('class', 'submit');
}
public function handleSignUp($form)
{
$formValues = $form->getValues();
$insertValues = array(
'email' => $formValues['email'],
'username' => $formValues['username'],
'password' => $formValues['password']
);
$userExist = $this->database->user()->where('email', $formValues['email'])->count('*');
if ($userExist === 0) {
$this->database->user()->insert($insertValues);
$this->flashMessage('Registrace proběhla úspěšně!', 'success');
}
else {
$this->flashMessage('Uživatel pod tímto emailem již existuje!', 'error');
}
Debugger::barDump($formValues);
Debugger::barDump($userExist);
}
}
soubor Database.php
use Nette\Database\Connection,
Nette\Database\Table\Selection,
Nette\Object;
class Database extends Object
{
/**
* Spojení s databází
*/
public $database;
public function __construct(Connection $database)
{
$database =
$this->database = $database;
}
public function user() {
return $this->database->table('user');
}
public function transaction() {
return $this->database->table('transaction');
}
}
soubor config.neon
common:
parameters:
database:
driver: mysql
host: localhost
dbname: ledger
user: root
password:
php:
date.timezone: Europe/Prague
nette:
session:
autoStart: smart
database:
default:
dsn: '%database.driver%:host=%database.host%;dbname=%database.dbname%'
user: %database.user%
password: %database.password%
services:
database: @Nette\Database\Connection
databaseModel: Database(@database)
authenticator: Authenticator( @database )
factories:
production < common:
development < common: