Những thay đổi của Laravel ở phiên bản 5.5 - Phần 2

Ở phần 1, mình đã liệt kê một số thay đổi nhỏ tuy nhiên khá cần thiết và dễ dàng có thể cập nhật của Laravel ở phiên bản 5.5. Hôm nay mình trở lại với Phần 2 cũng là phần cuối trong series này. Mình sẽ đưa ra những thay đổi lớn hơn và dĩ nhiên sẽ vẫn rất hữu dụng cho việc tìm hiểu Laravel và cập nhật cho dự án.

Request Validation

Request Validation Method

Ở những phiên bản Laravel trước đây, khi muốn validate một request, ví dụ như việc tạo tài khoản Admin thì ở hàm store trong controller:

    $this->validate($request, [
        'first_name' => 'required',
        'last_name' => 'required',
    ]);

Phiên bản 5.5 đã hỗ trợ chúng ta viết code ngắn gọn hơn bằng cách chỉ cần gọi validate ở request object mà hiệu quả không hề thua kém cách cũ:

    $data = request()->validate([
        'title' => 'required',
        'artist' => 'required',
        'description' => 'required',
    ]);

Một tác dụng khác từ kiểu gọi validation này là giá trị trả về hoạt động như Request :: only(), chỉ trả lại các key hoặc field được cung cấp khi gọi validation. Việc chỉ trả lại các key hoặc field được cung cấp sẽ rất tiện lợi cho việc debug, tránh việc sử dụng Request::all().

Custom Validation Rules

Trong các phiên bản của Laravel kể cả 5.5, khi muons customize validation chúng ta sẽ sử dụng method:

    validator::extend()

Ở phiên bản này, việc sử dụng cách viết Rules mới sẽ dễ dàng hơn và code sẽ clear hơn. Mình xin được giới thiệu lại đoạn code mà Taylor Otwell đã dùng để nói về tính năng này. Đoạn code kiểm tra một Github Repo và branch có tồn tại hay không:

namespace App\Rules;
use App\Source;
use Illuminate\Contracts\Validation\Rule;
class ValidRepository implements Rule
{
    /**
     * The source control provider instance.
     *
     * @var \App\Source
     */
    public $source;
    /**
     * The branch name.
     *
     * @var string
     */
    public $branch;
    /**
     * Create a new rule instance.
     *
     * @param  \App\Source  $source
     * @param  string  $branch
     * @return void
     */
    public function __construct($source, $branch)
    {
        $this->source = $source;
        $this->branch = $branch;
    }
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        if (! $this->source instanceof Source) {
            return false;
        }
        return $this->source->client()->validRepository(
            $value, $this->branch
        );
    }
    /**
     * Get the validation error message.
     *
     * @return string
     */

Method passes sẽ nhận tham số attributevalue từ Laravel Validator. attribute chính là tên của trường sẽ được validate, trong khi value sẽ chứa giá trị của trường đó. Method này chỉ cần trả về true hoặc false tùy thuộc vào điều khiện value có hợp lệ hay không. Source là Eloquent Model đại diện cho một nhà cung cấp dịch vụ quản lý mã nguồn như Github. Method message cần trả về thông báo lỗi thích hợp khi dữ liệu không hợp lệ. Tất nhiên, bạn nên lấy dữ liệu từ validation files. Khi chúng ta đã khai báo xong "custom validation rule", chúng ta có thể sử dụng nó trong một Request. Để gán Rule cho một trường, chúng ta đơn giản là để nó trong array of rules

    use App\Rules\ValidRepository;

    $request->validate([
        'repository' => [
            'required', 
            new ValidRepository($this->source(), $request->branch),
        ],
    ]);

Ngoài ra, để tạo một rule mới ta có thể dùng lệnh Artisan sau:

    $ php artisan make:rule MyCustomRule

Whoops Package

Từ phiên bản Laravel 4.x có cung cấp package flip/whoops giúp thông báo lỗi, debug và nhiều tiện ích hay ho khác. Và nó đã trở lại ở phiên bản 5.5.

Collection Dumping

Thông thường khi debug dữ liệu trong Laravel chúng ta thường sử dụng method dd(), nó có tác dụng tương tự khi ta sử dụng var_dump() kèm die() trong PHP thuần. Tuy nhiên giả sử khi ở trong vòng lặp, bạn muốn xem giá trị mỗi khi vòng lặp chạy mà không làm đứng vòng lặp. Lúc này dùng dd() thì đúng là die luôn nhỉ. Và method dump() sinh ra để làm những việc như thế:

    collect([1,2,3])map(function($i) { 
        return $i * 2; 
    })
    ->reject(function($i){ 
        return $i < 3; 
    });

Muốn xem giá trị của collection đang filter à:

    collect([1,2,3])->map(function($i){ 
        return $i * 2; 
    })->dump()->reject(function($i){ 
        return $i < 3; 
    });

Exception Rendering

Thông thường, ở các phiên bản cũ hơn của Laravel, bạn có thể thêm Exception vào method App\Exceptions\Handler::render(), sau đó gửi trả response dựa vào điều kiện Exception. Trong phiên bản 5.5, bạn chỉ có thể throw exception, và nó có thể trả lời mà không cần thêm xử lý logic trong đó:

    namespace App\Exceptions;

    use App\Song;

    class TerribleSongException extends \Exception
    {
        /**
         * @var \App\Song
         */
        protected $song;

        public function __construct(Song $song)
        {
            $this->song = $song;
        }

        /**
         * @param \Illuminate\Http\Request $request
         */
        public function render($request)
        {
            return response("The song '{$this->song->title}' by '{$this->song->artist}' is terrible.");    
        }
    }

The Responsable Interface

Có vẻ như Responsable Interface ở phiên bản mới này giúp chúng ta tách nhỏ và chuyên môn hoá cho việc response Ví dụ dưới đây sẽ cho thấy điều đó:

    public function store(Request $request)
    {
        $data = request()->validate([
            'title' => 'required',
            'artist' => 'required',
            'description' => 'required',
            'duration' => 'required|numeric',
            'released_on' => 'required|date_format:Y-m-d',
            'gold' => 'boolean',
            'platinum' => 'boolean',
        ]);

        $song = new Song($data);
        $song->save();

        return new NewSongResponse($song);
    }
    namespace App\Http\Responses;

    use App\Song;
    use Illuminate\Contracts\Support\Responsable;

    class NewSongResponse implements Responsable
    {
        /**
         * @var \App\Song
         */
        protected $song;

        /**
         * @param \App\Song $song
         */
        public function __construct(Song $song)
        {
           $this->song = $song; 
        }

        public function toResponse($request)
        {
            if ($request->wantsJson()) {
                return response()
                    ->json($this->song)
                    ->header('Location', route('songs.show', $this->song))
                    ->setStatusCode(201);
            }

            return redirect()
                ->route('songs.show', $this->song);
        }
    }

Trong ví dụ này, nếu bạn thực hiện request qua AJAX, bạn có thể tự động response về kết quả JSON và chuyển hướng đường dẫn sang songs.show.

Kết luận

Đã vài tháng trôi qua kể từ khi phiên bản 5.5 được phát hành chính thức, tuy nhiên mình vẫn muốn hoàn thành series này vì đây là phiên bản mà các dự án trong tương lai sẽ xây dựng hoặc refactor lại code sử dụng Laravel muốn hướng tới. Vì vậy hãy tận dụng những thay đổi hữu ích và chú ý tới những vấn đề tương thích phiên bản để có thể làm việc hiệu quả nhất. Cám ơn các bạn đã đọc.