Kasulikud Eloquenti reposiitorid?

Eelmisel nĂ€dalal kirjutasin artikli Eloquent objektide jaoks mĂ”ttetu Repository malli kohta, kuid lubasin rÀÀkida, kuidas seda osaliselt kasulikult kasutada. Selleks pĂŒĂŒan analĂŒĂŒsida, kuidas seda malli projektides tavaliselt kasutatakse. Minimiaalne vajalik meetodite komplekt repozitooriumi jaoks:

<php
interface PostRepository
{
    public function getById($id): Post;
    public function save(Post $post);
    public function delete($id);
}

Kuid reaalsetes projektides, kui repozitooriume otsustatakse kasutada, lisatakse neisse sageli meetodeid andmete valimiseks:

<php
interface PostRepository
{
    public function getById($id): Post;
    public function save(Post $post);
    public function delete($id);

    public function getLastPosts();
    public function getTopPosts();
    public function getUserPosts($userId);
}

Need meetodid vÔiks teostada lÀbi Eloquent skoopide, kuid klasside koormamine iseenda valikute tegemisega ei ole parim lahendus ja selle vastutuse viimine repozitooriumi klassidesse tundub loogiline. Kas see on tÔsi? Olen spetsiaalselt visuaalselt jaganud selle liidese kaheks osaks. Esimene osa meetoditest kasutatakse salvestamisoperatsioonides.

Standardsed salvestamisoperatsioonid on:

  • uue objekti konstrueerimine ja ĂŒleskutse PostRepository::save
  • PostRepository::getById, manipulatsioonid objekti ja ĂŒleskutse PostRepository::save
  • ĂŒleskutse PostRepository::delete

Salvestamisoperatsioonides ei kasutata valiku meetodeid. Lugemise operatsioonides kasutatakse aga ainult meetodeid get*. Kui lugeda Interface Segregation Principle (tÀhe I ja SOLID), siis on selge, et meie liides on liiga suur ja tÀidab vÀhemalt kahte erinevat kohustust. On aeg jagada see kaheks. Meetod getById on vajalik mÔlemas, kuid rakendusi keerukama rakenduse puhul on need erinevad. Seda nÀeme veidi hiljem. Kirjutasin eelmisel artiklil write-osa mÔttetusest, nii et selles artiklis unustan selle lihtsalt.

Loetud osa ei tundu mulle siiski nii kasutu, kuna isegi Eloquent'i puhul vÔib siin olla mitu rakendust. Kuidas klassi nimetada? VÔib-olla ReadPostRepository, kuid malliga Repository on see juba vÀhe seotud. VÔib lihtsalt nimetada PostQueries:

<php
interface PostQueries
{
    public function getById($id): Post;
    public function getLastPosts();
    public function getTopPosts();
    public function getUserPosts($userId);
}

Selle teostamine Eloquent'i abil on ĂŒsna lihtne:

limit(/*some limit*/)
            ->get();
    }
    /**
    * @return Post[] | Collection
    */
    public function getTopPosts()
    {
        return Post::orderBy('rating', 'desc')
            ->limit(/*some limit*/)
            ->get();
    }

    /**
    * @param int $userId
    * @return Post[] | Collection
    */
    public function getUserPosts($userId)
    {
        return Post::whereUserId($userId)
            ->orderBy('created_at', 'desc')
            ->get();
    }
}

Liides peab olema seotud rakendusega, nÀiteks AppServiceProvider:

app->bind(PostQueries::class, 
            EloquentPostQueries::class);
    }
}

See klass on juba kasulik. See tĂ€idab oma ĂŒlesande, leevendades kas kontrollerite vĂ”i entiteedi klassi koormust. Kontrolleris saab seda kasutada jĂ€rgmiselt:

$postQueries->getLastPosts(),
        ]);
    }
} 

Meetod PostsController::lastPosts lihtsalt kĂŒsib endale mĂ”nda teostust PostsQueries ja töötab selle kallal. Pakkujas seostasime PostQueries klassi EloquentPostQueries ja kontrolleris asendatakse see klass.

Kujutage ette, et meie rakendus on saanud vĂ€ga populaarseks. Tuhanded kasutajad avavad minutis viimaste postituste lehte. KĂ”ige populaarsemaid postitusi loetakse samuti vĂ€ga sageli. Andmebaasid ei suuda selliste koormustega hĂ€sti toime tulla, seega kasutatakse standardlahendust — vahemĂ€lu. Lisaks andmebaasile hoitakse andmete koopiaid teatud operatsioonide jaoks optimeeritud salvestuses — memcached vĂ”i redis.

VahemĂ€lustamise loogika ei ole tavaliselt kuigi keeruline, kuid selle rakendamine EloquentPostQueries-s ei ole Ă”igete pĂ”hjuste tĂ”ttu vĂ€ga Ă”ige (vĂ€hemalt sellepĂ€rast, et Kandidaadi Üksiku Vastutuse Printsiip). Loomulikult on loogilisem kasutada kaunistamismustrit Dekoraator ja rakendada vahemĂ€llu salvestamine kui pĂ”hitegevuse kaunistamine:

base = $base;
        $this->cache = $cache;
    }

    /**
    * @return Post[] | Collection
    */
    public function getLastPosts()
    {
        return $this->cache->remember('last_posts', 
            self::LASTS_DURATION, 
            function(){
                return $this->base->getLastPosts();
            });
    }

    // muud meetodid on praktiliselt samad
}

Ärge muretsege liidese pĂ€rast Repository ehitajas. Ilmselt on mingil pĂ”hjusel otsustatud nimetada Laravelis vahemĂ€lu liidest nii.

Klass CachedPostQueries rakendab ainult vahemĂ€lu. $this->cache->remember kontrollib, kas antud kirje on vahemĂ€lus ja kui seda ei ole, kĂ€ivitab ta tagasikutse ning salvestab vahemĂ€llu tagastatud vÀÀrtuse. JÀÀnud on vaid see klass rakendusse integreerida. Peame tagama, et kĂ”ik klassid, mis rakenduses kĂŒsivad liidese rakendust PostQueries hakaksid saama klassi eksemplari CachedPostQueries. Kuid ise CachedPostQueries peab konstruktorisse saama klassi EloquentPostQueries, kuna tal ei ole vĂ”imalik töötada ilma "pĂ€ris" rakenduse. Muudame AppServiceProvider:

app->bind(PostQueries::class, 
            CachedPostQueries::class);

        $this->app->when(CachedPostQueries::class)
            ->needs(PostQueries::class)
            ->give(EloquentPostQueries::class);
    }
}

KĂ”ik minu soovid on ĂŒsna loomulikult kirjeldatud pakkujas. Nii oleme realiseerinud vahemĂ€lu meie pĂ€ringutele, kirjutades vaid ĂŒhe klassi ja muutes konteineri konfiguratsiooni. ÜlejÀÀnud rakenduse kood ei ole muutunud.

Muidugi, et vahemÀlu tÀielikult rakendada, tuleb veel teostada kehtetuks tunnistamine, et eemaldatud artikkel ei jÀÀks veebilehele veel mÔneks ajaks, vaid kustutataks kohe. Kuid see on juba pisiasjad.

KokkuvĂ”te: kasutasime mitte ĂŒhte, vaid lausa kahte malli. Mall Command Query Responsibility Segregation (CQRS) soovitab tĂ€ielikult eristada lugemise ja kirjutamise toimingud liidese tasemel. Ma jĂ”udsin sellele lĂ€bi Interface Segregation Principle, mis nĂ€itab, et ma oskan ĆĄabloone ja pĂ”himĂ”tteid osavalt manipuleerida ning tuua ĂŒhe teisest nagu teoreemi 🙂 Muidugi ei vaja iga projekt sellist abstraktsiooni olendite valikute jaoks, kuid jagan teiega nĂ€punĂ€idet. Rakenduse arendamise algfaasis saate lihtsalt luua klassi PostQueries tavalise rakendusega lĂ€bi Eloquent:

<?php
final class PostQueries
{
    public function getById($id): Post
    {
        return Post::findOrFail($id);
    }

    // teised meetodid
}

Kui vahemĂ€lu rakendamine muutub vajalikuks, on kerge kĂ€eliigutusega luua liides (vĂ”i abstraktne klass) selle klassi kohale PostQueries, selle rakendus kopeerida klassi EloquentPostQueries ja liikuda skeemile, mida ma varem kirjeldasin. ÜlejÀÀnud rakenduse koodi ei ole vaja muuta.

KĂ”ik need nipid klasside, liideste, SĂ”ltuvuse sĂŒstimine ja CQRS on pĂ”hjalikult kirjeldatud minu raamatus "Ahnne web-rakenduste arhitektuur". Seal see vastus, miks kĂ”ik minu klassid selles artiklis on mĂ€rgitud kui final.

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster