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

visavi / rotor / 37155982131

03 Oct 2026 09:41PM UTC coverage: 65.362% (+0.2%) from 65.184%
37155982131

push

github

visavi
Вложения api через FileService, общий лимит с файлами, загруженными заранее, scope File::pending

  - Сообщения и комментарии api сохраняют файлы через attachUploaded: видео конвертируется, сообщения забирают файлы, загруженные заранее
  - FileService::rules вычитает из maxfiles файлы, ждущие привязки к записи
  - Аватар, фото профиля и стикеры читают расширения через extensionList
  - FileService::accept: пустой список не выводит accept=""
  - File::pending($type, $userId) вместо ручного where(relate_id, 0)

12 of 23 new or added lines in 10 files covered. (52.17%)

4908 of 7509 relevant lines covered (65.36%)

24.43 hits per line

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

58.46
/app/Http/Controllers/MessageController.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace App\Http\Controllers;
6

7
use App\Models\Dialogue;
8
use App\Models\File;
9
use App\Models\Flood;
10
use App\Models\Message;
11
use App\Models\User;
12
use App\Support\Validator;
13
use Illuminate\Database\Query\JoinClause;
14
use Illuminate\Http\RedirectResponse;
15
use Illuminate\Http\Request;
16
use Illuminate\Support\Facades\DB;
17
use Illuminate\View\View;
18
use Symfony\Component\HttpFoundation\Response;
19

20
class MessageController extends Controller
21
{
22
    public ?User $user;
23

24
    /**
25
     * Конструктор
26
     */
27
    public function __construct()
5 ✔
28
    {
29
        $this->middleware(function ($request, $next) {
5 ✔
30
            $this->user = $request->user();
4 ✔
31

32
            return $next($request);
4 ✔
33
        });
5 ✔
34
    }
35

36
    /**
37
     * Главная страница
38
     */
39
    public function index(): View
2 ✔
40
    {
41
        $lastMessage = Dialogue::query()
2 ✔
42
            ->select(
2 ✔
43
                'author_id',
2 ✔
44
                DB::raw('max(message_id) as message_id'),
2 ✔
45
                DB::raw('min(case when reading then 1 else 0 end) as all_reading')
2 ✔
46
            )
2 ✔
47
            ->where('user_id', $this->user->id)
2 ✔
48
            ->groupBy('author_id');
2 ✔
49

50
        $messages = Message::query()
2 ✔
51
            ->select('d.*', 'm.text', 'd2.all_reading', 'd3.reading as recipient_read')
2 ✔
52
            ->from('messages as m')
2 ✔
53
            ->join('dialogues as d', 'd.message_id', 'm.id')
2 ✔
54
            ->joinSub($lastMessage, 'd2', static function (JoinClause $join) {
2 ✔
55
                $join->on('d.message_id', 'd2.message_id');
2 ✔
56
            })
2 ✔
57
            ->leftJoin('dialogues as d3', function ($join) {
2 ✔
58
                $join->on('d.user_id', 'd3.author_id')
2 ✔
59
                    ->whereColumn('d.message_id', 'd3.message_id');
2 ✔
60
            })
2 ✔
61
            ->where('d.user_id', $this->user->id)
2 ✔
62
            ->with('author')
2 ✔
63
            ->orderByDesc('d.created_at')
2 ✔
64
            ->paginate(setting('privatpost'));
2 ✔
65

66
        return view('messages/index', compact('messages'));
2 ✔
67
    }
68

69
    /**
70
     * Диалог
71
     */
72
    public function talk(string $login): View
3 ✔
73
    {
74
        if (is_numeric($login)) {
3 ✔
75
            $user = (new User())->setAttribute('id', $login);
×
76
        } else {
77
            $user = getUserByLogin($login);
3 ✔
78

79
            if (! $user) {
3 ✔
80
                abort(404, __('validator.user'));
×
81
            }
82
        }
83

84
        if ($user->id === $this->user->id) {
3 ✔
85
            abort(200, __('messages.empty_dialogue'));
1 ✔
86
        }
87

88
        $messages = Message::query()
2 ✔
89
            ->select('d.*', 'm.id', 'm.text', 'd2.reading as recipient_read')
2 ✔
90
            ->from('messages as m')
2 ✔
91
            ->join('dialogues as d', 'd.message_id', 'm.id')
2 ✔
92
            ->leftJoin('dialogues as d2', function ($join) {
2 ✔
93
                $join->on('d.user_id', 'd2.author_id')
2 ✔
94
                    ->whereColumn('d.message_id', 'd2.message_id');
2 ✔
95
            })
2 ✔
96
            ->where('d.user_id', $this->user->id)
2 ✔
97
            ->where('d.author_id', $user->id)
2 ✔
98
            ->orderByDesc('d.created_at')
2 ✔
99
            ->with('user', 'author', 'files')
2 ✔
100
            ->paginate(setting('privatpost'));
2 ✔
101

102
        Dialogue::query()
2 ✔
103
            ->where('user_id', $this->user->id)
2 ✔
104
            ->where('author_id', $user->id)
2 ✔
105
            ->where('reading', 0)
2 ✔
106
            ->update(['reading' => 1]);
2 ✔
107

108
        $files = File::query()
2 ✔
109
            ->pending(Message::$morphName, $this->user->id)
2 ✔
110
            ->ordered()
2 ✔
111
            ->get();
2 ✔
112

113
        $countMessages = $this->user->getCountNewMessages();
2 ✔
114
        $view = $user->id ? 'messages/talk' : 'messages/talk_system';
2 ✔
115

116
        return view($view, compact('messages', 'user', 'files', 'countMessages'));
2 ✔
117
    }
118

119
    /**
120
     * Отправка сообщений
121
     */
122
    public function send(Request $request, Validator $validator, Flood $flood): RedirectResponse
2 ✔
123
    {
124
        $login = $request->input('user');
2 ✔
125
        $msg = $request->input('msg');
2 ✔
126

127
        $user = getUserByLogin($login);
2 ✔
128

129
        if (! $user) {
2 ✔
130
            abort(404, __('validator.user'));
2 ✔
131
        }
132

133
        $validator
×
134
            ->length($msg, setting('comment_text_min'), setting('comment_text_max'), ['msg' => __('validator.text')])
×
135
            ->false($flood->isFlood(), ['msg' => __('validator.flood', ['sec' => $flood->getPeriod()])])
×
136
            ->notEqual($user->id, $this->user->id, __('messages.send_yourself'));
×
137

138
        if (! captchaVerify() && $this->user->point < setting('privatprotect')) {
×
139
            $validator->addError(['protect' => __('validator.captcha')]);
×
140
        }
141

142
        if ($validator->isValid()) {
×
143
            $message = $user->sendMessage($this->user, (string) $msg);
×
144

145
            File::query()
×
NEW
146
                ->pending(Message::$morphName, $this->user->id)
×
147
                ->update(['relate_id' => $message->id]);
×
148

149
            $flood->saveState();
×
150

151
            return redirect('messages/talk/' . $user->login)
×
152
                ->with('success', __('messages.success_sent'));
×
153
        }
154

155
        return redirect('messages/talk/' . $user->login)
×
156
            ->withInput()
×
157
            ->withErrors($validator->getErrors());
×
158
    }
159

160
    /**
161
     * Удаление переписки
162
     */
163
    public function delete(int $uid, Request $request, Validator $validator): RedirectResponse
×
164
    {
165
        $page = int($request->input('page', 1));
×
166

167
        $dialogues = Dialogue::query()
×
168
            ->where('user_id', $this->user->id)
×
169
            ->where('author_id', $uid)
×
170
            ->get();
×
171

172
        $validator
×
173
            ->notEmpty($dialogues->count(), ['user' => __('messages.empty_dialogue')])
×
174
            ->empty($this->user->getCountNewMessages(), __('messages.unread_messages'));
×
175

176
        if (! $validator->isValid()) {
×
177
            return redirect('messages?page=' . $page)
×
178
                ->withErrors($validator->getErrors());
×
179
        }
180

181
        foreach ($dialogues as $dialogue) {
×
182
            $dialogue->delete();
×
183
        }
184

185
        return redirect('messages?page=' . $page)
×
186
            ->with('success', __('messages.success_deleted'));
×
187
    }
188

189
    /**
190
     * New messages
191
     */
192
    public function newMessages(Request $request): Response
3 ✔
193
    {
194
        if (! $request->ajax()) {
3 ✔
195
            return redirect('/');
2 ✔
196
        }
197

198
        $countMessages = Dialogue::query()
1 ✔
199
            ->where('user_id', $this->user->id)
1 ✔
200
            ->where('reading', 0)
1 ✔
201
            ->count();
1 ✔
202

203
        if ($countMessages) {
1 ✔
204
            $dialogues = Dialogue::query()
×
205
                ->select(
×
206
                    'author_id',
×
207
                    DB::raw('max(created_at) as last_created_at')
×
208
                )
×
209
                ->selectRaw('count(*) as cnt')
×
210
                ->where('user_id', $this->user->id)
×
211
                ->where('reading', 0)
×
212
                ->with('author')
×
213
                ->groupBy('author_id')
×
214
                ->limit(3)
×
215
                ->get();
×
216

217
            if ($dialogues->isNotEmpty()) {
×
218
                $view = view('messages/_new', compact('dialogues'))->render();
×
219

220
                return response()->json([
×
221
                    'success'       => true,
×
222
                    'dialogues'     => $view,
×
223
                    'countMessages' => $countMessages,
×
224
                ]);
×
225
            }
226
        }
227

228
        return response()->json(['success' => false]);
1 ✔
229
    }
230
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc