Nuttige bewaarplekke met Eloquent?

Verlede week het ek geskryf artikel oor die nutteloosheid van die Repository-sjabloon vir welsprekende entiteiteHy het egter belowe om my te vertel hoe om dit gedeeltelik tot sy voordeel te gebruik. Om dit te doen, sal ek probeer om te analiseer hoe hierdie sjabloon gewoonlik in projekte gebruik word. Die minimum vereiste stel metodes vir 'n bewaarplek:

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

In werklike projekte, as daar besluit is om bewaarplekke te gebruik, word metodes vir die herwinning van rekords egter dikwels daarby gevoeg:

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

Hierdie metodes kan deur Eloquent-bestekke geΓ―mplementeer word, maar die oorlaai van entiteitsklasse met die verantwoordelikheid om hulself te gaan haal is nie die beste idee nie, en die verskuiwing van hierdie verantwoordelikheid na bewaarplekklasse lyk logies. Is dit so? Ek het hierdie koppelvlak spesifiek visueel in twee dele verdeel. Die eerste deel van die metodes sal in skryfbewerkings gebruik word.

Die standaard skryfbewerking is:

  • konstruksie van 'n nuwe voorwerp en uitdaging PostRepository::save
  • PostRepository::getById, entiteit manipulasie en dagvaarding PostRepository::save
  • 'n uitdaging PostRepository::delete

Skryfbewerkings gebruik nie haalmetodes nie. In leesbewerkings word slegs get*-metodes gebruik. As jy lees oor Interface Segregation Beginsel (brief I Π² VASTE), dan sal dit duidelik word dat ons koppelvlak te groot is en ten minste twee verskillende verantwoordelikhede uitvoer. Dit is tyd om dit deur twee te deel. Metode getById is nodig in beide, maar soos die toepassing meer kompleks word, sal die implementering daarvan verskil. Ons sal dit 'n bietjie later sien. Ek het in 'n vorige artikel oor die nutteloosheid van die skryfdeel geskryf, so in hierdie een sal ek eenvoudig daarvan vergeet.

Die Lees-deel lyk vir my nie so nutteloos nie, aangesien daar selfs vir Eloquent verskeie implementerings hier kan wees. Wat om die klas te noem? Kan ReadPostRepository, maar na die sjabloon bewaarplek hy het reeds min relevansie. Jy kan maar PostNavrae:

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

Om dit met Eloquent te implementeer is redelik eenvoudig:

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

Die koppelvlak moet geassosieer word met die implementering, byvoorbeeld in AppService Provider:

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

Hierdie klas is reeds nuttig. Hy besef sy verantwoordelikheid deur Γ³f die beheerders Γ³f die entiteitklas te onthef. In 'n kontroleerder kan dit soos volg gebruik word:

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

metode PostsController::laaste plasings vra net vir 'n mate van implementering PostsNavrae en werk daarmee. In die verskaffer wat ons gekoppel het PostNavrae met klas EloquentPostQueries en hierdie klas sal in die beheerder vervang word.

Kom ons stel ons voor dat ons toepassing baie gewild geword het. Duisende gebruikers per minuut maak die bladsy oop met die nuutste publikasies. Die gewildste publikasies word ook baie gereeld gelees. Databasisse hanteer nie sulke vragte baie goed nie, daarom gebruik hulle 'n standaardoplossing - 'n kas. Benewens die databasis, word 'n sekere data-snapshot gestoor in die stoor wat geoptimaliseer is vir sekere bewerkings - memcached of herhaal.

Kaslogika is gewoonlik nie so ingewikkeld nie, maar om dit in EloquentPostQueries te implementeer is nie baie korrek nie (al is dit net omdat Beginsel vir enkele verantwoordelikheid). Dit is baie meer natuurlik om 'n sjabloon te gebruik Versierder en implementeer caching as versiering vir die hoofaksie:

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

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

Ignoreer die koppelvlak bewaarplek in die konstruktor. Om een ​​of ander onbekende rede het hulle besluit om die koppelvlak vir cache in Laravel op hierdie manier te noem.

Klas CachedPostQueries implementeer slegs caching. $hierdie->kas->onthou kontroleer of hierdie inskrywing in die kas is en indien nie, bel dan terugbel en skryf die teruggekeerde waarde na die kas. Al wat oorbly is om hierdie klas in die toepassing te implementeer. Ons benodig alle klasse wat in die toepassing is om 'n implementering van die koppelvlak te versoek PostNavrae begin om 'n instansie van die klas te ontvang CachedPostQueries. Hy het egter self CachedPostQueries die konstruktor moet 'n klas as 'n parameter ontvang EloquentPostQueriesaangesien dit nie kan werk sonder 'n "regte" implementering nie. Ons verander 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);
    }
}

Al my wense word heel natuurlik in die verskaffer beskryf. Ons het dus net kas vir ons versoeke geΓ―mplementeer deur een klas te skryf en die houerkonfigurasie te verander. Die res van die toepassingskode het nie verander nie.

Natuurlik, om die kas volledig te implementeer, is dit ook nodig om ongeldigmaking te implementeer sodat die geskrap artikel nie vir 'n geruime tyd op die webwerf hang nie, maar onmiddellik uitgevee word. Maar dit is klein dingetjies.

Bottom line: ons het nie een nie, maar twee sjablone gebruik. Voorbeeld Bevelnavraagverantwoordelikheidsskeiding (CQRS) stel voor om lees- en skryfbewerkings op die koppelvlakvlak heeltemal te skei. Ek het deur na hom gekom Interface Segregation Beginsel, wat daarop dui dat ek patrone en beginsels vaardig manipuleer en die een as 'n stelling van die ander aflei :) Natuurlik het elke projek nie so 'n abstraksie nodig om entiteite te kies nie, maar ek sal die truuk met jou deel. By die aanvanklike stadium van toepassing ontwikkeling, kan jy eenvoudig 'n klas skep PostNavrae met die gewone implementering via Eloquent:

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

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

Wanneer die behoefte aan caching ontstaan, kan jy met 'n eenvoudige skuif 'n koppelvlak (of abstrakte klas) in die plek van hierdie klas skep PostNavrae, kopieer die implementering daarvan na die klas EloquentPostQueries en gaan na die skema wat ek vroeΓ«r beskryf het. Die res van die toepassingskode hoef nie verander te word nie.

Al hierdie truuks met klasse, koppelvlakke, Afhanklikheid Inspuiting ΠΈ CQRS in detail beskryf in my boek "Argitektuur van komplekse webtoepassings". Daar is ook 'n oplossing vir die raaisel waarom al my klasse in die voorbeelde vir hierdie artikel as finaal gemerk word.

Bron: will.com

Voeg 'n opmerking