PostController::actionAjaxSearch()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 14
rs 9.4285
cc 2
eloc 9
nc 2
nop 0
1
<?php
2
/**
3
 * @link http://www.writesdown.com/
4
 * @copyright Copyright (c) 2015 WritesDown
5
 * @license http://www.writesdown.com/license/
6
 */
7
8
namespace backend\controllers;
9
10
use common\models\Option;
11
use common\models\Post;
12
use common\models\PostType;
13
use common\models\search\Post as PostSearch;
14
use common\models\Term;
15
use common\models\TermRelationship;
16
use Yii;
17
use yii\filters\AccessControl;
18
use yii\filters\VerbFilter;
19
use yii\web\Controller;
20
use yii\web\ForbiddenHttpException;
21
use yii\web\NotFoundHttpException;
22
use yii\web\Response;
23
24
/**
25
 * PostController implements the CRUD actions for Post model.
26
 *
27
 * @author Agiel K. Saputra <[email protected]>
28
 * @since 0.1.0
29
 */
30
class PostController extends Controller
31
{
32
    /**
33
     * @inheritdoc
34
     */
35
    public function behaviors()
36
    {
37
        return [
38
            'access' => [
39
                'class' => AccessControl::className(),
40
                'rules' => [
41
                    [
42
                        'actions' => ['index', 'create', 'update', 'delete', 'bulk-action', 'ajax-search'],
43
                        'allow' => true,
44
                        'roles' => ['subscriber'],
45
                    ],
46
                ],
47
            ],
48
            'verbs' => [
49
                'class' => VerbFilter::className(),
50
                'actions' => [
51
                    'delete' => ['post'],
52
                    'bulk-action' => ['post'],
53
                    'ajax-search' => ['post'],
54
                ],
55
            ],
56
        ];
57
    }
58
59
    /**
60
     * Lists all Post models on specific post type.
61
     * If there is user, the action will generate list of all Post models based on user.
62
     *
63
     * @param integer $type
64
     * @param null|string $user
65
     * @throws \yii\web\ForbiddenHttpException
66
     * @throws \yii\web\NotFoundHttpException
67
     * @return mixed
68
     */
69
    public function actionIndex($type, $user = null)
70
    {
71
        $postType = $this->findPostType($type);
72
        $searchModel = new PostSearch();
73
        $dataProvider = $searchModel->search(Yii::$app->request->queryParams, $type, $user);
74
75
        if (!Yii::$app->user->can($postType->permission)) {
76
            throw new ForbiddenHttpException(Yii::t('writesdown', 'You are not allowed to perform this action.'));
77
        }
78
79
        return $this->render('index', [
80
            'searchModel' => $searchModel,
81
            'dataProvider' => $dataProvider,
82
            'postType' => $postType,
83
            'user' => $user,
84
        ]);
85
    }
86
87
    /**
88
     * Creates a new Post model.
89
     * If creation is successful, the browser will be redirected to the 'update' page.
90
     *
91
     * @param integer $type Post type ID
92
     * @throws \yii\web\ForbiddenHttpException
93
     * @throws \yii\web\NotFoundHttpException
94
     * @return mixed
95
     */
96
    public function actionCreate($type)
97
    {
98
        $model = new Post();
99
        $postType = $this->findPostType($type);
100
        $model->comment_status = Option::get('default_comment_status');
101
102
        if (!Yii::$app->user->can($postType->permission)) {
103
            throw new ForbiddenHttpException(Yii::t('writesdown', 'You are not allowed to perform this action.'));
104
        }
105
106
        if ($model->load(Yii::$app->request->post())) {
107
            $model->type = $postType->id;
108
            $model->date = date('Y-m-d H:i:s', strtotime($model->date));
109
            if ($model->save()) {
110
                if ($termIds = Yii::$app->request->post('termIds')) {
111
                    foreach ($termIds as $termId) {
112
                        $termRelationship = new TermRelationship();
113
                        $termRelationship->setAttributes([
114
                            'term_id' => $termId,
115
                            'post_id' => $model->id,
116
                        ]);
117
                        if ($termRelationship->save() && $term = $this->findTerm($termId)) {
118
                            $term->updateAttributes(['count' => ++$term->count]);
0 ignored issues
show
Bug introduced by
The method updateAttributes cannot be called on $term (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
119
                        }
120
                    }
121
                }
122 View Code Duplication
                if ($meta = Yii::$app->request->post('meta')) {
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...
123
                    foreach ($meta as $name => $value) {
124
                        $model->setMeta($name, $value);
125
                    }
126
                }
127
                Yii::$app->getSession()->setFlash('success',
0 ignored issues
show
Bug introduced by
The method getSession does only exist in yii\web\Application, but not in yii\console\Application.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
128
                    Yii::t('writesdown', '{type} successfully saved.', ['type' => $postType->singular_name, ]));
129
                return $this->redirect(['update', 'id' => $model->id]);
130
            }
131
        }
132
133
        return $this->render('create', [
134
            'model' => $model,
135
            'postType' => $postType,
136
        ]);
137
    }
138
139
    /**
140
     * Updates an existing Post model.
141
     * If update is successful, the browser will be redirected to the 'view' page.
142
     *
143
     * @param integer $id
144
     * @throws \yii\web\ForbiddenHttpException
145
     * @throws \yii\web\NotFoundHttpException
146
     * @return mixed
147
     */
148
    public function actionUpdate($id)
149
    {
150
        $model = $this->findModel($id);
151
        $this->getPermission($model);
152
        $postType = $model->postType;
153
154
        if ($model->load(Yii::$app->request->post())) {
0 ignored issues
show
Bug introduced by
The method load cannot be called on $model (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
155
            $model->date = date('Y-m-d H:i:s', strtotime($model->date));
156
            if ($model->save()) {
0 ignored issues
show
Bug introduced by
The method save cannot be called on $model (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
157 View Code Duplication
                if ($meta = Yii::$app->request->post('meta')) {
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...
158
                    foreach ($meta as $name => $value) {
159
                        $model->setMeta($name, $value);
0 ignored issues
show
Bug introduced by
The method setMeta cannot be called on $model (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
160
                    }
161
                }
162
                Yii::$app->getSession()->setFlash('success',
0 ignored issues
show
Bug introduced by
The method getSession does only exist in yii\web\Application, but not in yii\console\Application.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
163
                    Yii::t('writesdown', '{type} successfully saved.', ['type' => $postType->singular_name, ]));
164
                return $this->redirect(['post/update', 'id' => $id]);
165
            }
166
        }
167
168
        return $this->render('update', [
169
            'model' => $model,
170
            'postType' => $postType,
171
        ]);
172
    }
173
174
    /**
175
     * Deletes an existing Post model.
176
     * If deletion is successful, the browser will be redirected to the 'index' page.
177
     *
178
     * @param integer $id
179
     * @throws \Exception
180
     * @throws \yii\web\ForbiddenHttpException
181
     * @throws \yii\web\NotFoundHttpException
182
     * @return mixed
183
     */
184
    public function actionDelete($id)
185
    {
186
        $model = $this->findModel($id);
187
        $this->getPermission($model);
188
        $terms = $model->terms;
189
190
        if ($model->delete()) {
0 ignored issues
show
Bug introduced by
The method delete cannot be called on $model (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
191
            foreach ($terms as $term) {
192
                $term->updateAttributes(['count' => --$term->count]);
193
            }
194
        }
195
196
        return $this->redirect(['index', 'type' => $model->type]);
197
    }
198
199
    /**
200
     * Bulk action for Post triggered when button 'Apply' clicked.
201
     * The action depends on the value of the dropdown next to the button.
202
     * Only accept POST HTTP method.
203
     */
204
    public function actionBulkAction()
205
    {
206
        if (Yii::$app->request->post('action') === Post::STATUS_PUBLISH) {
207
            foreach (Yii::$app->request->post('ids', []) as $id) {
208
                $model = $this->findModel($id);
209
                $this->getPermission($model);
210
                $model->updateAttributes(['status' => Post::STATUS_PUBLISH]);
0 ignored issues
show
Bug introduced by
The method updateAttributes cannot be called on $model (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
211
            }
212 View Code Duplication
        } elseif (Yii::$app->request->post('action') === Post::STATUS_DRAFT) {
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...
213
            foreach (Yii::$app->request->post('ids', []) as $id) {
214
                $model = $this->findModel($id);
215
                $this->getPermission($model);
216
                $model->updateAttributes(['status' => Post::STATUS_DRAFT]);
0 ignored issues
show
Bug introduced by
The method updateAttributes cannot be called on $model (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
217
            }
218
        } elseif (Yii::$app->request->post('action') === Post::STATUS_PRIVATE) {
219
            foreach (Yii::$app->request->post('ids', []) as $id) {
220
                $model = $this->findModel($id);
221
                $this->getPermission($model);
222
                $model->updateAttributes(['status' => Post::STATUS_PRIVATE]);
0 ignored issues
show
Bug introduced by
The method updateAttributes cannot be called on $model (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
223
            }
224 View Code Duplication
        } elseif (Yii::$app->request->post('action') === Post::STATUS_TRASH) {
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...
225
            foreach (Yii::$app->request->post('ids', []) as $id) {
226
                $model = $this->findModel($id);
227
                $this->getPermission($model);
228
                $model->updateAttributes(['status' => Post::STATUS_TRASH]);
0 ignored issues
show
Bug introduced by
The method updateAttributes cannot be called on $model (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
229
            }
230
        } elseif (Yii::$app->request->post('action') === Post::STATUS_REVIEW) {
231
            foreach (Yii::$app->request->post('ids', []) as $id) {
232
                $model = $this->findModel($id);
233
                $this->getPermission($model);
234
                $model->updateAttributes(['status' => Post::STATUS_REVIEW]);
0 ignored issues
show
Bug introduced by
The method updateAttributes cannot be called on $model (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
235
            }
236
        } elseif (Yii::$app->request->post('action') === 'delete') {
237
            foreach (Yii::$app->request->post('ids', []) as $id) {
238
                $model = $this->findModel($id);
239
                $this->getPermission($model);
240
                $terms = $model->terms;
241
                if ($model->delete()) {
0 ignored issues
show
Bug introduced by
The method delete cannot be called on $model (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
242
                    foreach ($terms as $term) {
243
                        $term->updateAttributes(['count' => --$term->count]);
244
                    }
245
                }
246
            }
247
        }
248
    }
249
250
    /**
251
     * Search POST model via AJAX with JSON as the response.
252
     */
253
    public function actionAjaxSearch()
254
    {
255
        Yii::$app->response->format = Response::FORMAT_JSON;
256
        $query = Post::find()
257
            ->select(['id', 'title'])
258
            ->andWhere(['like', 'title', Yii::$app->request->post('title')])
259
            ->limit(10);
260
261
        if ($postType = Yii::$app->request->post('type')) {
262
            $query->andWhere(['type' => $postType]);
263
        }
264
265
        return $query->all();
266
    }
267
268
    /**
269
     * Get permission to access model by current user.
270
     * If the user does not obtain the permission, a 403 exeption will be thrown.
271
     *
272
     * @param $model Post
273
     * @throws ForbiddenHttpException
274
     */
275
    public function getPermission($model)
276
    {
277
        if (!$model->getPermission()) {
278
            throw new ForbiddenHttpException(Yii::t('writesdown', 'You are not allowed to perform this action.'));
279
        }
280
    }
281
282
    /**
283
     * Finds the Post model based on its primary key value.
284
     * If the model is not found, a 404 HTTP exception will be thrown.
285
     *
286
     * @param integer $id
287
     * @return Post the loaded model
288
     * @throws NotFoundHttpException if the model cannot be found
289
     */
290
    protected function findModel($id)
291
    {
292
        if (($model = Post::findOne($id)) !== null) {
293
            return $model;
294
        }
295
296
        throw new NotFoundHttpException('The requested page does not exist.');
297
    }
298
299
    /**
300
     * Finds the PostType model based on its primary key value.
301
     * If the model is not found, a 404 HTTP exception will be thrown.
302
     *
303
     * @param integer $id
304
     * @return PostType the loaded model
305
     * @throws NotFoundHttpException if the model cannot be found
306
     */
307
    protected function findPostType($id)
308
    {
309
        if (($model = PostType::findOne($id)) !== null) {
310
            return $model;
311
        }
312
313
        throw new NotFoundHttpException('The requested page does not exist.');
314
    }
315
316
    /**
317
     * Finds the Term model based on its primary key value.
318
     * If the model is not found, it return false.
319
     *
320
     * @param integer $id
321
     * @return Term|bool|null|static
322
     */
323
    protected function findTerm($id)
324
    {
325
        if (($model = Term::findOne($id)) !== null) {
326
            return $model;
327
        }
328
329
        return false;
330
    }
331
}
332