Eloquent-тэй ашигтай репозиторууд?

Өнгөрсөн долоо хоногт би бичсэн Уран чадварлаг байгууллагуудад зориулсан Repository загвар нь ашиггүй байдлын тухай нийтлэлГэсэн хэдий ч тэрээр үүнийг хэрхэн хэсэгчлэн өөрт ашигтайгаар ашиглахыг хэлж өгнө гэж амласан. Үүнийг хийхийн тулд би энэ загварыг ихэвчлэн төслүүдэд хэрхэн ашигладаг талаар дүн шинжилгээ хийхийг хичээх болно. Хадгалах газарт шаардлагатай хамгийн бага аргуудын багц:

<?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 scope-ээр дамжуулан хэрэгжүүлж болох боловч өөрсдийгөө татах үүрэг бүхий аж ахуйн нэгжийн ангиудыг хэт ачаалах нь хамгийн сайн санаа биш бөгөөд энэ хариуцлагыг агуулах анги руу шилжүүлэх нь логик юм. Тийм юм уу? Би энэ интерфейсийг тусгайлан хоёр хэсэгт хуваасан. Аргуудын эхний хэсгийг бичих үйлдлүүдэд ашиглана.

Стандарт бичих үйлдэл нь:

  • шинэ объект барих, сорилт PostRepository::save
  • PostRepository::getById, аж ахуйн нэгжийг удирдах, дуудах PostRepository::save
  • сорилт PostRepository::устгах

Бичих үйлдлүүд нь татах аргыг ашигладаггүй. Унших үйлдлүүдэд зөвхөн get* аргыг ашигладаг. Хэрэв та уншсан бол Интерфэйсийг тусгаарлах зарчим (захидал I в Солид), тэгвэл бидний интерфэйс хэтэрхий том бөгөөд дор хаяж хоёр өөр үүрэг хариуцлага хүлээх нь тодорхой болно. Үүнийг хоёр хуваах цаг болжээ. Арга getById аль алинд нь зайлшгүй шаардлагатай боловч програм нь илүү төвөгтэй болохын хэрээр хэрэгжилт нь өөр байх болно. Бид үүнийг хэсэг хугацааны дараа харах болно. Би өмнөх нийтлэлдээ бичих хэсэг нь ашиггүй байдлын талаар бичсэн тул энэ нийтлэлд би үүнийг мартах болно.

Унших хэсэг нь надад тийм ч хэрэггүй юм шиг санагдаж байна, учир нь Eloquent-ийн хувьд ч энд хэд хэдэн хэрэгжүүлэлт байж болно. Ангиа юу гэж нэрлэх вэ? Чадах ReadPostRepository, гэхдээ загварт Репозитор тэр аль хэдийн бага зэрэг хамааралтай болсон. Та зүгээр л чадна Шуудангийн асуулга:

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

Интерфейс нь хэрэгжилттэй холбоотой байх ёстой, жишээлбэл 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 зүгээр л зарим нэг хэрэгжилтийг гуйж байна Бичлэгийн асуулга мөн түүнтэй ажилладаг. Бид холбосон үйлчилгээ үзүүлэгч дээр Шуудангийн асуулга ангитай EloquentPostQueries мөн энэ анги нь хянагчаар солигдох болно.

Манай програм маш их алдартай болсон гэж төсөөлөөд үз дээ. Нэг минутад мянга мянган хэрэглэгчид хамгийн сүүлийн үеийн нийтлэлүүдтэй хуудсыг нээдэг. Хамгийн алдартай хэвлэлүүдийг бас их уншдаг. Өгөгдлийн сан нь ийм ачааллыг маш сайн зохицуулдаггүй тул стандарт шийдлийг ашигладаг - кэш. Мэдээллийн сангаас гадна тодорхой өгөгдлийн хормын хувилбарыг тодорхой үйлдлүүдэд зориулан оновчтой хадгалах санд хадгалдаг. memcached буюу давтан.

Кэш хийх логик нь ихэвчлэн тийм ч төвөгтэй биш боловч үүнийг 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->санаж байна Энэ оруулга нь кэшэд байгаа эсэхийг шалгаад, хэрэв байхгүй бол буцааж дуудаж, буцаасан утгыг кэш рүү бичнэ. Үлдсэн зүйл бол энэ ангийг програмд ​​​​хэрэгжүүлэх явдал юм. Интерфэйсийг хэрэгжүүлэх хүсэлт гаргахын тулд бидэнд програмын бүх ангиуд хэрэгтэй Шуудангийн асуулга ангийн жишээг хүлээн авч эхлэв 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);
    }
}

Миний бүх хүслийг үйлчилгээ үзүүлэгч дээр байгалийн жамаар дүрсэлсэн байдаг. Тиймээс бид зөвхөн нэг анги бичиж, контейнерийн тохиргоог өөрчилснөөр өөрсдийн хүсэлтийн кэшийг хэрэгжүүлсэн. Үлдсэн програмын код өөрчлөгдөөгүй.

Мэдээжийн хэрэг, кэшийг бүрэн хэрэгжүүлэхийн тулд устгасан нийтлэл хэсэг хугацаанд сайт дээр үлдэхгүй, харин нэн даруй устгагдахын тулд хүчингүй болгох шаардлагатай. Гэхдээ эдгээр нь жижиг зүйлүүд юм.

Доод шугам: бид нэг биш, хоёр загвар ашигласан. Дээж Command Query Responsibility Segregation (CQRS) интерфэйсийн түвшинд унших, бичих үйлдлүүдийг бүрэн тусгаарлахыг санал болгож байна. Би түүн дээр ирсэн Интерфэйсийг тусгаарлах зарчим, энэ нь би хэв маяг, зарчмуудыг чадварлаг удирдаж, нэгийг нь теорем болгон гаргаж авахыг санал болгож байна :) Мэдээжийн хэрэг, төсөл болгонд объект сонгоход ийм хийсвэрлэл хэрэггүй, гэхдээ би та бүхэнтэй энэ заль мэхийг хуваалцах болно. Хэрэглээний эхний шатанд хөгжүүлэх, та зүгээр л анги үүсгэж болно Шуудангийн асуулга Eloquent-ээр дамжуулан ердийн хэрэгжүүлэлтээр:

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

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

Кэш хийх хэрэгцээ гарвал энгийн нүүдлийн тусламжтайгаар та энэ ангийн оронд интерфейс (эсвэл хийсвэр анги) үүсгэж болно. Шуудангийн асуулга, түүний хэрэгжилтийг анги руу хуулна EloquentPostQueries мөн миний өмнө тайлбарласан схем рүү очно уу. Үлдсэн програмын кодыг өөрчлөх шаардлагагүй.

Ангиуд, интерфейстэй эдгээр бүх заль мэх, Хамааралтай тарилга и CQRS хэсэгт дэлгэрэнгүй тайлбарласан миний ном "Цогцолбор вэб програмын архитектур". Энэ нийтлэлийн жишээн дэх миний бүх анги яагаад эцсийн гэж тэмдэглэгдсэн бэ гэдэг оньсого бас бий.

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх