Completed
Push — master ( b57892...2bef99 )
by Andrey
03:54
created

CommonAdminController::beforeAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
3
namespace Itstructure\AdminModule\controllers;
4
5
use Yii;
6
use yii\db\ActiveRecordInterface;
7
use yii\helpers\ArrayHelper;
8
use yii\base\{UnknownMethodException, InvalidConfigException};
9
use yii\web\{IdentityInterface, ConflictHttpException, BadRequestHttpException, NotFoundHttpException};
10
use Itstructure\AdminModule\interfaces\{ModelInterface, ValidateComponentInterface};
11
12
/**
13
 * Class BaseController
14
 * Base controller class for the `admin` module.
15
 *
16
 * @property bool $viewCreated Watch or not created record.
17
 * @property array $additionFields Addition fields for the view template.
18
 * @property array $additionAttributes Addition attributes with values for the model.
19
 * @property bool $isMultilanguage Installing the multilingual mode.
20
 * @property string $urlPrefix Url prefix for redirect and view links.
21
 * @property ModelInterface $model Model object record.
22
 * @property ActiveRecordInterface $searchModel Search new model object.
23
 * @property ValidateComponentInterface $validateComponent Validate component.
24
 *
25
 * @package Itstructure\AdminModule\controllers
26
 *
27
 * @author Andrey Girnik <[email protected]>
28
 */
29
abstract class CommonAdminController extends AdminController
30
{
31
    /**
32
     * Watch or not created record.
33
     * @var bool
34
     */
35
    protected $viewCreated = false;
36
37
    /**
38
     * Addition fields for the view template.
39
     * @var array
40
     */
41
    protected $additionFields = [];
42
43
    /**
44
     * Addition attributes with values for the model.
45
     * [
46
     *    'key1' => 'value1',
47
     *    'key2' => 'value2',
48
     * ]
49
     * @var array
50
     */
51
    protected $additionAttributes = [];
52
53
    /**
54
     * Installing the multilingual mode.
55
     * @var bool
56
     */
57
    protected $isMultilanguage = false;
58
59
    /**
60
     * Url prefix for redirect and view links.
61
     * @var string
62
     */
63
    protected $urlPrefix = '';
64
65
    /**
66
     * Model object record.
67
     * @var ModelInterface
68
     */
69
    private $model;
70
71
    /**
72
     * Search new model object.
73
     * @var ActiveRecordInterface
74
     */
75
    private $searchModel;
76
77
    /**
78
     * Validate component.
79
     * @var ValidateComponentInterface
80
     */
81
    private $validateComponent;
82
83
    /**
84
     * Returns the name of the base model.
85
     * @return string
86
     */
87
    abstract protected function getModelName():string;
88
89
    /**
90
     * Returns the name of the model to search it.
91
     * @return string
92
     */
93
    abstract protected function getSearchModelName():string;
94
95
    /**
96
     * Initialize.
97
     * @throws InvalidConfigException
98
     * @return void
99
     */
100
    public function init()
101
    {
102
        if ($this->isMultilanguage){
103
            $validateComponent = $this->module->get('multilanguage-validate-component');
104
105
            if (null === $validateComponent){
106
                throw new InvalidConfigException('Multilanguage validate component is not defined.');
107
            }
108
            /** @var ValidateComponentInterface $validateComponent */
109
            $this->setValidateComponent($validateComponent);
110
        }
111
112
        parent::init();
113
    }
114
115
    /**
116
     * @param \yii\base\Action $action
117
     * @return bool
118
     */
119
    public function beforeAction($action)
120
    {
121
        $this->view->params['urlPrefix'] = $this->urlPrefix;
122
123
        return parent::beforeAction($action);
124
    }
125
126
    /**
127
     * Set model.
128
     * @param $model ModelInterface
129
     */
130
    public function setModel(ModelInterface $model): void
131
    {
132
        $this->model = $model;
0 ignored issues
show
Documentation Bug introduced by
It seems like $model of type object<Itstructure\Admin...erfaces\ModelInterface> is incompatible with the declared type object<ModelInterface> of property $model.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
133
    }
134
135
    /**
136
     * Set search model.
137
     * @param $model ActiveRecordInterface
138
     */
139
    public function setSearchModel(ActiveRecordInterface $model): void
140
    {
141
        $this->searchModel = $model;
142
    }
143
144
    /**
145
     * Set validate component for main model.
146
     * @param $component ValidateComponentInterface
147
     */
148
    public function setValidateComponent(ValidateComponentInterface $component): void
149
    {
150
        $this->validateComponent = $component;
0 ignored issues
show
Documentation Bug introduced by
It seems like $component of type object<Itstructure\Admin...dateComponentInterface> is incompatible with the declared type object<ValidateComponentInterface> of property $validateComponent.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
151
    }
152
153
    /**
154
     * Returns model.
155
     * @return ModelInterface
156
     */
157
    public function getModel(): ModelInterface
158
    {
159
        return $this->model;
160
    }
161
162
    /**
163
     * Returns search model.
164
     * @return ActiveRecordInterface
165
     */
166
    public function getSearchModel(): ActiveRecordInterface
167
    {
168
        return $this->searchModel;
169
    }
170
171
    /**
172
     * Get validate component for main model.
173
     * @return ValidateComponentInterface
174
     */
175
    public function getValidateComponent(): ValidateComponentInterface
176
    {
177
        return $this->validateComponent;
178
    }
179
180
    /**
181
     * List of records.
182
     * @return string
183
     */
184
    public function actionIndex()
185
    {
186
        $this->setSearchModel(
187
            $this->getNewSearchModel()
188
        );
189
190
        return $this->render('index',
191
            ArrayHelper::merge(
192
                [
193
                    'searchModel' => $this->searchModel,
194
                    'dataProvider' => $this->searchModel->search(Yii::$app->request->queryParams)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface yii\db\ActiveRecordInterface as the method search() does only exist in the following implementations of said interface: Itstructure\AdminModule\models\LanguageSearch.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
195
                ],
196
                $this->getAdditionFields()
197
            )
198
        );
199
    }
200
201
    /**
202
     * Displays one model entry.
203
     * @param int|string $id
204
     * @return mixed
205
     */
206
    public function actionView($id)
207
    {
208
        return $this->render('view',
209
            ArrayHelper::merge(
210
                [
211
                    'model' => $this->findModel($id)
212
                ],
213
                $this->getAdditionFields()
214
            )
215
        );
216
    }
217
218
    /**
219
     * Creates a new model record.
220
     * If the result of creation is successful, there will be a redirect to the 'view' or 'index' page.
221
     * @return string|\yii\web\Response
222
     */
223
    public function actionCreate()
224
    {
225
        $this->setModelByConditions();
226
227
        if (Yii::$app->request->isPost &&
228
            $this->model->load(Yii::$app->request->post()) &&
229
            $this->setAdditionAttributes() &&
230
            $this->model->save()) {
231
232
            if ($this->viewCreated) {
233
                $redirectParams = [
234
                    $this->urlPrefix.'view',
235
                    'id' => $this->model->getId(),
236
                ];
237
            } else {
238
                $redirectParams = [
239
                    $this->urlPrefix.'index',
240
                ];
241
            }
242
243
            return $this->redirect($redirectParams);
244
        }
245
246
        return $this->render('create',
247
            ArrayHelper::merge(
248
                [
249
                    'model' => $this->model
250
                ],
251
                $this->getAdditionFields()
252
            )
253
        );
254
    }
255
256
    /**
257
     * Updates the current model entry.
258
     * If the result of creation is successful, there will be a redirect to the 'view' or 'index' page.
259
     * @param int|string $id
260
     * @return string|\yii\web\Response
261
     */
262
    public function actionUpdate($id)
263
    {
264
        $this->setModelByConditions($id);
265
266
        if (Yii::$app->request->isPost &&
267
            $this->model->load(Yii::$app->request->post()) &&
268
            $this->setAdditionAttributes() &&
269
            $this->model->save()) {
270
271
            return $this->redirect([
272
                $this->urlPrefix.'view',
273
                'id' => $this->model->getId(),
274
            ]);
275
        }
276
277
        return $this->render('update',
278
            ArrayHelper::merge(
279
                [
280
                    'model' => $this->model
281
                ],
282
                $this->getAdditionFields()
283
            )
284
        );
285
    }
286
287
    /**
288
     * Deletes the current model entry.
289
     * If the result of the deletion is successful, there will be a redirect to the 'index' page.
290
     * @param int|string $id
291
     * @return \yii\web\Response
292
     * @throws ConflictHttpException
293
     */
294
    public function actionDelete($id)
295
    {
296
        $model = $this->findModel($id);
297
298
        if (($model instanceof IdentityInterface) && $id == Yii::$app->user->identity->getId()) {
299
            throw new ConflictHttpException('You can not delete yourself.');
300
        };
301
302
        $model->delete();
303
304
        return $this->redirect([$this->urlPrefix.'index']);
305
    }
306
307
    /**
308
     * Set addition attributes for model.
309
     * @return bool
310
     */
311
    protected function setAdditionAttributes(): bool
312
    {
313
        $this->model->setAttributes($this->additionAttributes, false);
314
315
        return true;
316
    }
317
318
    /**
319
     * Get addition fields for the view template.
320
     * @return array
321
     */
322
    protected function getAdditionFields(): array
323
    {
324
        return $this->additionFields;
325
    }
326
327
    /**
328
     * Returns new object of main model.
329
     * @return mixed
330
     */
331
    protected function getNewModel()
332
    {
333
        $modelName = $this->getModelName();
334
        return new $modelName;
335
    }
336
337
    /**
338
     * Returns new object of search main model.
339
     * @return mixed
340
     */
341
    protected function getNewSearchModel()
342
    {
343
        $searchModelName = $this->getSearchModelName();
344
        return new $searchModelName;
345
    }
346
347
    /**
348
     * Find model record.
349
     * If the model is not found, a 404 HTTP exception will be displayed.
350
     * @param int|string $key
351
     * @throws BadRequestHttpException
352
     * @throws UnknownMethodException
353
     * @throws NotFoundHttpException
354
     * @return mixed
355
     */
356
    private function findModel($key)
357
    {
358
        if (null === $key){
359
            throw new BadRequestHttpException('Key parameter is not defined in findModel method.');
360
        }
361
362
        $modelObject = $this->getNewModel();
363
364
        if (!method_exists($modelObject, 'findOne')){
365
            $class = (new\ReflectionClass($modelObject));
366
            throw new UnknownMethodException('Method findOne does not exists in ' . $class->getNamespaceName() . '\\' . $class->getShortName().' class.');
367
        }
368
369
        $result = call_user_func([
370
            $modelObject,
371
            'findOne',
372
        ], $key);
373
374
        if ($result !== null) {
375
            return $result;
376
        }
377
378
        throw new NotFoundHttpException('The requested page does not exist.');
379
    }
380
381
    /**
382
     * Returns an intermediate model for working with the main.
383
     * @param int|string|null $key
384
     * @return void
385
     */
386
    private function setModelByConditions($key = null): void
387
    {
388
        $model = null === $key ? $this->getNewModel() : $this->findModel($key);
389
390
        if (null === $this->validateComponent){
391
            $this->setModel($model);
392
        } else {
393
            $this->setModel(
394
                $this->validateComponent->setModel($model)
395
            );
396
        }
397
    }
398
}