Tính năng mới và cách nâng cấp version từ Laravel 8 lên Laravel 10

Tính năng mới và cách nâng cấp version từ Laravel 8 lên Laravel 10

Vì sao bạn nên nâng cấp version cho dự án Laravel 8?

Các phiên bản mới của Laravel sẽ được release mỗi năm. Và các đợt release này sẽ có ít hay nhiều những thay đổi, chẳng hạn như cập nhật những tính năng mới, hoặc bỏ đi những tính năng không hợp lí, … Vì vậy, nếu bạn càng để nhiều version cần phải nâng cấp, thì nó càng khó khăn và tốn nhiều thời gian hơn để nâng cấp.  Lí do chính cho sự khó khăn khi nâng cấp như vậy là do dự án của bạn sẽ liên tục trở nên lớn dần theo thời gian, và các hàm, các method sẽ ngày càng phụ thuộc lẫn nhau. Điều này khiến cho việc thay đổi một tính năng nào đó sẽ ảnh hưởng không nhỏ đến các tính năng còn lại. Một điều đáng lưu ý nữa là từ khi Laravel 10 được release, thì Laravel 8 đã không còn được bảo trì cũng như có support cho nó. Vì vậy, ban nên nâng cấp càng sớm càng tốt.

Đó cũng là một trong các nội dung chính của blog này là sẽ chỉ cho bạn cách nâng cấp lên version Laravel 10. Tuy nhiên, việc nâng cấp theo hướng dẫn bên dưới chỉ là thủ công, không phải tự động bằng việc sử dụng công cụ như Laravel Shift (công cụ này tốn phí để dùng), vì vậy bạn hãy cân nhắc nhé!

Giới thiệu tính năng mới trong Laravel 10:

1. Laravel Pennant:

Đây là một package mới trong Laravel 10. Nó cung cấp tính năng feature flag cho ứng dụng của bạn. Nghĩa là bạn có thể flag một tính năng là có thể sử dụng hoặc không thể sử dụng. Để dễ hình dung, giả sử ứng dụng của bạn có một tính năng gọi là “đặt lịch họp – meeting booking” và tính năng này chỉ có boss thực hiện được thôi. Có nghĩa là tính năng này có một thuộc tính gọi là tính khả dụng. Và để kiểm tra tính khả dụng của tính năng đó, bạn có thể khai báo Feature và check nó như bên dưới:

Để học cách dùng package này, bạn hãy xem Laravel Pennant documentation.

2. Process Interaction:

Laravel 10 cung cấp một lớp abstraction để bạn có thể tương tác với trình thực thi của ứng dụng – còn gọi là process. Cụ thể tính năng này cho phép bạn gọi các câu lệnh command, hoặc gọi nó dưới dạng fake để thực hiện testing:

3. Test Profiling:

Lệnh test chạy bằng artisan giờ đây đã có thêm một option parameter mới đó là –profile. Khi truyền option này, nó sẽ giúp bạn biết là file test nào chạy chậm hay chạy nhanh trong bao nhiêu giây.

php artisan test --profile

Kết quả thực thi test khi có truyền –profile:

4. Pest Scaffolding:

Pest là một PHP Testing Framework được cung cấp bởi PHPUnit. Và Laravel giờ đây đã hỗ trợ làm việc với Pest. Khi tạo mới project, bạn có thể chọn mặc định tạo Pest test scaffolding bằng cách truyền option param –pest:

laravel new example-application --pest

Giới thiệu tính năng mới trong Laravel 9:

1. Accessors and Mutators đã đơn giản hơn xưa:

“Accessors and Mutators” hay còn gọi là “getter and setter”. Theo kiểu truyền thống,  để khai báo một getter hay setter thì bạn sẽ thêm một prefix “get”, “set” và theo sau là tên thuộc tính đó. Cách này vẫn còn dùng được trong các version mới chứ không phải đã bị bỏ đi. Tuy nhiên, bạn nên dùng cách mới như bên dưới vì nó gọn và đơn giản hơn nhiều:

2. Enum Attribute Casting:

PHP 8 giờ đã hỗ trợ khai báo enum. Vì thế, Laravel 9 cũng đã cập  nhật tính năng mới này. Giờ đây bạn có thể casting một thuộc tính của Eloquent model bằng enum. Ví dụ, bạn có thể khai báo và sử dụng 1 enum như sau:

3. Route Bindings với Enums:

Tính năng này cho phép binding giá trị của Request parameter bằng enum. Nghĩa là bạn khai báo trước một enum, và binding nó như bên dưới. Nếu request gọi tới truyền vào giá trị không tồn tại trong khai báo của enum thì nó sẽ trả về lỗi HTTP 404, thay cho việc chúng ta phải kiểm tra ở Controller như trước đây:

4. Forced Scoping Of Route Bindings:

Trong các phiên bản trước Laravel, bạn có thể khai báo route như bên dưới để cho scope của Eloquent model thứ hai truyền vào hàm handler tham chiếu đến model thứ nhất. Chẳng hạn, hãy tham khảo cách khai báo route như bên dưới, chúng ta khai báo rằng post này thuộc về một user và có thể sử dụng slug để truy vấn:

Khi báo như trên, Laravel sẽ tự động xác định các trường khoá ngoại (foreign key) dựa vào các quy tắc sẵn (conventions). Tuy nhiên, bạn cần phải khai báo custom key binding cho :slug trước đó, nếu không, nó sẽ không biết cần phải binding model thứ hai truyền vào.

Tuy nhiên thì trong phiên bản mới, bạn chỉ việc gọi đến hàm scopeBinding() là được:

5. Controller Route Groups:

Giờ đây bạn có thể group các route theo controller như sau:

6. Full Text Indexes / Where Clauses:

Truy vấn full-text giờ đã được hỗ trợ với các hàm mới như whereFullText(), orWhereFullText():

7. Laravel Scout Database Engine:

Mục đích chính mà Scout mong muốn mang đến là đơn giản hoá quá trình làm việc của ứng dụng với một full-text search engine. Scout là được xây dựng theo hướng driver based, nghĩa là bạn có thể làm việc với đa đạng các engine như Elasticsearch, Algolia, MeiliSearch, hay MySql, Postgre… mà chỉ thông qua API của Scout là được.

Ý tưởng chủ chốt là nó sẽ cho phép Eloquent model gọi đến một hàm search()  để thực hiện full-text search. Đồng thời, khi bạn thực hiện thêm, xoá, sửa dữ liệu trong database, thì nó cũng sẽ tự động đồng bộ lên full-text engine tương ứng. Tuy nhiên, vì hàm search này chỉ hỗ trợ query đơn giản nên nó không phù hợp với các dự án lớn mà chỉ dùng cho dự án vừa và nhỏ.

Hãy tham khảo lời gọi hàm search dưới đây:

8. Rendering Inline Blade Templates:

Thay vì phải tạo một file blade.php thì giờ đây, mã Blade có thể được biên dịch bằng lời gọi hàm Blade::render(). Đây là một tính năng rất hữu ích trong trường hợp bạn cần gọi AJAX để lấy một partial view để cập nhật lên giao diện của ứng dụng SPA.

9. Slot Name Shortcut:

Trong phiên bản cũ, slot name được truyền thông qua name attribute trong thẻ x-slot. Nhưng với Laravel 9.x, bạn có thể truyền một cách gọn gàng hơn như sau:

10. Lấy giá trị value của Nested Array cho Validation:

Đối với các input dạng nested array, đôi khi bạn sẽ cần phải kiểm tra giá trị của input trước khi quyết định sẽ gắn rule validation nào cho nó. Giờ đây bạn có thể dùng hàm Rule::forEach() cho việc này:

11. Laravel Breeze API & Next.js:

Laravel Breeze starter kit đã được thêm chế độ scaffolding mới đó là “API” để tương thích với các ứng dụng với Next.js làm frontend. Bộ starter kit này giúp bạn nhanh chóng setup một ứng dụng Javascript frontend mà phía backend sẽ được xây dựng với Laravel, đồng thời cho phép xác thực user với Sanctum API.

12. Bunlding with Vite:

Thay vì bundling các file javascript hoặc css bằng Mix webpack, Laravel đang chuyển dần sang sử dụng Vite. Chi tiết cách sử dụng Vite có thể xem ở đây.

13. Cải thiện giao diện cho trang Exception:

Giao diện và tính năng mới đã được thêm vào trang Exception để dễ sử dụng và debug hiệu quả hơn.

(src: https://laravel.com/docs/9.x/releases#exception-page)


Lỗ hổng bảo mật đã được cập nhật trong phiên bản mới:

Ngăn chặn tải file thuộc định dạng .phar:

Khi bạn upload các file thuộc định dạng “PHP executable”, thì Laravel sẽ ngăn chặn và lọc nó ra vì yếu tố bảo mật. Bao gồm các file có định dạng '.php', '.php3', '.php4', '.php5', '.php7', '.php8', '.phtml' . Tuy nhiên vẫn còn một định dạng bị thiếu đó là '.phar' và Laravel đã cập nhật nó trong phiên bản mới.

SQL Injection trong SQL Server:

Laravel đã cập nhật một lỗ hổng bảo mật có liên quan đến các cuộc tấn công kiểu SQL Injection. Lỗ hổng này xảy ra khi bạn truyền trực tiếp user input vào các hàm limit() và offset() của SQL Server (Các database loại khác như MySQL hay Postgres thì không bị ảnh hưởng bởi lỗ hổng này).

SQL Injection – Binding query parameter với array:

Đoạn code dưới đây có thể dẫn đến một cuộc tấn công kiểu SQL Injection. Nguyên do là vì is_admin sẽ được gán giá trị bằng 1 thay vì 0. Sự gán sai giá trị  này xảy ra khi bạn truyền  một array vào $value = [1,1] cho hàm where().

User::where('id', [1,1])->where('is_admin', 0)->first();
// sql: select * from `users` where `id` = 1 and `is_admin` = 1

Nhưng lỗ hổng này đã được cập nhật. Và giờ đoạn query sẽ được tạo như sau:

// sql: select * from `users` where `id` = 1 and `is_admin` = 0

Trên đây, chúng tôi đã trình bày những tính năng mới trong Laravel 9, 10. Đồng thời, chúng tôi cũng đã đề cập đến các lỗ hổng bảo mật mà Laravel đã cập nhật. Tiếp theo, phần bên dưới sẽ hướng dẫn cách nâng cấp version cho dự án từ Laravel 8 lên Laravel 10.


Hướng dẫn nâng cấp dự án từ Laravel 8 lên Laravel 10:

Nâng cấp lên PHP 8.1.0 & Composer 2.2.0:

Laravel 10 yêu cầu phải cài đặt PHP 8.1.0 trở lên và Composer 2.2.0 trở lên, vì vậy bạn cũng cần phải điều chỉnh version sao cho phù hợp. Đây là bước bắt buộc trước khi bạn tiến hành nâng cấp lên Laravel 10.

Thay đổi về dependencies trong composer.json:

a. Các Packages cần được nâng cấp version:

Bên dưới là các package cần được nâng cấp lên version mới hơn. Bạn hãy làm theo hướng dẫn bên dưới.

Trong file composer.json thay đổi giá trị version của các package như sau:

{
  "require": {
    "php": "^8.1",
    "laravel/framework": "^10.0",
    "laravel/sanctum": "^3.2",
    "doctrine/dbal": "^3.0",
    "laravel/passport": "^11.0"
  },
  "require-dev": {
    "nunomaduro/collision": "^6.1",
    "spatie/laravel-ignition": "^2.0"
  }
}

Nếu bạn có sử dụng tính năng Broadcasting, thì hãy thay đổi version như bên dưới:

{
  "require": {
    "pusher/pusher-php-server": "^5.0"
  }
}

Nếu bạn có sử dụng driver S3, FTP, hoặc SFTP thông qua facade Storage, thì hãy thay đổi version của package tương ứng như bên dưới:

{
  "require": {
    "league/flysystem-aws-s3-v3": "^3.0",
    "league/flysystem-ftp": "^3.0",
    "league/flysystem-sftp-v3": "^3.0"
  }
}

Nếu như bạn muốn sử dụng PHPUnit 10, hãy xoá thuộc tính processUncoveredFiles="true" của phần <coverage> trong file config có tên là phpunit.xml. Sau đó, thay đổi version của các package như bên dưới:

{
  "require-dev": {
    "nunomaduro/collision": "^7.0",
    "phpunit/phpunit": "^10.0"
  }
}

b. Các packages mà Laravel đã loại bỏ:

Những package dưới đây đã không còn được Laravel sử dụng nữa, vì vậy, bạn có thể xoá nó đi để nhẹ source hơn.

Trusted Proxies:

Trong file app/Http/Middleware/TrustProxies.php file, hãy đổi như bên dưới:

use Fideloper\Proxy\TrustProxies as Middleware;

đổi như sau

use Illuminate\Http\Middleware\TrustProxies as Middleware;

Tiếp theo, trong file app/Http/Middleware/TrustProxies.php, đổi giá trị của thuộc tính $headers như sau:

// Before...
protected $headers = Request::HEADER_X_FORWARDED_ALL;
// After...
protected $headers =
    Request::HEADER_X_FORWARDED_FOR |
    Request::HEADER_X_FORWARDED_HOST |
    Request::HEADER_X_FORWARDED_PORT |
    Request::HEADER_X_FORWARDED_PROTO |
    Request::HEADER_X_FORWARDED_AWS_ELB;

Và sau đó hãy bỏ package như bên dưới:

{
  "require": {
    // Remove this package
    "fideloper/proxy": "^4.4"
  }
}

Fruitcake/laravel-cors:

Package này không còn được bảo trì và nâng cấp bởi chủ sở hữu nữa. Nên trong Laravel 10, nó đã được bỏ đi. Để cập nhật thay đổi này, đầu tiên trong file app\Http\Kernel.php, bạn hãy đổi namespace của middleware như sau:

\Fruitcake\Cors\HandleCors::class

đổi như sau

\Illuminate\Http\Middleware\HandleCors::class

Sau đó, loại bỏ package này đi trong composer.json:

{
  "require": {
    // Remove this package
    "fruitcake/laravel-cors": "^2.0"
  }
}

c. Những Packages được thay thế:

Đây là các package đã được thay thế bằng một package khác, vì vậy bạn hãy thực hiện thay đổi như sau:

{
  "require-dev": {
    // Replace this
    "facade/ignition": "^2.5"
    // With this
    "spatie/laravel-ignition": "^1.0"
  }
}

Liên quan đến tính năng Laravel SMS Notifications , vì giờ đây Vonage đã sở hữu Nexmo, nên tên của package này cũng đã được thay đổi tương ứng. Hãy sửa như sau trong file composer.json:

{
  "require": {
    // Replace this
    "laravel/nexmo-notification-channel": "^2.0"
    // With this
    "laravel/vonage-notification-channel": "^3.0"
  }
}

d. Symfony Mailer thay cho SwiftMailer:

Một trong những thay đổi lớn nhất trong Laravel 9.x là tính năng gửi mail đã chuyển sang sử dụng Symfony Mailer thay cho SwiftMailer, bởi vì SwiftMailer đã không còn được bảo trì nữa. Để cập nhật thay đổi đó bạn hãy chạy hai lệnh bên dưới:

composer remove wildbit/swiftmailer-postmark
composer require symfony/mailgun-mailer symfony/postmark-mailer symfony/http-client

Thực thi lệnh “composer update”:

Sau khi bạn đã cập nhật xong file composer.json. Hãy chạy lệnh “composer update”. Có một điều bạn nên lưu ý, đó là những thay đổi đã trình bày ở trên chỉ dành cho một ứng dụng Laravel cơ bản. Bởi mỗi dự án sẽ sử dụng thêm các package khác nhau nên đôi khi các package đó sẽ xung đột về version. Vì vậy, bạn cũng cần phải cập nhật version cho các package đó.

Khi đã chạy lệnh update thành công, tiếp theo, bạn sẽ cần phải “refactor – cấu trúc lại” lại source code bởi vì có những tính năng đã lỗi thời, hoặc cách hoạt động của nó đã bị thay đổi. Sau cùng thì bạn có thể cân nhắc sử dụng các tính năng mới của Laravel cho ứng dụng của bạn.

Những thay đổi trong cấu hình (configuration):

Nếu dự án sử dụng Postgres, thì trong file config/database.php, đổi tên ‘schema’ thành ‘search_path’:

'connections' => [
  'pgsql' => [
    // replace this
    'schema' => 'public',
    // by this
    'search_path' => 'public',
  ]
]

Nếu bạn có sử dụng tính năng SFTP thông qua facade Storage, thì sửa file config/filesystems.php như sau:

'sftp' => [
  // Replace this         
  'password' => env('SFTP_PASSPHRASE'),
  // By this
  'passphrase' => env('SFTP_PASSPHRASE'),
]

Khai báo các cấu hình cho stream cho SMTP đã không còn được hỗ trợ nữa. Thay vào đó, bạn phải khai báo trực tiếp . Ví dụ, để tắt xác thực TSL, bạn sẽ phải khai báo như bên dưới. Còn một điều nữa là  auth_mode đã không còn cần thiết nữa, bởi auth_mode sẽ được tự động xác định khi kết nối đến SMTP server.

'smtp' => [
  // 'auth_mode' => null,

  // Laravel 8.x...
  'stream' => [
      'ssl' => [
          'verify_peer' => false,
      ],
  ],
  // Laravel 9.x...
  'verify_peer' => false,
]

Thay đổi về cấu trúc thư mục:

Trong phiên bản mới của Laravel, thư mục /resources/lang directory đã được đổi thành /lang, tức là lang sẽ nằm ở thư mục gốc của project. Nếu trong code bạn đang hardcode đường dẫn cho thư mục lang, hãy chuyển sang dùng hàm app()->langPath().


Thực hiện chỉnh sửa mã nguồn:

Trong laravel 10, một số tính năng đã bị bỏ đi, hoặc chúng đã thay đổi cách thức sử dụng và hoạt động khác đi. Nên bạn cũng cần phải thực hiện cập nhật tương ứng cho mã nguồn của project. Bạn không cần phải cập nhật tất cả tính năng, hãy chỉ cập nhật tính năng mà mình đang sử dụng mà thôi.

Tính năng đã bị bỏ đi:

Các hàm dưới đây chỉ có trong Laravel 8 và không còn tồn tại nữa trong version mới, vì vậy, hãy tìm kiếm tất cả lời gọi đến các hàm này và trực tiếp cập nhật nó như sau:

EnumeratesValues Trait: method reduceWithKeys() của trait này đã bị bỏ, thay vào đó hãy dùng method reduce(). Và method reduceMany() đã được đổi tên thành reduceSpread().

Illuminate\Database\Schema\Builder::registerCustomDoctrineType() method này đã bị bỏ đi. Thay vào đó hãy dùng method registerDoctrineType() DB facade, hoặc khai báo custom Doctrine types trong file config/database.php.

Testing: assertDeleted() method đã bị bỏ, hãy sử dụng assertModelMissing().

Queue: Bus::dispatchNow()dispatch_now() methods đã bị bỏ đi. Hãy sử dụng Bus::dispatchSync() và dispatch_sync() methods.

Redirect::home() method đã bị bỏ đi. Thay vào đó, hãy redirect bằng cách gọi return Redirect::route('home').

[Lỗi thời] – Blade – Lazy Collections & The $loop Variable:

@php
    use App\Models\Car;
    $cars = Car::cursor();  // cars lazy collection
    foreach($cars as $c) {
        echo $loop->iteration;
    }
@endphp

Trong đoạn code bên trên, bạn đang truy xuất đến biến $loop bên trong một vòng lặp thuộc kiểu LazyCollection. Cách làm trên bây giờ đã lỗi thời. Bởi vì khi gọi như vậy, toàn bộ collection sẽ được tải vào trong bộ nhớ của RAM (điều này đi ngược lại hoàn toàn cách hoạt động của một LazyCollection).

[Đã bỏ] – Storage:

Storage – Flysystem đã không còn hỗ trợ cached-adapters. Hãy xoá nó bằng cách gọi command bên dưới:

composer require league/flysystem-cached-adapter

[Đã bỏ] Truy xuất DB Expression string value:

Ở các version trước, bạn có thể lấy giá trị kiểu chuỗi của một Expression bằng cách ép kiểu như sau:

$expression = DB::raw('select * from news');
$expStr = (string)$expression;

Cách làm này không còn được hỗ trợ nữa, thay vào đó hãy làm như sau:

$expStr = $expression->getValue(DB::connection()->getQueryGrammar());

[Đã bỏ] thuộc tính $date trong Eloquent model:

Trước đây, để ép kiểu các thuộc tính về datetime một cách tự động bạn có thể liệt kê nó trong $date. Nhưng giờ thuộc tính $date đã bị bỏ đi:

protected $dates = [
    'deployed_at'
];

Thay vào đó, hãy liệt kê nó trong $casts:

protected $casts = [
    'deployed_at' => 'datetime',
];

[Đã bỏ] Testing – Service Mocking:

Một số method của MocksApplicationServices trait đã bị bỏ đi. Cụ thể là các method dùng để testing như expectsEvents(), expectsJobs(), và expectsNotifications(). Nếu như bạn đang sử dụng các method này, hãy thay thế bằng method fake() của service tương ứng, chẳng hạn như Event::fake, Bus::fake, và Notification::fake. Để biết thêm thông tin về cách tạo mock bằng hàm fake, hãy tham khảo phần tài liệu của service tương ứng mà bạn muốn fake.

[Thay đổi] Validation – rule ‘password’ :

password rule dùng để so khớp mật khẩu của user đăng nhập, giơ đây đã được đổi tên thành current_password.

[Thay đổi] Blade – Tránh việc ghi đè Vue snippet:

Laravel 9 cung cấp các blade directives mới là @diabled, @checked, @selected. Điều đáng nói là Vue cũng đang sử dụng các directive tương tự. Vì vậy, để tránh việc ghi đè lên Vue, bạn cần phải đánh dấu như sau: @@disabled, @@checked, @@selected.

[Thay đổi] – Collections:

Giờ đây bạn có thể truyền một closure vào tham số đầu tiên cho các method  Collection::when(), unless() và các closure này sẽ được thực thi (trong version trước, nó không được thực thi)

$collection->when(function ($collection) {
  // This closure is executed...
  return false;
}, function ($collection) {
  // Not executed since first closure returned "false"...
  $collection->merge([1, 2, 3]);
});

[Thay đổi] – Eloquent – custom cast với giá trị null:

Trong Laravel 8, the mutator set() method sẽ không thực thi khi bạn gán giá trị $value = null. Nhưng trong Laravel 9, nó sẽ được thực thi. Ví dụ như với đoạn code dưới đây, output mà nó cho ra là hoàn toàn khác nhau khi truyền null:

// In App\Casts\FilenameWithTimestamp.php
public function set($model, $key, $value, $attributes) {
  return time() . '_' . $value;
}

// In App\Models\File.php
protected $casts = [
  'filename' => FilenameWithTimestamp::class
];

// Somewhere in your program
// With Laravel 8, the result of echo statement will be: null
// With Laravel 9, the result of echo statement will be: "20230322_"
$file = File::first();
$file->filename = null;
echo $file->filename;

Bạn thấy đó, trong version Laravel 9, giá trị của filename được gắn thêm timestamp mặc dù bạn truyền null, điều này không hợp logic tí nào. Vì vậy, bạn hãy kiểm tra lại mã nguồn và đảm bảo rằng khi truyền null, ứng dụng của bạn vẫn xử lý được. Chẳng hạn, đoạn code trên có thể được sửa lại cho check null như sau:

// In App\Casts\FilenameWithTimestamp.php
public function set($model, $key, $value, $attributes) {
  if (empty($value)) {
    return '';
  }
  return time() . '_' . $value;
}

Sau khi sửa như trên, code đã chạy đúng.

[Thay đổi] Storage – Hành vi trả lỗi exception:

Trong phiên bảng trước, laravel sẽ throw exception nếu như có lỗi xảy ra trong quá trình đọc, hoặc ghi file chẳng hạn như xoá một file không tồn tại. Nhưng trong version mới, laravel sẽ không throw exception mà chỉ đơn giản là trả về một kết quả tương ứng như true, false, null, …

Thay đổi này có thể dẫn đến ứng dụng của bạn hoạt động không đúng, chẳng hạn như bạn đang try-catch exception. Cách ứng phó với thay đổi này là bạn hãy chuyển sang check lỗi bằng if-else, hoặc cấu hình để laravel throw exception như cũ bằng cách như sau:

'public' => [
  'driver' => 'local',
  // ...
  'throw' => true,
]

[Thay đổi] Storage – Custom Filesystems:

Một vài thay đổi nhỏ trong cách đăng ký một custom filesystem driver. Vì vậy, nếu như trước đó bạn có đăng kí các custom filesystem drivers, hoặc bạn có sử dụng một package có đăng ký custom driver, thì bạn cần phải cập nhật code của mình, và cập nhật cả những dependencies liên quan.

Ví dụ, trong Laravel 8.x, một custom filesystem driver có thể được đăng ký như sau:

use Illuminate\Support\Facades\Storage;
use League\Flysystem\Filesystem;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;

Storage::extend('dropbox', function ($app, $config) {
    $client = new DropboxClient(
        $config['authorization_token']
    );

    return new Filesystem(new DropboxAdapter($client));
});

Tuy nhiên, trong Laravel 9.x, hàm callback truyền vào Storage::extend nên trả về trực tiếp một object kiểu Illuminate\Filesystem\FilesystemAdapter:

use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Facades\Storage;
use League\Flysystem\Filesystem;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;

Storage::extend('dropbox', function ($app, $config) {
    $adapter = new DropboxAdapter(
        new DropboxClient($config['authorization_token'])
    );

    return new FilesystemAdapter(
        new Filesystem($adapter, $config),
        $adapter,
        $config
    );
});

[Thay đổi] Thay thế Swift Mailer bằng Symfony Mailer:

Trong laravel 8, thư viện Swift Mailer được dùng để gửi mail. Nhưng thư viện này đã không còn được duy trì, nên laravel đã thay thế nó bằng thư viện Symfony Mailer. Nó ảnh hưởng không nhỏ vì hầu như các lời gọi đến thư viện đều thay đổi. Và bạn cũng cần phải cập nhật nó tương ứng.

Đổi tên các method của “Swift”:  nhiều method liên quan đến Swift Mailer đã được đổi tên để phù hợp với Symfony Mailer. Bạn có thể sử dụng tính năng advanced search trong IDE và thực hiện ghi đè hàng loạt các lời gọi tới các method bằng method tương ứng của nó như bên dưới:

Message::getSwiftMessage();
Message::getSymfonyMessage();

Mailable::withSwiftMessage($callback);
Mailable::withSymfonyMessage($callback);

MailMessage::withSwiftMessage($callback);
MailMessage::withSymfonyMessage($callback);

Mailer::getSwiftMailer();
Mailer::getSymfonyTransport();

Mailer::setSwiftMailer($swift);
Mailer::setSymfonyTransport(TransportInterface $transport);

MailManager::createTransport($config);
MailManager::createSymfonyTransport($config);

Thay đổi của MessageSent Event:

Illuminate\Mail\Events\MessageSent event sẽ cung cấp một biến kiểu Symfony\Component\Mime\Email thay vì kiểu Swift_Message như trước đây. Biến này chứa các thông tin về message trước khi nó được gửi đi. Đồng thời,  một property mới là sent đã được thêm vào. Property mới này chứa thông tin về message khi nó đã được gửi, chẳng hạn như MessageID.

Danh sách người nhận mail không thành công:

Bạn sẽ không thể truy xuất danh sách người nhận đã thất bại khi gửi mail đến họ nữa. Thay vào đó, một exception sẽ được trả về, exception này thuộc kiểu Symfony\Component\Mailer\Exception\TransportExceptionInterface.

Comprehensive Exam:

Công ty đã tạo một bài test để kiểm tra mức độ hiểu của các bạn khi đọc bài viết này.
Các bạn hãy làm thử nhé!!
https://exam-site.briswell-vn.com/startTest/KGtxLm0dvU

Tham khảo: