الگوی طراحی Facade در لاراول


ءFacade به معنی نما میباشد، دلیل استفاده از این الگوی طراحی به خاطر پنهان کردن بیشتر منطق نرم افزار و ایجاد handler هایی میباشد که کاربر میتواند با استفاده از آنها به نتیجه اولیه دست پیدا کند ، facade  به عنوان یک در بر گیرنده یا یک wrapper کار میکند، که همه محتوا را در درون خود مخفی و متدهایی را بر اساس logic مخفی شده به کاربر نشان میدهد .


class LetterProcess{

public function sendLetter(){

        $inputs = $this->request->except([&quot_token&quot]);

        // check if letter going out
        if ($this->isGoingOut($inputs[&quotsend_type&quot])) {

            if ($this->hasPermission($this->auth->id, $inputs[&quotsend_type&quot]))
                $this->proccessLetter($inputs);

            else
                $this->sendLetterToManagerForAccepting($inputs);

        } else {

            if( $this->sendLetterToAnotherSection($inputs[&quotto_id&quot]) ){

                if ($this->hasPermission($this->auth->id, $inputs[&quotsend_type&quot]))
                    $this->proccessLetter($inputs);
                else
                    $this->sendLetterToManagerForAccepting($inputs);
            } else
                  $this->proccessLetter($inputs);
        }
    }
}


این تیکه کد بخشی از ارسال نامه اداری میباشد و به این شکل همیشه ساده نیست ولی من جهت درک بیشتر مطلب ساده نوشتمش . خوب شما فرض کنید که عملیات دیگه ایی هم باید انجام بدیم مثل ایجاد step و ایجاد notification و ارسال ایمیل و ارجاع و کارهای دیگه ، خوب اگه بخوایم از facade استفاده کنیم . خیلی راحت میتونیم به صورت زیر عمل کنیم .

class sendLetterFacade{

    public function process(){
        $letter = new LetterProcces();
        $letter->sendLetter();
    }
}

همانطور که از کد بالا میتونید ببینید، کلاس facade به عنوان یک wrapper میباشد که فقط یک متد به نام process دارد، این متد تمامی کارای که باید انجام بشه رو انجام میده، ولی در صورتی که به متدهای بیشتری جهت انجام کار نیاز داشته باشیم ، میتونیم متدهای دیگری را ایجاد کنیم ، که البته باید معنی این متد به کاری که میخواد انجام بشه نزدیک باشه و همچنین اینرو بهم باید یادمون باشه که نباید کلاس facade رو با متدهای زیادی پیچیده اش کنیم چون دلیل ایجاد کلاس پنهان کردن logic است که در کلاسهای قبلی استفاده شده است.


facade کلاس در لاراول


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

ما فرض میکنیم که از یک facade کلاس به نام Route در لاراول استفاده میکنیم .

Route::get(&quotpost&quot, &quotPostController@getIndex&quot)->name(&quotpost.index&quot);

خوب اگه این کلاس رو پیدا کنیم .

class Route extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'router';
    }
}

همانطور که از کد بالا میشه فهمید فقط یک کلاس که یدونه متد هم بیشتر نداره که اسمش هم هست getFacadeAccessor در کلاس Route وجود دارد، خوب پس کد اصلی این کلاس کجاست؟ همه متدها کجان؟ Route::get و Route::post چطوری صدا زده میشه . خوب کلاس Route از کلاس Facade ارث بری انجام میدهد، بزارید یه نگاهی به کلاس Facade بندازیم .

abstract class Facade
{

    /**
     * Handle dynamic, static calls to the object.
     *
     * @param  string  $method
     * @param  array   $args
     * @return mixed
     *
     * @throws \RuntimeException
     */
    public static function __callStatic($method, $args)
    {
        $instance = static::getFacadeRoot();

        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
        }

        return $instance->$method(...$args);
    }
}

خوب در این کلاس لاراول از این magic متد جهت صدا زدن اون متد get از کلاس Route استفاده می کند. بعد از اجرا شدن این متد در اولین خط متد getFacadeRoot صدا زده می شود.

/**
     * Get the root object behind the facade.
     *
     * @return mixed
     */
    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }

همانطور که از کد بالا مشخص است این متد بعد صدا زده شدن یک کار را انجام می دهد که اون هم صدا زدن متد دیگری به نام ResolveFacadeInstance میباشد. اگه به آرگومان ورودی این متد توجه کنید، از متد getFacadeAccessor استفاده شده است که این متد نام کلاس Router را بر می گرداند، چند خط بالا به آن اشاره شد . پس نام کلاس Router به متد resolveFacadeInstance ارسال میشود.

/**
     * Resolve the facade root instance from the container.
     *
     * @param  string|object  $name
     * @return mixed
     */
    protected static function resolveFacadeInstance($name)
    {
        if (is_object($name)) {
            return $name;
        }

        if (isset(static::$resolvedInstance[$name])) {
            return static::$resolvedInstance[$name];
        }

        return static::$resolvedInstance[$name] = static::$app[$name];
    }

خوب همانطور که از کد بالا مشخص است ، بعد از ارسال نام کلاس که Router میباشد ، چک میشود که آیا نام مورد نظر Object است یا خیر ، سپس چک می شود که آیا در خصوصیت resolvedIsntances وجود دارد یا خیر در صورت وجود نداشتن آن ، در ابتدا نام این Router را از Container بازگشایی میکند و سپس  در خصوصیت resolvedInstance ذخیره می شود .

قبل از اجرای متد facade ، کلاس Application  اجرا میشود ، و عملیاتی مثل bootstrap کردن و همچنین register کردن را شروع میکند، در پوشه config/app یک آرایه به نام aliases وجود دارد که از key=>value ایجاد شده است، این آرایه بعد از register شدن به ما قابلیت دسترسی به تمامی کلاسها درون خود را میدهد. بر فرض مثال ما یک کلاس به نام Route داریم که در صورت صدا زدن نام این کلاس ، این کلاس اشاره میکند به آدرس facades\route ، لاراول با استفاده از کلاس aliasLoader این نامها را register میکند.

'Route' => Illuminate\Support\Facades\Route::class,

بعد از register کردن alias ها ، سپس لاراول کلاسهای داخلی خود را ثبت می کنند ، این کلاسها در کلاس application و در متد   registerCoreContainerAliases میباشد . ما در متد getFacadeAccessor ، کلاسی به نام Router را برگرداندیم.

'router' => ['Illuminate\Routing\Router', 'Illuminate\Contracts\Routing\Registrar'],

در صورت نیاز به این کلاس لاراول خصوصیتی به نام Aliases در کلاس application  را بررسی می کند و سپس concrete کلاس را resolve  میکند . که همون Illuminate\Routing\Router . اون aliases که در فایل config/app وجود داشت را با این aliases اشتباه نگیرید، اندیس aliases که در فایل config/app وجود داشت جهت register کردن facade کلاس استفاده می شود ولی خصوصیت aliases جهت resolve کلاسهایی می باشد که لاراول آنها را در container خود ثبت کرده است .

برگردیم به کلاس Facade

abstract class Facade
{

    /**
     * Handle dynamic, static calls to the object.
     *
     * @param  string  $method
     * @param  array   $args
     * @return mixed
     *
     * @throws \RuntimeException
     */
    public static function __callStatic($method, $args)
    {
        $instance = static::getFacadeRoot();

        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
        }

        return $instance->$method(...$args);
    }
}

خوب بعد از اینکه متد getFacadeRoot صدا زده شد نتیجه کد پایین به این متد برگشت داده می شود که حاوی کلاس Router میباشد.

return static::$resolvedInstance[$name] = static::$app[$name];

سپس با استفاده از متغیر instance میتوان به کلاس Router و متدهای این کلاس دسترسی پیدا کرد که نتیجه را با return به محلی که Route::get صدا زده شد برگشت داده می شود.

منتظر نوشته های جدید باشید ...