خانه توسعهدهنده تکنولوژی بکاند لاراول الگوی طراحی Repository
الگوی طراحی Repository
همان طور که میدانید، الگوها راهحلهایی برای مشکلات رایج در طراحی شیءگرا هستند. در ادامه قصد داریم تا به بررسی الگوی طراحی Repository بپردازیم.
الگوی طراحی Repository، یکی از الگوهای پرکاربرد برای مدیریت دادهها است و همان طور که از اسمش پیداست، یک الگوی طراحی است که بهعنوان مخازنی واسط بین منطق برنامه و دادهها قرار میگیرند. این مخازن کلاسهایی هستند که فقط برای کنترل (ذخیره، دریافت و…) دادهها مورد استفاده قرار میگیرند.
خوب است در اینجا اصل جداسازی رفتارها (قابلیتها) (Sepration of Concerns یا SOC) یا همان ماژولاریتی در مهندسی نرمافزار نیز مطرح شود. طبق این اصل، منطق برنامه باید به بخشهای کوچک که هرکدام قابلیتی از برنامه مدنظرمان را پیادهسازی میکنند، تقسیم شود. طبق تعریف گفتهشده از الگوی طراحی Repository، واضح است که این الگو بهخوبی از اصل جداسازی رفتارها تبعیت میکند.
بهطور کلی این الگو ساختاری مشابه زیر دارد:
همان طور که در تصویر هم مشخص است، بدون Repository مستقیماً از کنترلر به پایگاهداده دسترسی پیدا میکنیم، ولی با استفاده از Repository دسترسی به دیتابیس توسط کلاسهای Repository صورت میپذیرد.
در ادامه به بررسی یک مثال عملی از این الگو در زبان PHP و فریمورک Laravel خواهیم پرداخت. علت انتخاب این فریمورک را نیز در ادامه بررسی خواهیم کرد.
یک مثال عملی از الگوی طراحی Repository
همان طور که گفته شد، برای مثال عملی الگوی طراحی Repository در این مقاله از فریمورک لاراول استفاده خواهیم کرد. علت انتخاب این فریمورک این است که لاراول بهصورت پیشفرض از الگوی Active Record استفاده میکند. این الگو بهطور کلی منطق تمام موارد مدیریت داده را بهصورت متمرکز در کلاسها نگه میدارد و ما قصد داریم تا با الگوی طراحی Repository قابلیتهای مختلف آن را جدا کنیم. در ادامه، نمونهی سادهای از الگوی Repository را برای این فریمورک پیادهسازی میکنیم.
در ابتدا باید ساختار فایلی زیر را تشکیل دهیم:
app
├── Repository
│ ├── Eloquent
│ │ ├── BaseRepository.php
│ │ └── UserRepository.php
├── EloquentRepositoryInterface.php
└── UserRepositoryInterface.php
همان طور که میبینید ما قصد داریم تا اینترفیسهایی بسازیم و با کلاسهایی به پیادهسازی این اینترفیسها بپردازیم. BaseRepository مخزنی است شامل تمام متدهای مشترک در مخازن مختلف که EloquentRepositoryInterface را پیادهسازی میکند:
<?php
namespace App\Repository;
use Illuminate\Database\Eloquent\Model;
interface EloquentRepositoryInterface
{
public function create(array $attributes): Model;
public function find($id): ?Model;
}
<?php
namespace App\Repository\Eloquent;
use App\Repository\EloquentRepositoryInterface;
use Illuminate\Database\Eloquent\Model;
class BaseRepository implements EloquentRepositoryInterface
{
protected $model;
public function __construct(Model $model)
{
$this->model = $model;
}
public function create(array $attributes): Model
{
return $this->model->create($attributes);
}
public function find($id): ?Model
{
return $this->model->find($id);
}
}
حال قصد داریم کلاسهای مربوط به مدیریت دادههای کاربران را کامل کنیم:
<?php
namespace App\Repository;
use App\Model\User;
use Illuminate\Support\Collection;
interface UserRepositoryInterface
{
public function all(): Collection;
}
<?php
namespace App\Repository\Eloquent;
use App\Model\User;
use App\Repository\UserRepositoryInterface;
use Illuminate\Support\Collection;
class UserRepository extends BaseRepository implements UserRepositoryInterface
{
public function __construct(User $model)
{
parent::__construct($model);
}
public function all(): Collection
{
return $this->model->all();
}
}
واضح است که با کامل شدن این کلاسها، قابلیتهای مدیریت دادهی مختلف برنامهی موردنظرمان بدون وابستگی به سایر قسمتها تکمیل میشوند.
در گام آخر باید لاراول را از وجود این کلاسها مطلع کنیم. برای این کار میتوانیم از سرویس پروایدرها استفاده کنیم:
php artisan make:provider RepositoryServiceProvider
و بعد از رجیستر کردن این سرویس پروایدر:
<?php
namespace App\Providers;
use App\Repository\EloquentRepositoryInterface;
use App\Repository\UserRepositoryInterface;
use App\Repository\Eloquent\UserRepository;
use App\Repository\Eloquent\BaseRepository;
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(EloquentRepositoryInterface::class, BaseRepository::class);
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
}
}
میتوانید بهصورت زیر از این مخازن استفاده کنید:
class UsersController extends Controller
{
private $userRepository;
public function __construct(UserRepositoryInterface $userRepository)
{
$this->userRepository = $userRepository;
}
public function index()
{
$users = $this->userRepository->all();
return view('users.index', [
'users' => $users
]);
}
}
همانطور که گفته شد، این مثال شکل بسیار سادهای از پیادهسازی الگوی طراحی Repository بود، اما در پروژههای بزرگ موجود در صنعت، این الگو کمک شایانی در جلوگیری از بروز کدهای تکراری، امکان استفاده مجدد (Reusability) و… به شما خواهد کرد.
خوب است در انتها مثالی کوتاه از استفاده از این الگو برای مدیریت سفارشات ببینیم:
interface OrderRepositoryInterface
{
public function getAllOrders();
public function getOrderById($orderId);
public function deleteOrder($orderId);
public function createOrder(array $orderDetails);
public function updateOrder($orderId, array $newDetails);
public function getFulfilledOrders();
}
class OrderRepository implements OrderRepositoryInterface
{
public function getAllOrders()
{
return Order::all();
}
public function getOrderById($orderId)
{
return Order::findOrFail($orderId);
}
public function deleteOrder($orderId)
{
Order::destroy($orderId);
}
public function createOrder(array $orderDetails)
{
return Order::create($orderDetails);
}
public function updateOrder($orderId, array $newDetails)
{
return Order::whereId($orderId)->update($newDetails);
}
public function getFulfilledOrders()
{
return Order::where('is_fulfilled', true);
}
}
در این مقاله، الگوی طراحی Repository بهصورت کامل بیان شد. برای درک بهتر این الگو میتوانید از دورههای آموزش پی اچ پی و آموزش لاراول کوئرا کالج استفاده کرده و با مفاهیم شیءگرایی و اصول مختلف در مهندسی نرمافزار آشنا شوید.