Passed
Push — master ( 35ff5f...63edb8 )
by Ion
03:15
created

UserController   F

Complexity

Total Complexity 62

Size/Duplication

Total Lines 541
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 212
c 1
b 0
f 0
dl 0
loc 541
rs 3.44
wmc 62

10 Methods

Rating   Name   Duplication   Size   Complexity  
A resendActivationCode() 0 24 4
A loginWithGoogle() 0 35 5
A register() 0 22 3
A activateAccount() 0 24 4
A changeUserPicture() 0 20 3
A loginWithRememberToken() 0 34 5
B updateUser() 0 38 7
A loginWithFacebook() 0 35 5
A loginWithTwitter() 0 35 5
A login() 0 26 5

How to fix   Complexity   

Complex Class

Complex classes like UserController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use UserController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace App\Http\Controllers;
4
5
use App\Constants\TranslationCode;
6
use App\Models\User;
7
use App\Models\UserToken;
8
use App\Services\LogService;
9
use App\Services\UserService;
10
use Carbon\Carbon;
11
use Exception;
12
use Illuminate\Http\JsonResponse;
13
use Illuminate\Http\Request;
14
use Illuminate\Support\Facades\Auth;
15
use Illuminate\Support\Facades\DB;
16
use Illuminate\Support\Facades\Hash;
17
use Illuminate\Support\Facades\Log;
18
use Laravel\Socialite\Facades\Socialite;
19
use Laravel\Socialite\Two\User as SocialiteUser;
20
21
/**
22
 * Class UserController
23
 *
24
 * @package App\Http\Controllers
25
 */
26
class UserController extends Controller
27
{
28
    /** @var UserService */
29
    private $userService;
30
31
    /**
32
     * UserController constructor.
33
     */
34
    public function __construct()
35
    {
36
        parent::__construct();
37
38
        $this->userService = new UserService();
39
    }
40
41
    /**
42
     * Login user with email and password or remember token
43
     *
44
     * @param Request $request
45
     *
46
     * @return JsonResponse
47
     */
48
    public function login(Request $request)
49
    {
50
        try {
51
            $validator = $this->userService->validateLoginRequest($request);
52
53
            if (!$validator->passes()) {
54
                return $this->userErrorResponse($validator->messages());
55
            }
56
57
            $user = $this->userService->loginUser($request->only('email', 'password'));
58
59
            if (!$user) {
60
                return $this->userErrorResponse(['credentials' => TranslationCode::ERROR_CREDENTIALS_INVALID]);
61
            }
62
63
            if ($user->status === User::STATUS_UNCONFIRMED) {
64
                return $this->userErrorResponse(['account' => TranslationCode::ERROR_ACCOUNT_UNACTIVATED]);
65
            }
66
67
            $loginData = $this->userService->generateLoginData($user, $request->has('remember'));
68
69
            return $this->successResponse($loginData);
70
        } catch (Exception $e) {
71
            Log::error(LogService::getExceptionTraceAsString($e));
72
73
            return $this->errorResponse();
74
        }
75
    }
76
77
    /**
78
     * Login with remember token
79
     *
80
     * @param Request $request
81
     *
82
     * @return JsonResponse
83
     */
84
    public function loginWithRememberToken(Request $request)
85
    {
86
        try {
87
            $validator = $this->userService->validateTokenLoginRequest($request);
88
89
            if (!$validator->passes()) {
90
                return $this->userErrorResponse($validator->messages());
91
            }
92
93
            $rememberToken = $request->get('rememberToken');
94
95
            $user = $this->userService->loginUserWithRememberToken($rememberToken);
96
97
            if (!$user) {
98
                return $this->userErrorResponse(['rememberToken' => TranslationCode::ERROR_REMEMBER_TOKEN_INVALID]);
99
            }
100
101
            if ($user->status === User::STATUS_UNCONFIRMED) {
102
                return $this->userErrorResponse(['account' => TranslationCode::ERROR_ACCOUNT_UNACTIVATED]);
103
            }
104
105
            DB::beginTransaction();
106
107
            $this->userService->updateRememberTokenValability($rememberToken);
108
109
            $loginData = $this->userService->generateLoginData($user);
110
111
            DB::commit();
112
113
            return $this->successResponse($loginData);
114
        } catch (Exception $e) {
115
            Log::error(LogService::getExceptionTraceAsString($e));
116
117
            return $this->errorResponse();
118
        }
119
    }
120
121
    /**
122
     * Login with facebook
123
     *
124
     * @param Request $request
125
     *
126
     * @return JsonResponse
127
     */
128
    public function loginWithFacebook(Request $request)
129
    {
130
        try {
131
            $validator = $this->userService->validateFacebookLoginRequest($request);
132
133
            if (!$validator->passes()) {
134
                return $this->userErrorResponse($validator->messages());
135
            }
136
137
            $token = $request->get('accessToken');
138
139
            /** @var SocialiteUser $facebookUser */
140
            $facebookUser = Socialite::driver('facebook')->userFromToken($token);
0 ignored issues
show
Bug introduced by
The method userFromToken() does not exist on Laravel\Socialite\Contracts\Provider. It seems like you code against a sub-type of Laravel\Socialite\Contracts\Provider such as Laravel\Socialite\Two\AbstractProvider. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

140
            $facebookUser = Socialite::driver('facebook')->/** @scrutinizer ignore-call */ userFromToken($token);
Loading history...
141
142
            if ($facebookUser->getId() !== $request->get('facebookId')) {
143
                return $this->userErrorResponse(['token' => TranslationCode::ERROR_TOKEN_MISMATCH]);
144
            }
145
146
            if (!$facebookUser->getEmail()) {
147
                return $this->userErrorResponse(['permission' => TranslationCode::ERROR_PERMISSION_EMAIL]);
148
            }
149
150
            DB::beginTransaction();
151
152
            $user = $this->userService->loginUserWithSocial($facebookUser, $this->baseService->getLanguage($request), 'facebook_id');
153
154
            $loginData = $this->userService->generateLoginData($user);
155
156
            DB::commit();
157
158
            return $this->successResponse($loginData);
159
        } catch (Exception $e) {
160
            Log::error(LogService::getExceptionTraceAsString($e));
161
162
            return $this->errorResponse();
163
        }
164
    }
165
166
    /**
167
     * Login with twitter
168
     *
169
     * @param Request $request
170
     *
171
     * @return JsonResponse
172
     */
173
    public function loginWithTwitter(Request $request)
174
    {
175
        try {
176
            $validator = $this->userService->validateTwitterLoginRequest($request);
177
178
            if (!$validator->passes()) {
179
                return $this->userErrorResponse($validator->messages());
180
            }
181
182
            $token = $request->get('accessToken');
183
184
            /** @var SocialiteUser $twitterUser */
185
            $twitterUser = Socialite::driver('twitter')->userFromToken($token);
186
187
            if ($twitterUser->getId() !== $request->get('twitter_id')) {
188
                return $this->userErrorResponse(['token' => TranslationCode::ERROR_TOKEN_MISMATCH]);
189
            }
190
191
            if (!$twitterUser->getEmail()) {
192
                return $this->userErrorResponse(['permission' => TranslationCode::ERROR_PERMISSION_EMAIL]);
193
            }
194
195
            DB::beginTransaction();
196
197
            $user = $this->userService->loginUserWithSocial($twitterUser, $this->baseService->getLanguage($request), 'twitter_id');
198
199
            $loginData = $this->userService->generateLoginData($user);
200
201
            DB::commit();
202
203
            return $this->successResponse($loginData);
204
        } catch (Exception $e) {
205
            Log::error(LogService::getExceptionTraceAsString($e));
206
207
            return $this->errorResponse();
208
        }
209
    }
210
211
    /**
212
     * Login with google
213
     *
214
     * @param Request $request
215
     *
216
     * @return JsonResponse
217
     */
218
    public function loginWithGoogle(Request $request)
219
    {
220
        try {
221
            $validator = $this->userService->validateGoogleLoginRequest($request);
222
223
            if (!$validator->passes()) {
224
                return $this->userErrorResponse($validator->messages());
225
            }
226
227
            $token = $request->get('accessToken');
228
229
            /** @var SocialiteUser $googleUser */
230
            $googleUser = Socialite::driver('twitter')->userFromToken($token);
231
232
            if ($googleUser->getId() !== $request->get('google_id')) {
233
                return $this->userErrorResponse(['token' => TranslationCode::ERROR_TOKEN_MISMATCH]);
234
            }
235
236
            if (!$googleUser->getEmail()) {
237
                return $this->userErrorResponse(['permission' => TranslationCode::ERROR_PERMISSION_EMAIL]);
238
            }
239
240
            DB::beginTransaction();
241
242
            $user = $this->userService->loginUserWithSocial($googleUser, $this->baseService->getLanguage($request), 'google_id');
243
244
            $loginData = $this->userService->generateLoginData($user);
245
246
            DB::commit();
247
248
            return $this->successResponse($loginData);
249
        } catch (Exception $e) {
250
            Log::error(LogService::getExceptionTraceAsString($e));
251
252
            return $this->errorResponse();
253
        }
254
    }
255
256
    /**
257
     * Register the user, send activation code on email
258
     *
259
     * @param Request $request
260
     *
261
     * @return JsonResponse
262
     */
263
    public function register(Request $request)
264
    {
265
        try {
266
            $validator = $this->userService->validateRegisterRequest($request);
267
268
            if (!$validator->passes()) {
269
                return $this->userErrorResponse($validator->messages());
270
            }
271
272
            $request->merge(['password' => Hash::make($request->get('password'))]);
273
274
            DB::beginTransaction();
275
276
            $this->userService->registerUser($request, $this->baseService->getLanguage($request));
277
278
            DB::commit();
279
280
            return $this->successResponse();
281
        } catch (Exception $e) {
282
            Log::error(LogService::getExceptionTraceAsString($e));
283
284
            return $this->errorResponse();
285
        }
286
    }
287
288
    /**
289
     * Generate and send a forgot code on email
290
     *
291
     * @param Request $request
292
     *
293
     * @return JsonResponse
294
     */
295
    public function forgotPassword(Request $request)
296
    {
297
        try {
298
            $validator = $this->userService->validateForgotPasswordRequest($request);
299
300
            if (!$validator->passes()) {
301
                return $this->userErrorResponse($validator->messages());
302
            }
303
304
            $user = User::whereEncrypted('email', $request->get('email'))->first();
305
306
            if ($user->status === User::STATUS_UNCONFIRMED) {
307
                return $this->userErrorResponse(['account' => TranslationCode::ERROR_ACCOUNT_UNACTIVATED]);
308
            }
309
310
            if ($user->updated_at->addMinute() > Carbon::now()) {
311
                return $this->userErrorResponse(['forgot' => TranslationCode::ERROR_FORGOT_CODE_SEND_COOLDOWN]);
312
            }
313
314
            DB::beginTransaction();
315
316
            $this->userService->sendForgotPasswordCode($user, $this->baseService->getLanguage($request));
317
318
            DB::commit();
319
320
            return $this->successResponse();
321
        } catch (Exception $e) {
322
            Log::error(LogService::getExceptionTraceAsString($e));
323
324
            return $this->errorResponse();
325
        }
326
    }
327
328
    /**
329
     * Change password with generated code
330
     *
331
     * @param Request $request
332
     *
333
     * @return JsonResponse
334
     */
335
    public function changePassword(Request $request)
336
    {
337
        try {
338
            $validator = $this->userService->validateChangePasswordRequest($request);
339
340
            if (!$validator->passes()) {
341
                return $this->userErrorResponse($validator->messages());
342
            }
343
344
            /** @var User $user */
345
            $user = User::whereEncrypted('email', $request->get('email'))
346
                ->where('forgot_code', $request->get('code'))
347
                ->first();
348
349
            if (!$user) {
0 ignored issues
show
introduced by
$user is of type App\Models\User, thus it always evaluated to true.
Loading history...
350
                return $this->userErrorResponse(['forgot' => TranslationCode::ERROR_FORGOT_CODE_INVALID]);
351
            }
352
353
            if (Carbon::parse($user->forgot_time)->addHour() < Carbon::now()) {
354
                return $this->userErrorResponse(['forgot' => TranslationCode::ERROR_FORGOT_PASSED_1H]);
355
            }
356
357
            DB::beginTransaction();
358
359
            $this->userService->updatePassword($user, $request->get('password'));
360
361
            DB::commit();
362
363
            return $this->successResponse();
364
        } catch (Exception $e) {
365
            Log::error(LogService::getExceptionTraceAsString($e));
366
367
            return $this->errorResponse();
368
        }
369
    }
370
371
    /**
372
     * Activate account
373
     *
374
     * @param Request $request
375
     *
376
     * @return JsonResponse
377
     */
378
    public function activateAccount(Request $request)
379
    {
380
        try {
381
            $validator = $this->userService->validateActivateAccountOrChangeEmailRequest($request);
382
383
            if (!$validator->passes()) {
384
                return $this->userErrorResponse($validator->messages());
385
            }
386
387
            DB::beginTransaction();
388
389
            $activated = $this->userService->activateUserAccount($request->get('email'), $request->get('code'));
390
391
            if (!$activated) {
0 ignored issues
show
introduced by
The condition $activated is always true.
Loading history...
392
                return $this->userErrorResponse(['code' => TranslationCode::ERROR_CODE_INVALID]);
393
            }
394
395
            DB::commit();
396
397
            return $this->successResponse();
398
        } catch (Exception $e) {
399
            Log::error(LogService::getExceptionTraceAsString($e));
400
401
            return $this->errorResponse();
402
        }
403
    }
404
405
    /**
406
     * Resend activation code
407
     *
408
     * @param Request $request
409
     *
410
     * @return JsonResponse
411
     */
412
    public function resendActivationCode(Request $request)
413
    {
414
        try {
415
            $validator = $this->userService->validateResendActivationCodeRequest($request);
416
417
            if (!$validator->passes()) {
418
                return $this->userErrorResponse($validator->messages());
419
            }
420
421
            DB::beginTransaction();
422
423
            $error = $this->userService->resendRegisterMail($request, $this->baseService->getLanguage($request));
424
425
            DB::commit();
426
427
            if (!$error) {
428
                return $this->successResponse();
429
            } else {
430
                return $this->userErrorResponse($error);
431
            }
432
        } catch (Exception $e) {
433
            Log::error(LogService::getExceptionTraceAsString($e));
434
435
            return $this->errorResponse();
436
        }
437
    }
438
439
    /**
440
     * Logout user
441
     *
442
     * @param Request $request
443
     *
444
     * @return JsonResponse
445
     */
446
    public function logout(Request $request)
447
    {
448
        try {
449
            /** @var User $user */
450
            $user = Auth::user();
451
452
            if ($request->has('rememberToken')) {
453
                DB::beginTransaction();
454
455
                UserToken::where('token', $request->get('rememberToken'))
456
                    ->where('user_id', $user->id)
457
                    ->where('type', UserToken::TYPE_REMEMBER_ME)
458
                    ->delete();
459
460
                DB::commit();
461
            }
462
463
            return $this->successResponse();
464
        } catch (Exception $e) {
465
            Log::error(LogService::getExceptionTraceAsString($e));
466
467
            return $this->errorResponse();
468
        }
469
    }
470
471
    /**
472
     * Get logged user
473
     *
474
     * @param Request $request
475
     *
476
     * @return JsonResponse
477
     */
478
    public function getUser(Request $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

478
    public function getUser(/** @scrutinizer ignore-unused */ Request $request)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
479
    {
480
        try {
481
            $user = Auth::user();
482
483
            return $this->successResponse($user);
484
        } catch (Exception $e) {
485
            Log::error(LogService::getExceptionTraceAsString($e));
486
487
            return $this->errorResponse();
488
        }
489
490
    }
491
492
    /**
493
     * Update profile
494
     *
495
     * @param Request $request
496
     *
497
     * @return JsonResponse
498
     */
499
    public function updateUser(Request $request)
500
    {
501
        try {
502
            /** @var User $user */
503
            $user = Auth::user();
504
505
            $validator = $this->userService->validateUpdateUserRequest($request);
506
507
            if (!$validator->passes()) {
508
                return $this->userErrorResponse($validator->messages());
509
            }
510
511
            $email = $request->get('email');
512
513
            if ($user->email !== $email) {
514
                /** @var User $userExists */
515
                $userExists = User::whereEncrypted('email', $email)->first();
516
517
                if ($userExists) {
0 ignored issues
show
introduced by
$userExists is of type App\Models\User, thus it always evaluated to true.
Loading history...
518
                    return $this->userErrorResponse(['email' => TranslationCode::ERROR_EMAIL_REGISTERED]);
519
                }
520
            }
521
522
            if ($request->has('newPassword') && !app('hash')->check($request->get('oldPassword'), $user->password)) {
523
                return $this->userErrorResponse(['oldPassword' => TranslationCode::ERROR_OLD_PASSWORD_WRONG]);
524
            }
525
526
            DB::beginTransaction();
527
528
            $this->userService->updateLoggedUser($user, $request);
529
530
            DB::commit();
531
532
            return $this->successResponse($user);
533
        } catch (Exception $e) {
534
            Log::error(LogService::getExceptionTraceAsString($e));
535
536
            return $this->errorResponse();
537
        }
538
    }
539
540
    /**
541
     * Change picture
542
     *
543
     * @param Request $request
544
     *
545
     * @return JsonResponse
546
     */
547
    public function changeUserPicture(Request $request)
548
    {
549
        try {
550
            $validator = $this->userService->validateUpdateUserPictureRequest($request);
551
552
            if (!$validator->passes()) {
553
                return $this->userErrorResponse($validator->messages());
554
            }
555
556
            DB::beginTransaction();
557
558
            $this->userService->updateLoggedUserPicture($request->file('picture'));
0 ignored issues
show
Bug introduced by
It seems like $request->file('picture') can also be of type Illuminate\Http\UploadedFile[] and array and null; however, parameter $picture of App\Services\UserService...dateLoggedUserPicture() does only seem to accept Symfony\Component\HttpFoundation\File\UploadedFile, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

558
            $this->userService->updateLoggedUserPicture(/** @scrutinizer ignore-type */ $request->file('picture'));
Loading history...
559
560
            DB::commit();
561
562
            return $this->successResponse();
563
        } catch (Exception $e) {
564
            Log::error(LogService::getExceptionTraceAsString($e));
565
566
            return $this->errorResponse();
567
        }
568
    }
569
}
570