07/09/2018, 17:05

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

Một dự án khi có nhiều người cùng tham gia, thời gian kéo dài, chức năng tăng lên thì nảy sinh cả tỉ tỉ vấn đề nhức nhối. Một trong những vấn đề đó chính là controller càng ngày càng như 1 đống hổ lốn, khó đọc, khó bảo trì và rất... bự. Chỉ cần nghĩ đến việc fix 1 bug nhỏ nhỏ lúc đăng nhập thôi ...

Một dự án khi có nhiều người cùng tham gia, thời gian kéo dài, chức năng tăng lên thì nảy sinh cả tỉ tỉ vấn đề nhức nhối. Một trong những vấn đề đó chính là controller càng ngày càng như 1 đống hổ lốn, khó đọc, khó bảo trì và rất... bự.
Chỉ cần nghĩ đến việc fix 1 bug nhỏ nhỏ lúc đăng nhập thôi nhưng khi nhìn cái file với số dòng thế này thì ai mà không nản chứ

Một auth controller chỉ đơn giản với việc đăng nhập, đăng ký, đăng xuất thôi mà hơn 1000 dòng. Nếu cần tìm kiếm để đọc và fix bug thì đúng là cực hình. Vì vậy chúng ta cần bắt buộc... giảm cân cho controller. Code càng chia nhỏ thì càng dễ quản lý. Ta sẽ xét một vài giải pháp sau đây.

Single Action Controllers

<?php

namespace AppHttpControllers;

use AppUser;
use AppHttpControllersController;

class ShowProfile extends Controller
{
    /**
     * Show the profile for the given user.
     *
     * @param  int  $id
     * @return Response
     */
    public function __invoke($id)
    {
        return view('user.profile', ['user' => User::findOrFail($id)]);
    }
}

Trong route chỉ cần khai báo

Route::get('user/{id}', 'ShowProfile');

Tham khảo: https://laravel.com/docs/5.4/controllers#single-action-controllers
Khi đó mỗi function sẽ là 1 file controller, khai báo trong route cũng bớt rườm rà như cũ. Khi cần tìm kiếm function thì chỉ cần kiếm file đó là được.

Abstract Factory

Cách này sử dụng thường khi hầu hết các request đều có nhiều điểm giống nhau nên có thể gộp chung vào factory. Ví dụ tất cả dữ liệu không dùng database mà lấy api từ một bên thứ ba. Mỗi request sẽ phải kết nối gọi api, truyền param để thu về kết quả, sau đó xử lý kết quả đó để đổ ra view.

abstract class BusAbstract
{
    protected static $instance = null;
    
    protected $result = [];

    /**
     * @return self
     */
    public final static function getInstance()
    {
        if (static::$instance === null) {
            static::$instance = new static();
        }

        return static::$instance;
    }
    
    /**
     * Create request before calling http client api
     *
     * @param null $param
     * @return mixed
     */
    public abstract function makeRequest($param = null);
    
    /**
     * Handle result before render to view
     *
     * @param null $param
     *
     * @return array $this->result
     */
    public abstract function handleBeforeRender($param = null);
     
    public final function getResult($param = null)
    {
        $this->makeRequest($param)
        $this->getResponses();

        return $this->handleBeforeRender($param);
    }

Ở đây ta có 2 hàm abstract makeRequest và handleBeforeRender cần khai báo ở class ShopDetailByIdcon khi extends BusAbstract

<?php

namespace AppHelpersBus;

class ShopDetailById extends BusAbstract
{
    public function makeRequest($param = null)
    {
        $path = sprintf('/api/shops/%s', $param['id']);
        $this->addRequest($path);
    }

    public function handleBeforeRender($param = null)
    {
        if($this->result) {
            $this->makeCache($param);
        }
        
        return $this->result;
    }
}

Trong code trên, function addRequest là function trong BusAbstract chịu trách nhiệm tạo thông tin cho request sẽ thực hiện khi BusAbstract lấy kết quả và lưu vào $this->result.
Khai báo controller

public function getDetailById($id, Request $request)
{
    $result = ShopDetailById::getInstance()->getResult($request->input());
    
    return view('shop/detail', $data);
}

Như vậy controller cực kỳ gọn nhẹ, dễ đọc và dễ bảo trì.

0