Completed
Push — master ( b4d6a5...352f6d )
by Basil
02:42
created

ModuleReflection   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 227
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 21
lcom 1
cbo 7
dl 0
loc 227
rs 10
c 0
b 0
f 0

11 Methods

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

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
252
            Yii::$app->controller = $this->controller;
253
        });
254
        /**
255
         * Restore the original controller instance after rendering.
256
         *
257
         * @see https://github.com/luyadev/luya/issues/1768
258
         */
259
        $this->controller->on(Controller::EVENT_AFTER_ACTION, function ($event) use ($originalController) {
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
260
            Yii::$app->controller = $originalController;
261
        });
262
        
263
        // run the action on the provided controller object
264
        return $this->controller->runAction($controller[1], $requestRoute['args']);
265
    }
266
}
267