Service Provider và 4 ứng dụng chính

Hê sờ lô anh em, Vậy là chúng ta đã đồng hành cùng nhau hơn 1 nửa chặng đường trong seri này rồi nhỉ, hôm nay chúng ta sẽ tiếp tục với chủ đề Service Provider – Từ lý thuyết đến thực hành trong seri Laravel core.

Dù không muốn lắm mồm đâu nhưng việc quan trọng nên được nhắc lại nhiều lần =)) mình vẫn cứ phải nhắc :v, ở các bài trước chúng ta đã cùng nhau thảo luận về các khái niệm cực kì quan trọng trong laravel, để hiểu được bài này thì anh em bắt buộc phải nắm được các kiến thức của các bài trước nhá. Ít nhất là 3 bài sau:

Không lan man nữa, chúng ta cùng đi tìm hiểu xem Service Provider là gì? và ứng dụng thực tế của nó trong dự án là như nào nhé.

Ô cê lẹt gô…!

Service Provider là gì?

Service Provider là gì

ở bài viết trước chúng ta đã tìm hiểu về Service Container là nơi quản lý các dependence và inject các sự phụ thuộc trong laravel đúng không. Chúng ta chỉ cần bind 1 class vào trong service container và có thể gọi chúng ở bất cứ đâu mà không cần quan tâm đến những phụ thuộc của class đó. Vậy anh em có bao giờ thắc mắc là chúng ta sẽ bind nó ở đâu không, chẳng lẽ mình cứ thích bind ở đâu thì bind??

Thực tế thì laravel đã viết ra 1 class chuyên dụng cho việc bind các class này vào trong laravel rồi. Và đó chính là nội dung chính của ngày hôm nay Service Provider. Tại đây chúng ta sẽ thực hiện bind các class vào Service Container

Cách khai báo Service Container

Khai báo Service Container

Chắc hẳn anh em nào code laravel cũng không ít lần đụng tới file config/app.php rồi đúng không? Chúng ta hay dùng file này để setup những thông số của dự án như là chế độ debug, timezone, đường dẫn…

Nhưng không biết anh em có để ý không trong file này còn có 1 mục rất quan trọng khác đó là providers. Và khi vào đọc code thì laravel đã comment như sau:

    /*
     * Package Service Providers...
     */
     /*
     * Application Service Providers...
     */

Nó là khởi nguyên của việc bind class vào trong service container. Tuy nhiên như code của bài trước mình có giới thiệu tới anh em muốn bind 1 thằng service container thì mình sử dụng app()->bind() hoặc app()->instance() đúng không?

Nhưng ở đây chúng ta không cần làm như thế nữa, thay vào đó trong file app.php này thì chúng ta cần truyền tên class vào. Các class này thường có đuôi là ServiceProvider. Ví dụ DoDzServiceProvider =)) đùa thôi nhé anh em đừng đặt tên class như này, sau bị chửi cho sml:v

Ngoài ra nếu anh em cài các thư viện, package vào dự án thì cũng cần phải thêm class vào trong phần providers của file này. Lý thuyết là vậy, giờ chúng ta sẽ đi vào thực tiễn xem thằng cha này nó làm cái gì.

Service Provider thực chiến

Service Provider thực chiến

Các Service Provider đều được kế thừa từ một abstract class mà Laravel cung cấp, đó là Illuminate\Support\ServiceProvider (abtract class là gì thì chúng ta sẽ tìm hiểu vào bài sau).

Abstract ServiceProvider bao gồm một abstract function là register(), điều đó có nghĩa là các class mà kế thừa từ abtract class ServiceProvider  đều phải ghi đè method register này. Và đây chính là nơi để thực hiện binding vào service container. Chúng ta cùng xem 1 ví dụ đơn giản là thằng AuthServiceProvider xem nó có gì nhé..!

Bình thường, khi vào file config/app.php bạn sẽ thấy providers đó được khai báo dưới dạng

App\Providers\AuthServiceProvider::class

, dựa trên cái tên đó ta có thể tìm ra file provider tại

vendor\laravel\framework\src\Illuminate\Hashing\HashServiceProvider.php
class HashServiceProvider extends ServiceProvider implements DeferrableProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('hash', function ($app) {
            return new HashManager($app);
        });

        $this->app->singleton('hash.driver', function ($app) {
            return $app['hash']->driver();
        });
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return ['hash', 'hash.driver'];
    }
}

Ta có thể thấy trong HashServiceProvider laravel đã thực hiện việc binding từ khoá ‘hash’

, có thể coi đây là tên service, với một instance của class HashManager qua method singleton. Như vậy thì:

  • get_class(app()->make(‘hash’)) sẽ trả ra kết quả là Illuminate\Hashing\HashManager.php;
  • vì đang bind singleton nên app()->make(‘hash’) === app()->make(‘hash’) sẽ trả ra kết quả true (mình đã giải thích chi tiết trong bài Service Container), Vì singleton chỉ tạo ra 1 object, những lần gọi sau đó nó sẽ lấy object cũ ra khỏi service container.

Bên cạnh đó, nếu ai đã từng code ở những phiên bản cũ hơn thì các bạn sẽ thấy các class provider sẽ có thêm 1 biến là $defer, biến này sẽ quyết định xem class này được binding khi nào. Nếu $defer = true thì quá trình binding sẽ chỉ diễn ra khi class được gọi. Ngược lại nếu $defer = false thì class này sẽ được binding ngay khi load class.

Tuy nhiên ở bản hiện tại là laravel 10 thì mình thấy đã bỏ biến này đi, thy vào đó sẽ implement thằng DeferrableProvider.

If your provider is only registering bindings in the service container, you may choose to defer its registration until one of the registered bindings is actually needed. Deferring the loading of such a provider will improve the performance of your application, since it is not loaded from the filesystem on every request.

Laravel compiles and stores a list of all of the services supplied by deferred service providers, along with the name of its service provider class. Then, only when you attempt to resolve one of these services does Laravel load the service provider.

To defer the loading of a provider, implement the

\Illuminate\Contracts\Support\DeferrableProvider

interface and define a provides method. The provides method should return the service container bindings registered by the provider:

theo Laravel document

class RiakServiceProvider extends ServiceProvider implements DeferrableProvider

Ngoài hàm register thì ta còn thấy trong class này còn có 1 hàm nữa là provides. Hàm này sẽ báo cho laravel biết rằng provider này trả về cái gì.

public function provides()
{
    return ['hash', 'hash.driver'];
}

Việc load các service một cách defer sẽ có tác dụng rất lớn đến việc cải thiện performance cho application. Có 1 điểm nữa mà chúng ta cần chú ý, do laravel load các class theo kiểu sơ đồ cây tức là sẽ load các dependence trong cùng trước, dẫn tới việc có thể có việc là class được khai báo trong provider chưa được load mà đã load provider này rồi.

Chính vì vậy không chúng ta không nên khai báo các service event, listener, routes ở trong hàm register của provider vì chúng có thể bị lỗi. Thay vào đó hãy khai báo chúng tại hàm boot, hàm này có 1 đặc điểm là n load hết các class rồi mới chạy hàm này cuối nên sẽ đảm bảo được việc load không đồng bộ.

Service Provider và ứng dụng

Service Provider và ứng dụng
Service Provider và ứng dụng

Service Provider giúp cho quá trình bootstrapping của Laravel Application trở lên dễ dàng hơn bao giờ hết, ta chỉ việc gọi những class mình cần trong Service container sau khi Container được khởi chạy.

Giải thích qua để cho các bạn hiểu thêm về bootstrapping. Khi laravel hay bất cứ 1 ngôn ngữ, framework nào muốn hoạt động được đều phải có 1 tiến trình để kết hợp các phần trong framework lại với nhau để xử lý các chức năng trong hệ thống, thì thằng laravel cũng vậy. trong bootstrapping sẽ bao gồm autoload, services và app…

Lý do mình rất thích laravel vì nó quá mềm dẻo, và linh hoạt, các service hoạt động độc lập mà lại có thể giao tiếp được với nhau 1 cách dễ dàng và tường minh.

1- Không những thế laravel còn tạo trước cho chúng ta 1 cấu trúc rõ ràng để mình làm mà không cần lo lắng quá nhiều về việc cần tạo những service provider nào. Khi nhìn vào phần khai báo providers trong Config/app.php, chúng ta sẽ thấy sẽ có phần cho

App\Providers\AppServiceProvider::class

. Laravel đã tạo sẵn cho chúng ta một nơi lý tưởng để bạn có thể thêm vào những binding hay những bootstrapping config của mình.

Khi chúng ta muốn xử lý 1 công việc gì đó đầu tiên như validation chẳng hạn thì chúng ta chỉ cần viết vào trong hàm boot() của service này là được.

2- Không chỉ thế chúng ta cũng có thể tạo ra các service của riêng mình bằng cách extends thằng ServiceProvider là được. Việc này có thể sẽ gặp khá thường xuyên khi trong dự án yêu cầu có các class chuyên dụng để xử lý những ngiệp vụ riêng như xử lý images, file, excel, pdf bla bla.

3- 1 yếu tố quan trọng nữa là khi chúng ta muốn cài các thư viện, package ở ngoài vào thì cũng cần khai báo bootstrapping ở trong file này. Hay bạn muốn viết 1 package cho người khác dùng thì ũng nên nhớ viết service provider cho nó nhé, người dùng cũng sẽ dễ dàng sử dụng package của bạn hơn.

4- Khi bạn muốn custom 1 chức năng nào đó của thằng laravel cũng khá dễ dàng là chỉ cần tạo ra 1 class theo ý mình, sao đó config lại trong service provider là xong rồi, khá dễ dàng đúng không nào.

Lời kết

Vậy là hết rồi, trên đây mình chỉ giới thiệu 1 cách cơ bản và tổng quan về khái niệm, ý nghĩa và ứng dụng của service provider thôi. Hi vọng bài viết này sẽ giúp các bạn có cái nhìn tổng quan về laravel và ứng dụng chúng 1 cách linh hoạt trong công việc nhé

Bài viết tiếp theo mình sẽ giới thiệu về Facades trong laravel, 1 khái niệm mà không 1 IDE nào hiểu được nó hoạt động như thế nào. Và việc laravel tạo ra Facades đã khiến cho code của laravel được rút gọn và có vẻ tường minh hơn.


Bài viết liên quan

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *