-
Notifications
You must be signed in to change notification settings - Fork 207
/
ModuleReflection.php
266 lines (235 loc) · 7.82 KB
/
ModuleReflection.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
<?php
namespace luya\base;
use luya\web\Controller;
use luya\web\Request;
use luya\web\UrlManager;
use Yii;
use yii\base\BaseObject;
use yii\base\InvalidConfigException;
use yii\web\NotFoundHttpException;
/**
* Run any route inside the provided module.
*
* In order to run a module reflection route You have to instantiate the module via the Yii::createObject method
*
* ```php
* $reflection = Yii::createObject(['class' => ModuleReflection::className(), 'module' => $module]);
* ```
*
* Now you can pass optional parameters before the run process, for example to define what controller action you may run:
*
* ```php
* $reflection->defaultRoute("my-controller", "the-action", ['param1' => 'foo']);
* ```
*
* Now in order to return the content of the modules route execute the run method:
*
* ```php
* $content = $reflection->run();
* ```
*
* @property \luya\base\Module $module The module where the route should be run from.
* @property string $suffix The suffix which should be used to attach to the url rules.
*
* @author Basil Suter <basil@nadar.io>
* @since 1.0.0
*/
class ModuleReflection extends BaseObject
{
/**
* @var \luya\web\Request Request object from DI-Container.
*/
public $request;
/**
* @var \luya\web\UrlManager UrlManager object from DI-Container.
*/
public $urlManager;
/**
* @var \yii\base\Controller|null The controller paramter is null until the [[run()]] method has been applied.
*/
public $controller;
private $_defaultRoute;
/**
* Class constructor in order to consum from DI Container.
*
* @param Request $request
* @param UrlManager $urlManager
* @param array $config
*/
public function __construct(Request $request, UrlManager $urlManager, array $config = [])
{
$this->request = $request;
$this->urlManager = $urlManager;
parent::__construct($config);
}
/**
* @inheritdoc
*/
public function init()
{
if ($this->module === null) {
throw new InvalidConfigException('The module attribute is required and can not be null.');
}
// add the module specific url rules to the url manager
$this->urlManager->addRules($this->module->urlRules, true);
}
private $_module;
/**
* Setter for the module property.
*
* @param Module $module
*/
public function setModule(Module $module)
{
$this->_module = $module;
}
/**
* Getter for the module property.
*
* @return \luya\base\Module
*/
public function getModule()
{
return $this->_module;
}
private $_suffix;
/**
* Setter for the suffix property.
*
* @param string $suffix
*/
public function setSuffix($suffix)
{
$this->_suffix = $suffix;
$this->request->setPathInfo(implode('/', [$this->module->id, $suffix]));
}
/**
* Getter for the suffix property.
*
* @return string
*/
public function getSuffix()
{
return $this->_suffix;
}
private $_requestRoute;
/**
* Determine the default route based on current defaultRoutes or parsedRequested by the UrlManager.
*
* @return array An array with
* + route: The path/route to the controller
* + args: If the url has no params, it returns all params from get request.
* + originalArgs: The arguments (params) parsed from the url trogh url manager
*
* @see Related problems and changes:
* + https://github.com/luyadev/luya/issues/1885
* + https://github.com/luyadev/luya/issues/1267
* + https://github.com/luyadev/luya/issues/754
*/
public function getRequestRoute()
{
if ($this->_requestRoute !== null) {
return $this->_requestRoute;
}
if ($this->_defaultRoute !== null && empty($this->getSuffix())) {
$array = $this->_defaultRoute;
} else {
// parse request against urlManager
$route = $this->urlManager->parseRequest($this->request);
// return human readable array
$array = [
'route' => $route[0],
'args' => $route[1],
'originalArgs' => $route[1],
];
}
// resolve the current route by the module
$array['route'] = $this->module->resolveRoute($array['route']);
// if there are no arguments, all get params are assigned. In order to use the original arguments from parse request use `originalArgs` instead of `args`.
if (empty($array['args'])) {
$array['args'] = $this->request->get();
}
// @see https://github.com/luyadev/luya/issues/1267
if ($this->_defaultRoute !== null) {
$array['args'] = array_merge($this->_defaultRoute['args'], $array['args']);
}
$this->_requestRoute = $array;
return $array;
}
/**
* Setter method for the requested route
* @param string $route
* @param array $args
*/
public function setRequestRoute($route, array $args = [])
{
$this->_requestRoute = ['route' => $route, 'args' => $args, 'originalArgs' => $args];
}
/**
* Inject a defaultRoute.
*
* @param string $controller
* @param string $action
* @param array $args
*/
public function defaultRoute($controller, $action = null, array $args = [])
{
$this->_defaultRoute = [
'route' => implode('/', [$this->module->id, $controller, (empty($action)) ? 'index' : $action]),
'args' => $args,
'originalArgs' => $args,
];
}
/**
* Returns the url rule parameters which are taken from the requested route.
*
* @return array
*/
public function getUrlRule()
{
$request = $this->getRequestRoute();
return [
'module' => $this->module->id,
'route' => $this->module->id . '/' . $request['route'],
'params' => $request['originalArgs'],
];
}
/**
* Run the route based on the values.
*
* @return string|\yii\web\Response The response of the action, can be either a string or an object from response.
* @throws \yii\web\NotFoundHttpException
*/
public function run()
{
$requestRoute = $this->getRequestRoute();
// create controller object
$controller = $this->module->createController($requestRoute['route']);
// throw error if the requests request does not returns a valid controller object
if (!$controller || !isset($controller[0]) || !is_object($controller[0])) {
throw new NotFoundHttpException(sprintf("Unable to create controller '%s' for module '%s'.", $requestRoute['route'], $this->module->id));
}
Yii::debug('LUYA module run module "'.$this->module->id.'" route ' . $requestRoute['route'], __METHOD__);
$this->controller = $controller[0];
$originalController = Yii::$app->controller;
/**
* Override the current application controller in order to ensure current() url handling which is used
* for relativ urls/rules.
*
* @see https://github.com/luyadev/luya/issues/1730
*/
$this->controller->on(Controller::EVENT_BEFORE_ACTION, function ($event) {
Yii::$app->controller = $this->controller;
});
/**
* Restore the original controller instance after rendering.
*
* @see https://github.com/luyadev/luya/issues/1768
*/
$this->controller->on(Controller::EVENT_AFTER_ACTION, function ($event) use ($originalController) {
Yii::$app->controller = $originalController;
});
// run the action on the provided controller object
return $this->controller->runAction($controller[1], $requestRoute['args']);
}
}