Helper, Collection, Http-client trong Laravel


Khi làm việc với Laravel, đôi khi các bạn không để ý nhưng chúng ta vẫn đang sử dụng những tính năng mà Laravel hỗ trợ. Bắt tay vào câu chuyện hôm nay thôi nào!

Helpers

Giới thiệu

Laravel đã cung cấp cho người dùng rất nhiều những hàm global helpers giúp bạn có thể sử dụng bất cứ đâu trong ứng dụng khi cần thiết. Chúng giúp xử lý những công việc liên quan tới Array, Object, Path, String, URL … trở nên dễ dàng hơn bao giờ hết. Mặc dù nó dân tặng răng nhưng đôi khi tùy vào dự án và đặc thù mà chúng ta muốn tạo riêng những hàm, class để tránh việc lặp code và dễ dàng bảo trì hơn.

Tạo file Helper

Bạn có thể tạo file Helper ở bất cứ đâu, và đặt tên gì tùy thích. Nhưng mình thường dùng như này app/Helpers/Helpers.php. Khai báo một function trong file helpers:

if (!function_exists('env')) {
  function env()
  {
    //Xử lý hàm env()
  }
}

if (!function_exists('aFunctionName')) {
  function aFunctionName()
  {
     // Xử lý hàm aFunctionName()
  }
}

Bằng việc sử dụng phương thức function_exists(), bạn có thể kiểm tra được xem Laravel đã có hàm đó chưa, nhằm tránh việc trùng tên hàm với những hàm helpers có sẵn. ở trường hợp này, hàm env() không được tạo do Laravel đã định nghĩa trước một hàm env rồi.

Nếu bạn sử dụng class và các phương thức của class đó , bạn sẽ cần phải thêm vào đầu file namespace tương ứng, ở trường hợp mình thì là: namespace App\Helpers;

Sử dụng file Helpers

Autoload bằng Composer

Chúng ta sử dụng cơ chế autoload file của composer vô cùng đơn giản và khá dễ dàng. Composer có hỗ trợ key files ( một mảng chữa những đường dẫn file mà bạn muốn load một cách tự động). Mở file composer.json và khai vào như sau:

"autoload": {
    "files": [
        "app/Helpers/Helper.php"
    ],
    "psr-4": {
        "App\\": "app/",
        "Database\\Factories\\": "database/factories/",
        "Database\\Seeders\\": "database/seeders/"
    }
},

Sau khi thay đổi nhớ chạy lệnh command composer dump-autoload, lúc này file helper của bạn sẽ được tự động load bên trong ứng dụng của bạn.

Autoload bằng ServiceProviders

Khi làm việc nhiều chắc các bạn không xa lạ gì với ServiceProviders và biết nó dùng để làm gì rồi.
Mặc định Laravel đã cung cấp cho chúng ta 5 file ServiceProviders. Mối cái có một chức năng riêng, mình sẽ nói rõ trong những bài viết khác. Ở đây mình sử dụng AppServiceProvider.php để load file Helpers.php. Mở file và khai báo ngay thôi nào:

public function register()
{
    $file = app_path('Helpers/Helper.php');
    if (file_exists($file)) {
        require_once($file);
    }
    // hoặc đơn giản 
    require_once __DIR__.'/../Helpers/Helpers.php';
}

Ở các dự án lớn bạn có thể cần dùng đến nhiều file helper trong thư mục, bạn có thể thay đổi code như sau:

public function register()
{
    foreach (glob(app_path() . '/Helpers/*.php') as $file) {
        require_once($file);
    }
}

Bằng cách này, nó sẽ require tất cả các file trong folder Helper.
Nếu file helper của bạn có class chứa những hàm helper và bạn có những namespace xác định, bạn có thể sử dụng dễ dàng bằng cách định nghĩa một alias . Bạn có thể làm điều đó bằng cách thêm dòng sau đây vào cuối mảng aliases trong config/app.php file:

'Helper' => App\Helpers\Helpers::class,

Bằng cách này, bạn có thể gọi helper bằng cách sử dụng keyword Helper.

Lưu ý: Các hàm trong class phải khai báo static

class Helpers {
    public static function aFunctionName()
    {
        // Xử lý hàm aFunctionName()
    }
}

Để gọi helper ta dùng như sau và không phải thêm gì vào cả :)

    \Helper::aFunctionName()

Collection

Giới thiệu

Collection trong Laravel là một class được tích hợp sẵn các phương thức thường xuyên được sử dụng để xử lý dữ liệu nhằm giảm thiểu tối đa thời gian cho các lập trình viên. Đặc biệt là khi làm việc với API kết nối với database vì dữ liệu từ database trả về sẵn kiểu là Collection.

Bạn có thể xem qua ví dụ sau:

$collection = collect(['Quang', 'Dat', null])->map(function ($name) {
    return strtoupper($name);
})->reject(function ($name) {
    return empty($name);
});

Ở ví dụ trên mình sử dụng collection trong Laravel thực hiện chuyển đổi tất cả các phần tử trong mảng thành in hoa và đồng thời loại bỏ các phần tử null trong mảng. Các bạn có thấy nó tiện không?

Tạo Collections

Cách 1: Sử dụng helper collect

$collection = collect([1, 2, 3]);

Cách 2: Sử dụng new Collection()

use Illuminate\Support\Collection;

$collection = new Collection([1, 2, 3]);

Cách 3: Sử dụng Collection::make()

use Illuminate\Support\Collection;

$collection = Collection::make([1, 2, 3]);

Trong tất cả cách cách trên thì dữ liệu đầu vào các bạn có thể bỏ trống để tạo ra một collection rỗng.

Extending Collections

Collection trong Laravel đã hỗ trợ chúng ta rất nhiều phương thức. Tuy nhiên, nếu bạn cần tạo thêm phương thức cho collection thì Laravel cũng hỗ trợ bạn.

Bạn có thể sử dụng phương thức macro để tạo thêm phương thức với cú pháp:

Collection::macro($methodName, $closure);
  • $methodName là tên của phương thức bạn muốn thêm.
  • $closure là một callback function chứa logic xử lý của phương thức.’

Ví dụ:

Collection::macro('toUpper', function () {
    return $this->map(function ($value) {
        return Str::upper($value);
    });
});

Lúc này ở collection các bạn có thể gọi như bình thường.

$collection = collect(['first', 'second']);

$upper = $collection->toUpper();

// ['FIRST', 'SECOND']

Macro với tham số truyền vào

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Str;

Collection::macro('toLocale', function ($locale) {
    return $this->map(function ($value) use ($locale) {
        return Lang::get($value, [], $locale);
    });
});

$collection = collect(['first', 'second']);

$translated = $collection->toLocale('es');

Thông thường mình sẽ khai báo các phương thức Collection::macro trong một class và gọi nó vào trong providers.

Lazy Collections

Collection trong Laravel đã là đủ mạnh mẽ rồi. Nhưng nếu như bạn muốn sử dụng collection với dữ liệu lớn bạn có thể xem xét sử dụng LazyCollection.

Ví dụ với 69.000 bản ghi user. Nếu bây giờ chúng ta lấy ra tất cả các bản ghi cùng 1 lúc thì sao? Chúng ta nhận được lỗi 500 do quá tải bộ nhớ, bởi vì tất cả 60.000 user đều được tải vào bộ nhớ cùng lúc.

$users = \App\User::all();

Và để loại bỏ việc sử dụng bộ nhớ này chúng ta sử dụng method cursor(). Method này cho phép chỉ thực hiện 1 câu truy vấn duy nhất và chỉ 1 Eloquent model được load ra tại 1 thời điểm .

Trong ví dụ này, việc gọi filter sẽ không được thực thi cho đến khi từng user được lặp lại, làm giảm đáng kể việc sử dụng bộ nhớ

$users = App\User::cursor()->filter(function ($user) {
    return $user->id > 500;
});

foreach ($users as $user) {
    echo $user->id;
}

Ngoài ra chúng ta có thể sử dụng lazy collection để đọc hàng GB khổng lồ. Method make() dùng để tạo 1 lazy collection class object. Chúng ta lấy 4 bản ghi với method chuck(), load các bản ghi vào LogEntry model và lặp qua các bản ghi đó.

use App\Models\LogEntry;
use Illuminate\Support\LazyCollection;

LazyCollection::make(function () {
    $handle = fopen('log.txt', 'r');

    while (($line = fgets($handle)) !== false) {
        yield $line;
    }
})->chunk(4)->map(function ($lines) {
    return LogEntry::fromLines($lines);
})->each(function (LogEntry $logEntry) {
    // Process the log entry...
});

Điểm chú ý ở đây chính là việc ta sử dụng hàm yield của php thay cho return như Collection. Thay vì dừng thực thi hàm và trả về (return), yield trả về giá trị khi giá trị đó cần sử dụng đến mà không lưu trữ tất cả các giá trị trong bộ nhớ.

Còn lại các phương thức các bạn có thể sử dụng giống như collection.

HTTP Client

Giới thiệu

Laravel http client sử dụng Guzzle http client giúp việc gửi http request để giao tiếp với các ứng dụng web khác đơn giản dễ dàng. Trước khi sử dụng cần chắc chắn rằng bạn đã cài Guzzle package như để 1 dependency của ứng dụng. Ở các phiên bản laravel mới mặc định package này đã được cài sẵn.

Các lợi thế của Guzzle:

  • Dễ dàng thực hiện tạo query string, POST request, streaming large upload, streaming large download, sử dụng HTTP cookies, upload dữ liệu Json….
  • Có thể gửi cả request đồng bộ và không đồng bộ bằng cách sử dụng cùng một interface.
  • Sử dụng tiểu chuẩn PSR-7 cho request, response, stream. Điều này giúp bạn dễ dàng tích hợp các thư viện khác sử dụng PSR-7 với Guzzle. (Các phiên bản trước không sử dụng PSR-7)
  • Không phụ thuộc chặt vào cURL, PHP stream, sockets hoặc vòng lặp không bị chặn.
  • Hệ thống Middleware cho phép bạn kiểm soát hành vi của client

Cài đặt Guzzle thông qua composer:

composer require guzzlehttp/guzzle

Tạo Requests

Ở đây chúng ta có thể sử dụng các phương thức get, post, put, patch, delete. Đầu tiên bắt đầu với ví dụ căn bản dưới đây.

use Illuminate\Support\Facades\Http;

$response = Http::get('http://example.com');

Phương thức get trả về một instance của Illuminate\Http\Client\Response, nó cung cấp nhiều phương thức để kiểm trả response:

$response->body() : string;
$response->json() : array|mixed;
$response->object() : object;
$response->collect() : Illuminate\Support\Collection;
$response->status() : int;
$response->ok() : bool;
$response->successful() : bool;
$response->failed() : bool;
$response->serverError() : bool;
$response->clientError() : bool;
$response->header($header) : string;
$response->headers() : array;

Để tạo Request ta thực hiện như sau:

use Illuminate\Support\Facades\Http;

$response = Http::post('http://example.com/users', [
    'name' => 'Steve',
    'role' => 'Network Administrator',
]);

Các tham số truyền vào thông qua array và data được gửi với kiểu application/json.

application/x-www-form-urlencoded
Nếu bạn muốn gửi dữ liệu với application/x-www-form-urlencoded thì có thể dùng phương thức asForm:

$response = Http::asForm()->post('http://example.com/users', [
    'name' => 'Sara',
    'role' => 'Privacy Consultant',
]);

A Raw Request Body
Bằng phương thức withBody chúng ta có thể gửi dữ liệu thô khi tạo requet. Content type truyền vào như đối số thứ 2:

$response = Http::withBody(
    base64_encode($photo), 'image/jpeg'
)->post('http://example.com/photo');

Multi-Part Requests
Nếu muốn gửi file, chúng ta có thể dùng attach:

$response = Http::attach(
    'attachment', file_get_contents('photo.jpg'), 'photo.jpg'
)->post('http://example.com/attachments');

// hoặc
$photo = fopen('photo.jpg', 'r');

$response = Http::attach(
    'attachment', $photo, 'photo.jpg'
)->post('http://example.com/attachments');

Headers

Header của một requet được thêm vào thông qua phương thức withHeaders.

$response = Http::withHeaders([
    'X-First' => 'foo',
    'X-Second' => 'bar'
])->post('http://example.com/users', [
    'name' => 'Taylor',
]);

Ngoài ra có thể sử dụng phương thức accept cho những type đặc biệt:

$response = Http::accept('application/json')->get('http://example.com/users');

Authentication

// Basic authentication...
$response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(...);

// Digest authentication...
$response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(...);

// Bearer Tokens
$response = Http::withToken('token')->post(...);

Timeout

Chắc hẳn các bạn không còn xa lạ gì với timeout nhỉ. Nó là thời gian để xử lý 1 requet:

$response = Http::timeout(3)->get(...);

Sau đó sẽ có một instance Illuminate\Http\Client\ConnectionException sẽ được ném ra.

Retries

Phương thức retry cho biết số lần thử lại và thời gian (mili giây) giữa các lần thử:

$response = Http::retry(3, 100)->post(...);

Kết luận

Tuy phần này lượng kiến thức không nhiều và cũng không khó, nhưng nó cực kỳ phổ biến và sử dụng nhiều trong ứng dụng của chúng ta. Hãy thực hành nhiều để hiểu rõ hơn từng chức năng của nó nhá.

Nguồn tham khảo:

Rất mong được sự ủng hộ của mọi người để mình có động lực ra những bài viết tiếp theo.
{\__/}
( ~.~ )
/ > ♥️ I LOVE YOU 3000

JUST DO IT!


Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source Nguyễn Quang Đạt !
Comments
  TOC