Laravel 8 có gì mới?

Sự kiện Laracon mới nhất đã được diễn ra vào thứ Tư, 26/08/2020 theo giờ Mỹ, được tổ chức trực tuyến thay vì một hội nghị thông thường. Có tất cả 15 diễn giả đã thực hiện những bài thuyết trình của mình, trong đó có một diễn giả đặc biệt được mọi người đang chờ đợi hơn cả…

Taylor Otwell bước lên bục (ảo) để thông báo về các tính năng sắp có trong phiên bản tiếp theo của Laravel 8. Anh bắt đầu với một số thay đổi nhỏ, trước khi thực sự nói về những tính năng đáng giá và giới thiệu một loạt các các cải tiến mới sẽ có trên Laravel 8 khi nó được phát hành, vào ngày 8 tháng 9 năm 2020 tới đây.

Giờ, chúng ta hãy cùng xem Taylor đã nâng cấp Laravel như thế nào!

Trang Landing Page mới

Trang chủ mặc định khi cài đặt mới đã có một sự nâng cấp và hiện được xây dựng với TailwindCSS đi kèm với các phiên bản sáng / tối. Nó vẫn chứa các liên kết đến các sản phẩm SaaS khác nhau từ Laravel, cũng như các trang cộng đồng. Ngoài ra còn có một liên kết đến Laravel Shop nếu bạn muốn mua một vài món hàng.

Thư mục app/models

Kể từ Laravel 8, tất cả các Models sẽ được mặc định nằm trong thư mục app/models, thay vì nằm bên trong app như các phiên bản trước. Theo một cuộc thăm dò do Taylor thực hiện, hơn 80% nhà phát triển đã phải thực hiện thủ công để làm việc này.

Ví dụ, nếu bạn chạy command php artisan make:model Post, thì class Post sẽ được tự động nằm trong app/models thay vì như trước kia bạn sẽ phải gõ php artisan make:model Models/Post – sẽ bất tiện hơn một chút.

Tuy nhiên, nếu bạn muốn giữ các Model của mình trong thư mục app và xóa thư mục models mới, Laravel sẽ tôn trọng điều đó và tạo các Model bên trong thư mục app như mong muốn của bạn.

Gỡ bỏ Controller namespace prefix

Trong các phiên bản trước, file RouteServiceProvider.php có thuộc tính $namespace được sử dụng để đặt tiền tố (App\Http\Controllers) cho namespace của Controller một cách tự động . Nếu bạn đang sử dụng callable syntax trong file route web.php của mình, thì việc này sẽ dẫn đến việc tiền tố App\Http\Controllers bị chèn vào 2 lần dẫn đến namespace bị sai. Thuộc tính này đã bị gỡ bỏ trong Laravel 8, vì vậy bạn có thể import các Controller của mình vào các file route của mình mà không gặp vấn đề gì.

Cải thiện route caching

Route caching là chức năng mà ở đó Laravel sẽ cache lại tất cả các route của ứng dụng dưới dạng một mảng array giúp cho việc truy vấn route nhanh hơn thay vì phải luôn phân tích các file route để tìm ra chính xác route nào được yêu cầu.

php artisan route:cache

Tuy nhiên cho đến trước Laravel 8, route caching chỉ có thể hoạt động nếu bạn, và các package mà bạn đã cài đặt, không sử dụng Clousure trong route. Nếu không bạn sẽ gặp lỗi khi chạy command bên trên.

Thật may là điều này sẽ không còn nữa với Laravel 8 – hiện đã hỗ trợ route caching cho bất kỳ route nào, kể cả nó có sử dụng Clousure hay không. Và bạn sẽ không còn lý do gì để không sử dụng tính năng tuyệt vời này.

Thuộc tính Blade component

Trong Laravel 7, nếu bạn extend một Blade component (ví dụ: DangerButton component được extend từ Button component) thì DangerButton sẽ không có các thuộc tính được truyền cho nó từ Button. Điều này đã thay đổi trong Laravel 8 – giờ đây tất cả các component con sẽ có các thuộc tính của component cha, giúp việc mở rộng các component dễ dàng hơn.

Cú pháp Event Listener dựa trên Closure rõ ràng hơn

Trong các phiên bản Laravel trước, khi đăng ký một Event Listener dựa trên Closure, trước tiên bạn phải xác định Event class, sau đó đăng ký một Closure và type-hint Event class cho nó, ví dụ:

Event::listen(OrderShipped::class, function(OrderShipped $event) { 
    // Làm gì đó ở đây
});

Thì trong Laravel 8, bạn sẽ có thể bỏ qua tham số đầu tiên vì framework giờ đây đã có thể suy ra nó từ type-hint – ví dụ:

Event::listen(function(OrderShipped $event) { 
        // Làm gì đó ở đây
    });

Queueable anonymous event listeners

Trong Laravel 8, bạn sẽ có thể gửi một Job dựa trên Closure vào hàng đợi từ các callback trong Model Event, ví dụ:

class User extends Model {
    
        protected static function booting() 
        {
            static::created(queueable(function(User $user) {
               // Làm gì đó ở đây
            }))
        }
    }

Các phiên bản Laravel trước đó không thể làm được việc này, mà bạn sẽ phải tạo một Event class và Event Listener class, sau đó implement interface ShouldQueue và sử dụng ShouldQueue trait. Tính năng mới này giúp bạn làm việc nhanh hơn và chính là hàm có namespace đầu tiên được giới thiệu trong Laravel: Illuminate\Events\queueable

Truy cập an toàn trong Maintenance mode

Hiện tại, nếu bạn đang đưa ứng dụng của mình vào trạng thái bảo trì: php artisan down nhưng muốn cấp cho một số người có quyền truy cập vào đó thì cách duy nhất để làm điều này là thông qua danh sách whitelist IP. Điều này không hoạt động tốt và thực sự nhiều khi gây ức chế nếu bạn muốn cấp quyền truy cập cho nhiều người hoặc nếu bạn có địa chỉ IP động thường xuyên thay đổi.

Laravel 8 giờ khác rồi!

Giờ đây, bạn sẽ có thể sử dụng thêm flag “secret” khi đưa trang web của mình vào chế độ bảo trì – ví dụ:

php artisan down --secret=ma-bi-mat

Giá trị của flag secret sẽ trở thành một route mới trong khi trang web của bạn vẫn đang ở chế độ bảo trì. Nếu sau đó bạn điều hướng đến route này (sử dụng ví dụ ở trên, https://www.example.com/ma-bi-mat), thì Laravel sẽ tạo một cookie để hướng ứng dụng của bạn bỏ qua chế độ bảo trì khi bạn truy cập nó (kéo dài trong vài giờ) và chuyển hướng bạn đến trang chủ của ứng dụng, sau đó bạn có thể truy cập như bình thường.

Trang Pre-rendered trong Maintenance mode

Trong các phiên bản Laravel 7 trở về trước, nếu bạn chạy php artisan down để đưa ứng dụng của mình vào chế độ bảo trì trong deploy và sau đó chạy Composer như một phần của quá trình deploy này, ứng dụng của bạn có thể sẽ gặp lỗi trong khi các dependency được thay đổi và file autoload được ghi – có nghĩa là người dùng cuối sẽ nhìn thấy một trang lỗi, thay vì trang chế độ bảo trì như mong đợi.

Vấn đề này đã được giải quyết trong Laravel 8! Bây giờ bạn có thể truyền tên của một view cho flag “render” như một phần của lệnh artisan down, ví dụ:

php artisan down --render="errors::che-do-bao-tri"

Laravel sẽ pre-render view này (trong trường hợp này là error/che-do-bao-tri.blade.php) và sau đó đưa ứng dụng vào chế độ bảo trì. Sau đó, nếu bất kỳ ai cố gắng truy cập vào trang web, họ sẽ thấy view này. Laravel thậm chí sẽ không cố tải file autoload của Composer, nghĩa là lỗi sẽ không được ném ra.

Tuyệt vời!

Xử lý lỗi các Queue Closure

Đôi khi chúng ta có thể gửi một Closure vào hàng đợi cho các tiến trình chạy dưới nền, thì việc xử lý lỗi xảy ra trong các tiến trình này lại khá khó khăn. Bơi nếu điều đó xảy ra, các lỗi sẽ được ghi lại vào bảng failed_jobs trong trong cơ sở dữ liệu, và chúng ta không thể thực thi bất kỳ đoạn code nào khác sau đó.

Giờ đây trong Laravel 8, bạn sẽ có thể đăng ký một callback để chạy khi công việc không được xử lý thành công, thông qua phương thức catch() – tương tự như fail() trong một Job class thông thường. Ví dụ:

dispatch(function() {
        // Làm gì đó ở đây ...
    })->catch(function(Throwable $e) {
        // Xử lý nếu có lỗi được ném ra...
    });

Tùy chỉnh thời gian thử lại sau mỗi Job failed

Bắt đầu với Laravel 8, bạn có thể thêm một phương thức backoff() mới vào các Job class của mình để trả về một mảng các số nguyên nhằm quyết định thời gian chờ giữa các lần thử nếu không thành công – ví dụ:

class ExampleJob
    {
        //
        
        public function backoff()
        {
            return [1, 5, 10];
        }
        
        //
    }

Trong ví dụ trên, nếu Job không thành công trong lần thử đầu tiên, nó sẽ đợi 1 giây trước khi thử lại. Nếu Job sau đó tiếp tục không thành công trong lần thử thứ hai, nó sẽ đợi 5 giây trước khi thử lại. Sau đó, nếu Job vẫn không thành công vào lần thứ ba (và các lần sau), nó sẽ phải đợi 10 giây trước khi được tiếp tục thử lại.

Điều này có thể rất hữu ích nếu bạn đang làm việc với một API giới hạn số lượt truy cập để tăng thời gian chờ đợi mỗi khi Job không thành công.

Gửi Job batching vào queue

Job batching là một tính năng mạnh mẽ mới có trong Laravel 8. Nó sẽ cho phép bạn gửi nhiều Job vào hàng đợi cùng một lúc (trong một “batch”) để được xử lý đồng thời (giả sử bạn có đủ queue worker đợi đang chạy) và đăng ký các callback để kích hoạt khi tất cả các Job đã hoàn thành. Có 3 callback có sẵn, đó là:

  • then() – sẽ kích hoạt khi tất cả các Job trong batch đã hoàn thành và thành công.
  • catch() – sẽ kích hoạt khi có lỗi xảy ra trong batch.
  • finally() – sẽ kích hoạt khi tất cả các Job trong batch đã hoàn thành thực thi (một số có thể đã thành công, một số có thể không)

Tất cả các callback đều có quyền truy cập vào đối tượng $batch chứa nhiều phương thức khác nhau – chẳng hạn như kiểm tra trạng thái, xác định xem có lỗi không, hủy batch, v.v. Ví dụ, chúng ta có thể sử dụng $batch trong catch() để dừng phần còn lại của batch nếu có lỗi xảy ra.

Đây là cách sử dụng để bạn có thể tương tác với batch:

Bus::batch([
        new ImportJob(),
        new ImportJob(),
        new ImportJob(),
        new ImportJob(),
        new ImportJob(),
    ])->then(function(Batch $batch){
        // Xử lý khi tất cả các Job đã được thực thi thành công, như gửi mail thông báo... 
    })->catch(function(Batch $batch) {
        // Xử lý khi có lỗi xảy ra ở đây...
    })->finally(function(Batch $batch) {
        // Tất cả các Job đã được chạy xong
    })->dispatch();

Ngoài ra còn có các tính năng nâng cao như khả năng thêm các Job khác vào một batch hiện có từ bên trong một Job trong batch đó bằng cách gọi $this->batch() trong một Job bất kỳ. Điều này có thể hữu ích nếu bạn đang xử lý một lượng lớn dữ liệu và cần phải làm việc với chunk.

Nếu bạn có UUID của batch, có một phương thức helper khác có sẵn trên Bus – Bus::findBatch($batchUuid). Thao tác này sẽ trả về một đối tượng chứa thông tin về batch – bao gồm cả tiến trình hiện tại. Bạn có thể sử dụng nó để hiển thị tiến trình theo thời gian thực của batch trong giao diện người dùng bằng AJAX.

Cải thiện Rate Limiting

Bắt đầu từ Laravel 8, bạn sẽ có thể xác định nhiều bộ giới hạn truy cập (Rate Limiter) thông qua một facade RateLimiter mới – đặt tên cho bộ giới hạn này và một Closure trả về chi tiết của nó. Ví dụ:

RateLimiter::for('authentication', function(Request $request) {
        return Limit::perMinute(50);
    });

Rate Limiter này có thể được sử dụng với middleware trong file route của bạn, nhưng thay vì truyền vào một số nguyên làm tham số, thì bạn có thể truyền trực tiếp tên của Rate Limiter đã xác định. Ví dụ:

Route::get('/login')->middleware(['throttle:authentication']);

2 đoạn code bên trên sẽ cho phép tối đa 50 yêu cầu/phút đến trang đăng nhập đối với tất cả khách truy cập vì trang không yêu cầu xác thực.

Vì các Rate Limiter sử dụng Closure, nên chúng ta có thể viết logic để xác định điều kiện áp dụng giới hạn này. Ví dụ nếu bạn có route /download/{document} yêu cầu người dùng đăng nhập tài khoản để tải file tài liệu, thì bạn có thể giới hạn người dùng thông thường sẽ chỉ có thể truy cập 1 lần/phút, đồng thời cho phép những người dùng cấp cao hơn có thể truy cập không giới hạn.

Database Schema Dumping

Nếu bạn đang làm việc trên một ứng dụng có số lượng lớn database migration, Laravel 8 có một tính năng mới được gọi là Schema Dumping để giúp bạn thu gọn chúng lại. Để sử dụng, bạn cần chạy php artistan schema:dump – khi đó Laravel sẽ tạo một file SQL trong thư mục database/schema, chứa toàn bộ lược đồ cho cơ sở dữ liệu của bạn dưới dạng SQL thô.

Khi bạn chạy php artian migrate, trước tiên Laravel sẽ tìm kiếm file schema này trước, sau đó mới đến các file migration.

Theo mặc định, command này sẽ không xóa các file migration hiện có. Tuy nhiên, nếu bạn thêm flag --prune vào lệnh, nó sẽ xóa tất cả các file migration và chỉ để lại cho bạn một file schema duy nhất.

Nếu bạn chạy php artistan schema:dump, sau đó tạo file migration mới, sau đó chạy lại lệnh, nó sẽ nối file migration mới này vào file schema hiện có.

Bạn có thể sử dụng nó cho các hệ cơ sở dữ liệu được hỗ trợ của Laravel, bao gồm MySQLPostgreSQL và SQLite – các công cụ cơ sở dữ liệu khác như MSSQL trong tương lai cũng sẽ được hỗ trợ bởi cộng đồng lớn mạnh của Laravel.

Model Factory mới

Model Factory đã được viết lại hoàn toàn trong Laravel 8, khiến chúng trở nên cực kỳ mạnh mẽ và dễ làm việc. Bây giờ các Model Factory này sẽ dựa trên các class, giống như một số package hiện có.

Đối với mỗi Model, bạn sẽ có một Factory class, ví dụ: UserFactory, chứa phương thức definition() trả về một mảng các thuộc tính, tương tự như các Factory trong Laravel 7 trở về trước, ví dụ:

class UserFactory extends Factory
    {
        public function definition()
        {
            return [
                'name'  => $this->faker->name,
                'email' => $this->faker->safeEmail,
                'role'  => 'admin',
            ];
        }
    }

Các Model của bạn sẽ sử dụng trait HasFactory mới, chứa phương thức factory() trả về một instance của Factory class. Trên đó bạn có thể gọi các phương thức quen thuộc như create() hay make() – ví dụ:

use App\Models\User;
    
    $fake_user = User::factory()->create();

Tất nhiên, bạn có thể tùy chọn truyền một giá trị cụ thể cho một mảng thuộc tính mà bạn muốn cung cấp như bạn đã làm trong các phiên bản Laravel trước đó.

Bởi vì các Factory hiện dựa trên class, nên chúng ta sẽ có thể viết chúng một cách trôi chảy và dễ hiểu hơn – ví dụ: nếu bạn muốn tạo 10 người dùng:

$fake_users = User::factory()->times(10)->create();

Ngoài ra còn có những cải tiến đối với các state của Factory – thay vì dựa trên Closure, giờ đây bạn có thể tạo các phương thức tùy chỉnh của riêng mình ngay trên chính các Factory này. Bạn có thể viết bất kỳ logic nào bạn muốn và cũng có thể truyền vào đó các tham số. Đây là một ví dụ đơn giản về việc ghi đè một thuộc tính:

class UserFactory extends Factory
    {   
        public function withActiveStatus()
        {
            return $this->state(fn(array $attributes) => [
                'status' => 'active',
            ]);
        }    
    }

Relationship trong các Model Factory cũng đã được cải thiện rất nhiều trong Laravel 8. Ví dụ dưới đây cho thấy chúng ta đang tạo một model User có các Order:

$user_with_order = User::factory()
        ->create()
        ->has(Order::factory())

Hay bạn thậm chí cũng có thể chain các phương thức này lại với nhau:

$user_with_orders = User::factory()
        ->withActiveStatus()
        ->has(
            Order::factory()
                ->times(5)
                ->withStatus('shipped')
                ->state(function(array $attributes, User $user) {
                    return ['customer_name' => $user->name];
                })
        )
        ->create();

Trong ví dụ trên, chúng ta tạo một User có 5 Order đều ở trạng thái “shipped“, với thuộc tính customer_name của Order được đặt thành tên của User.

Ngoài ra còn có các magic method để làm việc với các mối quan hệ, nghĩa là bạn có thể làm cho mã của mình đơn giản hơn nữa. Lấy chủ đề của ví dụ trên, đây là ví dụ về việc tạo Người dùng với 3 Đơn hàng bằng magic method:

$user_with_orders = User::factory()
        ->hasOrders(3)
        ->create();

Những ví dụ này chỉ đề cập đến mối quan hệ “has many“, nhưng có rất nhiều phương thức (cả thông thường lẫn magic) có sẵn cho nhiều kiểu quan hệ khác nhau.

Và dường như để cho dân tình khỏi xôn xao và than thở vì phải viết lại cả đống Factory, Taylor đã tạo sẵn một package chính thức được viết riêng để có thể hỗ trợ cho định dạng cũ được gọi là laravel/legacy-factories. Package này cũng sẽ cho phép bạn sử dụng cả Factory cũ và mới cùng một lúc.

Yêu quá! ♥

Laravel JetStream & Laravel Fortify

Mặc dù đây không phải là core của Laravel 8, nhưng là một thông báo lớn và gây háo hức đến mức tôi không thể bỏ qua!

Chắc hẳn bạn cũng đã biết: các phiên bản trước của Laravel có một package được gọi là laravel/ui cung cấp khung xác thực cơ bản cho ứng dụng của mình với đẩy đủ cả Frontend và Backend.

Và giờ là lúc say hello với Laravel JetStream – một package mới cho Laravel 8 để mang đến cho bạn một khởi đầu tuyệt vời hơn nữa với các ứng dụng của mình.

Không chỉ có chức năng xác thực người dùng thông thường, mà Laravel JetStream còn là một khung sẽ thiết lập cho bạn các đoạn code cả Frontend lẫn Backend hoàn chỉnh cho nhiều chức năng phổ biến trong các ứng dụng web, bao gồm:

  • Xác thực (đăng nhập, truy cập trang dashboard, đăng xuất, lấy lại mật khẩu, v.v…).
  • Xác thực hai yếu tố (thông qua một ứng dụng như Google Authenticator) mà không cần phải chỉnh sửa một dòng code nào, bao gồm cả các tính năng như mã khôi phục.
  • Quản lý các phiên trình duyệt (cho phép người dùng xem họ đã đăng nhập ở đâu và đăng xuất khỏi tất cả các phiên khác trên các trình duyệt khác).
  • Quản lý mã API token cho người dùng được cung cấp bởi Laravel Sanctum, với đầy đủ các quyền có thể tùy chỉnh (tương tự như scope của oAuth).
  • Chức năng Team (người dùng có thể tạo nhóm, mời người dùng khác vào nhóm, xem các thành viên khác trong nhóm, quản lý vai trò (& quyền) của các thành viên trong nhóm, chuyển đổi giữa nhiều nhóm, v.v…).
  • Chỉnh sửa hồ sơ người dùng (cho phép người dùng cập nhật thông tin chi tiết của họ) bao gồm ảnh hồ sơ (dễ dàng bật / tắt trong cấu hình) và thay đổi mật khẩu.
  • Xóa chức năng tài khoản.
  • Giao diện người dùng được hỗ trợ bởi JavaScript (xem bên dưới) cho các tương tác mượt mà đẹp mắt mà không cần tải lại toàn bộ trang, mang lại cảm giác thực sự rất sướng.
  • Giao diện người dùng CSS được xây dựng bằng Tailwind CSS, bao gồm các thành phần từ thư viện Premium Tailwind UI, mang lại một giao diện tuyệt vời nhưng vẫn dễ dàng tùy chỉnh.

Khi nói đến phần còn lại của giao diện người dùng, có hai lựa chọn – bạn có thể chọn sử dụng Laravel Livewire & Alpine.js hoặc Inertia (với Vue.js). Cả hai đều cung cấp cho bạn các tính năng và giao diện người dùng tương tự nhau – chỉ là cho bạn lựa chọn công nghệ ưa thích của mình mà thôi.

Tất cả các thành phần của giao diện người dùng (như button, form control, card, modal, v.v.) có trong JetSream có thể được tái sử dụng ở những nơi khác trong ứng dụng của bạn, ngay cả các thành phần tương tác thường yêu cầu JavaScript như modals – giúp bạn tiết kiệm vô số thời gian! Và vì nó là một scaffolding package, bạn có toàn quyền kiểm soát việc tùy chỉnh.

Đây thực sự là một bổ sung cực lớn cho hệ sinh thái Laravel và giúp tiết kiệm rất nhiều thời gian cho các nhà phát triển, cho phép họ tập trung vào việc viết logic cho các chức năng khác trong ứng dụng.

Như đã đề cập trước đó, Laravel JetStream sẽ là một gói mã nguồn mở, miễn phí và sẽ được phát hành cùng thời điểm với Laravel 8 – ngày 08/09/2020.

Kết

Chắc hẳn cũng như tôi, cảm giác háo hức là điều đang tràn đầy trong bạn lúc này đúng không? Laravel 8 có một số tính năng mới phải nói tuyệt vời – Taylor và nhóm của anh chắc chắn đã rất bận rộn.

Nếu bạn muốn tự mình xem bài nói chuyện của Taylor, hoặc bất kỳ bài nói chuyện tuyệt vời nào khác (tôi thực sự khuyên bạn nên xem những bài của Jack Ellis và Jonathan Reinink), bạn có thể mua ($29) video từ trang web Laracon (thực sự sử dụng Laravel JetStream!).

Hẹn gặp lại bạn trong các bài viết tiếp theo về Laravel!

Hotline: 0905 063 126
Zalo: 0905.063.126