Mengapa menggunakan corak repositori (Repositori) dalam Laravel? Artikel berikut akan memperkenalkan kepada anda kelebihan menggunakan mod repositori saya harap ia akan membantu anda!
Dalam artikel sebelum ini, saya menerangkan apakah corak repositori, bagaimana ia berbeza daripada corak Rekod Aktif, dan cara melaksanakannya dalam Laravel. Sekarang saya ingin melihat dengan lebih dekat mengapa anda perlu menggunakan corak repositori.
Saya perhatikan dalam ulasan siaran sebelumnya bahawa Corak repositori ialah topik kontroversi dalam komuniti Laravel. Sesetengah orang tidak melihat sebab untuk menggunakannya dan kekal dengan mod Rekod Aktif terbina dalam. Orang lain lebih suka menggunakan kaedah lain untuk memisahkan akses data daripada domain logik. Sila ambil perhatian bahawa saya menghormati pendapat ini dan akan menumpukan catatan blog yang akan datang untuk topik ini.
Dengan penafian itu, mari kita fahami kelebihan menggunakan corak repositori.
Prinsip Tanggungjawab Tunggal ialah diskriminasi utama untuk membezakan antara corak Rekod Aktif dan corak Repositori. Kelas model sudah memegang data dan menyediakan kaedah pada objek domain. Apabila menggunakan corak Rekod Aktif, akses data adalah tanggungjawab tambahan yang diperkenalkan. Ini adalah sesuatu yang saya ingin gambarkan dalam contoh berikut:
/** * @property string $first_name * @property int $company_id */ class Employee extends Model {} $jack = new Employee(); $jack->first_name = 'Jack'; $jack->company_id = $twitterId; $jack->save();
Walaupun model domain dan teknologi akses data mempunyai pelbagai tanggungjawab, ia secara intuitif masuk akal. Dalam aplikasi kami, pekerja perlu disimpan dalam pangkalan data entah bagaimana, jadi mengapa tidak memanggil save()
pada objek. Satu objek ditukar menjadi satu baris data dan disimpan.
Tetapi mari kita melangkah lebih jauh dan lihat apa lagi yang boleh kita lakukan dengan pekerja:
$jack->where('first_name', 'John')->firstOrFail()->delete(); $competition = $jack->where('company_id', $facebookId)->get();
Kini, ia menjadi tidak intuitif malah melanggar model domain kami. Mengapa Jack tiba-tiba memadamkan pekerja lain yang mungkin bekerja di syarikat lain? Atau mengapa dia boleh membawa pekerja Facebook?
Sudah tentu, contoh ini dibuat-buat, tetapi ia masih menunjukkan bagaimana corak Rekod Aktif tidak membenarkan pemodelan domain yang disengajakan. Garis antara pekerja dan senarai semua pekerja menjadi kabur. Anda sentiasa perlu mempertimbangkan sama ada pekerja itu digunakan sebagai pekerja sebenar atau sebagai mekanisme untuk mengakses pekerja lain.
Mod gudang menyelesaikan masalah ini dengan menguatkuasakan pembahagian asas ini. Satu-satunya tujuannya ialah untuk mengenal pasti koleksi objek domain, bukan objek domain itu sendiri.
Isi penting:
Sesetengah projek menaburkan pertanyaan pangkalan data ke seluruh projek. Di bawah ialah contoh di mana kami mendapat senarai daripada pangkalan data dan memaparkannya dalam paparan Blade.
class InvoiceController { public function index(): View { return view('invoices.index', [ 'invoices' => Invoice::where('overdue_since', '>=', Carbon::now()) ->orderBy('overdue_since') ->paginate() ]); } }
Apabila pertanyaan seperti ini menjadi lebih kompleks dan digunakan di berbilang tempat, pertimbangkan untuk mengekstraknya ke dalam kaedah Repositori.
Corak repositori membantu mengurangkan pertanyaan pendua dengan membungkusnya ke dalam kaedah ekspresi. Jika anda perlu melaraskan pertanyaan, anda hanya perlu menukarnya sekali.
class InvoiceController { public __construct(private InvoiceRepository $repo) {} public function index(): View { return view('invoices.index', [ 'invoices' => $repo->paginateOverdueInvoices() ]); } }
Kini pertanyaan itu dilaksanakan sekali sahaja dan boleh diuji secara berasingan dan digunakan di tempat lain. Selain itu, prinsip tanggungjawab tunggal kembali dimainkan, kerana pengawal tidak bertanggungjawab untuk mendapatkan data, tetapi hanya untuk memproses permintaan HTTP dan mengembalikan respons.
Takeaway:
PenjelasanPrinsip Penyongsangan Kebergantungan layak untuk catatan blognya sendiri. Saya hanya ingin menggambarkan bahawa repositori boleh mendayakan penyongsangan kebergantungan.
Apabila melapis komponen, biasanya komponen peringkat lebih tinggi bergantung pada komponen peringkat rendah. Sebagai contoh, pengawal akan bergantung pada kelas model untuk mendapatkan data daripada pangkalan data:
class InvoiceController { public function index(int $companyId): View { return view( 'invoices.index', ['invoices' => Invoice::where('company_id', $companyId)->get()] ); } }
Kebergantungan adalah dari atas ke bawah dan berganding rapat. InvoiceController
bergantung pada kelas Invoice
tertentu. Sukar untuk memisahkan kedua-dua kelas ini, seperti mengujinya secara berasingan atau menggantikan mekanisme penyimpanan. Dengan memperkenalkan antara muka Repositori, kita boleh mencapai penyongsangan kebergantungan:
interface InvoiceRepository { public function findByCompanyId($companyId): Collection; } class InvoiceController { public function __construct(private InvoiceRepository $repo) {} public function index(int $companyId): View { return view( 'invoices.index', ['invoices' => $this->repo->findByCompanyId($companyId)] ); } } class EloquentInvoiceRepository implements InvoiceRepository { public function findByCompanyId($companyId): Collection { // 使用 Eloquent 查询构造器实现该方法 } }
Pengawal kini hanya bergantung pada antara muka Repositori, sama seperti pelaksanaan Repositori Kedua-dua kelas ini kini hanya bergantung pada satu abstraksi , Dengan itu mengurangkan gandingan Seperti yang saya akan terangkan dalam bahagian seterusnya, ini membawa kepada kelebihan selanjutnya.
Takeaway:
存储库 提高了可读性 因为复杂的操作被具有表达性名称的高级方法隐藏了.
访问存储库的代码与底层数据访问技术分离. 如有必要,您可以切换实现,甚至可以省略实现,仅提供 Repository 接口。 这对于旨在与框架无关的库来说非常方便。
OAuth2 服务包 —— league/oauth2-server
也用到这个抽象类机制。 Laravel Passport 也通过 实现这个库的接口 集成 league/oauth2-server 包。
正如 @bdelespierre 在 评论 里回应我之前的一篇博客文章时向我指出的那样,你不仅可以切换存储库实现,还可以将它们组合在一起。大致以他的示例为基础,您可以看到一个存储库如何包装另一个存储库以提供附加功能:
interface InvoiceRepository { public function findById(int $id): Invoice; } class InvoiceCacheRepository implements InvoiceRepository { public function __construct( private InvoiceRepository $repo, private int $ttlSeconds ) {} public function findById(int $id): Invoice { return Cache::remember( "invoice.$id", $this->ttlSeconds, fn(): Invoice => $this->repo->findById($id) ); } } class EloquentInvoiceRepository implements InvoiceRepository { public function findById(int $id): Invoice { /* 从数据库中取出 $id */ } } // --- 用法: $repo = new InvoiceCacheRepository( new EloquentInvoiceRepository(); );
要点:
存储库模式提供的抽象也有助于测试。
如果你有一个 Repository 接口,你可以提供一个替代的测试实现。 您可以使用数组支持存储库,而不是访问数据库,将所有对象保存在数组中:
class InMemoryInvoiceRepository implements InvoiceRepositoryInterface { private array $invoices; // implement the methods by accessing $this->invoices... } // --- Test Case: $repo = new InMemoryInvoiceRepository(); $service = new InvoiceService($repo);
通过这种方法,您将获得一个现实的实现,它速度很快并且在内存中运行。 但是您必须为测试提供正确的 Repository 实现,这 ** 本身可能需要大量工作**。 在我看来,这在两种情况下是合理的:
您正在开发一个(与框架无关的)库,它本身不提供存储库实现。
测试用例复杂,Repository 的状态很重要。
另一种方法是“模仿”,要使用这种技术,你不需要适当的接口。你可以模仿任何 non-final 类。
使用 PHPUnit API ,您可以明确规定如何调用存储库以及应该返回什么。
$companyId = 42; /** @var InvoiceRepository&MockObject */ $repo = $this->createMock(InvoiceRepository::class); $repo->expects($this->once()) ->method('findInvoicedToCompany') ->with($companyId) ->willReturn(collect([ /* invoices to return in the test case */ ])); $service = new InvoiceService($repo); $result = $service->calculateAvgInvoiceAmount($companyId); $this->assertEquals(1337.42, $result);
有了 mock,测试用例就是一个适当的单元测试。上面示例中测试的唯一代码是服务。没有数据库访问,这使得测试用例的设置和运行非常快速。
另外:
原文地址:https://dev.to/davidrjenni/why-use-the-repository-pattern-in-laravel-2j1m
译文地址:https://learnku.com/laravel/t/62521
【相关推荐:laravel视频教程】
Atas ialah kandungan terperinci Analisis ringkas tentang kelebihan corak repositori (Repositori) dalam Laravel. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!