Depo të dobishme me Eloquent?

Javën e kaluar shkrova artikull për padobishmërinë e shabllonit të Depove për entitetet elokuente, megjithatë, ai premtoi të më tregonte se si ta përdorja pjesërisht në avantazhin e tij. Për ta bërë këtë, do të përpiqem të analizoj se si përdoret zakonisht ky shabllon në projekte. Grupi minimal i kërkuar i metodave për një depo:

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

Sidoqoftë, në projektet reale, nëse vendoset të përdoren depo, shpesh u shtohen metoda për marrjen e të dhënave:

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

Këto metoda mund të zbatohen përmes fushave elokuente, por mbingarkesa e klasave të entiteteve me përgjegjësinë për të marrë veten nuk është ideja më e mirë dhe zhvendosja e kësaj përgjegjësie në klasat e depove duket logjike. A është kështu? Unë e ndava vizualisht këtë ndërfaqe në dy pjesë. Pjesa e parë e metodave do të përdoret në operacionet e shkrimit.

Operacioni standard i shkrimit është:

  • ndërtimi i një objekti të ri dhe sfida PostRepository::save
  • PostRepository::getById, manipulimi dhe thirrja e subjektit PostRepository::save
  • sfidë PostRepository::fshij

Operacionet e shkrimit nuk përdorin metoda të marrjes. Në operacionet e leximit, përdoren vetëm metodat get*. Nëse lexoni për Parimi i ndarjes së ndërfaqes (letër I в NGURTA), atëherë do të bëhet e qartë se ndërfaqja jonë është shumë e madhe dhe kryen të paktën dy përgjegjësi të ndryshme. Është koha ta ndajmë me dy. Metoda merrniById është i nevojshëm në të dyja, por ndërsa aplikacioni bëhet më kompleks, zbatimet e tij do të ndryshojnë. Këtë do ta shohim pak më vonë. Kam shkruar për padobishmërinë e pjesës së shkrimit në një artikull të mëparshëm, kështu që në këtë thjesht do ta harroj.

Pjesa Lexo më duket jo aq e kotë, pasi edhe për Eloquent mund të ketë disa zbatime këtu. Si ta emërtoni klasën? Mund ReadPostRepository, por në shabllon depo ai tashmë ka pak rëndësi. Thjesht mundesh Pyetjet e postuara:

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

Zbatimi i tij me Eloquent është mjaft i thjeshtë:

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

Ndërfaqja duhet të shoqërohet me zbatimin, për shembull në AppService Provider:

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

Kjo klasë është tashmë e dobishme. Ai e kupton përgjegjësinë e tij duke lehtësuar ose kontrollorët ose klasën e njësisë ekonomike. Në një kontrollues mund të përdoret si kjo:

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

Метод PostsController::lastPosts thjesht duke kërkuar një zbatim Pyetjet e postimeve dhe punon me të. Në ofruesin që lidhëm Pyetjet e postuara me klasën EloquentPostQueries dhe kjo klasë do të zëvendësohet në kontrollues.

Le të imagjinojmë që aplikacioni ynë është bërë shumë popullor. Mijëra përdorues në minutë hapin faqen me publikimet më të fundit. Publikimet më të njohura lexohen gjithashtu shumë shpesh. Bazat e të dhënave nuk trajtojnë shumë mirë ngarkesa të tilla, kështu që ato përdorin një zgjidhje standarde - një cache. Përveç bazës së të dhënave, një fotografi e caktuar e të dhënave ruhet në ruajtje të optimizuar për operacione të caktuara - e memorizuar ose redis.

Logjika e memorizimit zakonisht nuk është aq e komplikuar, por zbatimi i saj në EloquentPostQueries nuk është shumë i saktë (nëse vetëm sepse Parimi i vetëm i përgjegjësisë). Është shumë më e natyrshme të përdorësh një shabllon bojaxhi dhe zbatoni caching si dekorim për veprimin kryesor:

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

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

Injoroni ndërfaqen depo në konstruktor. Për një arsye të panjohur, ata vendosën të emërtojnë ndërfaqen për caching në Laravel në këtë mënyrë.

Klasë CachedPostQueries zbaton vetëm caching. $this->cache-> mbaj mend kontrollon nëse kjo hyrje është në cache dhe nëse jo, atëherë thërret përsëri dhe shkruan vlerën e kthyer në cache. Gjithçka që mbetet është të zbatohet kjo klasë në aplikacion. Ne kemi nevojë për të gjitha klasat që në aplikacion të kërkojnë një implementim të ndërfaqes Pyetjet e postuara filloi të marrë një shembull të klasës CachedPostQueries. Megjithatë, ai vetë CachedPostQueries konstruktori duhet të marrë një klasë si parametër EloquentPostQueriespasi nuk mund të funksionojë pa një implementim “real”. Ne ndryshojmë AppService Provider:

<?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ë gjitha dëshirat e mia përshkruhen natyrshëm në ofruesin. Kështu, ne kemi zbatuar caching për kërkesat tona vetëm duke shkruar një klasë dhe duke ndryshuar konfigurimin e kontejnerit. Pjesa tjetër e kodit të aplikimit nuk ka ndryshuar.

Sigurisht, për të zbatuar plotësisht caching, është gjithashtu e nevojshme të zbatohet pavlefshmëria në mënyrë që artikulli i fshirë të mos varet në sit për ca kohë, por të fshihet menjëherë. Por këto janë gjëra të vogla.

Përfundimi: ne përdorëm jo një, por dy shabllone. Mostra Ndarja e përgjegjësisë së pyetjes së komandës (CQRS) propozon të ndahen plotësisht operacionet e leximit dhe të shkrimit në nivelin e ndërfaqes. Unë erdha tek ai përmes Parimi i ndarjes së ndërfaqes, që sugjeron që unë të manipuloj me mjeshtëri modele dhe parime dhe të nxjerr njërën nga tjetra si teoremë :) Sigurisht që jo çdo projekt ka nevojë për një abstraksion të tillë për përzgjedhjen e entiteteve, por unë do ta ndaj trukun me ju.Në fazën fillestare të aplikimit zhvillim, thjesht mund të krijoni një klasë Pyetjet e postuara me zbatimin e zakonshëm nëpërmjet Eloquent:

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

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

Kur lind nevoja për caching, me një lëvizje të thjeshtë mund të krijoni një ndërfaqe (ose klasë abstrakte) në vend të kësaj klase. Pyetjet e postuara, kopjoni zbatimin e tij në klasë EloquentPostQueries dhe shkoni te skema që përshkrova më herët. Pjesa tjetër e kodit të aplikacionit nuk ka nevojë të ndryshohet.

Të gjitha këto truke me klasa, ndërfaqe, Injeksioni i varësisë и CQRS përshkruar në detaje në libri im "Arkitektura e aplikacioneve komplekse në ueb". Ekziston gjithashtu një zgjidhje për enigmën pse të gjitha klasat e mia në shembujt për këtë artikull janë shënuar si përfundimtare.

Burimi: www.habr.com

Shto një koment