Вход Регистрация
Файл: app/Traits/HandlesComments.php
Строк: 269
<?php

declare(strict_types=1);

namespace 
AppTraits;

use 
AppClassesValidator;
use 
AppModelsComment;
use 
AppModelsFile;
use 
AppModelsFlood;
use 
AppModelsUser;
use 
IlluminateDatabaseEloquentModel;
use 
IlluminateDatabaseEloquentRelationsMorphMany;
use 
IlluminateDatabaseQueryJoinClause;
use 
IlluminateHttpJsonResponse;
use 
IlluminateHttpRedirectResponse;
use 
IlluminateHttpRequest;
use 
IlluminateSupportCollection;
use 
IlluminateSupportStr;

trait 
HandlesComments
{
    
/**
     * Связь комментариев модели (morph-имя relate единое по движку)
     *
     * @return MorphMany<Comment, Model>
     */
    
private function commentsRelation(Model $model): MorphMany
    
{
        return 
$model->morphMany(Comment::class, 'relate')->with('relate');
    }

    
/**
     * Возвращает [routeName, params] для редиректа на страницу модели
     */
    
protected function commentableViewRoute(Model $model): array
    {
        
$plural = Str::plural(Str::snake(class_basename($this->commentableModelClass)));

        return [
$plural . '.view', ['id' => $model->getKey()]];
    }

    
/**
     * Находит родительскую модель по id или прерывает с 404
     */
    
protected function findCommentParent(int $id): Model
    
{
        
$model = ($this->commentableModelClass)::query()->find($id);
        if (! 
$model) {
            
abort(404, __('main.record_not_found'));
        }

        return 
$model;
    }

    
/**
     * Проверяет доступность модели (прерывает если active = false)
     */
    
protected function checkCommentableModel(Model $model): void
    
{
        if (
array_key_exists('active', $model->getAttributes()) && ! $model->getAttribute('active')) {
            
abort(200, __('main.record_not_active'));
        }
    }

    
/**
     * Возвращает комментарии в виде дерева и файлы для передачи во вью
     */
    
protected function getCommentsData(Model $model): array
    {
        
$user = getUser();

        
$files = $user
            
? File::query()
                ->
where('relate_type', Comment::$morphName)
                ->
where('relate_id', 0)
                ->
where('user_id', $user->id)
                ->
orderBy('created_at')
                ->
get()
            : 
collect();

        
$allComments = $this->commentsRelation($model)
            ->
withoutGlobalScope('active')
            ->
select('comments.*', 'polls.vote')
            ->
leftJoin('polls', static function (JoinClause $join) {
                
$join->on('comments.id', 'polls.relate_id')
                    ->
where('polls.relate_type', Comment::$morphName)
                    ->
where('polls.user_id', getUser('id'));
            })
            ->
orderBy('created_at')
            ->
with(['user', 'files'])
            ->
get();

        
$comments = $this->buildCommentTree($allComments);

        return 
compact('comments', 'files');
    }

    
/**
     * Строит дерево комментариев из плоской коллекции
     */
    
protected function buildCommentTree(Collection $all, ?int $parentId = null): Collection
    
{
        return 
$all
            
->where('parent_id', $parentId)
            ->
map(function (Comment $comment) use ($all) {
                
$comment->setRelation('children', $this->buildCommentTree($all, $comment->id));

                return 
$comment;
            })
            ->
values();
    }

    
/**
     * Редирект на страницу с якорем на добавленный комментарий
     */
    
protected function redirectAfterCommentAdded(Model $model, int $commentId): RedirectResponse
    
{
        [
$route, $params] = $this->commentableViewRoute($model);

        return 
redirect()
            ->
route($route, $params)
            ->
withFragment('comment_' . $commentId);
    }

    
/**
     * Добавление комментария (POST-обработчик)
     */
    
public function storeComment(int $id, Request $request, Validator $validator, Flood $flood): RedirectResponse|JsonResponse
    
{
        
$model = $this->findCommentParent($id);

        [
$viewRoute, $viewParams] = $this->commentableViewRoute($model);

        
$user = getUser();
        
$msg = $request->input('msg');

        
$validator
            
->true($user, __('main.not_authorized'))
            ->
false($flood->isFlood(), ['msg' => __('validator.flood', ['sec' => $flood->getPeriod()])])
            ->
length($msg, setting('comment_text_min'), setting('comment_text_max'), ['msg' => __('validator.text')]);

        
$validator->empty($model->getAttribute('closed'), ['msg' => __('main.closed_comments')]);

        if (
$validator->isValid()) {
            
$msg = antimat($msg);

            
$parentId = null;
            
$depth = 0;
            
$parentComment = $request->input('parent_id')
                ? 
Comment::query()->find((int) $request->input('parent_id'))
                : 
null;

            if (
$parentComment && $parentComment->relate_id === $model->getKey()) {
                if (
$parentComment->depth >= setting('comment_depth')) {
                    
$parentId = $parentComment->parent_id;
                    
$depth = $parentComment->depth;
                } else {
                    
$parentId = $parentComment->id;
                    
$depth = $parentComment->depth + 1;
                }
            }

            
$comment = $this->commentsRelation($model)->create([
                
'text'      => $msg,
                
'user_id'   => $user->id,
                
'parent_id' => $parentId,
                
'depth'     => $depth,
                
'ip'        => getIp(),
                
'brow'      => getBrowser(),
            ]);

            
File::query()
                ->
where('relate_type', Comment::$morphName)
                ->
where('relate_id', 0)
                ->
where('user_id', $user->id)
                ->
update(['relate_id' => $comment->id]);

            
$user->increment('point', setting('comment_point'));
            
$user->increment('money', setting('comment_money'));

            
$model->increment('count_comments');

            
$flood->saveState();

            
$this->notifyAfterComment($model, $comment, $msg, $viewRoute, $viewParams, $parentComment);

            if (
$request->wantsJson()) {
                return 
response()->json(['redirect' => route($viewRoute, $viewParams) . '#comment_' . $comment->id]);
            }

            return 
$this->redirectAfterCommentAdded($model, $comment->id)
                ->
with('success', __('main.comment_added_success'));
        }

        if (
$request->wantsJson()) {
            return 
response()->json(['errors' => $validator->getErrors()], 422);
        }

        return 
redirect()->route($viewRoute, $viewParams)
            ->
withInput()
            ->
withErrors($validator->getErrors());
    }

    
/**
     * Уведомление о добавлении комментария
     */
    
private function notifyAfterComment(Model $model, Comment $comment, string $msg, string $viewRoute, array $viewParams, ?Comment $parentComment): void
    
{
        
$url = route($viewRoute, $viewParams, false) . '#comment_' . $comment->id;
        
$title = (string) $model->getAttribute('title');
        
$skip = [];

        
$owner = $model->getRelationValue('user');
        
$owner = $owner instanceof User && $owner->exists ? $owner : null;
        if (
$owner && $owner->notify_comment && $owner->id !== getUser('id') && $parentComment === null) {
            
$login = getUser('login');
            
$owner->sendMessage(null, textNotice('comment_added', compact('login', 'url', 'title') + ['text' => $msg]));
            
$skip[] = $owner->login;
        }

        
$replyUser = $parentComment?->user?->exists ? $parentComment->user : null;
        if (
$replyUser && ! in_array($replyUser->login, $skip, true) && $replyUser->notify_reply && $replyUser->id !== getUser('id')) {
            
$login = getUser('login');
            
$replyUser->sendMessage(null, textNotice('comment_reply', compact('login', 'url', 'title') + ['text' => $msg]));
            
$skip[] = $replyUser->login;
        }

        
sendNotify($msg, $url, $title, $skip);
    }
}
Онлайн: 2
Реклама