Eloquent ile faydalı depolar?

Geçen hafta yazdım Eloquent varlıkları için Depo şablonunun yararsızlığı hakkında makaleancak bunu kısmen kendi avantajına nasıl kullanacağını bana anlatacağına söz verdi. Bunu yapmak için bu şablonun genellikle projelerde nasıl kullanıldığını analiz etmeye çalışacağım. Bir depo için gereken minimum yöntem seti:

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

Bununla birlikte, gerçek projelerde, depoların kullanılmasına karar verilmişse, bunlara genellikle kayıtları alma yöntemleri eklenir:

<?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 yöntemler Eloquent kapsamları aracılığıyla uygulanabilir, ancak varlık sınıflarına kendilerini getirme sorumluluğuyla aşırı yükleme yapmak en iyi fikir değildir ve bu sorumluluğu depo sınıflarına taşımak mantıklı görünmektedir. Öyle mi? Bu arayüzü özellikle görsel olarak iki bölüme ayırdım. Yöntemlerin ilk kısmı yazma işlemlerinde kullanılacaktır.

Standart yazma işlemi:

  • yeni bir nesnenin inşası ve meydan okuma Gönderi Deposu::kaydet
  • PostRepository::getById, varlık manipülasyonu ve çağırma Gönderi Deposu::kaydet
  • вызов Gönderi Deposu::delete

Yazma işlemleri getirme yöntemlerini kullanmaz. Okuma işlemlerinde yalnızca get* yöntemleri kullanılır. Eğer hakkında okursan Arayüz Ayrımı İlkesi (mektup I в KATI), o zaman arayüzümüzün çok büyük olduğu ve en az iki farklı sorumluluğu yerine getirdiği anlaşılacaktır. Bunu ikiye bölmenin zamanı geldi. Yöntem getById her ikisinde de gereklidir, ancak uygulama karmaşıklaştıkça uygulamaları farklılaşacaktır. Bunu biraz sonra göreceğiz. Önceki bir makalede yazma kısmının yararsızlığından bahsetmiştim, bu yüzden bu makaleyi unutacağım.

Okuma kısmı bana o kadar da işe yaramaz gelmiyor çünkü Eloquent için bile burada birkaç uygulama olabilir. Sınıfa ne ad verilir? Olabilmek ReadPostRepository, ancak şablona depo zaten çok az ilgisi var. Sadece yapabilirsin Gönderi Sorguları:

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

Eloquent ile uygulamak oldukça basittir:

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

Arayüz uygulama ile ilişkilendirilmelidir, örneğin Uygulama Hizmeti Sağlayıcısı:

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

Bu sınıf zaten kullanışlıdır. Sorumluluğunu ya kontrolörleri ya da varlık sınıfını rahatlatarak gerçekleştirir. Bir denetleyicide şu şekilde kullanılabilir:

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

yöntem PostsController::lastPosts sadece biraz uygulama rica ediyorum GönderilerSorgular ve onunla çalışır. Bağladığımız sağlayıcıda Gönderi Sorguları sınıfla EloquentPost Sorguları ve bu sınıf denetleyiciye yerleştirilecektir.

Uygulamamızın çok popüler hale geldiğini düşünelim. Dakikada binlerce kullanıcı en son yayınların bulunduğu sayfayı açıyor. En popüler yayınlar da çok sık okunmaktadır. Veritabanları bu tür yükleri pek iyi işleyemez, bu nedenle standart bir çözüm olan önbellek kullanırlar. Veritabanına ek olarak, belirli işlemler için optimize edilmiş depolama alanında belirli bir veri anlık görüntüsü saklanır - memcached veya Redis.

Önbelleğe alma mantığı genellikle o kadar karmaşık değildir, ancak bunu EloquentPostQueries'te uygulamak pek doğru değildir (eğer sadece Tek Sorumluluk İlkesi). Bir şablon kullanmak çok daha doğaldır dekoratör ve ana eylem için dekorasyon olarak önbelleğe almayı uygulayın:

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

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

Arayüzü görmezden gelin depo yapıcıda. Bilinmeyen bir nedenden ötürü, Laravel'de önbelleğe alma arayüzünü bu şekilde adlandırmaya karar verdiler.

sınıf Önbelleğe Alınmış Gönderi Sorguları yalnızca önbelleğe almayı uygular. $this->cache->hatırla bu girişin önbellekte olup olmadığını kontrol eder, değilse geri aramayı çağırır ve döndürülen değeri önbelleğe yazar. Geriye kalan tek şey bu sınıfı uygulamaya uygulamaktır. Arayüzün uygulanmasını talep etmek için uygulamadaki tüm sınıflara ihtiyacımız var Gönderi Sorguları sınıfın bir örneğini almaya başladı Önbelleğe Alınmış Gönderi Sorguları. Ancak kendisi Önbelleğe Alınmış Gönderi Sorguları yapıcının parametre olarak bir sınıf alması gerekir EloquentPost Sorgularıçünkü "gerçek" bir uygulama olmadan çalışamaz. Biz değiştiririz Uygulama Hizmeti Sağlayıcısı:

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

Tüm dileklerim sağlayıcıda oldukça doğal bir şekilde anlatılıyor. Böylece sadece tek bir sınıf yazıp, konteyner konfigürasyonunu değiştirerek isteklerimize önbellekleme uyguladık. Uygulama kodunun geri kalanı değişmedi.

Elbette, önbelleğe almayı tam olarak uygulamak için, silinen makalenin bir süre sitede kalmaması, hemen silinmesi için geçersiz kılmanın da uygulanması gerekir. Ama bunlar önemsiz şeyler.

Özetle: Bir değil iki şablon kullandık. Örnek Komut Sorgu Sorumluluğu Ayrımı (CQRS) Okuma ve yazma işlemlerini arayüz düzeyinde tamamen ayırmayı önerir. onun sayesinde geldim Arayüz Ayrımı İlkesiBu da kalıpları ve ilkeleri ustaca manipüle ettiğimi ve teorem olarak birbirlerinden türetdiğimi gösteriyor :) Elbette her projede varlık seçimi için böyle bir soyutlamaya ihtiyaç yoktur ama püf noktasını sizlerle paylaşacağım. geliştirme, basitçe bir sınıf oluşturabilirsiniz Gönderi Sorguları Eloquent aracılığıyla olağan uygulamayla:

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

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

Önbelleğe alma ihtiyacı ortaya çıktığında basit bir hareketle bu sınıfın yerine bir arayüz (veya soyut sınıf) oluşturabilirsiniz. Gönderi Sorguları, uygulamasını sınıfa kopyalayın EloquentPost Sorguları ve daha önce anlattığım şemaya gidin. Uygulama kodunun geri kalanının değiştirilmesine gerek yoktur.

Sınıflar, arayüzler ile ilgili tüm bu hileler, Bağımlılık Enjeksiyonu и CQRS ayrıntılı olarak açıklanan kitabım “Karmaşık Web Uygulamalarının Mimarisi”. Bu makalenin örneklerindeki tüm sınıflarımın neden final olarak işaretlendiği bilmecesinin de bir çözümü var.

Kaynak: habr.com

Yorum ekle