07/09/2018, 17:24

Laravel Controller: Ngưng ngược đãi (2)

Tương tự bài viết trước, mình lại tiếp tục giải thoát cho controller bởi sự ngược đãi của dev khi luôn tống mọi thứ logic, code vào controller. Ta xét một ví dụ cơ bản về đăng ký user sau: /** * Handle a registration request for the application. * * @param Request $request * * @return ...

Tương tự bài viết trước, mình lại tiếp tục giải thoát cho controller bởi sự ngược đãi của dev khi luôn tống mọi thứ logic, code vào controller.
Ta xét một ví dụ cơ bản về đăng ký user sau:

 /**
 * Handle a registration request for the application.
 *
 * @param Request $request
 *
 * @return IlluminateHttpResponse
 */
public function register(Request $request)
{
    if (Auth::check()) {
        return redirect('/home');
    }
    Validator::make($request->all(), [
        'name'     => 'required|string|max:255',
        'email'    => 'required|string|email|max:255|unique:users',
        'password' => 'required|string|min:6|confirmed',
    ])->validate();
    $user = User::create(
        [
            'name'     => $request->input('name'),
            'email'    => $request->input('email'),
            'password' => bcrypt($request->input('password')),
        ]
    );

    Auth::guard()->login($user);
    Mail::send('user.registered', function($message) use ($user) {
        $message->from('[email protected]', 'Your Application');

        $message->to($user->email, $user->name)->subject('Welcome to my app');
    });

    return redirect('/home');
}

Nhìn qua thì có vẻ code thực hiện đúng những yêu cầu có thể có khi đăng ký user. Nhưng càng phát triển lên nó sẽ càng ngày phình to ra, khó đọc, khó bảo trì.
Có nhiều cách tổ chức tùy kinh nghiệm, sở thích và yêu cầu dự án. Bài viết này sẽ dùng Service Layers.
Trước hết tạo file AppServicesUser.php và copy toàn bộ nội dung function register của controller qua

<?php

namespace AppServices;

use IlluminateHttpRequest;

class User
{
    public function register(Request $request)
    {
        if (Auth::check()) {
            return redirect('/home');
        }
        ...
        return redirect('/home');
    }
}

Chỉnh sửa lại controller

<?php

namespace AppHttpControllersAuth;

use AppHttpControllersController;
use AppServicesUser as UserService;
use IlluminateHttpRequest;

class RegisterController extends Controller
{
    /**
     * @var UserService
     */
    protected $user;

    /**
     * Create a new controller instance.
     *
     * @param UserService $user
     */
    public function __construct(UserService $user)
    {
        $this->user = $user;
    }

    /**
     * Handle a registration request for the application.
     *
     * @param Request $request
     *
     * @return IlluminateHttpResponse
     */
    public function register(Request $request)
    {
        return $this->user->register($request);
    }
}

Như vậy mình vừa giải thoát cho controller thành công, hẹn gặp lại các bạn trong các bài viết tiếp theo ?
.
.
.
.
.
.
.
À, hình như có gì đó sai sai ? giờ đến lượt Service bị ngược đãi ? Tiếp tục chỉnh sửa UserService. Ta sẽ xét từng phần code
Check guest

if (Auth::check()) {
    return redirect('/home');
}

User đã đăng nhập tất nhiên không thể đăng ký user, cho quay lại trang home. Nhưng đúng ra phần logic này nên thực hiện ở middleware. Các bạn có thể tự tạo middleware theo ý thích nhưng ở đây laravel đã cung cấp sẵn middleware guest có chức năng tương tự.

public function handle($request, Closure $next, $guard = null)
{
    if (Auth::guard($guard)->check()) {
        return redirect('/home');
    }

    return $next($request);
}

Có nhiều cách gọi middleware nhưng ở đây mình thêm vào route

Route::post('register', 'Auth[email protected]')->middleware('guest');

Validate

Validator::make($request->all(), [
    'name'     => 'required|string|max:255',
    'email'    => 'required|string|email|max:255|unique:users',
    'password' => 'required|string|min:6|confirmed',
])->validate();

Tạo file AppServicesValidation và đưa phần code ở trên qua

<?php

namespace AppServices;

use IlluminateHttpRequest;
use Validator;

class Validation
{
    /**
     * @param Request $request
     */
    public function registerUser($request)
    {
        Validator::make(
            $request->all(),
            [
                'name'     => 'required|string|max:255',
                'email'    => 'required|string|email|max:255|unique:users',
                'password' => 'required|string|min:6|confirmed',
            ]
        )->validate();
    }
}

Chỉnh sửa thêm ValidationService vào UserService

...
use AppServicesValidation as ValidationService;
...

class User
{
    /**
     * @var ValidationService
     */
    protected $validation;

    ...
    public function __construct(ValidationService $validation)
    {
        $this->validation = $validation;
    }

    public function register(Request $request)
    {
        // validator
        $this->validation->registerUser($request);
        // create user
        ...
        // login
        ...
        // send mail
        ...
    }

Cá nhân mình lại thích để phần validate này ở app/Http/Requests, validate trước khi vào đến controller. Tham khảo: Form Request Validation
Các phần còn lại
Thực hiện tương tự phần validate với 1 service riêng, tạo các file
AppServicesAuthentication

<?php

namespace AppServices;

use Auth;

class Authentication
{
    protected function guard()
    {
        return Auth::guard();
    }

    public function login($user)
    {
        $this->guard()->login($user);
    }
}

AppServicesMail

<?php

namespace AppServices;

use IlluminateMailMessage;
use Mail as MailHelper;

class Mail
{
    public function registeredUser($user)
    {
        MailHelper::send(
            'user.registered',
            function ($message) use ($user) {
                /** @var Message $message */
                $message->from('[email protected]', 'Your Application');
                $message->to($user->email, $user->name)->subject('Welcome to my app');
            }
        );
    }
}

File AppServicesUserService hoàn chỉnh

<?php

namespace AppServices;

use AppRepositoriesContractsUser as UserRepository;
use AppServicesAuthentication as AuthService;
use AppServicesMail as MailService;
use AppServicesValidation as ValidationService;
use IlluminateHttpRequest;

class User
{
    /**
     * @var UserRepository
     */
    protected $user;

    /**
     * @var AuthService
     */
    protected $auth;

    /**
     * @var MailService
     */
    protected $mail;

    /**
     * @var ValidationService
     */
    protected $validation;

    /**
     * UserService constructor.
     *
     * @param UserRepository    $user
     * @param AuthService       $auth
     * @param MailService       $mail
     * @param ValidationService $validation
     */
    public function __construct(
        UserRepository $user,
        AuthService $auth,
        MailService $mail,
        ValidationService $validation
    ) {
        $this->user = $user;
        $this->auth = $auth;
        $this->mail = $mail;
        $this->validation = $validation;
    }

    public function register(Request $request)
    {
        // validator
        $this->validation->registerUser($request);
        // create user
        $user = $this->create($request);
        // login
        $this->auth->login($user);
        // send mail
        $this->mail->registeredUser($user);
        
        return redirect('/home');
    }

    public function create(Request $request)
    {
        return $this->user->create(
            [
                'name'         => $request->input('name'),
                'email'         => $request->input('email'),
                'password' => bcrypt($request->input('password')),
            ]
        );
    }
}

Ở trên ta đã kết hợp sử dụng repository, các bạn có thể tìm hiểu rất nhiều bài viết trong viblo với tag repository. Nhưng điều đó cũng không quan trọng, bạn có thể sử dụng User Eloquent tùy ý ?
Đây chỉ là 1 ví dụ đơn giản với lượng yêu cầu logic ít nhưng cách tổ chức này sẽ rất có lợi khi logic nhiều, dự án càng ngày càng phát triển. Ví dụ bạn muốn gửi mail cc thêm cho một số địa chỉ cũng như với điều kiện khác, bạn chỉ cần tìm vào MailService->registeredUser() và sửa, bạn muốn thay đổi điều kiện đăng ký user, bạn tìm ValidationService->registerUser()... Khi đó việc bảo trì sẽ bớt nặng nề hơn với việc mò từng dòng trong function controller dài dài dài dài ?

0