آموزش لاراول - شخصی سازی کلاس‌ها با Macro

ماکرو (Macro) یکی از ویژگی‌های قدرتمند فریم‌ورک لاراول هست که انعطاف‌پذیری بیشتری برای گسترش کلاس‌های درونی به شما می‌ده. ماکروهای لاراول به شما این امکان رو میدن که قابلیت‌های سفارشی خودتون رو به اجزای داخلی لاراول اضافه کنید. در ادامه و در این آموزش نحوه کار با این خاصیت رو بررسی خواهیم کرد.


همانطور که گفتیم شما با این خاصیت می‌تونید قابلیت‌های خودتون رو به کلاس‌های داخلی لاراول اضافه کنید. منتهی پیش‌ از اون باید مطمئن باشید که اون کلاس خاصیت ماکرو رو داره یا اصطلاحا Macroable هست. برای این منظور کافیه اون کلاس مدنظرتون رو نگاه کنید و ببینید که آیا این قابلیت در کلاس وجود داره یا نه:

use Macroable;

در زیر می‌تونید لیستی از کلاس‌ها در لاراول که  خاصیت ماکرو دارند و شما می‌توانید قابلیت‌های خودتون رو به اونها اضافه کنید لیست شدند:

Illuminate\Auth\RequestGuard
Illuminate\Auth\SessionGuard
Illuminate\Cache\Repository
Illuminate\Console\Command
Illuminate\Console\Scheduling\Event
Illuminate\Cookie\CookieJar
Illuminate\Database\Eloquent\FactoryBuilder
Illuminate\Database\Eloquent\Relations\Relation
Illuminate\Database\Grammar
Illuminate\Database\Query\Builder
Illuminate\Database\Schema\Blueprint
Illuminate\Filesystem\Filesystem
Illuminate\Foundation\Testing\TestResponse
Illuminate\Http\JsonResponse
Illuminate\Http\RedirectResponse
Illuminate\Http\Request
Illuminate\Http\Response
Illuminate\Http\UploadedFile
Illuminate\Mail\Mailer
Illuminate\Routing\PendingResourceRegistration
Illuminate\Routing\Redirector
Illuminate\Routing\ResponseFactory
Illuminate\Routing\Route
Illuminate\Routing\Router
Illuminate\Routing\UrlGenerator
Illuminate\Support\Arr
Illuminate\Support\Collection
Illuminate\Support\LazyCollection
Illuminate\Support\Str
Illuminate\Support\Testing\Fakes\NotificationFake
Illuminate\Translation\Translator
Illuminate\Validation\Rule
Illuminate\View\Factory
Illuminate\View\View

در ادامه نحوه افزودن چند قابلیت به کلاس Illuminate\Support\Str و سفارشی کردن این کلاس رو آموزش خواهم داد. برای این منظور من ابتدا یک پروژه خام لاراول رو نصب کردم. اجازه بدید پیش از شروع کلاس Str رو باهم یک نگاه بکنیم همانطور که مشاهده می‌کنید این کلاس خاصیت Macroable رو داره:

خاصیت Macroable در کلاس Str
خاصیت Macroable در کلاس Str
 خاصیت Macroable در کلاس Str

خاصیت Macroable هم یکسری متدهایی رو داره که امکان تعریف قابلیت‌های جدید رو هر کلاسی مهیا می‌کنه! در مورد خاصیت‌ و trait همانطور که پیش‌تر هم گفتیم می‌تونید صفاتی رو که برای یک هدف معین لازم دارید در قالب یک trait چندین بار مورد استفاده قرار دهید.

بیایید کمی عمیق‌تر به این trait نگاهی بیاندازیم. اگر وارد مسیر ‍ Illuminate\Support\Traits\Macroable بشوید در این خاصیت دو تابع به نام macro و mixin وجود دارند که هر دو برای تعریف قابلیت جدید شما استفاده میشه. در macro شما دو آرگومان دارید که یکی نام قابلیت جدید و دیگری یک متد calback هست که در واقع می‌تونید اینجا اون قابلیت مورد نظرتون رو وارد می‌کنید و تابع بعدی  mixin هم همانطور که از توضیحاتش مشخصه امکان افزودن کلاسی که قابلیت جدید رو باهاش تعریف می‌کنید میده. بنظرتون این نوع تعریف آشنا نیست!!!!

اگر آموزشارو دنبال کرده باشید ما پیشتر چیزی شبیه به این نوع تعریف رو در View Composer مشاهده کردیم مقاله اون هم در زیر قرار دادم می‌تونید مطالعه کنید:

 

اما تابع دیگری که بیشتر می‌خوام به اون توجه کنید تابع callStatic__ هست در مورد نحوه عملکرد این تابع جالب که جزو خاصیت‌های خود PHP هست هم بطور کامل در آموزش Facade بررسی کردیم که  مقاله اون هم در زیر قرار دادم:

 

اگر به این تابع در این خاصیت Macroable توجه کنید:

Image for post
Image for post

خواهید دید که در ابتدا بررسی میشه که آیا تابع مورد نظر قبلا تعریف شده یا نه! یعنی اگر تابع سفارشی ما در آرایه macro$ وجود داشته آن رو به ما برمی‌گردونه درغیر اینصورت خطای تعریف نشدن تابع در کلاس رو دریافت خواهیم کرد. 

در ادامه اگر تابع ما از نوع Closure باشه scope اون رو به کلاس مد نظر ما متصل می‌کنه. دقت کنید که در اینجا static::class به کلاسی که خاصیت ماکرو داره اشاره می‌کنه. 

در نهایت هم ورودی‌هایی که در نظر گرفتیم رو به این تابع میده و خروجی تابع رو به ما برمی‌گردونه!

 

اجازه دهید قبل از ادامه مفهوم Closure مرور کنیم:

آشنایی با Closure 

این کلاس برای نمایش نوع توابع بی‌نام (Anonymous functions) استفاده میشه! بنابراین توابع بی‌نام از نوع Closure هستند. داخل فریم‌ورک لاراول از این توابع بسیار استفاده شده، شما پیش‌ از این با این نوع توابع کار کردید برای مثال داخل  Route‌ها:

Route::get('/macro', function () {
    dd(Str::currency('10000000') );
});

یکی از مشخصه‌های این توابع استفاده از use هست که امکان دسترسی به متغیرهای خارج از تابع رو هم میده! از طرفی هم عموما برای صدا زدن این توابع یکبار آنها را به یک متغیر پاس میدن و بعد اون متغیر رو صدا میزنن:

$greeting = function () {
  return "Hello world";
}

$greeting();

اما همانطور که در خاصیت Macroable هم مشاهده کردید Closureها قابلیت‌های دیگری هم دارند که یکی از آنها  bindTo هست، به مثال زیر توجه کنید:

<?php

$myClosure = function() {
    echo $this->property;
};

class MyClass
{
    public $property = 'Hello world!';
}

$myInstance = new MyClass();
$myBoundClosure = $myClosure->bindTo($myInstance);
$myBoundClosure();

>> "Hello world!"

همانطور که در داک php هم مشاهده می‌کنید اولین آرگومان این تابع به یک شی اشاره می‌کنه و به موجب اون کلمه کلیدی که داخل تابع بی‌نام تعریف شده معنی پیدا می‌کنه. همچنین در مثال فوق اگر متغیر از نوع protected یا private می‌بود آنوقت تابع بی‌نام ما دسترسی نمی‌داشت و شما خطای عدم دسترسی به متغیر رو دریافت می‌کردید. در اینصورت پای آرگومان دوم این تابع میاد وسط که با تعریف کلاس در آنجا فضای کاری یا scope تابع رو هم در کلاس تعریف می‌کردید:

<?php

$myClosure = function() {
    echo $this->property;
};

class MyClass
{
    private $property = 'Hello world!';
}

$myInstance = new MyClass();
$myBoundClosure = $myClosure->bindTo($myInstance, MyClass::class);
$myBoundClosure();

>> "Hello world!"

من برای اینکه متوجه تاثیر این تابع در کار خودمون بشیم به خاصیت Macroable رفتم و خطی که مربوط به bind کردن رو تست کردم. همانطور که مشاهده می‌کنید اگر این خط حذف بشه تابع بی‌نام ما به کلاس MyStrMixins اشاره می‌کنه:

Image for post
Image for post

در حالیکه ما می‌خواستیم کلاس Str رو توسعه بدیم بنابراین توابع جدید باید محدود به کلاس Str باشند. در نتیجه تاثیر عملکرد bindTo بصورت زیر باعث میشه تا توابع جدید به کلاس مورد نظر ارجاع داد بشن:

Image for post
Image for post

 

 

خب حالا بیایید به AppServiceProvider برگردیم و در متد boot یک تابع سفارشی به کلاس Str اضافه کنیم برای اینکار همانطور که گفتیم دو متد وجود داره. اجازه بدید ابتدا با متد macro اینکارو انجام بدیم:

Image for post
Image for post

همانطور که مشاهده می‌کنید من در اینجا یک تابع با نام currency به کلاس Str اضافه کردم که با کمک regex نمایش پولی ایجاد می‌کنه:

Str::currency('10000000');

>> "10,000,000"

به همین راحتی ما کلاس Str از لاراول رو گسترش دادیم. به همین صورت می‌تونید برای کلاس‌های دیگری که Macroable هستند و لیست آنها رو در بالا آوردیم این کار رو انجام بدید. اما اگر توابع ما زیاد باشند وارد کردن همه اینها اینجا مناسب نیست لذا می‌توان از تابع دیگر mixin که به آن اشاره شد استفاده کنیم. برای این منظور ابتدا کلاسی بصورت زیر تعریف می‌کنیم:

Image for post
Image for post

همانطور که گفته شد از آنجایی که در خاصیت Macro هر تابع بصورت بی‌نام هست بنابراین در این کلاس ما یک تابع رو بازمی‌گردونیم. ضمنا توجه کنید که اگر بخواهیم آرگومان‌های دیگری هم تعریف کنیم داخل تابع بی‌نام این کار انجام میشه! 

در ادامه کافیه که به متد boot در AppServiceProvider برگردیم و با کمک mixin کلاس Str رو با توابع خودمون شخصی‌سازی کنیم:

Image for post
Image for post

حالا اگر بار دیگه توابع خودمون رو صدا بزنید می‌بینیم که به درستی کار خواهند کرد:

Str::currency('10000000');
>> "10,000,000"

Str::currency_prefix('10000000');
>> "10,000,000 ریال"

Str::currency_prefix('10000000', 'toman');
>> "10,000,000 toman"

خب تبریک می‌گم شما در این آموزش یاد گرفتید که چطور میشه کلاس‌های درونی لاراول رو هم تغییر بدید و برای کار خودتون اون رو شخصی‌سازی کنید. یکی از کاربردهای این ابزار که خود من معمولا از اون استفاده می‌کنم بخش response است که می‌تونید خروجی‌هارو متناسب با نیازتون ویرایش کنید.


امیدوارم از این آموزش هم لذت برده باشید!

کدهای مربوط به این آموزش در گیت‌هاب رو می‌تونید از اینجا مشاهده کنید: