diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a969f0730c..c573cd3a93d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,8 +16,9 @@
- Added parameters `skip_on_insert`, `skip_on_update` and `allow_empty_string` and fixed a bug for renamed integer columns in `Phalcon\Mvc\Model\MetaData\Strategy\Annotations::getMetaData`
- Added way to disable setters in `Phalcon\Mvc\Model::assign` by using `Phalcon\Mvc\Model::setup` or ini option
- Added ability to sanitize special characters to `Phalcon\Filter`
-- Added a new `Phalcon\Mvc\Model\Binder::findBoundModel` method. Params fetched from cache are being added to `internalCache` class property in `Phalcon\Mvc\Model\Binder::getParamsFromCache`.
+- Added a new `Phalcon\Mvc\Model\Binder::findBoundModel` method. Params fetched from cache are being added to `internalCache` class property in `Phalcon\Mvc\Model\Binder::getParamsFromCache`
- Added `Phalcon\Mvc\Model\Criteria::createBuilder` to create a query builder from criteria
+- Added `dispatcher::beforeForward` event to allow forwarding request to the separated module [#121](https://github.com/phalcon/cphalcon/issues/121), [#12417](https://github.com/phalcon/cphalcon/issues/12417)
- Fixed Dispatcher forwarding when handling exception [#11819](https://github.com/phalcon/cphalcon/issues/11819), [#12154](https://github.com/phalcon/cphalcon/issues/12154)
- Fixed params view scope for PHP 7 [#12648](https://github.com/phalcon/cphalcon/issues/12648)
- Fixed `Phalcon\Mvc\Micro::handle` to prevent attemps to send response twice [#12668](https://github.com/phalcon/cphalcon/pull/12668)
diff --git a/phalcon/dispatcher.zep b/phalcon/dispatcher.zep
index 2bdd5325334..8a93c11a942 100755
--- a/phalcon/dispatcher.zep
+++ b/phalcon/dispatcher.zep
@@ -699,17 +699,16 @@ abstract class Dispatcher implements DispatcherInterface, InjectionAwareInterfac
}
/**
- * Forwards the execution flow to another controller/action
- * Dispatchers are unique per module. Forwarding between modules is not allowed
+ * Forwards the execution flow to another controller/action.
*
- *
+ *
* $this->dispatcher->forward(
* [
* "controller" => "posts",
* "action" => "index",
* ]
* );
- *
+ *
*
* @param array forward
*/
diff --git a/phalcon/mvc/dispatcher.zep b/phalcon/mvc/dispatcher.zep
index ef02780d8a6..9a4bbf3d40d 100644
--- a/phalcon/mvc/dispatcher.zep
+++ b/phalcon/mvc/dispatcher.zep
@@ -163,6 +163,72 @@ class Dispatcher extends BaseDispatcher implements DispatcherInterface
}
}
+ /**
+ * Forwards the execution flow to another controller/action.
+ *
+ *
+ * use Phalcon\Events\Event;
+ * use Phalcon\Mvc\Dispatcher;
+ * use App\Backend\Bootstrap as Backend;
+ * use App\Frontend\Bootstrap as Frontend;
+ *
+ * // Registering modules
+ * $modules = [
+ * "frontend" => [
+ * "className" => Frontend::class,
+ * "path" => __DIR__ . "/app/Modules/Frontend/Bootstrap.php",
+ * "metadata" => [
+ * "controllersNamespace" => "App\Frontend\Controllers",
+ * ],
+ * ],
+ * "backend" => [
+ * "className" => Backend::class,
+ * "path" => __DIR__ . "/app/Modules/Backend/Bootstrap.php",
+ * "metadata" => [
+ * "controllersNamespace" => "App\Backend\Controllers",
+ * ],
+ * ],
+ * ];
+ *
+ * $application->registerModules($modules);
+ *
+ * // Setting beforeForward listener
+ * $eventsManager = $di->getShared("eventsManager");
+ *
+ * $eventsManager->attach(
+ * "dispatch:beforeForward",
+ * function(Event $event, Dispatcher $dispatcher, array $forward) use ($modules) {
+ * $metadata = $modules[$forward["module"]]["metadata"];
+ *
+ * $dispatcher->setModuleName($forward["module"]);
+ * $dispatcher->setNamespaceName($metadata["controllersNamespace"]);
+ * }
+ * );
+ *
+ * // Forward
+ * $this->dispatcher->forward(
+ * [
+ * "module" => "backend",
+ * "controller" => "posts",
+ * "action" => "index",
+ * ]
+ * );
+ *
+ *
+ * @param array forward
+ */
+ public function forward(var forward)
+ {
+ var eventsManager;
+
+ let eventsManager = this->_eventsManager;
+ if typeof eventsManager == "object" {
+ eventsManager->fire("dispatch:beforeForward", this, forward);
+ }
+
+ parent::forward(forward);
+ }
+
/**
* Possible controller class name that will be located to dispatch the request
*/
diff --git a/tests/unit/Mvc/DispatcherTest.php b/tests/unit/Mvc/DispatcherTest.php
index 28cf761dca6..4fe0016e39d 100644
--- a/tests/unit/Mvc/DispatcherTest.php
+++ b/tests/unit/Mvc/DispatcherTest.php
@@ -441,8 +441,6 @@ public function testDispatcherForward2()
function () {
$di = new Di();
- //$di->set("response", new Response());
-
$dispatcher = new Dispatcher();
$dispatcher->setDI($di);
@@ -481,6 +479,64 @@ function () {
);
}
+ /**
+ * Tests beforeForward event
+ *
+ * @test
+ * @issue 212
+ * @author Serghei Iakovlev
+ * @since 2017-05-21
+ */
+ public function dispatcherBeforeForward()
+ {
+ $this->specify(
+ 'beforeForward event should be fired',
+ function () {
+ $di = new Di();
+ $manager = new Manager();
+
+ $modules = [
+ 'backend' => [
+ 'className' => 'App\Backend\Bootstrap',
+ 'path' => '/app/Modules/Backend/Bootstrap.php',
+ 'metadata' => [
+ 'controllersNamespace' => 'App\Backend\Controllers',
+ ],
+ ],
+ ];
+
+ $manager->attach(
+ 'dispatch:beforeForward',
+ function (Event $event, Dispatcher $dispatcher, array $forward) use ($modules) {
+ $metadata = $modules[$forward['module']]['metadata'];
+
+ $dispatcher->setModuleName($forward['module']);
+ $dispatcher->setNamespaceName($metadata['controllersNamespace']);
+ }
+ );
+
+ $dispatcher = new Dispatcher();
+ $dispatcher->setDI($di);
+ $dispatcher->setEventsManager($manager);
+
+ $di->set('dispatcher', $dispatcher);
+
+ $dispatcher->forward(
+ [
+ 'module' => 'backend',
+ 'controller' => 'posts',
+ 'action' => 'index',
+ ]
+ );
+
+ expect($dispatcher->getModuleName())->equals('backend');
+ expect($dispatcher->getNamespaceName())->equals('App\Backend\Controllers');
+ expect($dispatcher->getControllerName())->equals('posts');
+ expect($dispatcher->getActionName())->equals('index');
+ }
+ );
+ }
+
public function testGetControllerClass()
{
$this->specify(