Completed
Push — master ( 3f24f8...42984c )
by Andrey
01:17
created

BaseController::actionCreate()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 8.7857
c 0
b 0
f 0
cc 6
nc 3
nop 0
1
<?php
2
3
namespace Itstructure\RbacModule\controllers;
4
5
use Yii;
6
use yii\helpers\ArrayHelper;
7
use yii\filters\{VerbFilter, AccessControl};
8
use yii\base\{Model, UnknownMethodException};
9
use yii\web\{BadRequestHttpException, ConflictHttpException, NotFoundHttpException, Controller};
10
use Itstructure\RbacModule\Module;
11
use Itstructure\RbacModule\interfaces\{ModelInterface, ValidateComponentInterface, RbacIdentityInterface};
12
13
/**
14
 * Class BaseController
15
 * Base controller class for the `Rbac` module.
16
 *
17
 * @property Module $module
18
 * @property bool $viewCreated
19
 * @property array $additionFields
20
 * @property array $additionAttributes
21
 * @property string $urlPrefix Url prefix for redirect and view links.
22
 * @property string $urlPrefixNeighbor Url prefix for redirect and view links of neighbor entity.
23
 * @property ModelInterface $model
24
 * @property Model $searchModel
25
 * @property ValidateComponentInterface $validateComponent
26
 *
27
 * @package Itstructure\RbacModule\controllers
28
 */
29
abstract class BaseController extends Controller
30
{
31
    /**
32
     * Watch or not created record.
33
     *
34
     * @var bool
35
     */
36
    protected $viewCreated = false;
37
38
    /**
39
     * Addition fields for the template.
40
     *
41
     * @var array
42
     */
43
    protected $additionFields = [];
44
45
    /**
46
     * Addition attributes with values for the model.
47
     * [
48
     *    'key1' => 'value1',
49
     *    'key2' => 'value2',
50
     * ]
51
     *
52
     * @var array
53
     */
54
    protected $additionAttributes = [];
55
56
    /**
57
     * Url prefix for redirect and view links.
58
     *
59
     * @var string
60
     */
61
    protected $urlPrefix = '';
62
63
    /**
64
     * Url prefix for redirect and view links of neighbor entity.
65
     *
66
     * @var string
67
     */
68
    protected $urlPrefixNeighbor = '';
69
70
    /**
71
     * Model object record.
72
     *
73
     * @var ModelInterface
74
     */
75
    private $model;
76
77
    /**
78
     * Search new model object.
79
     *
80
     * @var Model
81
     */
82
    private $searchModel;
83
84
    /**
85
     * Multilanguage component.
86
     *
87
     * @var ValidateComponentInterface
88
     */
89
    private $validateComponent;
90
91
    /**
92
     * Returns the name of the base model.
93
     *
94
     * @return string
95
     */
96
    abstract protected function getModelName(): string;
97
98
    /**
99
     * Returns the name of the model to search it.
100
     *
101
     * @return string
102
     */
103
    abstract protected function getSearchModelName(): string;
104
105
    /**
106
     * @inheritdoc
107
     */
108
    public function behaviors()
109
    {
110
        return [
111
            'access' => [
112
                'class' => AccessControl::class,
113
                'rules' => [
114
                    [
115
                        'allow' => true,
116
                        'roles' => $this->module->accessRoles,
117
                    ],
118
                ],
119
            ],
120
            'verbs' => [
121
                'class' => VerbFilter::class,
122
                'actions' => [
123
                    'delete' => [
124
                        'POST',
125
                    ],
126
                ],
127
            ],
128
        ];
129
    }
130
131
    /**
132
     * Initializer.
133
     */
134
    public function init()
135
    {
136
        $this->view->params['user'] = Yii::$app->user->identity;
137
    }
138
139
    /**
140
     * @param \yii\base\Action $action
141
     *
142
     * @return bool
143
     */
144
    public function beforeAction($action)
145
    {
146
        $this->view->params['urlPrefix']         = $this->urlPrefix;
147
        $this->view->params['urlPrefixNeighbor'] = $this->urlPrefixNeighbor;
148
149
        return parent::beforeAction($action);
150
    }
151
152
    /**
153
     * Give ability of configure view to the module class.
154
     *
155
     * @return \yii\base\View|\yii\web\View
156
     */
157
    public function getView()
158
    {
159
        if (method_exists($this->module, 'getView')) {
160
            return $this->module->getView();
161
        }
162
163
        return parent::getView();
164
    }
165
166
    /**
167
     * Set model.
168
     *
169
     * @param $model ModelInterface
170
     */
171
    public function setModel(ModelInterface $model): void
172
    {
173
        $this->model = $model;
0 ignored issues
show
Documentation Bug introduced by
It seems like $model of type object<Itstructure\RbacM...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...
174
    }
175
176
    /**
177
     * Set search model.
178
     *
179
     * @param $model Model
180
     */
181
    public function setSearchModel(Model $model): void
182
    {
183
        $this->searchModel = $model;
0 ignored issues
show
Documentation Bug introduced by
It seems like $model of type object<yii\base\Model> is incompatible with the declared type object<Model> of property $searchModel.

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...
184
    }
185
186
    /**
187
     * Set validate component for main model.
188
     *
189
     * @param $component ValidateComponentInterface
190
     */
191
    public function setValidateComponent(ValidateComponentInterface $component): void
192
    {
193
        $this->validateComponent = $component;
0 ignored issues
show
Documentation Bug introduced by
It seems like $component of type object<Itstructure\RbacM...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...
194
    }
195
196
    /**
197
     * Returns model.
198
     *
199
     * @return ModelInterface
200
     */
201
    public function getModel(): ModelInterface
202
    {
203
        return $this->model;
204
    }
205
206
    /**
207
     * Returns search model.
208
     *
209
     * @return Model
210
     */
211
    public function getSearchModel(): Model
212
    {
213
        return $this->searchModel;
214
    }
215
216
    /**
217
     * Get validate component for main model.
218
     *
219
     * @return ValidateComponentInterface
220
     */
221
    public function getValidateComponent(): ValidateComponentInterface
222
    {
223
        return $this->validateComponent;
224
    }
225
226
    /**
227
     * List of records.
228
     *
229
     * @return string
230
     */
231
    public function actionIndex()
232
    {
233
        $this->setSearchModel(
234
            $this->getNewSearchModel()
235
        );
236
237
        return $this->render('index',
238
            ArrayHelper::merge(
239
                [
240
                    'searchModel' => $this->searchModel,
241
                    'dataProvider' => $this->searchModel->search(Yii::$app->request->queryParams),
242
                ],
243
                $this->getAdditionFields()
244
            )
245
        );
246
    }
247
248
    /**
249
     * Displays one model entry.
250
     *
251
     * @param int|string $id
252
     *
253
     * @return mixed
254
     */
255
    public function actionView($id)
256
    {
257
        return $this->render('view',
258
            ArrayHelper::merge(
259
                [
260
                    'model' => $this->findModel($id),
261
                ],
262
                $this->getAdditionFields()
263
            )
264
        );
265
    }
266
267
    /**
268
     * Creates a new model record.
269
     * If the result of creation is successful, there will be a redirect to the 'view' or 'index' page.
270
     *
271
     * @return string|\yii\web\Response
272
     */
273
    public function actionCreate()
274
    {
275
        $this->setModelByConditions();
276
277
        if (Yii::$app->request->isPost &&
278
            $this->model->load(Yii::$app->request->post()) &&
279
            $this->setAdditionAttributes() &&
280
            $this->model->save()) {
281
282
            if ($this->viewCreated) {
283
                $redirectParams = [
284
                    $this->urlPrefix.'view',
285
                    'id' => $this->model->getId(),
286
                ];
287
            } else {
288
                $redirectParams = [
289
                    $this->urlPrefix.'index',
290
                ];
291
            }
292
293
            return $this->redirect($redirectParams);
294
        }
295
296
        return $this->render('create',
297
            ArrayHelper::merge(
298
                [
299
                    'model' => $this->model,
300
                ],
301
                $this->getAdditionFields()
302
            )
303
        );
304
    }
305
306
    /**
307
     * Updates the current model entry.
308
     * If the result of creation is successful, there will be a redirect to the 'view' or 'index' page.
309
     *
310
     * @param int|string $id
311
     *
312
     * @return string|\yii\web\Response
313
     */
314
    public function actionUpdate($id)
315
    {
316
        $this->setModelByConditions($id);
317
318
        if (Yii::$app->request->isPost &&
319
            $this->model->load(Yii::$app->request->post()) &&
320
            $this->setAdditionAttributes() &&
321
            $this->model->save()) {
322
323
            return $this->redirect([
324
                $this->urlPrefix.'view',
325
                'id' => $this->model->getId(),
326
            ]);
327
        }
328
329
        return $this->render('update',
330
            ArrayHelper::merge(
331
                [
332
                    'model' => $this->model,
333
                ],
334
                $this->getAdditionFields()
335
            )
336
        );
337
    }
338
339
    /**
340
     * Deletes the current model entry.
341
     * If the result of the deletion is successful, there will be a redirect to the 'index' page.
342
     *
343
     * @param int|string $id
344
     *
345
     * @throws ConflictHttpException
346
     *
347
     * @return \yii\web\Response
348
     */
349
    public function actionDelete($id)
350
    {
351
        $model = $this->findModel($id);
352
353
        if (($model instanceof RbacIdentityInterface) && $id == Yii::$app->user->identity->getId()) {
354
            throw new ConflictHttpException('You can not delete yourself.');
355
        };
356
357
        $model->delete();
358
359
        return $this->redirect(['index']);
360
    }
361
362
    /**
363
     * Set addition attributes for model.
364
     *
365
     * @return bool
366
     */
367
    protected function setAdditionAttributes(): bool
368
    {
369
        $this->model->setAttributes($this->additionAttributes, false);
370
371
        return true;
372
    }
373
374
    /**
375
     * Get addition fields for the view template.
376
     *
377
     * @return array
378
     */
379
    protected function getAdditionFields(): array
380
    {
381
        return $this->additionFields;
382
    }
383
384
    /**
385
     * Returns new object of main model.
386
     *
387
     * @return mixed
388
     */
389
    protected function getNewModel()
390
    {
391
        $modelName = $this->getModelName();
392
        return new $modelName;
393
    }
394
395
    /**
396
     * Returns new object of search main model.
397
     *
398
     * @return RbacIdentityInterface|ModelInterface
399
     */
400
    protected function getNewSearchModel()
401
    {
402
        $searchModelName = $this->getSearchModelName();
403
        return new $searchModelName;
404
    }
405
406
    /**
407
     * Find model record.
408
     * If the model is not found, a 404 HTTP exception will be displayed.
409
     *
410
     * @param int|string $key
411
     *
412
     *
413
     * @throws BadRequestHttpException
414
     * @throws UnknownMethodException
415
     * @throws NotFoundHttpException
416
     *
417
     * @return mixed
418
     */
419
    private function findModel($key)
420
    {
421
        if (null === $key) {
422
            throw new BadRequestHttpException('Key parameter is not defined in findModel method.');
423
        }
424
425
        $modelObject = $this->getNewModel();
426
427 View Code Duplication
        if (!method_exists($modelObject, 'findOne')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
428
            $class = (new\ReflectionClass($modelObject));
429
            throw new UnknownMethodException('Method findOne does not exists in ' . $class->getNamespaceName() . '\\' .
430
                $class->getShortName().' class.');
431
        }
432
433
        $result = call_user_func([
434
            $modelObject,
435
            'findOne',
436
        ], $key);
437
438
        if ($result !== null) {
439
            return $result;
440
        }
441
442
        throw new NotFoundHttpException('The requested page does not exist.');
443
    }
444
445
    /**
446
     * Returns an intermediate model for working with the main.
447
     *
448
     * @param int|string|null $key
449
     *
450
     * @return void
451
     */
452
    private function setModelByConditions($key = null): void
453
    {
454
        $model = null === $key ? $this->getNewModel() : $this->findModel($key);
455
456
        if (null === $this->validateComponent) {
457
            $this->setModel($model);
458
        } else {
459
            $this->setModel(
460
                $this->validateComponent->setModel($model)
461
            );
462
        }
463
    }
464
}
465