Completed
Push — master ( 2bef99...ca8d01 )
by Andrey
01:41
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 ModelInterface $model Model object record.
21
 * @property ActiveRecordInterface $searchModel Search new model object.
22
 * @property ValidateComponentInterface $validateComponent Validate component.
23
 *
24
 * @package Itstructure\AdminModule\controllers
25
 *
26
 * @author Andrey Girnik <[email protected]>
27
 */
28
abstract class CommonAdminController extends AdminController
29
{
30
    /**
31
     * Watch or not created record.
32
     * @var bool
33
     */
34
    protected $viewCreated = false;
35
36
    /**
37
     * Addition fields for the view template.
38
     * @var array
39
     */
40
    protected $additionFields = [];
41
42
    /**
43
     * Addition attributes with values for the model.
44
     * [
45
     *    'key1' => 'value1',
46
     *    'key2' => 'value2',
47
     * ]
48
     * @var array
49
     */
50
    protected $additionAttributes = [];
51
52
    /**
53
     * Installing the multilingual mode.
54
     * @var bool
55
     */
56
    protected $isMultilanguage = false;
57
58
    /**
59
     * Model object record.
60
     * @var ModelInterface
61
     */
62
    private $model;
63
64
    /**
65
     * Search new model object.
66
     * @var ActiveRecordInterface
67
     */
68
    private $searchModel;
69
70
    /**
71
     * Validate component.
72
     * @var ValidateComponentInterface
73
     */
74
    private $validateComponent;
75
76
    /**
77
     * Returns the name of the base model.
78
     * @return string
79
     */
80
    abstract protected function getModelName():string;
81
82
    /**
83
     * Returns the name of the model to search it.
84
     * @return string
85
     */
86
    abstract protected function getSearchModelName():string;
87
88
    /**
89
     * Initialize.
90
     * @throws InvalidConfigException
91
     * @return void
92
     */
93
    public function init()
94
    {
95
        if ($this->isMultilanguage){
96
            $validateComponent = $this->module->get('multilanguage-validate-component');
97
98
            if (null === $validateComponent){
99
                throw new InvalidConfigException('Multilanguage validate component is not defined.');
100
            }
101
            /** @var ValidateComponentInterface $validateComponent */
102
            $this->setValidateComponent($validateComponent);
103
        }
104
105
        parent::init();
106
    }
107
108
    /**
109
     * Set model.
110
     * @param $model ModelInterface
111
     */
112
    public function setModel(ModelInterface $model): void
113
    {
114
        $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...
115
    }
116
117
    /**
118
     * Set search model.
119
     * @param $model ActiveRecordInterface
120
     */
121
    public function setSearchModel(ActiveRecordInterface $model): void
122
    {
123
        $this->searchModel = $model;
124
    }
125
126
    /**
127
     * Set validate component for main model.
128
     * @param $component ValidateComponentInterface
129
     */
130
    public function setValidateComponent(ValidateComponentInterface $component): void
131
    {
132
        $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...
133
    }
134
135
    /**
136
     * Returns model.
137
     * @return ModelInterface
138
     */
139
    public function getModel(): ModelInterface
140
    {
141
        return $this->model;
142
    }
143
144
    /**
145
     * Returns search model.
146
     * @return ActiveRecordInterface
147
     */
148
    public function getSearchModel(): ActiveRecordInterface
149
    {
150
        return $this->searchModel;
151
    }
152
153
    /**
154
     * Get validate component for main model.
155
     * @return ValidateComponentInterface
156
     */
157
    public function getValidateComponent(): ValidateComponentInterface
158
    {
159
        return $this->validateComponent;
160
    }
161
162
    /**
163
     * List of records.
164
     * @return string
165
     */
166
    public function actionIndex()
167
    {
168
        $this->setSearchModel(
169
            $this->getNewSearchModel()
170
        );
171
172
        return $this->render('index',
173
            ArrayHelper::merge(
174
                [
175
                    'searchModel' => $this->searchModel,
176
                    '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...
177
                ],
178
                $this->getAdditionFields()
179
            )
180
        );
181
    }
182
183
    /**
184
     * Displays one model entry.
185
     * @param int|string $id
186
     * @return mixed
187
     */
188
    public function actionView($id)
189
    {
190
        return $this->render('view',
191
            ArrayHelper::merge(
192
                [
193
                    'model' => $this->findModel($id)
194
                ],
195
                $this->getAdditionFields()
196
            )
197
        );
198
    }
199
200
    /**
201
     * Creates a new model record.
202
     * If the result of creation is successful, there will be a redirect to the 'view' or 'index' page.
203
     * @return string|\yii\web\Response
204
     */
205
    public function actionCreate()
206
    {
207
        $this->setModelByConditions();
208
209
        if (Yii::$app->request->isPost &&
210
            $this->model->load(Yii::$app->request->post()) &&
211
            $this->setAdditionAttributes() &&
212
            $this->model->save()) {
213
214
            if ($this->viewCreated) {
215
                $redirectParams = [
216
                    $this->urlPrefix.'view',
217
                    'id' => $this->model->getId(),
218
                ];
219
            } else {
220
                $redirectParams = [
221
                    $this->urlPrefix.'index',
222
                ];
223
            }
224
225
            return $this->redirect($redirectParams);
226
        }
227
228
        return $this->render('create',
229
            ArrayHelper::merge(
230
                [
231
                    'model' => $this->model
232
                ],
233
                $this->getAdditionFields()
234
            )
235
        );
236
    }
237
238
    /**
239
     * Updates the current model entry.
240
     * If the result of creation is successful, there will be a redirect to the 'view' or 'index' page.
241
     * @param int|string $id
242
     * @return string|\yii\web\Response
243
     */
244
    public function actionUpdate($id)
245
    {
246
        $this->setModelByConditions($id);
247
248
        if (Yii::$app->request->isPost &&
249
            $this->model->load(Yii::$app->request->post()) &&
250
            $this->setAdditionAttributes() &&
251
            $this->model->save()) {
252
253
            return $this->redirect([
254
                $this->urlPrefix.'view',
255
                'id' => $this->model->getId(),
256
            ]);
257
        }
258
259
        return $this->render('update',
260
            ArrayHelper::merge(
261
                [
262
                    'model' => $this->model
263
                ],
264
                $this->getAdditionFields()
265
            )
266
        );
267
    }
268
269
    /**
270
     * Deletes the current model entry.
271
     * If the result of the deletion is successful, there will be a redirect to the 'index' page.
272
     * @param int|string $id
273
     * @return \yii\web\Response
274
     * @throws ConflictHttpException
275
     */
276
    public function actionDelete($id)
277
    {
278
        $model = $this->findModel($id);
279
280
        if (($model instanceof IdentityInterface) && $id == Yii::$app->user->identity->getId()) {
281
            throw new ConflictHttpException('You can not delete yourself.');
282
        };
283
284
        $model->delete();
285
286
        return $this->redirect([$this->urlPrefix.'index']);
287
    }
288
289
    /**
290
     * Set addition attributes for model.
291
     * @return bool
292
     */
293
    protected function setAdditionAttributes(): bool
294
    {
295
        $this->model->setAttributes($this->additionAttributes, false);
296
297
        return true;
298
    }
299
300
    /**
301
     * Get addition fields for the view template.
302
     * @return array
303
     */
304
    protected function getAdditionFields(): array
305
    {
306
        return $this->additionFields;
307
    }
308
309
    /**
310
     * Returns new object of main model.
311
     * @return mixed
312
     */
313
    protected function getNewModel()
314
    {
315
        $modelName = $this->getModelName();
316
        return new $modelName;
317
    }
318
319
    /**
320
     * Returns new object of search main model.
321
     * @return mixed
322
     */
323
    protected function getNewSearchModel()
324
    {
325
        $searchModelName = $this->getSearchModelName();
326
        return new $searchModelName;
327
    }
328
329
    /**
330
     * Find model record.
331
     * If the model is not found, a 404 HTTP exception will be displayed.
332
     * @param int|string $key
333
     * @throws BadRequestHttpException
334
     * @throws UnknownMethodException
335
     * @throws NotFoundHttpException
336
     * @return mixed
337
     */
338
    private function findModel($key)
339
    {
340
        if (null === $key){
341
            throw new BadRequestHttpException('Key parameter is not defined in findModel method.');
342
        }
343
344
        $modelObject = $this->getNewModel();
345
346
        if (!method_exists($modelObject, 'findOne')){
347
            $class = (new\ReflectionClass($modelObject));
348
            throw new UnknownMethodException('Method findOne does not exists in ' . $class->getNamespaceName() . '\\' . $class->getShortName().' class.');
349
        }
350
351
        $result = call_user_func([
352
            $modelObject,
353
            'findOne',
354
        ], $key);
355
356
        if ($result !== null) {
357
            return $result;
358
        }
359
360
        throw new NotFoundHttpException('The requested page does not exist.');
361
    }
362
363
    /**
364
     * Returns an intermediate model for working with the main.
365
     * @param int|string|null $key
366
     * @return void
367
     */
368
    private function setModelByConditions($key = null): void
369
    {
370
        $model = null === $key ? $this->getNewModel() : $this->findModel($key);
371
372
        if (null === $this->validateComponent){
373
            $this->setModel($model);
374
        } else {
375
            $this->setModel(
376
                $this->validateComponent->setModel($model)
377
            );
378
        }
379
    }
380
}