Eloquent ilə faydalı depolar?

Keçən həftə yazmışdım Eloquent varlıqlar üçün Repository şablonunun yararsızlığı haqqında məqalə, lakin o, mənə bundan qismən öz xeyrinə necə istifadə edəcəyini deyəcəyini vəd etdi. Bunu etmək üçün bu şablonun adətən layihələrdə necə istifadə olunduğunu təhlil etməyə çalışacağam. Anbar üçün minimum tələb olunan metodlar dəsti:

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

Bununla birlikdə, real layihələrdə, depolardan istifadə etmək qərara alınarsa, onlara tez-tez qeydləri əldə etmək üsulları əlavə olunur:

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

Bu üsullar Eloquent əhatə dairəsi vasitəsilə həyata keçirilə bilərdi, lakin obyekt siniflərini özləri əldə etmək məsuliyyəti ilə həddən artıq yükləmək ən yaxşı fikir deyil və bu məsuliyyəti repozitor siniflərinə köçürmək məntiqli görünür. Belədir? Mən bu interfeysi vizual olaraq iki hissəyə ayırdım. Metodların birinci hissəsi yazma əməliyyatlarında istifadə olunacaq.

Standart yazma əməliyyatı belədir:

  • yeni obyektin tikintisi və problem PostRepository::save
  • PostRepository::getById, qurumun manipulyasiyası və çağırılması PostRepository::save
  • problem PostRepository::sil

Yazma əməliyyatları gətirmə üsullarından istifadə etmir. Oxu əməliyyatlarında yalnız get* metodlarından istifadə olunur. Haqqında oxusanız İnterfeys Seqreqasiya Prinsipi (məktub I в SOLID), onda aydın olacaq ki, interfeysimiz çox böyükdür və ən azı iki fərqli məsuliyyəti yerinə yetirir. Onu ikiyə bölmək vaxtıdır. Metod getById hər ikisində zəruridir, lakin tətbiq mürəkkəbləşdikcə onun həyata keçirilməsi fərqli olacaq. Bunu bir az sonra görəcəyik. Əvvəlki məqalədə yazı hissəsinin faydasızlığı haqqında yazmışdım, ona görə də bu yazıda onu sadəcə unudacağam.

Oxu hissəsi mənə elə də faydasız görünür, çünki hətta Eloquent üçün burada bir neçə tətbiq ola bilər. Sinfi nə adlandırmaq olar? Bacarmaq ReadPostRepository, lakin şablon üçün Depo onun artıq az əhəmiyyəti var. Sadəcə bilərsən PostSorğular:

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

Onu Eloquent ilə həyata keçirmək olduqca sadədir:

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

İnterfeys icra ilə əlaqələndirilməlidir, məsələn AppServiceProvider:

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

Bu sinif artıq faydalıdır. O, ya nəzarətçiləri, ya da müəssisə sinfini azad etməklə öz məsuliyyətini dərk edir. Bir nəzarətçidə belə istifadə edilə bilər:

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

üsul PostsController::son Posts sadəcə bir az həyata keçirilməsini xahiş edirəm PostsQueries və onunla işləyir. Əlaqələndirdiyimiz provayderdə PostSorğular siniflə EloquentPostQueries və bu sinif nəzarətçi ilə əvəz olunacaq.

Təsəvvür edək ki, tətbiqimiz çox populyarlaşdı. Dəqiqədə minlərlə istifadəçi ən son nəşrlərin olduğu səhifəni açır. Ən populyar nəşrlər də çox oxunur. Verilənlər bazaları bu cür yükləri çox yaxşı idarə etmirlər, buna görə də standart bir həlldən - keşdən istifadə edirlər. Verilənlər bazasına əlavə olaraq, müəyyən bir məlumat snapshot müəyyən əməliyyatlar üçün optimallaşdırılmış yaddaşda saxlanılır - memcached və ya redis.

Keşləmə məntiqi adətən o qədər də mürəkkəb deyil, lakin onu EloquentPostQueries-də həyata keçirmək çox düzgün deyil (yalnız ona görə ki, Tək məsuliyyət prinsipi). Şablondan istifadə etmək daha təbiidir Dekorator və əsas fəaliyyət üçün bəzək kimi keşləşdirməni həyata keçirin:

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

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

İnterfeysdən imtina edin Depo konstruktorda. Naməlum səbəblərdən onlar Laravel-də keşləmə interfeysini bu şəkildə adlandırmağa qərar verdilər.

Sinif CachedPostQueries yalnız keşləməni həyata keçirir. $this->cache->xatırla bu girişin keşdə olub-olmadığını yoxlayır və əgər yoxdursa, geri çağırır və geri qaytarılan dəyəri keş yaddaşa yazır. Qalan yalnız bu sinfi tətbiqdə tətbiq etməkdir. Tətbiqdə interfeysin həyata keçirilməsini tələb etmək üçün bizə bütün siniflər lazımdır PostSorğular sinfin bir nümunəsini almağa başladı CachedPostQueries. Halbuki onun özü CachedPostQueries konstruktor parametr kimi sinif almalıdır EloquentPostQueriesçünki "real" həyata keçirmədən işləyə bilməz. Biz dəyişirik 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);
    }
}

Bütün arzularım provayderdə olduqca təbii şəkildə təsvir edilmişdir. Beləliklə, biz yalnız bir sinif yazaraq və konteyner konfiqurasiyasını dəyişdirərək sorğularımız üçün keşləmə həyata keçirdik. Tətbiq kodunun qalan hissəsi dəyişməyib.

Əlbəttə ki, keşləşdirməni tam həyata keçirmək üçün silinmiş məqalənin bir müddət saytda qalmaması, dərhal silinməsi üçün etibarsızlaşdırmanı da həyata keçirmək lazımdır. Ancaq bunlar xırda şeylərdir.

Aşağı xətt: bir yox, iki şablondan istifadə etdik. Nümunə Komanda Sorğu Məsuliyyətinin Ayrılması (CQRS) interfeys səviyyəsində oxu və yazma əməliyyatlarını tamamilə ayırmağı təklif edir. vasitəsilə onun yanına gəldim İnterfeys Seqreqasiya Prinsipi, bu, mənim nümunələri və prinsipləri məharətlə manipulyasiya etməyi və bir-birindən teorem kimi çıxarmağı təklif edir :) Təbii ki, hər bir layihədə obyektlərin seçilməsi üçün belə bir abstraksiya lazım deyil, amma hiyləni sizinlə paylaşacağam.Tətbiqin ilkin mərhələsində inkişaf, sadəcə bir sinif yarada bilərsiniz PostSorğular Eloquent vasitəsilə adi icra ilə:

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

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

Keşləmə ehtiyacı yarandıqda, sadə bir hərəkətlə siz bu sinfin yerinə interfeys (və ya abstrakt sinif) yarada bilərsiniz. PostSorğular, onun həyata keçirilməsini sinfə köçürün EloquentPostQueries və əvvəllər təsvir etdiyim sxemə keçin. Tətbiq kodunun qalan hissəsinin dəyişdirilməsinə ehtiyac yoxdur.

Bütün bu fəndlər siniflər, interfeyslər, Asılılıq Enjeksiyonu и CQRS -də ətraflı təsvir edilmişdir “Mürəkkəb Veb Tətbiqlərinin Memarlığı” kitabım. Bu məqalənin nümunələrindəki bütün dərslərimin niyə yekun olaraq qeyd edildiyi tapmacasının həlli də var.

Mənbə: www.habr.com

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