رضا
رضا
خواندن ۳ دقیقه·۴ سال پیش

رعایت اصل DRY در لاراول

برای توضیح (Don't Repeat Yourself) DRY این متن رو از ویکیپدیا قرار میدم:

‍‍‍اصلی در برنامه‌نویسی رایانه‌ای است که به‌وسیلهٔ عدم تکرار یک یا چند خط کد در مکان‌های مختلف برنامه رعایت می‌شود. در واقع با این کار، برای اصلاح بخشی از برنامه نیاز به اصلاح قسمت‌های مختلف و جداگانه‌ای از کد نیست. برای رعایت این اصل معمولاً برنامه‌نویسان کد خود را داخل یک تابع یا کلاس قرار داده و در موارد مورد نیاز تابع را فراخوانی کرده یا یک شئ جدید از کلاس می‌سازند.

در پروژه های لاراولی عملیات CRUD برای Model ها علاوه بر Controller ها ممکن است در کلاس های Command یا Event یا حتی در کنترلر های دیگری که به طور خاص برای آن Model ساخته نشده اند اتفاق بیافتد. علاوه بر این CRUD برای هر مدل در هر قسمتی که انجام شود عموما یکسان است.

یک ذخیره داده ساده در لاراول معمولا مانند مثال زیر نوشته می شود:

public function store (Request $request) { User::create($request->all()); return redirect()->back(); }

و از آنجایی که Model ها در Eloquent تمامی سیستم ORM را به ارث می برند در ظاهر این کد یک خطی تمیز است مشکلی ندارد اما:

  • لایه Controller مجاز به دانستن این عملیات ها نیست و فقط مجاز به فراخوانی دیگر لایه ها است.
  • در صورت افزوده شدن عملیات های دیگر که باید بعد از ذخیره داده انجام شوند با تکرار کد مواجه می شویم. برای مثال بخواهیم برای بعد از ذخیره هر کاربر یک Blog هم برای آن ذخیره کنیم.
  • با توجه به اصل لیسکوف در SOLID برای مثال امکان جایگزینی Eloquent با یک ORM دیگر را نداریم.

پس نیاز به پیاده سازی لایه ای بالاتر که توسط Controller فراخوانده شود به شدت احساس می شود. لایه بالاتر که به آن Service Layer می گوییم به ازای هر کدام از Model ها ساخته شده و می تواند به هر کدام از حروف CRUD یک Method اختصاص بدهد (از آنجایی که READ در لایه Repository انجام می شود آن را در قرارداد این لایه قرار نمی دهیم):

interface ServiceLayerInterface { public function create (array $data): Model; public function update (int|string $id, array $data): Model; public function delete (int|string $id): void; }

اما با توجه به این که هر کدام از این Method ها کار جداگانه ای انجام می دهد پس با توجه به اصل تک مسئولیتی کلاس ها در SOLID می توانیم به هر کدام از عملیات ها یک کلاس اختصاص دهیم.

interface CreateServiceInterface { public function handle (array $data): Model; }

و طبق قرارداد بالا داشته باشیم:

class CreateUser implements CreateServiceInterface { public function handle (array $data): Model { $user = new User(); $user->name = $data['name]; $user->email = $data['email']; $user->password = Hash::make($data['password']); $user->save(); return $user; } }

حالا تنها کاری که باید انجام شود فراخوانی این کلاس در مکان های لازم است:

class UserController extends Controller { public function store(CreateUser $service, Request $request) { $user = $service->handle($request->all()); return view('profile', ['user' => $user]); } }

و یا برای مثال اگر قرار باشد توسط یک Command اطلاعات کاربران را از یک فایل بگیریم و در دیتابیس ذخیره کنیم:

class ImportUsers extends Command { protected $service; public function __construct (CreateUser $service) { $this->service = $service; } public function handle () { $users = // Read data from file as array; foreach ($users as $user) $this->service($user); } }


لاراولlaravelاصول solid
مشغول کُشتن زامبی‌ها
شاید از این پست‌ها خوشتان بیاید