میثم زندی
میثم زندی
خواندن ۵ دقیقه·۴ سال پیش

لاراول تریک | انتقال کد‌های چاق به لایه مدل | Skinny Controllers, Fat Models

لاراول یکی از فریم ورک های MVC که با زبان PHP نوشته شده و به همین خاطر ، وقتی از اون استفاده می کنیم، مدل ها و کنترل ها، کد‌های زیادی دارند، و این غیر منطقی نیست چونکه برای انجام کارهای پیچیده و عملیات های مختلف به این لایه‌ها نیاز داریم.
تا اینجای کار مشکلی نیست.

مشکل وقتی بوجود میاد که ما در یکی از لایه ها، کدها و مسئولیت زیادی داشته باشیم. این میتونه به کدهای اصلی ما آسیب بزنه.

حجیم شدن هرچیزی همیشه مخالف الگو است، خرد کردن نگرانی ها، به نگهداری و آزمایش بهتر آن کمک می کند.

اما وقتی می گیم: « Skinny Controllers, Fat Models » ، کنترلر های لاغر ، مدل های چاق ، منظور اینه که مدل فقط یک کلاس نیست بلکه یک لایه است. داشتن مدل خفن هم همیشه خوب نیست.

اجازه بدید باهم بررسی کنیم که چطوری کدهای خودمون در یک ساختار مناسب در لاراول توسعه بدیم.

چرا کنترلر لاغر؟

خب، وظیفه اصلی کنترلر چی هست؟
کنترلر ها تقاضا ها ( ریکوئست ها ) رو دریافت می کنند و پاسخ ( ریسپانس ) های مناسب رو بر میگردونند.

به طور کلی مسئولیت های زیر به عهده کنترلر هاست:

  • رسیدگی به یک درخواست و ایجاد پاسخ
  • فرم ها را ایجاد و مدیریت کند
  • دریافت و اتصال پارامترها به توابع
  • ست کردن و گرفتن آبجکت های یک جلسه ( session )
  • مدیریت تغییر مسیر ها

پس منطق های برنامه (business logic ) چی میشه؟ اینها باید در لایه مدل پیاده سازی بشه

کنترلر نباید کاری انجام بده بلکه باید کارها رو به کسی که میتونه انجام بده واگذار کنه.

اونها باید ساده ، لاغر ، کوتاه ، مختصر و با مسئولیت کمتری باشند.

اگر از این قانون پیروی کنیم ، از یکی از anti-pattern های تکراری جلوگیری می کنیم: کنترلر چاق.

توجه: شما میتونید حداکثر تعداد خطوطی رو که میخواهید در یک کنترلر داشته باشید رو برای خودتون تعریف کنید. به عنوان مثال ، 10 خط میتونه در بیشتر موارد کافی باشد.

و چرا داشتن کنترلرهای چاق بد است؟ اساساً به دو دلیل:

  • کدی که در کنترلر ها قرار میگیره نمیتونه دوباره مورد استفاده قرار بگیره.
  • انجام تست های واحدی ( Unit tests ) برای کنترلر ها خیلی مشکله.

البته لایه مدل میتونه از کلاسهای مختلفی تشکیل بشه و نه فقط یک کلاس.

مدل فقط یک کلاس نیست

باشه، الان کنترلر ما خیلی تمیزتر از قبل شده، اما یه مدل داریم با یه عالمه کد که نگهداریش مشکل شده.

ما باید مدل خودمون رو به یک ساختار تقسیم کنیم. این ساختار میتونه درون این لایه های کد باشه:

  • لایه FormRequest: داده ها را تایید میکند.
  • لایه Services: همه منطق تجاری برنامه را در بر میگیرد.
  • لایه Repositories: با پایگاه داده در تعامل است.
  • لایه Models:چیزهایی را که میخواهیم ارائه میدهد.
  • لایه Events: منطقی که به هنگام یک رویداد اجرا می شود را دربر میگیرد.
  • لایه Responsable: فرمت درخواست ها و پاسخ ها را مشخص می کند.
  • لایه View Composers تسک های ساده مورد نیاز بخش نمایش، مانند فرمت تاریخ و تبدیل واحد های پولی را در بر میگیرد.

لایه FormRequest

برای تأیید سناریوهای پیچیده تر ، می تونیم از FormRequest استفاده کنیم.

برای ایجاد کلاس FormRequest کافیه دستور زیر رو اجرا کنید.

php artisan make:request StoreBlogPost

کلاس ایجاد شده در مسیر app/Http/Requests در دسترس هست.
و در آخر میتونیم چند قانون رو به متد rules اضافه کنیم:

/** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]; }

حالا ببینیم تایید میشه یا نه:

// Retrieve the validated input data... $validated = $request->validated();

لایه Services

این لایه راهی برای متمرکز کردن کدها است. با استفاده از service provider میتونیم کدها را به کنترلر تزریق کنیم. ما میتونیم منطق های چاق خودمون رو ، که روی داده های ورودی اثر میگذارند ، اینجا بزاریم. همچنین ، میتونیم اونها رو با یکی از دو رویکرد زیر توسعه بدیم:

  • این سرویس ها ( service providers ) میتونن به عنوان: سرویس های عمومی ، کدها خام ساده و قدیمی PHP، کدهای جدا شده از لاراول، و یا انجام کارهایی که نتیجه رو به کنترلر برمیگردونه مورد استفاده واقع شوند.
  • مورد استفاده کنترلر ها جهت ذخیر دیتا با استفاده از مخازن ( repositories ) و سایر وابستگی هایی که می توانیم با لاراول داشته باشیم، واقع شوند.

یا اینکه: سرویسهایی هستند که همه منطق ها رو میتونه در بر بگیره.

اونها همه وابستگی هایی که تزریق شده اند رو دارند و وقتی اونها رو فراخوانی میکنیم همه عملیات ها اجرا خواهند شد. بیشتر از این چیزی نیاز نیست.

لایه Repositories

برای تعاملات با پایگاه داده ، می تونیم از الگوی طراحی Repository استفاده کنیم.

این ایده میگه که : مدل ها رو از کنترلر ها جدا کنید و نام های خوانا، برای کوئری ها پیچیده بزارید.

لایه Events

اگر نیاز به اجرای منطقی داشته باشیم که در نتیجه اجرای منطقی دیگه، لازم میشه و همچنین منطق اول وابسته به جواب منطق دوم نباشه از رویداد استفاده میکنیم.

و اگه بشه این منطق رو از چند جا فراخوانی کنیم، رویداد بهترین گزینه است.

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

در مثال بالا ثبت نام وابسته به نتیجه ارسال ایمیل نیست.

لایه Responsable

انتشار لاراول 5.5 ، Responsable Interface رو به همراه داشت که اجازه تبدیل آبجکت ها، در یک پاسخ (Response) به HTTP رو میده.

میتونید هر پارامتری رو به متد سازنده ( constructor ) پاس بدید و تمام منطق مورد نیاز خودتون رو در متد ()toResponse پیاده سازی کنید، این متد خودش بقیه کارها رو انجام میده. با این روش لایه مدل ما نیاز نیست نگران فرمت مورد نیاز کلاینت باشه. ما میتونیم این وظیفه رو به لایه Responsable واگذار کنیم.

همچنین در استفاده مجدد از این کد به ما کمک میکنه و ما میتونیم یک فرمت کلی و یکسان در خروجی داشته باشیم درحالی که هر عملیات میتونه از Responsable مربوط به خودش استفاده کنه.

لایه View Composers

یک وظیفه یا تسکی که در بخش نمایش داده ها (فرانت یا view) خیلی بهش نیاز داریم، قالب بندی داده هاست. مثل فرمت تاریخ یا تبدیل نرخ ارز و ...

با View Composers میتونید این کارها رو راحت کنید.

اینها callbacks یا کلاسهایی هستند که موقع نمایش view فراخوانی میشوند و این به ما کمک میکنه که منطق مورد نیاز در این بخش رو در مکانی واحد سازماندهی کنیم.

در نتیجه

آیا موافق نیستید که کنترلرها لاغر و مدل های چاق بسیار تمیزتر و ساده تر شدند؟ همچنین کد بیشتر قابل نگهداری و استفاده است؟

ما منطق تجاری کدهای خودمون رو در لایه مدل قرار دادیم و به این ترتیب از کدهای تکراری جلوگیری کردیم.

مطمئناً، همه مثالهایی که مطرح شد در همه ی موارد راه حل نیست، اما قاعده کلی اینه که:

اگر بتوان چیزی را از کنترلر حذف کرد، استفاده مجدد و تست راحت تر است.

ممنون که وقتتون رو برای مطالعه این مطلب صرف کردید.

اگه نکته و یا نظری دارید از شنیدنش خوشحال میشم.

سپاس


لاراولmvcphpکنترلرمدل
Software Engineer | Technical Lead | Engineering Manager
شاید از این پست‌ها خوشتان بیاید