Biltegi erabilgarriak Eloquent-ekin?

Joan den astean idatzi nuen entitate Elokuenteentzako Biltegiaren txantiloiaren erabilgarritasunari buruzko artikulua, ordea, bere onerako neurri batean nola erabili esango zidala agindu zidan. Horretarako, txantiloi hau proiektuetan nola erabiltzen den aztertzen saiatuko naiz. Biltegi baterako beharrezkoa den gutxieneko metodo multzoa:

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

Hala ere, benetako proiektuetan, biltegiak erabiltzea erabaki bazen, erregistroak berreskuratzeko metodoak gehitzen zaizkie sarritan:

<?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);
}

Metodo hauek Eloquent esparruen bidez inplementa litezke, baina entitate-klaseak beren burua eskuratzeko ardura duten gainkargatzea ez da ideiarik onena, eta ardura hori biltegi-klaseetara eramatea logikoa dirudi. Hala al da? Bereziki bisualki interfaze hau bi zatitan banatu nuen. Metodoen lehen zatia idazketa eragiketetan erabiliko da.

Idazteko eragiketa estandarra hau da:

  • objektu eta erronka berri baten eraikuntza PostRepository::gorde
  • PostRepository::getById, entitateen manipulazioa eta deialdia PostRepository::gorde
  • erronka bat PostRepository::delete

Idazteko eragiketek ez dute eskuratze metodorik erabiltzen. Irakurketa eragiketetan get* metodoak soilik erabiltzen dira. buruz irakurtzen baduzu Interfazearen bereizketa-printzipioa (gutuna I Π² SOLID), orduan argi geratuko da gure interfazea handiegia dela eta gutxienez bi ardura ezberdin betetzen dituela. Bitan banatzeko garaia da. Metodoa getById beharrezkoa da bietan, baina aplikazioa konplexuagoa denez, bere inplementazioak desberdinak izango dira. Hau pixka bat geroago ikusiko dugu. Idazteko zatiaren alferrikakotasunari buruz idatzi nuen aurreko artikulu batean, beraz, honetan, besterik gabe, ahaztuko naiz.

Irakurri zatia ez zait hain alferrikakoa iruditzen, Eloquent-entzat ere hainbat inplementazio egon baitaitezke hemen. Nola eman klaseari izena? Ahal ReadPostRepository, baina txantiloiari Biltegi dagoeneko garrantzi gutxi du. Besterik gabe, dezakezu PostQueries:

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

Eloquent-ekin ezartzea nahiko erraza da:

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

    /**
    * @return Post[] | Collection
    */
    public function getLastPosts()
    {
        return Post::orderBy('created_at', 'desc')
            ->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();
    }
}

Interfazea inplementazioarekin lotuta egon behar da, adibidez AppServiceProvider:

<?php
final class AppServiceProvider extends ServiceProvider 
{
    public function register()
    {
        $this->app->bind(PostQueries::class, 
            EloquentPostQueries::class);
    }
}

Klase hau erabilgarria da dagoeneko. Bere ardura konturatzen da kontrolatzaileak edo entitate-klasea kenduz. Kontrolagailu batean honela erabil daiteke:

<?php
final class PostsController extends Controller
{
    public function lastPosts(PostQueries $postQueries)
    {
        return view('posts.last', [
            'posts' => $postQueries->getLastPosts(),
        ]);
    }
} 

ΠœΠ΅Ρ‚ΠΎΠ΄ PostsController::lastPosts inplementazioren bat eskatu besterik ez PostsQueries eta horrekin lan egiten du. Lotu dugun hornitzailean PostQueries klasearekin EloquentPostQueries eta klase hau kontrolagailuan ordezkatuko da.

Imajina dezagun gure aplikazioa oso ezaguna bihurtu dela. Minutu bakoitzeko milaka erabiltzailek orria irekitzen dute azken argitalpenekin. Argitalpen ezagunenak ere oso maiz irakurtzen dira. Datu-baseek ez dituzte horrelako kargak oso ondo kudeatzen, beraz, irtenbide estandar bat erabiltzen dute - cachea. Datu-baseaz gain, datu-snapshot jakin bat eragiketa batzuetarako optimizatutako biltegian gordetzen da - memcached edo redis.

Cachearen logika normalean ez da horren konplikatua, baina EloquentPostQueries-en ezartzea ez da oso zuzena (baita besterik ez bada Erantzukizun Bakarraren Printzipioa). Askoz naturalagoa da txantiloi bat erabiltzea Dekoratzailea eta ezarri cachea ekintza nagusirako dekorazio gisa:

<?php
use IlluminateContractsCacheRepository;

final class CachedPostQueries implements PostQueries
{
    const LASTS_DURATION = 10;

    /** @var PostQueries */
    private $base;

    /** @var Repository */
    private $cache;

    public function __construct(
        PostQueries $base, Repository $cache) 
    {
        $this->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();
            });
    }

    // Π΄Ρ€ΡƒΠ³ΠΈΠ΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ практичСски Ρ‚Π°ΠΊΠΈΠ΅ ΠΆΠ΅
}

Ez ikusi egin interfazeari Biltegi eraikitzailean. Arrazoi ezezagun bategatik, Laravel-en cachean gordetzeko interfazeari horrela izena jartzea erabaki zuten.

Class CachedPostQueries cachea soilik ezartzen du. $hau->cache->gogoratu sarrera hau cachean dagoen egiaztatzen du, eta hala ez bada, deia itzultzen du eta itzulitako balioa idazten du cachean. Klase hau aplikazioan ezartzea besterik ez da geratzen. Aplikazioan dauden klase guztiak behar ditugu interfazearen inplementazioa eskatzeko PostQueries klaseko instantzia bat jasotzen hasi zen CachedPostQueries. Hala ere, berak CachedPostQueries eraikitzaileak klase bat jaso behar du parametro gisa EloquentPostQueries"benetako" ezarpenik gabe ezin baita funtzionatu. Gu aldatzen gara AppServiceProvider:

<?php
final class AppServiceProvider extends ServiceProvider 
{
    public function register()
    {
        $this->app->bind(PostQueries::class, 
            CachedPostQueries::class);

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

Nire nahi guztiak modu naturalean azaltzen dira hornitzailean. Horrela, gure eskaeretarako cachea inplementatu genuen klase bat idatziz eta edukiontziaren konfigurazioa aldatuz. Aplikazioaren gainerako kodea ez da aldatu.

Jakina, cachea guztiz ezartzeko, baliogabetzea ere beharrezkoa da, ezabatutako artikulua denbora pixka batean gunean zintzilikatu ez dadin, baina berehala ezabatzeko. Baina hauek gauza txikiak dira.

Beheko lerroa: txantiloi bat ez, bi erabili ditugu. Lagina Komandoen Kontsulten Erantzukizunen Segregazioa (CQRS) interfaze mailan irakurketa eta idazketa eragiketak guztiz bereiztea proposatzen du. Berarengana iritsi nintzen Interfazearen bereizketa-printzipioa, horrek iradokitzen du trebeziaz manipulatzen ditudala ereduak eta printzipioak eta bata bestetik teorema gisa eratortzen ditudala :) Jakina, proiektu guztiek ez dute halako abstrakziorik behar entitateak hautatzeko, baina trikimailua zurekin partekatuko dut. Aplikazioaren hasierako fasean garapena, klase bat sor dezakezu PostQueries Eloquent bidez ohiko ezarpenarekin:

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

    // Π΄Ρ€ΡƒΠ³ΠΈΠ΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹
}

Cachearen beharra sortzen denean, mugimendu sinple batekin interfaze bat (edo klase abstraktua) sor dezakezu klase honen ordez. PostQueries, kopiatu bere ezarpena klasean EloquentPostQueries eta joan lehen azaldu dudan eskemara. Aplikazioaren gainerako kodea ez da aldatu behar.

Trikimailu hauek guztiak klaseekin, interfazeekin, Mendekotasun Injekzioa ΠΈ CQRS atalean deskribatu zehatz-mehatz nire liburua "Web aplikazio konplexuen arkitektura". Artikulu honetako adibideetan nire klase guztiak behin betiko gisa markatzen diren igarkizunaren irtenbidea ere badago.

Iturria: www.habr.com

Gehitu iruzkin berria