Eloquent bilan foydali omborlar?

O'tgan hafta yozdim Eloquent ob'ektlar uchun Repozitoriy shablonining foydasizligi haqidagi maqola, ammo, u menga qisman o'z foydasiga qanday foydalanishni aytib berishga va'da berdi. Buning uchun men ushbu shablon odatda loyihalarda qanday ishlatilishini tahlil qilishga harakat qilaman. Ombor uchun minimal talab qilinadigan usullar to'plami:

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

Biroq, haqiqiy loyihalarda, agar omborlardan foydalanishga qaror qilingan bo'lsa, ko'pincha ularga yozuvlarni olish usullari qo'shiladi:

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

Ushbu usullarni Eloquent scopes orqali amalga oshirish mumkin edi, lekin ob'ektlar sinflarini o'zlarini olish mas'uliyati bilan ortiqcha yuklash eng yaxshi g'oya emas va bu mas'uliyatni ombor sinflariga o'tkazish mantiqiy ko'rinadi. Shundaymi? Men ushbu interfeysni vizual ravishda ikki qismga ajratdim. Usullarning birinchi qismi yozish operatsiyalarida qo'llaniladi.

Standart yozish operatsiyasi:

  • yangi ob'ektni qurish va muammo PostRepository::saqlash
  • PostRepository::getById, ob'ektni manipulyatsiya qilish va chaqirish PostRepository::saqlash
  • muammo PostRepository::o'chirish

Yozish operatsiyalari olish usullaridan foydalanmaydi. O'qish operatsiyalarida faqat get* usullari qo'llaniladi. Agar siz haqida o'qisangiz Interfeysni ajratish printsipi (xat I Π² SOLID), keyin bizning interfeysimiz juda katta ekanligi va kamida ikkita turli mas'uliyatni bajarishi aniq bo'ladi. Uni ikkiga bo'lish vaqti keldi. Usul getById ikkalasida ham zarur, lekin dastur murakkablashgani sayin uning amalga oshirilishi ham farqlanadi. Buni biroz keyinroq ko'ramiz. Men oldingi maqolada yozish qismining foydasizligi haqida yozgan edim, shuning uchun bu maqolada men buni shunchaki unutaman.

O'qish qismi menga unchalik befoydadek tuyuladi, chunki hatto Eloquent uchun bu erda bir nechta ilovalar bo'lishi mumkin. Sinfga nima nom berish kerak? mumkin ReadPostRepository, lekin shablonga Axborotlar u allaqachon unchalik ahamiyatli emas. Siz shunchaki mumkin PostQueries:

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

Uni Eloquent bilan amalga oshirish juda oddiy:

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

Interfeys amalga oshirish bilan bog'liq bo'lishi kerak, masalan AppServiceProvider:

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

Bu sinf allaqachon foydali. U o'z mas'uliyatini nazoratchilarni yoki ob'ekt sinfini ozod qilish orqali tushunadi. Tekshirish moslamasida u quyidagicha ishlatilishi mumkin:

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

usul PostsController::lastPosts faqat bir oz amalga oshirishni so'rayman Postlar so'rovlari va u bilan ishlaydi. Biz bog'langan provayderda PostQueries sinf bilan EloquentPostQueries va bu sinf boshqaruvchiga almashtiriladi.

Tasavvur qilaylik, bizning ilovamiz juda mashhur bo'ldi. Bir daqiqada minglab foydalanuvchilar eng so'nggi nashrlar bilan sahifani ochadilar. Eng mashhur nashrlar ham juda tez-tez o'qiladi. Ma'lumotlar bazalari bunday yuklarni juda yaxshi bajarmaydi, shuning uchun ular standart yechim - keshdan foydalanadilar. Ma'lumotlar bazasiga qo'shimcha ravishda, ma'lum bir ma'lumotlarning surati ma'lum operatsiyalar uchun optimallashtirilgan xotirada saqlanadi - yodlangan yoki redis.

Keshlash mantig'i odatda unchalik murakkab emas, lekin uni EloquentPostQueries-da amalga oshirish unchalik to'g'ri emas (agar faqat shu sababli). Yagona javobgarlik printsipi). Shablondan foydalanish ancha tabiiyroq Dekorator va asosiy harakat uchun bezak sifatida keshlashni amalga oshiring:

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

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

Interfeysga e'tibor bermang Axborotlar konstruktorda. Noma'lum sabablarga ko'ra ular Laravelda keshlash interfeysini shu tarzda nomlashga qaror qilishdi.

sinf CachedPostQueries faqat keshlashni amalga oshiradi. $this->kesh->eslab qoling ushbu yozuv keshda yoki yo'qligini tekshiradi, agar bo'lmasa, qayta qo'ng'iroq qiladi va qaytarilgan qiymatni keshga yozadi. Qolgan narsa bu sinfni ilovaga kiritishdir. Interfeysni amalga oshirishni talab qilish uchun bizga ilovadagi barcha sinflar kerak PostQueries sinfning namunasini qabul qila boshladi CachedPostQueries. Biroq, uning o'zi CachedPostQueries konstruktor parametr sifatida sinfni olishi kerak EloquentPostQuerieschunki u "haqiqiy" amalga oshirilmasdan ishlamaydi. Biz o'zgaramiz 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);
    }
}

Mening barcha istaklarim provayderda tabiiy ravishda tasvirlangan. Shunday qilib, biz faqat bitta sinf yozish va konteyner konfiguratsiyasini o'zgartirish orqali so'rovlarimiz uchun keshlashni amalga oshirdik. Qolgan dastur kodi o'zgarmagan.

Albatta, keshlashni to'liq amalga oshirish uchun, shuningdek, o'chirilgan maqola bir muncha vaqt saytda osilib qolmasligi, balki darhol o'chirilishi uchun bekor qilishni amalga oshirish kerak. Ammo bu kichik narsalar.

Xulosa: biz bir emas, ikkita shablondan foydalandik. Namuna Buyruqlar so'rovi mas'uliyatini ajratish (CQRS) interfeys darajasida o'qish va yozish operatsiyalarini butunlay ajratishni taklif qiladi. Men uning oldiga keldim Interfeysni ajratish printsipi, bu men naqsh va tamoyillarni mohirona manipulyatsiya qilishimni va teorema sifatida birini boshqasidan olishimni anglatadi :) Albatta, ob'ektlarni tanlash uchun har bir loyiha uchun bunday abstraktsiya kerak emas, lekin men siz bilan bu hiylani baham ko'raman.Qo'llashning dastlabki bosqichida rivojlantirish, siz shunchaki sinf yaratishingiz mumkin PostQueries Eloquent orqali odatiy amalga oshirish bilan:

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

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

Keshlash zarurati tug'ilganda, oddiy harakat bilan siz ushbu sinf o'rniga interfeys (yoki mavhum sinf) yaratishingiz mumkin. PostQueries, uning bajarilishini sinfga nusxalash EloquentPostQueries va men ilgari tasvirlab bergan sxemaga o'ting. Qolgan dastur kodini o'zgartirish shart emas.

Bularning barchasi sinflar, interfeyslar, Qarama-qarshi in'ektsiya ΠΈ CQRS da batafsil tavsiflangan "Murakkab veb-ilovalar arxitekturasi" kitobim. Ushbu maqoladagi misollarimdagi barcha darslarim yakuniy deb belgilanganligi haqidagi topishmoqning yechimi ham bor.

Manba: www.habr.com

a Izoh qo'shish