تکمیل کلاس App


در آموزش ساخت کلاس App به نوشتن بخش ابتدایی این کلاس پرداختیم به طوری که تا این مرحله به صورت زیر است:

<?php
namespace Core;

class App
{
    private $controller;
    private $action;
    private $params = [];

    private function parseUrl()
    {
        $request = trim($_SERVER['REQUEST_URI'], '/');
        $request = filter_var($request, FILTER_SANITIZE_URL);
        $request = explode('/', $request);
        return $request;
    }
}

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

<?php
namespace Core;

use Base\Controllers\DefaultController;

class App
{
    private $controller;
    private $action;
    private $params = [];

    private function parseUrl()
    {
        $request = trim($_SERVER['REQUEST_URI'], '/');
        $request = filter_var($request, FILTER_SANITIZE_URL);
        $request = explode('/', $request);
        return $request;
    }

    public function __construct()
    {
        $url = $this->parseUrl();
        $routing = new Routing;

        if ($routing->routes) {
            $this->controller = new DefaultController;
            $this->action = 'homepage';
            $controllerPlusAction = (isset($url[0]) ? $url[0] : '') . (isset($url[1]) ? '/' . $url[1] : '');
            foreach ($routing->routes as $route) {
                if ($route['route'] == $controllerPlusAction) {
                    if (file_exists('../app/' . $route['module'] . '/Controllers/' . $route['controller'] . '.php')) {
                        $dynamicControllerName = "\\" . $route['module'] . "\Controllers\\" . $route['controller'];
                        $this->controller = new $dynamicControllerName;
                        $this->action = $route['action'];
                        unset($url[0], $url[1]);
                        break;
                    } else {
                        $this->action = 'homepage';
                    }
                } else {
                    $this->action = 'notfound';
                }
            }
        }

        $this->params = $url ? array_values($url) : [];
        call_user_func_array([$this->controller, $this->action], $this->params);
    }
}

نیاز به توضیح نیست که به منظور ساخت کانستراکتور در زبان پی‌اچ‌پی می‌باید از ساختار ()construct__ استفاده نماییم. پیش از هر چیز، در بدنهٔ این کانستراکتور متغیری تحت عنوان url$ ساخته و تابع ()parseUrl که پیش این نوشته بودیم را به آن منتسب می‌کنیم و در ادامه متغیر دیگری به نام routing$ ساخته و مقدار آن را برابر با نمونه‌ای از روی کلاس Routing قرار می‌دهیم سپس با استفاده از یک دستور شرطی چک کرده‌ایم ببینیم که آیا آرایهٔ routes$ که داخل کلاس Routing قرار داد حاوی مقداری است یا خیر که اگر اینچنین بود بلافاصله پراپرتی‌های controller$ و action$ را به ترتیب با ساخت یک آبجکت از روی کلاس DefaultController و همچنین استرینگ homepage مقداردهی می‌کنیم.

در خط بیست‌وهشتم متغیری به نام controllerPlusAction$ می‌سازیم که قرار است تا دربرگیرندهٔ نام کنترلر + یک اَکشن باشد. برای همین منظور، با استفاده از تابع ()isset چک کرده‌ایم ببینیم که آیا خانهٔ صِفرم آرایهٔ url$ حاوی مقداری است یا خیر به طوری که اگر پُر بود آن را چاپ می‌کنیم و در غیر این صورت مقدار خالی '' را برایش در نظر می‌گیریم (لازم به یادآوری است که خانهٔ صفر متعلق به کنترلر و خانه یک متعلق به اَکشن است.) سپس یک علامت . قرار داده و همین روش را برای خانهٔ یکم این آرایه چک می‌کنیم به طوری که اگر پُر بود ابتدا یک علامت / چاپ کرده سپس خانهٔ یکم آرایهٔ url$ را چاپ می‌کنیم و در غیر این صورت مقدار خالی '' را در نظر می‌گیریم.

سپس آرایهٔ routes$ را با استفاده یک حلقه به اِلِمان‌های تشکیل‌دهنده‌اش تفکیک کرده و داخل بدنهٔ foreach با استفاده از یک دستور شرطی چک می‌کنیم ببینیم که آیا در کلید route در اِلِمان‌های آرایهٔ routes$ مقداری برابر با مقدار در نظر گرفته‌ شده برای controllerPlusAction$ وجود دارد یا خیر که اگر چنین مقداری وجود نداشت وارد دستور else مرتبط با این دستور شرطی شده و مقدار استرینگ notfound را برای پراپرتی action$ در نظر می‌گیریم که مرتبط با ویوی notfound.php به منظور نمایش صفحهٔ ۴۰۴ خواهد بود اما اگر چنین مقداری وجود داشت، بلافاصله با استفاده از یک دستور شرطی دیگر و همچنین تابع ()file_exsits چک می‌کنیم ببینیم که آیا در ماژول مربوطه کنترلی هم‌نام با کنترلری که در کلید controller متغیر route$ درج شده وجود دارد یا خیر که اگر نتیجه false بود وارد دستور else شده و استرینگ homepage به پراپرتی action$ اختصاص خواهد یافت و اگر نتیجه true بود، متغیری به صورت زیر می‌سازیم:

$dynamicControllerName = "\\" . $route['module'] . "\Controllers\\" . $route['controller'];

پیش از توضیح پیرامون مقدار در نظر گرفته شده برای این متغیر، فرض کنیم که در لینک http://mvc.local/default/users قرار داریم. مقدار اختصاص‌یافته به متغیر فوق برای چنین یوآرالی برابر با استرینگ Base\Controllers\DefaultController\ خواهد بود. به نظر می‌رسد حال که می‌دانیم خروجی به چه شکل باید باشد، می‌توانیم به شکل بهتری دست به تفسیر مقدار اختصاص‌یافته برای متغیر dynamicControllerName$ بزنیم.

در واقع، در ابتدای رشته دو علامت \\ قرار داده‌ایم که علامت \ اول به منظور اصطلاحاً Escape کردن علامت \ دوم به کار رفته است و برای همین منظور از دو علامت به صورت "\\" استفاده می‌کنیم به طوری که علامت \ اول منجر به بی‌تأثیر شدن علامت \ گردیده و صرفاً آن را چاپ می‌کند. سپس مقدار در نظر گرفته شده برای کلید module در متغیر route$ را هدف قرار گرفته‌ایم و در ادامه مجدد یک . قرار داده و استرینگ \\controllers\ را نوشته‌ایم که مجدداً علامت \ اول به منظور اِسکِیپ کردن علامت \ دوم به کار رفته است و در نهایت یک . قرار داده و مقدار در نظر گرفته شده برای کلید controller در آرایهٔ route$ را درج کرده‌ایم.

در این مرحله از کار، توانسته‌ایم به صورت دینامیک (پویا) اقدام به یافتن نِیم‌اِسپیسی که کنترلر در آن قرار دارد نماییم و در ادامه می‌توانیم به سادگی با استفاده از کیورد new نمونه‌ای از روی این کنترلر ساخته و آن را به پراپرتی controller$ اختصاص داده سپس مقدار موجود در کلید action در آرایهٔ route$ را نیز به پراپرتی action$ منتسب می‌کنیم.

همچنین می‌بینیم که در خط سی‌وپنجم با استفاده از متد ()unset اِلِمان‌های صفرم و یکم آرایهٔ url$ را خالی کرده‌ایم و این بدین دلیل است که چنانچه هر گونه پارامتری به عنوان اِلِمان سوم سِت شده بود، به سادگی در خط چهل‌وششم به پراپرتی params$ اختصاص یابد.

آنچه در اینجا بسیار حائز اهمیت می‌باشد، استفادهٔ صحیح از کیورد break است. در واقع، کاری که این دستور داخل حلقه‌ها انجا می‌دهد آن است که چنانچه شرطی خاص برآورده شود، می‌توان در انتهای شرط مذکور دستور break را نوشته تا دیگر پروسهٔ چرخیدن داخل آرایهٔ ورودی برای حلقهٔ foreach خاتمه یابد و در ارتباط با اسکریپت فوق، این دقیقاً همان چیزی است که به آن نیاز داریم؛‌ به عبارتی،‌ نیاز داریم تا به محض آنکه کنترلری در یوآرالی که توسط کاربر وارد شده با یکی از مقادیر موجود در کلید controller در آرایهٔ routes$ دقیقاً‌ یکسان بود، این حلقه دیگر ادامه نیابد.

این بخش از محتوا مخصوص کاربرانی است که ثبت‌نام کرده‌اند.
جهت مشاهدهٔ این بخش از محتوا لاگین نمایید.

در این آموزش علاوه بر تکمیل کانستراکتور کلاس App، با کاربرد دستور ()call_user_func_array در زبان پی‌اچ‌پی آشنا شدیم به طوری که این امکان را در اختیار توسعه‌دهندگان این زبان برنامه‌نویسی می‌گذارد تا بتوانند اقدام به فراخوانی متدهای مختلف یک آبجکت کرده و به صورت کاملاً دینامیک مقادیر مد نظر خود را به آن‌‌ها پاس دهند.


لیست نظرات
کاربر میهمان
دیدگاه شما چیست؟
کاربر میهمان
مجتبی دانشی
مجتبی دانشی
۱۳۹۸/۰۳/۰۶
به نام خدا
با سلام
ممنون از مطالب خوبتون، به نظرم یه مشکلی وجود داره، خط یکی مونده به آخر، پارامتر یک آرایه هست، به اکشن about یک آرایه پاس داده میشه. این آرایه در قسمت کنترلر به ایندکس param اساین میشه، حالا این دیتا در قسمت ویو چاپ شده، در صورتی که آرایه رو چاپ کردین.
من کد رو تست نکردم ولی به نظر میاد با ارسال پارامتر به اکشن about مشکلی پیش میاد.
با تشکر