Phalcon 3 - Dispatching Controllers

ارسال وحدات تحكم




phalcon

Phalcon\Mvc\Dispatcher هو العنصر المسؤول عن إنشاء وحدات تحكم فورية وتنفيذ الإجراءات المطلوبة عليها في تطبيق MVC. يساعدنا فهم عملياتها وقدراتها في الحصول على المزيد من الخدمات التي يوفرها الإطار.

حلقة الإرسال

هذه عملية مهمة لها علاقة كبيرة بتدفق MVC نفسه ، خاصة مع جزء جهاز التحكم. يحدث العمل داخل المرسل جهاز التحكم. تتم قراءة ملفات وحدة التحكم وتحميلها وإنشاء مثيل. ثم يتم تنفيذ الإجراءات المطلوبة. إذا قام أحد الإجراءات بإعادة توجيه التدفق إلى وحدة تحكم / إجراء آخر ، فسيبدأ مرسل وحدة التحكم مرة أخرى. لتوضيح هذا بشكل أفضل ، يوضح المثال التالي العملية التي يتم تنفيذها داخل Phalcon\Mvc\Dispatcher :

// Dispatch loop
while (!$finished) {
    $finished = true;

    $controllerClass = $controllerName . "Controller";

    // Instantiating the controller class via autoloaders
    $controller = new $controllerClass();

    // Execute the action
    call_user_func_array(
        [
            $controller,
            $actionName . "Action"
        ],
        $params
    );

    // '$finished' should be reloaded to check if the flow was forwarded to another controller
    $finished = true;
}

يفتقر الكود السابق إلى عمليات التحقق من الصحة والفلاتر والشيكات الإضافية ، ولكنه يوضح التدفق الطبيعي للعملية في المرسل.

إيفاد حلقة الأحداث

يستطيع EventsManager Phalcon\Mvc\Dispatcher إرسال الأحداث إلى EventsManager إذا كان موجودًا. يتم تشغيل الأحداث باستخدام نوع "إرسال". قد تؤدي بعض الأحداث التي تحدث عند إرجاع الكلمة المنطقية الباطلة إلى إيقاف العملية النشطة. يتم دعم الأحداث التالية:

يوضح البرنامج التعليمي INVO كيفية الاستفادة من إرسال أحداث تنفيذ عامل تصفية أمان مع Acl

يوضح المثال التالي كيفية إرفاق المستمعين بهذا المكون:

use Phalcon\Mvc\Dispatcher as MvcDispatcher;
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;

$di->set(
    "dispatcher",
    function () {
        // Create an event manager
        $eventsManager = new EventsManager();

        // Attach a listener for type "dispatch"
        $eventsManager->attach(
            "dispatch",
            function (Event $event, $dispatcher) {
                // ...
            }
        );

        $dispatcher = new MvcDispatcher();

        // Bind the eventsManager to the view component
        $dispatcher->setEventsManager($eventsManager);

        return $dispatcher;
    },
    true
);

تعمل وحدة التحكم التي تم استنساخها تلقائيًا كمستمع لأحداث الإرسال ، بحيث يمكنك تنفيذ الطرق كردات رد اتصال:

use Phalcon\Mvc\Controller;
use Phalcon\Mvc\Dispatcher;

class PostsController extends Controller
{
    public function beforeExecuteRoute(Dispatcher $dispatcher)
    {
        // Executed before every found action
    }

    public function afterExecuteRoute(Dispatcher $dispatcher)
    {
        // Executed after every found action
    }
}

ملحوظة

أساليب على المستمعين الحدث قبول كائن Phalcon\Events\Event كمعلمة الأول الخاصة بهم - لا الطرق في وحدات التحكم.

إعادة توجيه إلى إجراءات أخرى

تسمح حلقة الإرسال لنا بإعادة توجيه تدفق التنفيذ إلى وحدة تحكم / إجراء آخر. هذا مفيد جدًا للتحقق مما إذا كان المستخدم يمكنه الوصول إلى خيارات معينة أو إعادة توجيه المستخدمين إلى شاشات أخرى أو ببساطة إعادة استخدام الرمز.

use Phalcon\Mvc\Controller;

class PostsController extends Controller
{
    public function indexAction()
    {

    }

    public function saveAction($year, $postTitle)
    {
        // ... Store some product and forward the user

        // Forward flow to the index action
        $this->dispatcher->forward(
            [
                "controller" => "posts",
                "action"     => "index",
            ]
        );
    }
}

ضع في اعتبارك أن إنشاء "إعادة توجيه" لا يعني إجراء عملية إعادة توجيه HTTP. على الرغم من أنها حصلت على النتيجة نفسها. لا يؤدي "إعادة التوجيه" إلى إعادة تحميل الصفحة الحالية ، حيث يحدث كل إعادة التوجيه في طلب واحد ، بينما تحتاج عملية إعادة توجيه HTTP إلى طلبين لإكمال العملية.

المزيد من أمثلة إعادة التوجيه:

// Forward flow to another action in the current controller
$this->dispatcher->forward(
    [
        "action" => "search"
    ]
);

// Forward flow to another action in the current controller
// passing parameters
$this->dispatcher->forward(
    [
        "action" => "search",
        "params" => [1, 2, 3]
    ]
);

يقبل إجراء إعادة التوجيه المعاملات التالية:

معامل أثار
مراقب اسم وحدة تحكم صالحة لإعادة توجيهها.
عمل اسم إجراء صالح ليتم توجيهه إلى.
بارامس مجموعة من المعلمات للإجراء
مساحة الاسم اسم مساحة اسم مميز حيث يكون جهاز التحكم جزءًا منه

إعداد المعلمات

بفضل نقاط الخطافات التي تقدمها Phalcon\Mvc\Dispatcher يمكنك بسهولة تكييف التطبيق الخاص بك إلى أي مخطط URL:

على سبيل المثال ، تريد أن تبدو عناوين URL الخاصة بك: http://example.com/controller/key1/value1/key2/value

يتم تمرير المعلمات افتراضيًا عندما تأتي في URL إلى إجراءات ، يمكنك تحويلها إلى المخطط المطلوب:

use Phalcon\Dispatcher;
use Phalcon\Mvc\Dispatcher as MvcDispatcher;
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;

$di->set(
    "dispatcher",
    function () {
        // Create an EventsManager
        $eventsManager = new EventsManager();

        // Attach a listener
        $eventsManager->attach(
            "dispatch:beforeDispatchLoop",
            function (Event $event, $dispatcher) {
                $params = $dispatcher->getParams();

                $keyParams = [];

                // Use odd parameters as keys and even as values
                foreach ($params as $i => $value) {
                    if ($i & 1) {
                        // Previous param
                        $key = $params[$i - 1];

                        $keyParams[$key] = $value;
                    }
                }

                // Override parameters
                $dispatcher->setParams($keyParams);
            }
        );

        $dispatcher = new MvcDispatcher();

        $dispatcher->setEventsManager($eventsManager);

        return $dispatcher;
    }
);

إذا كان المخطط المطلوب هو: http://example.com/controller/key1:value1/key2:value ، فإن الشفرة التالية مطلوبة:

use Phalcon\Dispatcher;
use Phalcon\Mvc\Dispatcher as MvcDispatcher;
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;

$di->set(
    "dispatcher",
    function () {
        // Create an EventsManager
        $eventsManager = new EventsManager();

        // Attach a listener
        $eventsManager->attach(
            "dispatch:beforeDispatchLoop",
            function (Event $event, $dispatcher) {
                $params = $dispatcher->getParams();

                $keyParams = [];

                // Explode each parameter as key,value pairs
                foreach ($params as $number => $value) {
                    $parts = explode(":", $value);

                    $keyParams[$parts[0]] = $parts[1];
                }

                // Override parameters
                $dispatcher->setParams($keyParams);
            }
        );

        $dispatcher = new MvcDispatcher();

        $dispatcher->setEventsManager($eventsManager);

        return $dispatcher;
    }
);

الحصول على المعلمات

عندما يوفر المسار معلمات مسماة ، يمكنك استلامها في جهاز تحكم أو عرض أو أي مكون آخر يعمل على Phalcon\Di\Injectable .

use Phalcon\Mvc\Controller;

class PostsController extends Controller
{
    public function indexAction()
    {

    }

    public function saveAction()
    {
        // Get the post's title passed in the URL as parameter
        // or prepared in an event
        $title = $this->dispatcher->getParam("title");

        // Get the post's year passed in the URL as parameter
        // or prepared in an event also filtering it
        $year = $this->dispatcher->getParam("year", "int");

        // ...
    }
}

اعداد الاجراءات

يمكنك أيضًا تحديد مخطط تعسفي للإجراءات قبل إرسالها.

كاميليزي اسماء العمل

إذا كان عنوان URL الأصلي هو: http://example.com/admin/products/show-latest-products ، وعلى سبيل المثال ، ترغب في إظهار "عرض أحدث المنتجات" بـ "ShowLatestProducts" ، فإن الشفرة التالية مطلوبة:

use Phalcon\Text;
use Phalcon\Mvc\Dispatcher as MvcDispatcher;
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;

$di->set(
    "dispatcher",
    function () {
        // Create an EventsManager
        $eventsManager = new EventsManager();

        // Camelize actions
        $eventsManager->attach(
            "dispatch:beforeDispatchLoop",
            function (Event $event, $dispatcher) {
                $dispatcher->setActionName(
                    Text::camelize($dispatcher->getActionName())
                );
            }
        );

        $dispatcher = new MvcDispatcher();

        $dispatcher->setEventsManager($eventsManager);

        return $dispatcher;
    }
);

إزالة الإضافات القديمة

إذا كان عنوان URL الأصلي يحتوي دائمًا على ملحق ".php":

http://example.com/admin/products/show-latest-products.php http://example.com/admin/products/index.php

يمكنك إزالته قبل إرسال مجموعة وحدة التحكم / الإجراء:

use Phalcon\Mvc\Dispatcher as MvcDispatcher;
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;

$di->set(
    "dispatcher",
    function () {
        // Create an EventsManager
        $eventsManager = new EventsManager();

        // Remove extension before dispatch
        $eventsManager->attach(
            "dispatch:beforeDispatchLoop",
            function (Event $event, $dispatcher) {
                $action = $dispatcher->getActionName();

                // Remove extension
                $action = preg_replace("/\.php$/", "", $action);

                // Override action
                $dispatcher->setActionName($action);
            }
        );

        $dispatcher = new MvcDispatcher();

        $dispatcher->setEventsManager($eventsManager);

        return $dispatcher;
    }
);

حقن نماذج النموذج

في هذا المثال ، يريد المطور فحص المعلمات التي سيتلقاها إجراء من أجل إدخال أمثلة نموذجية بشكل ديناميكي.

تحكم يشبه:

use Phalcon\Mvc\Controller;

class PostsController extends Controller
{
    /**
     * Shows posts
     *
     * @param \Posts $post
     */
    public function showAction(Posts $post)
    {
        $this->view->post = $post;
    }
}

يتلقى الأسلوب 'showAction' مثيلًا لنماذج المشاركات ، يمكن لمطوّر البرامج فحص ذلك قبل إرسال الإجراء الذي يُعد المعلمة وفقًا لذلك:

use Exception;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Dispatcher as MvcDispatcher;
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;
use ReflectionMethod;

$di->set(
    "dispatcher",
    function () {
        // Create an EventsManager
        $eventsManager = new EventsManager();

        $eventsManager->attach(
            "dispatch:beforeDispatchLoop",
            function (Event $event, $dispatcher) {
                // Possible controller class name
                $controllerName = $dispatcher->getControllerClass();

                // Possible method name
                $actionName = $dispatcher->getActiveMethod();

                try {
                    // Get the reflection for the method to be executed
                    $reflection = new ReflectionMethod($controllerName, $actionName);

                    $parameters = $reflection->getParameters();

                    // Check parameters
                    foreach ($parameters as $parameter) {
                        // Get the expected model name
                        $className = $parameter->getClass()->name;

                        // Check if the parameter expects a model instance
                        if (is_subclass_of($className, Model::class)) {
                            $model = $className::findFirstById($dispatcher->getParams()[0]);

                            // Override the parameters by the model instance
                            $dispatcher->setParams([$model]);
                        }
                    }
                } catch (Exception $e) {
                    // An exception has occurred, maybe the class or action does not exist?
                }
            }
        );

        $dispatcher = new MvcDispatcher();

        $dispatcher->setEventsManager($eventsManager);

        return $dispatcher;
    }
);

تم تبسيط المثال السابق للأغراض الأكاديمية. يمكن لمطوّر البرامج تحسينه لحقن أي نوع من التبعية أو النموذج في الإجراءات قبل تنفيذها.

من 3.1.x فصاعدا يأتي أيضا المرسل مع خيار للتعامل مع هذا داخليا لجميع الموديلات التي تم تمريرها في إجراء وحدة تحكم باستخدام Phalcon\Mvc\Model\Binder .

use Phalcon\Mvc\Dispatcher;
use Phalcon\Mvc\Model\Binder;

$dispatcher = new Dispatcher();

$dispatcher->setModelBinder(new Binder());

return $dispatcher;
نظرًا لاستخدام كائن Binder داخليًا Reflection Api والذي يمكن أن يكون ثقيلًا ، توجد إمكانية لتعيين ذاكرة التخزين المؤقت. يمكن القيام بذلك باستخدام الوسيطة الثانية في setModelBinder() والتي يمكن أيضاً قبول اسم الخدمة أو فقط بتمرير مثيل التخزين المؤقت إلى مُنشئ Binder .

كما يقدم واجهة جديدة Phalcon\Mvc\Model\Binder\BindableInterface والتي تسمح لك بتحديد نماذج التحكم المرتبطة للسماح لربط النماذج في وحدات تحكم القاعدة.

على سبيل المثال ، لديك CrudController الأساسي الذي يمتد من PostsController الخاص بك. يبدو CrudController الخاص بك شيء من هذا القبيل:

use Phalcon\Mvc\Controller;
use Phalcon\Mvc\Model;

class CrudController extends Controller
{
    /**
     * Show action
     *
     * @param Model $model
     */
    public function showAction(Model $model)
    {
        $this->view->model = $model;
    }
}

في "PostsController" الخاص بك ، تحتاج إلى تحديد الطراز الذي ترتبط به وحدة التحكم. يتم ذلك عن طريق تطبيق Phalcon\Mvc\Model\Binder\BindableInterface الذي سيضيف طريقة getModelName() التي يمكنك من خلالها إرجاع اسم الطراز. يمكن إرجاع سلسلة مع اسم نموذج واحد أو صفيف اقتران واحد حيث المفتاح هو اسم المعلمة.

use Phalcon\Mvc\Model\Binder\BindableInterface;
use Models\Posts;

class PostsController extends CrudController implements BindableInterface
{
    public static function getModelName()
    {
        return Posts::class;
    }
}

من خلال التصريح عن النموذج المرتبط بـ PostsController ، يستطيع الموثق التحقق من وحدة التحكم getModelName() قبل تمرير النموذج المحدد إلى إجراء إظهار الأصل.

إذا كانت بنية مشروعك لا تستخدم أي وحدة تحكم رئيسية ، فيمكنك بالطبع ربط النموذج مباشرةً في إجراء وحدة التحكم:

use Phalcon\Mvc\Controller;
use Models\Posts;

class PostsController extends Controller
{
    /**
     * Shows posts
     *
     * @param Posts $post
     */
    public function showAction(Posts $post)
    {
        $this->view->post = $post;
    }
}
في الوقت الحالي ، سيستخدم الموثق فقط المفتاح الأساسي للنماذج لإجراء findFirst() . على سبيل المثال ، مسار ما سبق هو / posts / show / {1}

التعامل مع الاستثناءات غير الموجودة

باستخدام EventsManager ، من الممكن إدراج نقطة ربط قبل أن يلقي المرسل استثناءً في حالة عدم العثور على مجموعة وحدة التحكم / الإجراء:

use Exception;
use Phalcon\Dispatcher;
use Phalcon\Mvc\Dispatcher as MvcDispatcher;
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Mvc\Dispatcher\Exception as DispatchException;

$di->setShared(
    "dispatcher",
    function () {
        // Create an EventsManager
        $eventsManager = new EventsManager();

        // Attach a listener
        $eventsManager->attach(
            "dispatch:beforeException",
            function (Event $event, $dispatcher, Exception $exception) {
                // Handle 404 exceptions
                if ($exception instanceof DispatchException) {
                    $dispatcher->forward(
                        [
                            "controller" => "index",
                            "action"     => "show404",
                        ]
                    );

                    return false;
                }

                // Alternative way, controller or action doesn't exist
                switch ($exception->getCode()) {
                    case Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
                    case Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
                        $dispatcher->forward(
                            [
                                "controller" => "index",
                                "action"     => "show404",
                            ]
                        );

                        return false;
                }
            }
        );

        $dispatcher = new MvcDispatcher();

        // Bind the EventsManager to the dispatcher
        $dispatcher->setEventsManager($eventsManager);

        return $dispatcher;
    }
);

وبالطبع ، يمكن نقل هذه الطريقة إلى فئات إضافية مستقلة ، مما يسمح بأكثر من فئة واحدة باتخاذ إجراءات عند إنتاج استثناء في حلقة الإرسال:

use Exception;
use Phalcon\Events\Event;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Mvc\Dispatcher\Exception as DispatchException;

class ExceptionsPlugin
{
    public function beforeException(Event $event, Dispatcher $dispatcher, Exception $exception)
    {
        // Default error action
        $action = "show503";

        // Handle 404 exceptions
        if ($exception instanceof DispatchException) {
            $action = "show404";
        }

        $dispatcher->forward(
            [
                "controller" => "index",
                "action"     => $action,
            ]
        );

        return false;
    }
}
يتم فقط إخطار الاستثناءات التي ينتجها المرسل والاستثناءات المنتجة في الإجراء الذي تم تنفيذه في الأحداث 'beforeException'. يتم إعادة توجيه الاستثناءات التي يتم إنتاجها في المستمعين أو أحداث وحدة التحكم إلى أحدث تجربة / محاولة صيد.

تنفيذ المرسل الخاص بك

يجب تنفيذ واجهة Phalcon\Mvc\DispatcherInterface لإنشاء المرسل الخاص بك بدلاً من المرسل الذي يوفره Phalcon.