• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

visavi / rotor / 28323582235

28 Jun 2026 01:20PM UTC coverage: 16.474% (+0.06%) from 16.419%
28323582235

push

github

visavi
Переход на datetime

19 of 66 new or added lines in 23 files covered. (28.79%)

9 existing lines in 8 files now uncovered.

984 of 5973 relevant lines covered (16.47%)

2.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

15.09
/app/Models/Comment.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace App\Models;
6

7
use App\Casts\HtmlCast;
8
use App\Classes\Registry;
9
use App\Traits\FileableTrait;
10
use App\Traits\PollableTrait;
11
use App\Traits\SearchableTrait;
12
use App\Traits\UploadTrait;
13
use Carbon\CarbonImmutable;
14
use Illuminate\Database\Eloquent\Builder;
15
use Illuminate\Database\Eloquent\Model;
16
use Illuminate\Database\Eloquent\Relations\BelongsTo;
17
use Illuminate\Database\Eloquent\Relations\HasMany;
18
use Illuminate\Database\Eloquent\Relations\MorphTo;
19
use Illuminate\Database\Eloquent\Relations\Relation;
20
use Illuminate\Support\Collection;
21
use Illuminate\Support\Facades\DB;
22
use Illuminate\Support\HtmlString;
23
use Illuminate\Support\Str;
24

25
/**
26
 * Class Comment
27
 *
28
 * @property int                  $id
29
 * @property int                  $user_id
30
 * @property string               $relate_type
31
 * @property int                  $relate_id
32
 * @property string               $text
33
 * @property string               $ip
34
 * @property string               $brow
35
 * @property ?int                 $parent_id
36
 * @property int                  $depth
37
 * @property CarbonImmutable      $created_at
38
 * @property CarbonImmutable|null $deleted_at
39
 * @property-read User                      $user
40
 * @property-read ?Model                    $relate
41
 * @property-read Collection<int, File>     $files
42
 * @property-read Collection<int, self>     $children
43
 * @property-read Collection<int, Poll>     $polls
44
 * @property-read Poll                      $poll
45
 */
46
class Comment extends Model
47
{
48
    use PollableTrait;
49
    use FileableTrait;
50
    use SearchableTrait;
51
    use UploadTrait;
52

53
    /**
54
     * The name of the "updated at" column.
55
     */
56
    public const ?string UPDATED_AT = null;
57

58
    /**
59
     * The attributes that aren't mass assignable.
60
     */
61
    protected $guarded = [];
62

63
    /**
64
     * Get the attributes that should be cast.
65
     */
66
    protected function casts(): array
109✔
67
    {
68
        return [
109✔
69
            'user_id'    => 'int',
109✔
70
            'text'       => HtmlCast::class,
109✔
71
            'deleted_at' => 'datetime',
109✔
72
        ];
109✔
73
    }
74

75
    /**
76
    /**
77
     * Morph name
78
     */
79
    public static string $morphName = 'comments';
80

81
    /**
82
     * Директория загрузки файлов
83
     */
84
    public string $uploadPath = '/uploads/comments';
85

86
    /**
87
     * Возвращает поля участвующие в поиске
88
     */
89
    public function searchableFields(): array
×
90
    {
91
        return ['text'];
×
92
    }
93

94
    /**
95
     * Исключает мягко удалённые комментарии из всех запросов по умолчанию
96
     */
97
    protected static function booted(): void
109✔
98
    {
99
        static::addGlobalScope('active', static fn ($query) => $query->whereNull('deleted_at'));
109✔
100
    }
101

102
    /**
103
     * Возвращает дочерние комментарии (включая мягко удалённые для отображения заглушки)
104
     */
105
    public function children(): HasMany
×
106
    {
107
        return $this->hasMany(self::class, 'parent_id')
×
108
            ->withoutGlobalScope('active')
×
109
            ->orderBy('created_at');
×
110
    }
111

112
    /**
113
     * Возвращает количество всех дочерних комментариев
114
     */
115
    public function countAllDescendants(): int
×
116
    {
117
        $count = $this->children->count();
×
118
        foreach ($this->children as $child) {
×
119
            $count += $child->countAllDescendants();
×
120
        }
121

122
        return $count;
×
123
    }
124

125
    /**
126
     * Возвращает родительский комментарий
127
     */
128
    public function parent(): BelongsTo
×
129
    {
130
        return $this->belongsTo(self::class, 'parent_id');
×
131
    }
132

133
    /**
134
     * Возвращает связь пользователя
135
     */
136
    public function user(): BelongsTo
×
137
    {
138
        return $this->belongsTo(User::class, 'user_id')->withDefault();
×
139
    }
140

141
    /**
142
     * Возвращает связанные объекты
143
     */
144
    public function relate(): MorphTo
×
145
    {
146
        return $this->morphTo('relate');
×
147
    }
148

149
    /**
150
     * Видимые комментарии — у которых relate_type зарегистрирован в morphMap
151
     * (комментарии отключённых модулей скрываются)
152
     */
153
    public function scopeVisible(Builder $query): Builder
×
154
    {
155
        return $query->whereIn('relate_type', array_keys(Relation::morphMap()));
×
156
    }
157

158
    /**
159
     * Get text
160
     */
161
    public function getText(): HtmlString
×
162
    {
163
        return renderHtml($this->text, 'comment-' . $this->id);
×
164
    }
165

166
    /**
167
     * Удаление записи
168
     */
169
    public function delete(): ?bool
×
170
    {
171
        return DB::transaction(function () {
×
172
            $this->polls()->delete();
×
173

174
            $this->files->each(static function (File $file) {
×
175
                $file->delete();
×
176
            });
×
177

178
            return parent::delete();
×
179
        });
×
180
    }
181

182
    /**
183
     * Мягкое удаление: очищает контент, но сохраняет запись для дочерних комментариев
184
     */
185
    public function softDelete(): void
×
186
    {
187
        DB::transaction(function () {
×
188
            $this->polls()->delete();
×
189

190
            $this->files->each(static function (File $file) {
×
191
                $file->delete();
×
192
            });
×
193

NEW
194
            $this->update(['text' => null, 'deleted_at' => now()]);
×
195
        });
×
196
    }
197

198
    /**
199
     * Возвращает тип связанного объекта
200
     */
201
    public function getViewUrl(bool $absolute = true): string
×
202
    {
203
        $plural = Str::plural($this->relate_type);
×
204
        $params = $this->relate_type === 'articles'
×
205
            ? ['slug' => $this->relate?->getAttribute('slug')]
×
206
            : ['id' => $this->relate_id];
×
207

208
        return route($plural . '.view', $params, $absolute) . '#comment_' . $this->id;
×
209
    }
210

211
    /**
212
     * Возвращает тип связанного объекта
213
     */
214
    public function getRelateType(): string
×
215
    {
216
        return Registry::$labelTypes[$this->relate_type] ?? $this->relate_type;
×
217
    }
218
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc