CommentModel::getViewUrl()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 7
rs 10
1
<?php
2
3
namespace yii2mod\comments\models;
4
5
use paulzi\adjacencyList\AdjacencyListBehavior;
6
use Yii;
7
use yii\behaviors\BlameableBehavior;
8
use yii\behaviors\TimestampBehavior;
9
use yii\db\ActiveQuery;
10
use yii\db\ActiveRecord;
11
use yii\helpers\ArrayHelper;
12
use yii2mod\behaviors\PurifyBehavior;
13
use yii2mod\comments\traits\ModuleTrait;
14
use yii2mod\moderation\enums\Status;
15
use yii2mod\moderation\ModerationBehavior;
16
use yii2mod\moderation\ModerationQuery;
17
18
/**
19
 * Class CommentModel
20
 *
21
 * @property int $id
22
 * @property string $entity
23
 * @property int $entityId
24
 * @property string $content
25
 * @property int $parentId
26
 * @property int $level
27
 * @property int $createdBy
28
 * @property int $updatedBy
29
 * @property string $relatedTo
30
 * @property string $url
31
 * @property int $status
32
 * @property int $createdAt
33
 * @property int $updatedAt
34
 *
35
 * @method ActiveRecord makeRoot()
36
 * @method ActiveRecord appendTo($node)
37
 * @method ActiveQuery getDescendants()
38
 * @method ModerationBehavior markRejected()
39
 * @method AdjacencyListBehavior deleteWithChildren()
40
 */
41
class CommentModel extends ActiveRecord
42
{
43
    use ModuleTrait;
44
45
    const SCENARIO_MODERATION = 'moderation';
46
47
    /**
48
     * @var null|array|ActiveRecord[] comment children
49
     */
50
    protected $children;
51
52
    /**
53
     * @inheritdoc
54
     */
55
    public static function tableName()
56
    {
57
        return '{{%comment}}';
58
    }
59
60
    /**
61
     * @inheritdoc
62
     */
63
    public function rules()
64
    {
65
        return [
66
            [['entity', 'entityId'], 'required'],
67
            ['content', 'required', 'message' => Yii::t('yii2mod.comments', 'Comment cannot be blank.')],
68
            [['content', 'entity', 'relatedTo', 'url'], 'string'],
69
            ['status', 'default', 'value' => Status::APPROVED],
70
            ['status', 'in', 'range' => Status::getConstantsByName()],
71
            ['level', 'default', 'value' => 1],
72
            ['parentId', 'validateParentID', 'except' => static::SCENARIO_MODERATION],
73
            [['entityId', 'parentId', 'status', 'level'], 'integer'],
74
        ];
75
    }
76
77
    /**
78
     * @param $attribute
79
     */
80
    public function validateParentID($attribute)
81
    {
82
        if (null !== $this->{$attribute}) {
83
            $parentCommentExist = static::find()
84
                ->approved()
85
                ->andWhere([
86
                    'id' => $this->{$attribute},
87
                    'entity' => $this->entity,
88
                    'entityId' => $this->entityId,
89
                ])
90
                ->exists();
91
92
            if (!$parentCommentExist) {
93
                $this->addError('content', Yii::t('yii2mod.comments', 'Oops, something went wrong. Please try again later.'));
94
            }
95
        }
96
    }
97
98
    /**
99
     * @inheritdoc
100
     */
101
    public function behaviors()
102
    {
103
        return [
104
            'blameable' => [
105
                'class' => BlameableBehavior::class,
106
                'createdByAttribute' => 'createdBy',
107
                'updatedByAttribute' => 'updatedBy',
108
            ],
109
            'timestamp' => [
110
                'class' => TimestampBehavior::class,
111
                'createdAtAttribute' => 'createdAt',
112
                'updatedAtAttribute' => 'updatedAt',
113
            ],
114
            'purify' => [
115
                'class' => PurifyBehavior::class,
116
                'attributes' => ['content'],
117
                'config' => [
118
                    'HTML.SafeIframe' => true,
119
                    'URI.SafeIframeRegexp' => '%^(https?:)?//(www\.youtube(?:-nocookie)?\.com/embed/|player\.vimeo\.com/video/)%',
120
                    'AutoFormat.Linkify' => true,
121
                    'HTML.TargetBlank' => true,
122
                    'HTML.Allowed' => 'a[href], iframe[src|width|height|frameborder], img[src]',
123
                ],
124
            ],
125
            'adjacencyList' => [
126
                'class' => AdjacencyListBehavior::class,
127
                'parentAttribute' => 'parentId',
128
                'sortable' => false,
129
            ],
130
            'moderation' => [
131
                'class' => ModerationBehavior::class,
132
                'moderatedByAttribute' => false,
133
            ],
134
        ];
135
    }
136
137
    /**
138
     * @inheritdoc
139
     */
140
    public function attributeLabels()
141
    {
142
        return [
143
            'id' => Yii::t('yii2mod.comments', 'ID'),
144
            'content' => Yii::t('yii2mod.comments', 'Content'),
145
            'entity' => Yii::t('yii2mod.comments', 'Entity'),
146
            'entityId' => Yii::t('yii2mod.comments', 'Entity ID'),
147
            'parentId' => Yii::t('yii2mod.comments', 'Parent ID'),
148
            'status' => Yii::t('yii2mod.comments', 'Status'),
149
            'level' => Yii::t('yii2mod.comments', 'Level'),
150
            'createdBy' => Yii::t('yii2mod.comments', 'Created by'),
151
            'updatedBy' => Yii::t('yii2mod.comments', 'Updated by'),
152
            'relatedTo' => Yii::t('yii2mod.comments', 'Related to'),
153
            'url' => Yii::t('yii2mod.comments', 'Url'),
154
            'createdAt' => Yii::t('yii2mod.comments', 'Created date'),
155
            'updatedAt' => Yii::t('yii2mod.comments', 'Updated date'),
156
        ];
157
    }
158
159
    /**
160
     * @return ModerationQuery
161
     */
162
    public static function find()
163
    {
164
        return new ModerationQuery(get_called_class());
165
    }
166
167
    /**
168
     * @inheritdoc
169
     */
170
    public function beforeSave($insert)
171
    {
172
        if (parent::beforeSave($insert)) {
173
            if ($this->parentId > 0 && $this->isNewRecord) {
174
                $parentNodeLevel = static::find()->select('level')->where(['id' => $this->parentId])->scalar();
175
                $this->level += $parentNodeLevel;
176
            }
177
178
            return true;
179
        } else {
180
            return false;
181
        }
182
    }
183
184
    /**
185
     * @inheritdoc
186
     */
187
    public function afterSave($insert, $changedAttributes)
188
    {
189
        parent::afterSave($insert, $changedAttributes);
190
191
        if (!$insert) {
192
            if (array_key_exists('status', $changedAttributes)) {
193
                $this->beforeModeration();
194
            }
195
        }
196
    }
197
198
    /**
199
     * @return bool
200
     */
201
    public function saveComment()
202
    {
203
        if ($this->validate()) {
204
            if (empty($this->parentId)) {
205
                return $this->makeRoot()->save();
206
            } else {
207
                $parentComment = static::findOne(['id' => $this->parentId]);
208
209
                return $this->appendTo($parentComment)->save();
210
            }
211
        }
212
213
        return false;
214
    }
215
216
    /**
217
     * Author relation
218
     *
219
     * @return \yii\db\ActiveQuery
220
     */
221
    public function getAuthor()
222
    {
223
        return $this->hasOne($this->getModule()->userIdentityClass, ['id' => 'createdBy']);
224
    }
225
226
    /**
227
     * Get comments tree.
228
     *
229
     * @param string $entity
230
     * @param string $entityId
231
     * @param null $maxLevel
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $maxLevel is correct as it would always require null to be passed?
Loading history...
232
     *
233
     * @return array|ActiveRecord[]
234
     */
235
    public static function getTree($entity, $entityId, $maxLevel = null)
236
    {
237
        $query = static::find()
238
            ->alias('c')
239
            ->approved()
240
            ->andWhere([
241
                'c.entityId' => $entityId,
242
                'c.entity' => $entity,
243
            ])
244
            ->orderBy(['c.parentId' => SORT_ASC, 'c.createdAt' => SORT_ASC])
245
            ->with(['author']);
246
247
        if ($maxLevel > 0) {
248
            $query->andWhere(['<=', 'c.level', $maxLevel]);
249
        }
250
251
        $models = $query->all();
252
253
        if (!empty($models)) {
254
            $models = static::buildTree($models);
255
        }
256
257
        return $models;
258
    }
259
260
    /**
261
     * Build comments tree.
262
     *
263
     * @param array $data comments list
264
     * @param int $rootID
265
     *
266
     * @return array|ActiveRecord[]
267
     */
268
    protected static function buildTree(&$data, $rootID = 0)
269
    {
270
        $tree = [];
271
272
        foreach ($data as $id => $node) {
273
            if ($node->parentId == $rootID) {
274
                unset($data[$id]);
275
                $node->children = self::buildTree($data, $node->id);
276
                $tree[] = $node;
277
            }
278
        }
279
280
        return $tree;
281
    }
282
283
    /**
284
     * @return array|null|ActiveRecord[]
285
     */
286
    public function getChildren()
287
    {
288
        return $this->children;
289
    }
290
291
    /**
292
     * @param $value
293
     */
294
    public function setChildren($value)
295
    {
296
        $this->children = $value;
297
    }
298
299
    /**
300
     * @return bool
301
     */
302
    public function hasChildren()
303
    {
304
        return !empty($this->children);
305
    }
306
307
    /**
308
     * @return string
309
     */
310
    public function getPostedDate()
311
    {
312
        return Yii::$app->formatter->asRelativeTime($this->createdAt);
313
    }
314
315
    /**
316
     * @return mixed
317
     */
318
    public function getAuthorName()
319
    {
320
        if ($this->author->hasMethod('getUsername')) {
0 ignored issues
show
Bug Best Practice introduced by
The property author does not exist on yii2mod\comments\models\CommentModel. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug introduced by
The method hasMethod() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

320
        if ($this->author->/** @scrutinizer ignore-call */ hasMethod('getUsername')) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
321
            return $this->author->getUsername();
322
        }
323
324
        return $this->author->username;
325
    }
326
327
    /**
328
     * @return string
329
     */
330
    public function getContent()
331
    {
332
        return nl2br($this->content);
333
    }
334
335
    /**
336
     * Get avatar of the user
337
     *
338
     * @return string
339
     */
340
    public function getAvatar()
341
    {
342
        if ($this->author->hasMethod('getAvatar')) {
0 ignored issues
show
Bug Best Practice introduced by
The property author does not exist on yii2mod\comments\models\CommentModel. Since you implemented __get, consider adding a @property annotation.
Loading history...
343
            return $this->author->getAvatar();
344
        }
345
346
        return 'http://www.gravatar.com/avatar?d=mm&f=y&s=50';
347
    }
348
349
    /**
350
     * Get list of all authors
351
     *
352
     * @return array
353
     */
354
    public static function getAuthors()
355
    {
356
        $query = static::find()
357
            ->alias('c')
358
            ->select(['c.createdBy', 'a.username'])
359
            ->joinWith('author a')
360
            ->groupBy(['c.createdBy', 'a.username'])
361
            ->orderBy('a.username')
362
            ->asArray()
363
            ->all();
364
365
        return ArrayHelper::map($query, 'createdBy', 'author.username');
366
    }
367
368
    /**
369
     * @return int
370
     */
371
    public function getCommentsCount()
372
    {
373
        return (int) static::find()
374
            ->approved()
375
            ->andWhere(['entity' => $this->entity, 'entityId' => $this->entityId])
376
            ->count();
377
    }
378
379
    /**
380
     * @return string
381
     */
382
    public function getAnchorUrl()
383
    {
384
        return "#comment-{$this->id}";
385
    }
386
387
    /**
388
     * @return null|string
389
     */
390
    public function getViewUrl()
391
    {
392
        if (!empty($this->url)) {
393
            return $this->url . $this->getAnchorUrl();
394
        }
395
396
        return null;
397
    }
398
399
    /**
400
     * Before moderation event
401
     *
402
     * @return bool
403
     */
404
    public function beforeModeration()
405
    {
406
        $descendantIds = ArrayHelper::getColumn($this->getDescendants()->asArray()->all(), 'id');
407
408
        if (!empty($descendantIds)) {
409
            static::updateAll(['status' => $this->status], ['id' => $descendantIds]);
410
        }
411
412
        return true;
413
    }
414
}
415