Zadejte hledaný výraz...

Entity Repository Mapper dle mého

Skelet
verified
rating uzivatele
(9 hodnocení)
14. 6. 2011 23:36:41
Tak,
"pracuji" na Nette+dibi a dlouho jsem si nerozumněl s písmenkem "M" (Model) ze slavné architektury "MVC", nyní si myslím že jsem konečně našel správnou cestu, jen se potřebuji dostal do cíle. Nyní se to snažím implementovat na svojí srandu "serialspot", takže názvy tříd jsou dosti "osobní". Snad se mi lehounká ukázka střev webu nevymstí :D
Jedná se o tři základní třídy: Entity, Repository, Mapper.
V ukázkách se objevuje namespace ModelQuote, ten je dán adresáři, ve kterých jsou uložené soubory.
1. Mapper
class QuoteDbMapper extends Object{
public function save(Quote $entity){
$data = $this->entityToData($entity);
unset ($data);
if($entity->id == NULL){//INSERT
dibi::query('INSERT INTO ', $data);
$returnId = dibi::insertId();
}else{//UPDATE
dibi::query('UPDATE SET', $data,' WHERE quote = %i', $entity->id);
$returnId = $entity->id;
}
return $this->find($returnId);
}
public function find($entityId){
$data = dibi::fetch('SELECT * FROM WHERE quote = %i', $entityId);
return $this->load($data);
}
public function findBy($where = NULL, $by = NULL, $limit = NULL, $offset = NULL){
$rows = dibi::fetchAll('
SELECT * FROM
%if', isset($where),'WHERE %and', $where,' %end
%if', isset($by),'ORDER BY %by ', $by,' %end
%if', isset($limit),' %lmt', $limit,' %end
%if', isset($offset),' %ofs', $offset,'%end
');
if(!$rows){
return FALSE;
}
$arr = array();
foreach ($rows as $row){
$arr[] = $this->load($row);
}
return $arr;
}
public function findOneBy($where){
$data = dibi::fetch('SELECT * FROM %if', isset($where),' WHERE %and %end', $where);
return $this->load($data);
}
private function entityToData(Quote $entity){
return array(
'quote' => $entity->id,
'series' => $entity->getSeries(FALSE),
'user' => $entity->getUser(FALSE),
'content' => $entity->content
);
}
private function load($data){
$entity = new Quote();
foreach ($data as $k => $v){
$entity->$k = $v;
}
return $entity;
}
}
Mapper se mi stará o veškerou práci s databází, nikde jinde se název tabulky či jiný sql dotaz nevyskytuje.
2. Entity
class Quote extends Entity{
private $series;
private $user;
public $content;
public function setQuote($quoteId){
$this->setId($quoteId);
}
public function setSeries($seriesId){
$this->series = $seriesId;
}
public function getSeries($full = TRUE){
if($full == FALSE){
return ($this->series instanceof Series)? $this->series->id : $this->series;
}elseif(!$this->series instanceof Series){
$seriesRp = new SeriesRepository();
$this->series = $seriesRp->singleSeries($this->series);
}
return $this->series;
}
public function setUser($userId){
$this->user = $userId;
}
public function getUser($full = TRUE){
if($full == FALSE){
return ($this->user instanceof User)? $this->user->id : $this->user;
}elseif($this->user instanceof User){
$userRp = new UserRepository();
$this->user = $userRp->singleUser($this->user);
}
return $this->user;
}
}
Entita je "otiskem" tabulky, ale na některých sloupcích si on-demand vyžádá náležící řádky (Doufám že je to srozumitelné).
3.Repository
class QuoteRepository extends Object{
public $mapper;
public function __construct(){
$this->mapper = new QuoteDbMapper();
}
public function randomSeriesQuote($seriesId){
$this->mapper->findBy(array('series%i' => $seriesId), array('%sql' => 'RAND()'), 1);
}
}
Repozitář si budu následně upravovat (respektive přidávat metody) podle potřeb dalších částí systému.
Třida entity, od které dědí všechny entity:
namespace Model;
use NetteObject;
abstract class Entity extends Object{
/** @var integer */
private $id;
/**
*
* @param integer $id
*/
public function setId($id){
$this->id = $id;
}
/**
*
* @return integer
*/
public function getId(){
return $this->id;
}
}
Tak co na to říkáte?Vidíte někde nějajé zádrhely? Měl jsem pár otázek, ale ty jsem během psaní zapomněl, snad si později vzpomenu.
Čerpal jsem z těchto zdrojů:
http://wiki.nette.org/cs/cookbook/model-entity-repository-mapper
http://www.phpguru.cz/clanky/pet-vrstev-modelu
14. 6. 2011 23:36:41
https://webtrh.cz/diskuse/entity-repository-mapper-dle-meho#reply645820
Kód se dobře čte, takže za to palec nahoru. Čitelnější to už v PHP asi moc nepůjde. Odsadil bych těla funkcí, ale to je estetické rozhodnutí.
Zvážil bych, jestli QuoteDbMapper::load() nepatří alespoň zčásti spíš do do třídy Quote, protože manipuluje s jejími daty.
Kromě toho se mi rozdělení odpovědnosti zdá v pořádku.
Ve třídě Quote bych určitě oddělil manipulaci se seriesId vs series a userId vs user.
Můžeš pak využít type hinting a hlavně je jasnější, jak se mají metody používat.
A když to uděláš, můžeš využít můj oblíbený idiom z jQuery - spojit setter a getter do jedné metody.
Když pak zavoláš
$entity->series();
je to getter, když to zavoláš s proměnnou
$entity->series ( $series );
je to setter.
Pokud bys stejný idiom používal i pro jiné properties, můžeš tu funkčnost abstrahovat. Já používám
Všechny řádky, které vytvářejí novou instanci ( new Class ) bych extrahoval do vlastní třídy, ať je ta funkčnost v budoucnu zaměnitelná.
15. 6. 2011 14:50:43
https://webtrh.cz/diskuse/entity-repository-mapper-dle-meho#reply645819
Kamil Tomšík
verified
rating uzivatele
(3 hodnocení)
15. 6. 2011 15:54:37
je to relativne (instanceof, gettery, settery) ciste, prehledne, ale velmi tezce udrzovatelne - budes brecet pri kazde uprave - stejne jako ja kdysi (oddelene modely, mappery, dotazovaci objekty, views, controllery, vsechno)
problem je, ze pri kazdem pridani noveho db policka, musis upravit i view, dale validace, mapper, dotazy, pripadne i controller, pokud potrebujes neco vytahnout z requestu, a ted si predstav, ze ti po roce (kdyz jsi uz davno vsechno zapomel) zavola klient, ze potrebuje pridat par novych druhu obsahu, nic sloziteho, par udaju, par validaci, jinak to bude vypadat (a chovat se) stejne.
takze hura do cteni dokumentace (pokud mas), zjistovani, jak presne to funguje, kde je spravne misto pro novou metodu, a pak se konecne muzes pustit do implementace vsech 5ti casti, ze kterych takova "jednoducha" uprava sklada (+databazove schema jeste).
jakkoliv divne to zni, je lepsi drzet co nejvic souvisejicich veci v jedne jednotce (tride) - tzn. logiku, mapovani, pravidla, a nejlepe i zobrazovani (nedelam si srandu), coz samozrejme neznamena, ze entita musi obsahovat sql prikazy - porad muzes (a mel bys) zaviset na nejake abstrakci - obecny data mapper, apod.
pri nekontrolovanem oddelovani je strasne jednoduche skoncit u:
coz se nejenom spatne cte, ale jeste hur upravuje - a je to uplne beznej postup v J2EE svete - nedelam si srandu. takovy stav se potom resi tim, ze firma koupi drahy sw pro analyzu kodu - aby to vyvojari vubec dokazali pochopit.
snaz se drzet gettery na minimu a zbytecne neoddelovat veci - zkus si tipnout, kde bys metodu hledal, kdybys neumel vsechny ty "patterny"...
15. 6. 2011 15:54:37
https://webtrh.cz/diskuse/entity-repository-mapper-dle-meho#reply645818
Skelet
verified
rating uzivatele
(9 hodnocení)
16. 6. 2011 10:55:52
"Ve třídě Quote bych určitě oddělil manipulaci se seriesId vs series a userId": Tohle je určitě dobrý nápad, odpadá pak v mětodě getSeries ta nevzhledná podmínka...
"Zvážil bych, jestli QuoteDbMapper::load() nepatří alespoň zčásti spíš do do třídy Quote": Tam jsem se jí snažil dostat, ale nakonec jsem se vrátil k mapperu.
"getOrSet": Tak tohle se mi moc nelíbí... radši pěkně oddělené. Ikdyž bych si asi ušetřil psaní.
"Všechny řádky, které vytvářejí novou instanci ( new Class ) bych extrahoval do vlastní třídy, ať je ta funkčnost v budoucnu zaměnitelná.": Tak jestli to správně chápu, tak nad tím jsem taky přemýšlel. Udělat jeden společný "service", například class GetFull::series();
Vedouci: Ty jsi mě pěkně postrašil. Já to nemám v jedné třídě, ale složce :) .Jsem si myslel jak to je pěkné na další rozšiřování a udržování :(
//Edit: Aha, ty myslíš i s tou MVC architekturou...
Je pravda že u mých mapperů se liší pouze název tabulky a funkce entityToData. Chtěl jsem udělat základní abstraktní, ale z toho by mi pak vypadlo save(Quote $entity), co je tedy podle vás lepší?
Díky za nápady/rady
16. 6. 2011 10:55:52
https://webtrh.cz/diskuse/entity-repository-mapper-dle-meho#reply645817
Kamil Tomšík
verified
rating uzivatele
(3 hodnocení)
17. 6. 2011 00:50:01
Neber to jako straseni, chce to cit - mene (oddelovani) je nekdy vice (srozumitelne)
17. 6. 2011 00:50:01
https://webtrh.cz/diskuse/entity-repository-mapper-dle-meho#reply645816
Skelet
verified
rating uzivatele
(9 hodnocení)
20. 6. 2011 22:58:44
Koukám že jsme tu trochu umřeli.
"Je pravda že u mých mapperů se liší pouze název tabulky a funkce entityToData. Chtěl jsem udělat základní abstraktní, ale z toho by mi pak vypadlo save(Quote $entity), co je tedy podle vás lepší?"
Dědit, nebo interface?
20. 6. 2011 22:58:44
https://webtrh.cz/diskuse/entity-repository-mapper-dle-meho#reply645815
Kamil Tomšík
verified
rating uzivatele
(3 hodnocení)
20. 6. 2011 23:02:31
Dedeni je temer vzdy spatna volba - http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox.html
20. 6. 2011 23:02:31
https://webtrh.cz/diskuse/entity-repository-mapper-dle-meho#reply645814
Skelet
verified
rating uzivatele
(9 hodnocení)
23. 6. 2011 11:36:14
Ještě jedna věc. Kde mám vyhazovat vyjímku o nenalezeném záznamu? Napadá mě funkce load v mapperu a pak vyjímku odchytávat v controleru.
23. 6. 2011 11:36:14
https://webtrh.cz/diskuse/entity-repository-mapper-dle-meho#reply645813
Kamil Tomšík
verified
rating uzivatele
(3 hodnocení)
23. 6. 2011 12:50:55
Muze byt, nejlepsi je ale nevyhazovat nic - pokud muzes chybovemu stavu nejak zabranit, tak to udelej - v tomhle pripade to ale asi nepujde.
23. 6. 2011 12:50:55
https://webtrh.cz/diskuse/entity-repository-mapper-dle-meho#reply645812
Skelet
verified
rating uzivatele
(9 hodnocení)
21. 7. 2011 21:40:36
Repositář jsem udělal statickej, vidíte někde problém?
class QuoteRepository extends Object{
public class init(){
klasickej construct volanej za definicí.
}
}QuoteRepository::init();
21. 7. 2011 21:40:36
https://webtrh.cz/diskuse/entity-repository-mapper-dle-meho#reply645811
Pro odpověď se přihlašte.
Přihlásit