Prodej FOREX webů, včetně affiliate účtu se 7300 registracemi a výdělkem 277 000 Kč
Zobrazují se odpovědi 1 až 10 z 10

Entity Repository Mapper dle mého

  1. Skelet Hodnocení: 8 (100%) Skelet je na dobré cestě
    1
    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 Model\Quote, ten je dán adresáři, ve kterých jsou uložené soubory.

    1. Mapper
    PHP kód:
    <?php

    class QuoteDbMapper extends Object{

        public function 
    save(Quote $entity){

        
    $data $this->entityToData($entity);
        unset (
    $data['quote']);

        if(
    $entity->id == NULL){//INSERT

            
    \dibi::query('INSERT INTO [quotes]'$data);
            
    $returnId = \dibi::insertId();

        }else{
    //UPDATE

            
    \dibi::query('UPDATE [quotes] SET'$data,' WHERE quote = %i'$entity->id);
            
    $returnId $entity->id;

        }

        return 
    $this->find($returnId);

        }

        public function 
    find($entityId){

        
    $data = \dibi::fetch('SELECT * FROM [quotes] WHERE quote = %i'$entityId);
        return 
    $this->load($data);

        }

        public function 
    findBy($where NULL$by NULL$limit NULL$offset NULL){
        
    $rows = \dibi::fetchAll('
            SELECT * FROM [quotes]
            %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 [quotes] %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
    PHP kód:
    <?php

    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
    PHP kód:
    <?php

    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:
    PHP kód:
    <?php

        
    namespace Model;

        use 
    Nette\Object;

    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/mo...ository-mapper
    http://www.phpguru.cz/clanky/pet-vrstev-modelu

  2. Co se právě děje na Webtrhu?
  3. 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.
    Kód:
    function setSeries ( Series $series ) {}
    function setSeriesId ( $seriesId ) {}
    A když to uděláš, můžeš využít můj oblíbený idiom z jQuery - spojit setter a getter do jedné metody.
    Kód:
    function series ( Series $series = NULL ) {
      if(is_null($series)) {
        return $this->series;
      }
      $this->series = $series;
      return $this;
    }
    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

    Kód:
    public function id ( $id = NULL ) {
        return $this->getOrSet($this->id, $id);
    }
    
    protected function getOrSet ( &$property, $value = null ) {
        if(is_null($value)) {
            return $property;
        }
        $property = $value;
        return $this;
    }
    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á.

  4. vedouci Hodnocení: 3 (100%) vedouci bude brzy slavný/á
    3
    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, [b]kde je spravne misto pro novou metodu[b], 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:
    Kód:
    user = usersFactory.createNewUser();
    usersService.loadFromArray(user, request.getData());
    errors = usersValidator.validate(user);
    
    if (errors.length>0){
    ...
    return ...;
    }
    
    usersService.generateNewPassword(user);
    
    //possible missing "unit of work"...
    usersRepository.add(user);
    
    mailService.sendMail(usersService.generateRegisterMail(user));
    
    //a ted si predstav, ze jeste pri vytvareni uzivatele musis vytvorit dejme tomu bankovni ucet...
    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"...

  5. Skelet Hodnocení: 8 (100%) Skelet je na dobré cestě
    4
    "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
    Naposledy upravil Skelet : 16.06.2011 v 11:48

  6. vedouci Hodnocení: 3 (100%) vedouci bude brzy slavný/á
    5
    Neber to jako straseni, chce to cit - mene (oddelovani) je nekdy vice (srozumitelne)

  7. Skelet Hodnocení: 8 (100%) Skelet je na dobré cestě
    6
    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?

  8. vedouci Hodnocení: 3 (100%) vedouci bude brzy slavný/á
    7
    Dedeni je temer vzdy spatna volba - http://www.javaworld.com/javaworld/j...1-toolbox.html

  9. Skelet Hodnocení: 8 (100%) Skelet je na dobré cestě
    8
    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.

  10. vedouci Hodnocení: 3 (100%) vedouci bude brzy slavný/á
    9
    Muze byt, nejlepsi je ale nevyhazovat nic - pokud muzes chybovemu stavu nejak zabranit, tak to udelej - v tomhle pripade to ale asi nepujde.

  11. Skelet Hodnocení: 8 (100%) Skelet je na dobré cestě
    10
    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();

Hostujeme u Server powered by TELE3