I am creating some simple web application with the user system:
- Regular users can edit their profile (avatar, name, timezone), change password and change their email. There are 3 separate pages for that, one for editing profile (e.g.
www.example.com/edit_profile), one for changing password (e.g. www.example.com/edit_passowrd) and one for changing email (e.g. www.example.com/edit_email).
- Admins can go to the
/admin area and edit profile (avatar, name, timezone), password and email of other user accounts. There is only one page for doing that, for example www.example.com/admin/users/{id}/edit.
There are two controllers, one is UserController for regular users, and the other one is in the Admin subdirectory (Admin/UserController) and it is used when admins edit users.
Because of this I am having a situation where UserController and Admin/UserController actions use the same code/logic, so I decided to extract that logic into its own service-class in app/Services/UserService.
For editing email I have one method in the UserService which I then use in both UserController & Admin/UserController. Then I have one method for editing the name, then one for editing password and so on... and I'm using them like this:
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
// In `Admin/UserController`:
public function update(Request $request, $id)
{
// Some code
$user = $this->userService->setAvatar(Request $request, $user);
$user = $this->userService->setName($request->name, $user);
$user = $this->userService->setTmezone($request->timezone, $user);
$user = $this->userService->setEmail($request->email, $user);
$user = $this->userService->setPassword($request->password, $user);
. . .
$user->save();
return back()->with('success', 'Updated!');
}
// In `UserController`:
public function updateProfile(Request $request)
{
// Some code
$user = $this->userService->setAvatar(Request $request, auth()->user());
$user = $this->userService->setName($request->name, auth()->user());
$user = $this->userService->setTmezone($request->timezone, auth()->user());
. . .
$user->save();
return back()->with('success', 'Updated!');
}
The problem is with the setAvatar(Request $request, User $user) method - it has a lot of checks (if statements) and in many cases I need to return some specific error message to the user. For example:
<?php
namespace App\Services;
use App\User;
use DB;
use Illuminate\Http\Request;
use Intervention\Image\ImageManagerStatic as Image;
class UserService
{
/**
* Set the avatar for the specified user.
*
* @param Request $request
* @param User $user
* @return User|\Illuminate\Http\RedirectResponse
*/
public function setAvatar(Request $request, User $user)
{
// SOME CODE . . .
if ($something) {
return back()->withInput()->withErrors(['avatar' => 'Please upload an avatar']); // <=========
}
// SOME CODE . . .
if ($something_else) {
return back()->withInput()->with(['error' => 'some specific error message']); // <=========
}
// SOME CODE . . .
if ($foo === $bar) {
if (! $request->avatar && $something)
return back()->withInput()->with(['error' => 'another specific error message']); // <=========
}
// SOME CODE . . .
$user->avatar = $this->saveAvatar($request, $user);
return $user;
}
}
As you can see - in many cases I need to return \Illuminate\Http\RedirectResponse with the specific error message:
if ($something) {
return back()->withInput()->with(['error' => 'Some specific error message about $something']);
}
Of course, all these redirects will not work when I use this method in the controller:
public function updateProfile(Request $request)
{
// Some code
$user = $this->userService->setAvatar(Request $request, auth()->user());
// Some code
$user->save();
return back()->with('success', 'Updated!');
}
... and I understand why (it's clear), but I don't know how to handle this. I am not sure what is the proper way do it.
I was thinking about returning false instead of returning back()->withInput()->with(['error' => 'Some specific error message about $something']); - but then I will not know which check (if statement) returned false and what error message to show to the user.
Other way would be to return something like:
['status' => 'error', 'message' => 'specific message']
or
['status' => 'ok', 'user' => $user]
... and then in my controller I would do something like this:
$resonse = $this->userService->setAvatar($request, auth()->user());
if ($resonse['status'] === 'error') return back()->withInput()->with('error', $resonse['message']);
if ($resonse['status'] === 'ok') $user = $resonse['user'];
... but I'm not sure if that's the right way to do it. Or maybe my approach with the class-service is completely wrong?