Օգտակար պահոցներ Eloquent-ի հետ:

Անցյալ շաբաթ ես գրեցի հոդված Պահեստի կաղապարի անօգուտության մասին Eloquent սուբյեկտների համար, սակայն, նա խոստացավ ինձ ասել, թե ինչպես դա մասամբ օգտագործեմ իր օգտին։ Դա անելու համար ես կփորձեմ վերլուծել, թե ինչպես է այս ձևանմուշը սովորաբար օգտագործվում նախագծերում: Պահեստի համար անհրաժեշտ մեթոդների նվազագույն փաթեթը.

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

Այնուամենայնիվ, իրական նախագծերում, եթե որոշվել է օգտագործել շտեմարաններ, ապա դրանցում հաճախ ավելացվում են գրառումների առբերման մեթոդներ.

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

Այս մեթոդները կարող են իրականացվել Eloquent scopes-ի միջոցով, սակայն էության դասերի ծանրաբեռնվածությունը՝ իրենց բերելու պատասխանատվությամբ, լավագույն գաղափարը չէ, և այս պատասխանատվությունը պահեստային դասեր տեղափոխելը տրամաբանական է թվում: Այդպե՞ս է։ Ես հատուկ տեսողականորեն բաժանեցի այս ինտերֆեյսը երկու մասի: Մեթոդների առաջին մասը կօգտագործվի գրելու գործողություններում:

Ստանդարտ գրելու գործողությունը հետևյալն է.

  • նոր օբյեկտի կառուցում և մարտահրավեր PostRepository::save
  • PostRepository::getById, սուբյեկտի շահարկում և կանչում PostRepository::save
  • մարտահրավերը PostRepository::ջնջել

Գրելու գործողությունները չեն օգտագործում առբերման մեթոդներ: Ընթերցման գործողություններում օգտագործվում են միայն get* մեթոդները: Եթե ​​դուք կարդաք մասին Ինտերֆեյսի տարանջատման սկզբունքը (նամակ I в SOLID), ապա պարզ կդառնա, որ մեր ինտերֆեյսը չափազանց մեծ է և կատարում է առնվազն երկու տարբեր պարտականություններ։ Ժամանակն է բաժանել այն երկուսի: Մեթոդ getById անհրաժեշտ է երկուսում էլ, բայց քանի որ հավելվածը դառնում է ավելի բարդ, դրա իրականացումները կտարբերվեն: Սա կտեսնենք մի փոքր ուշ։ Գրելու մասի անօգուտության մասին գրել էի նախորդ հոդվածում, ուստի այս մեկում ես պարզապես կմոռանամ դրա մասին:

«Կարդալ» հատվածն ինձ այնքան էլ անօգուտ չի թվում, քանի որ նույնիսկ Eloquent-ի համար այստեղ կարող են լինել մի քանի իրականացումներ։ Ի՞նչ անվանել դասարանը: Կարող է ReadPostRepository, բայց կաղապարին Պահոց նա արդեն իսկ քիչ արդիականություն ունի: Դուք կարող եք պարզապես PostQuery:

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

Eloquent-ով այն իրականացնելը բավականին պարզ է.

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

Ինտերֆեյսը պետք է կապված լինի իրականացման հետ, օրինակ՝ in AppServiceProvider:

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

Այս դասն արդեն օգտակար է։ Նա գիտակցում է իր պատասխանատվությունը՝ ազատելով կա՛մ վերահսկիչներին, կա՛մ կազմակերպության դասին: Կարգավորիչում այն ​​կարող է օգտագործվել այսպես.

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

մեթոդ PostsController::lastPosts պարզապես որոշակի իրականացում խնդրելով Գրառումների հարցումներ և աշխատում է դրա հետ: Մեր կապած մատակարարում PostQuery դասի հետ EloquentPostQueries և այս դասը կփոխարինվի վերահսկիչի մեջ:

Պատկերացնենք, որ մեր հավելվածը շատ տարածված է դարձել։ Րոպեում հազարավոր օգտատերեր բացում են էջը վերջին հրապարակումներով: Ամենատարածված հրապարակումները նույնպես շատ հաճախ են կարդում։ Տվյալների բազաները այնքան էլ լավ չեն կառավարում նման բեռները, ուստի օգտագործում են ստանդարտ լուծում՝ քեշ: Տվյալների բազայից բացի, տվյալների որոշակի նկարը պահվում է որոշակի գործողությունների համար օպտիմիզացված պահեստում. հուշում կամ redis.

Քեշավորման տրամաբանությունը սովորաբար այնքան էլ բարդ չէ, բայց այն կիրառելը EloquentPostQueries-ում այնքան էլ ճիշտ չէ (եթե միայն այն պատճառով, որ Միայնակ պատասխանատվության սկզբունք) Կաղապար օգտագործելը շատ ավելի բնական է դեկորատոր և իրականացնել քեշավորումը որպես հիմնական գործողության ձևավորում.

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

    // другие методы практически такие же
}

Անտեսեք ինտերֆեյսը Պահոց կոնստրուկտորում։ Անհայտ պատճառով նրանք որոշել են անվանել Laravel-ում քեշավորման ինտերֆեյսը այսպես.

Դաս CachedPostQueries իրականացնում է միայն քեշավորումը: $this->cache->հիշիր ստուգում է, թե արդյոք այս գրառումը գտնվում է քեշում, և եթե ոչ, ապա զանգում է հետադարձ զանգ և գրում վերադարձված արժեքը քեշում: Մնում է միայն այս դասը ներդնել հավելվածում: Մեզ անհրաժեշտ են բոլոր դասերը, որոնք հավելվածում պահանջում են ինտերֆեյսի իրականացում PostQuery սկսեցին ստանալ դասի օրինակ CachedPostQueries. Այնուամենայնիվ, նա ինքը CachedPostQueries կոնստրուկտորը պետք է ստանա դաս՝ որպես պարամետր EloquentPostQueriesքանի որ այն չի կարող աշխատել առանց «իրական» իրականացման։ Մենք փոխվում ենք 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);
    }
}

Իմ բոլոր ցանկությունները միանգամայն բնական կերպով նկարագրված են մատակարարում: Այսպիսով, մենք իրականացրել ենք քեշավորում մեր հարցումների համար՝ գրելով միայն մեկ դաս և փոխելով կոնտեյների կոնֆիգուրացիան։ Դիմումի ծածկագրի մնացած մասը չի փոխվել:

Իհարկե, քեշավորումն ամբողջությամբ իրականացնելու համար անհրաժեշտ է իրականացնել նաև անվավերացում, որպեսզի ջնջված հոդվածը որոշ ժամանակով չկախվի կայքում, այլ անմիջապես ջնջվի։ Բայց սրանք չնչին բաներ են։

Ներքևի գիծ. մենք օգտագործել ենք ոչ թե մեկ, այլ երկու ձևանմուշ: Նմուշ Հրամանի հարցումների պատասխանատվության տարանջատում (CQRS) առաջարկում է ամբողջությամբ առանձնացնել կարդալու և գրելու գործողությունները ինտերֆեյսի մակարդակում: Ես նրա մոտ եմ եկել Ինտերֆեյսի տարանջատման սկզբունքը, ինչը հուշում է, որ ես հմտորեն շահարկում եմ օրինաչափություններն ու սկզբունքները և մեկը մյուսից բխում եմ որպես թեորեմ :) Իհարկե, ոչ բոլոր նախագծին պետք է նման վերացականություն սուբյեկտներ ընտրելու համար, բայց ես ձեզ հետ կկիսվեմ հնարքով: Կիրառման սկզբնական փուլում զարգացում, դուք կարող եք պարզապես ստեղծել դաս PostQuery Eloquent-ի միջոցով սովորական իրականացմամբ.

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

    // другие методы
}

Երբ քեշավորման անհրաժեշտություն է առաջանում, մի պարզ քայլով այս դասի փոխարեն կարող եք ստեղծել ինտերֆեյս (կամ վերացական դաս): PostQuery, պատճենեք դրա իրականացումը դասարանում EloquentPostQueries և անցեք ավելի վաղ նկարագրածս սխեմային: Դիմումի ծածկագրի մնացած մասը փոփոխության կարիք չունի:

Այս բոլոր հնարքները դասերի, ինտերֆեյսերի հետ, Կախվածության ներարկում и CQRS մանրամասն նկարագրված է իմ «Բարդ վեբ հավելվածների ճարտարապետություն» գիրքը. Գոյություն ունի նաև հանելուկի լուծում, թե ինչու այս հոդվածի օրինակներում իմ բոլոր դասերը նշվում են որպես վերջնական:

Source: www.habr.com

Добавить комментарий