Файл: app/Models/Comment.php
Строк: 153
<?php
declare(strict_types=1);
namespace AppModels;
use AppCastsHtmlCast;
use AppClassesRegistry;
use AppTraitsFileableTrait;
use AppTraitsPollableTrait;
use AppTraitsSearchableTrait;
use AppTraitsUploadTrait;
use CarbonCarbonImmutable;
use IlluminateDatabaseEloquentBuilder;
use IlluminateDatabaseEloquentModel;
use IlluminateDatabaseEloquentRelationsBelongsTo;
use IlluminateDatabaseEloquentRelationsHasMany;
use IlluminateDatabaseEloquentRelationsMorphTo;
use IlluminateDatabaseEloquentRelationsRelation;
use IlluminateSupportCollection;
use IlluminateSupportFacadesDB;
use IlluminateSupportHtmlString;
use IlluminateSupportStr;
/**
* Class Comment
*
* @property int $id
* @property int $user_id
* @property string $relate_type
* @property int $relate_id
* @property string $text
* @property string $ip
* @property string $brow
* @property ?int $parent_id
* @property int $depth
* @property CarbonImmutable $created_at
* @property CarbonImmutable|null $deleted_at
* @property-read User $user
* @property-read ?Model $relate
* @property-read Collection<int, File> $files
* @property-read Collection<int, self> $children
* @property-read Collection<int, Poll> $polls
* @property-read Poll $poll
*/
class Comment extends Model
{
use PollableTrait;
use FileableTrait;
use SearchableTrait;
use UploadTrait;
/**
* The name of the "updated at" column.
*/
public const ?string UPDATED_AT = null;
/**
* The attributes that aren't mass assignable.
*/
protected $guarded = [];
/**
* Get the attributes that should be cast.
*/
protected function casts(): array
{
return [
'user_id' => 'int',
'text' => HtmlCast::class,
'deleted_at' => 'datetime',
];
}
/**
/**
* Morph name
*/
public static string $morphName = 'comments';
/**
* Директория загрузки файлов
*/
public string $uploadPath = '/uploads/comments';
/**
* Возвращает поля участвующие в поиске
*/
public function searchableFields(): array
{
return ['text'];
}
/**
* Исключает мягко удалённые комментарии из всех запросов по умолчанию
*/
protected static function booted(): void
{
static::addGlobalScope('active', static fn ($query) => $query->whereNull('deleted_at'));
}
/**
* Возвращает дочерние комментарии (включая мягко удалённые для отображения заглушки)
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id')
->withoutGlobalScope('active')
->orderBy('created_at');
}
/**
* Возвращает количество всех дочерних комментариев
*/
public function countAllDescendants(): int
{
$count = $this->children->count();
foreach ($this->children as $child) {
$count += $child->countAllDescendants();
}
return $count;
}
/**
* Возвращает родительский комментарий
*/
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id');
}
/**
* Возвращает связь пользователя
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id')->withDefault();
}
/**
* Возвращает связанные объекты
*/
public function relate(): MorphTo
{
return $this->morphTo('relate');
}
/**
* Видимые комментарии — у которых relate_type зарегистрирован в morphMap
* (комментарии отключённых модулей скрываются)
*/
public function scopeVisible(Builder $query): Builder
{
return $query->whereIn('relate_type', array_keys(Relation::morphMap()));
}
/**
* Get text
*/
public function getText(): HtmlString
{
return renderHtml($this->text, 'comment-' . $this->id);
}
/**
* Удаление записи
*/
public function delete(): ?bool
{
return DB::transaction(function () {
$this->polls()->delete();
$this->files->each(static function (File $file) {
$file->delete();
});
return parent::delete();
});
}
/**
* Мягкое удаление: очищает контент, но сохраняет запись для дочерних комментариев
*/
public function softDelete(): void
{
DB::transaction(function () {
$this->polls()->delete();
$this->files->each(static function (File $file) {
$file->delete();
});
$this->update(['text' => null, 'deleted_at' => now()]);
});
}
/**
* Возвращает тип связанного объекта
*/
public function getViewUrl(bool $absolute = true): string
{
$plural = Str::plural($this->relate_type);
$params = $this->relate_type === 'articles'
? ['slug' => $this->relate?->getAttribute('slug')]
: ['id' => $this->relate_id];
return route($plural . '.view', $params, $absolute) . '#comment_' . $this->id;
}
/**
* Возвращает тип связанного объекта
*/
public function getRelateType(): string
{
return Registry::$labelTypes[$this->relate_type] ?? $this->relate_type;
}
}