Просмотр файла app/Models/Topic.php

Размер файла: 5.85Kb
<?php

declare(strict_types=1);

namespace App\Models;

use App\Traits\SearchableTrait;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\HtmlString;

/**
 * Class Topic
 *
 * @property int         $id
 * @property int         $forum_id
 * @property string      $title
 * @property int         $user_id
 * @property int         $closed
 * @property int         $locked
 * @property int         $count_posts
 * @property int         $visits
 * @property int         $updated_at
 * @property int         $created_at
 * @property string|null $moderators
 * @property string      $note
 * @property int         $last_post_id
 * @property int         $close_user_id
 * @property-read Forum                $forum
 * @property-read Collection<Post>     $posts
 * @property-read Collection<Bookmark> $bookmarks
 * @property-read Vote                 $vote
 */
class Topic extends BaseModel
{
    use SearchableTrait;

    /**
     * Indicates if the model should be timestamped.
     */
    public $timestamps = false;

    /**
     * The attributes that aren't mass assignable.
     */
    protected $guarded = [];

    /**
     * Counting field
     */
    public string $countingField = 'visits';

    /**
     * Morph name
     */
    public static string $morphName = 'topics';

    /**
     * Возвращает поля участвующие в поиске
     */
    public function searchableFields(): array
    {
        return ['title'];
    }

    /**
     * Возвращает сообщения
     */
    public function posts(): HasMany
    {
        return $this->hasMany(Post::class, 'topic_id');
    }

    /**
     * Возвращает закладки
     */
    public function bookmarks(): HasMany
    {
        return $this->hasMany(Bookmark::class, 'topic_id');
    }

    /**
     * Возвращает голосование
     */
    public function vote(): hasOne
    {
        return $this->hasOne(Vote::class, 'topic_id')->withDefault();
    }

    /**
     * Возвращает последнее сообщение
     */
    public function lastPost(): BelongsTo
    {
        return $this->belongsTo(Post::class, 'last_post_id')->withDefault();
    }

    /**
     * Возвращает раздел форума
     */
    public function forum(): BelongsTo
    {
        return $this->belongsTo(Forum::class, 'forum_id')->withDefault();
    }

    /**
     * Возвращает связь пользователей
     */
    public function closeUser(): BelongsTo
    {
        return $this->belongsTo(User::class, 'close_user_id')->withDefault();
    }

    /**
     * Удаление темы и связанных данных
     */
    public function delete(): ?bool
    {
        return DB::transaction(function () {
            // Удаление голосования
            $this->vote->delete();

            // Удаление закладок
            $this->bookmarks->each(static function (Bookmark $bookmark) {
                $bookmark->delete();
            });

            // Удаление сообщений
            $this->posts->each(static function (Post $post) {
                $post->delete();
            });

            return parent::delete();
        });
    }

    /**
     * Возвращает иконку в зависимости от статуса
     */
    public function getIcon(): string
    {
        if ($this->closed) {
            $icon = 'fa-lock';
        } elseif ($this->locked) {
            $icon = 'fa-thumbtack';
        } else {
            $icon = 'fa-folder-open';
        }

        return $icon;
    }

    /**
     * Генерирует постраничную навигацию для форума
     */
    public function pagination(string $url = '/topics'): ?HtmlString
    {
        if (! $this->count_posts) {
            return null;
        }

        $pages = [];
        $link = $url . '/' . $this->id;

        $pg_cnt = ceil($this->count_posts / setting('forumpost'));

        for ($i = 1; $i <= 5; $i++) {
            if ($i <= $pg_cnt) {
                $pages[] = [
                    'page'  => $i,
                    'title' => $i . ' страница',
                    'name'  => $i,
                    'url'   => $i > 1 ? $link . '?page=' . $i : $link,
                ];
            }
        }

        if ($pg_cnt > 5) {
            if ($pg_cnt > 6) {
                $pages[] = [
                    'separator' => true,
                    'name'      => ' ... ',
                ];
            }

            $pages[] = [
                'page'  => $pg_cnt,
                'title' => $pg_cnt . ' страница',
                'name'  => $pg_cnt,
                'url'   => $link . '?page=' . $pg_cnt,
            ];
        }

        return new HtmlString(view('forums/_pagination', compact('pages')));
    }

    /**
     * Пересчет темы
     */
    public function restatement(): void
    {
        $lastPost = Post::query()
            ->where('topic_id', $this->id)
            ->orderByDesc('updated_at')
            ->first();

        $countPosts = Post::query()->where('topic_id', $this->id)->count();

        $this->update([
            'count_posts'  => $countPosts,
            'last_post_id' => $lastPost->id ?? 0,
        ]);

        $this->forum->restatement();
    }

    /**
     * Get count posts
     */
    public function getCountPosts(): HtmlString
    {
        $newPosts = null;
        if ($this->bookmark_posts && $this->count_posts > $this->bookmark_posts) {
            $newPosts = ' <span style="color:#00aa00">+' . ($this->count_posts - $this->bookmark_posts) . '</span>';
        }

        return new HtmlString($this->count_posts . $newPosts);
    }
}