Giới thiệu

ORM Eloquent được đi kèm với Laravel sẽ cung cấp một implementation ActiveRecord đơn giản và đẹp mắt để làm việc với các cơ sở dữ liệu của bạn. Mỗi bảng trong cơ sở dữ liệu có một "Model" tương ứng và được sử dụng để tương tác với bảng đó. Các model cho phép bạn truy vấn dữ liệu vào trong các bảng, và cũng như thêm các bản ghi mới vào bảng.

Trước khi bắt đầu, bạn hãy chắc chắn là đã cấu hình kết nối cơ sở dữ liệu trong file config/database.php. Để biết thêm thông tin về cách cấu hình cơ sở dữ liệu của bạn, hãy xem tài liệu.

Định nghĩa Model

Để bắt đầu, bạn hãy tạo một model Eloquent. Các model thường được lưu trong thư mục app, nhưng bạn có thể tự do lưu chúng ở bất cứ đâu, mà có thể được auto-loaded trong file composer.json của bạn. Tất cả các model Eloquent đều được extend từ class Illuminate\Database\Eloquent\Model.

Cách dễ nhất để tạo một instance model là sử dụng lệnh Artisan make: model:

php artisan make:model Flight

Nếu bạn muốn tạo cả file migration cho cơ sở dữ liệu khi bạn tạo model, bạn có thể sử dụng tùy chọn --migration hoặc -m:

php artisan make:model Flight --migration

php artisan make:model Flight -m

Quy ước tên Eloquent Model

Bây giờ, chúng ta hãy xem một model Flight mẫu sẽ sử dụng để lấy và lưu trữ dữ liệu từ bảng flights trong cơ sở dữ liệu`:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    //
}

Table Names

Lưu ý rằng chúng ta đã không khai báo gì cho Eloquent biết rằng nó sẽ phải sử dụng bảng nào cho model Flight. Mặc định, "snake case" cộng với tên số nhiều của class sẽ được sử dụng làm tên bảng trừ khi bạn khai báo một tên khác. Vì vậy, trong trường hợp này, Eloquent sẽ giả định rằng: model Flight sẽ lưu các bản ghi vào trong bảng flights. Bạn có thể khai báo tuỳ biến tên bảng bằng cách định nghĩa thuộc tính table trên model của bạn:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'my_flights';
}

Primary Keys

Eloquent cũng sẽ giả định rằng mỗi bảng có một cột khóa chính có tên là id. Bạn có thể định nghĩa một thuộc tính protected $primaryKey để ghi đè lại quy ước này:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The primary key associated with the table.
     *
     * @var string
     */
    protected $primaryKey = 'flight_id';
}

Ngoài ra, Eloquent cũng giả định rằng khóa chính là một giá trị integer tăng dần đều, có nghĩa là mặc định khóa chính sẽ được tự động chuyển thành một int. Nếu bạn muốn sử dụng khóa chính không tăng dần đều hoặc không phải dạng integer, thì khoá chính của bạn phải định nghĩa trong thuộc tính public $incrementing trên model của bạn là false:

<?php

class Flight extends Model
{
    /**
     * Indicates if the IDs are auto-incrementing.
     *
     * @var bool
     */
    public $incrementing = false;
}

Nếu khóa chính của bạn không phải là dạng integer, bạn nên định nghĩa thuộc tính protected $keyType trên model của bạn là string.

<?php

class Flight extends Model
{
    /**
     * The "type" of the auto-incrementing ID.
     *
     * @var string
     */
    protected $keyType = 'string';
}

Timestamps

Mặc định, Eloquent sẽ yêu cầu các cột created_atupdated_at tồn tại trong các bảng của bạn. Nếu bạn không muốn các cột này được Eloquent tự động quản lý, hãy định nghĩa thuộc tính $timestamps trên model của bạn là false:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * Indicates if the model should be timestamped.
     *
     * @var bool
     */
    public $timestamps = false;
}

Nếu bạn cần tùy biến định dạng timestamp của bạn, hãy định nghĩa thuộc tính $dateFormat trong model của bạn. Thuộc tính này sẽ định nghĩa cách mà các thuộc tính date được lưu vào trong cơ sở dữ liệu, cũng như định dạng của chúng khi model được chuyển đổi thành các loại dữ liệu như một mảng hoặc một dạng JSON:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The storage format of the model's date columns.
     *
     * @var string
     */
    protected $dateFormat = 'U';
}

Nếu bạn cần tùy biến tên của các cột được sử dụng để lưu timestamp, bạn có thể set các hằng số CREATED_ATUPDATED_AT trong model của bạn:

<?php

class Flight extends Model
{
    const CREATED_AT = 'creation_date';
    const UPDATED_AT = 'last_update';
}

Database Connection

Mặc định, tất cả các model Eloquent sẽ sử dụng kết nối cơ sở dữ liệu mặc định được cấu hình trong application của bạn. Nếu bạn muốn khai báo một kết nối khác cho model, hãy sử dụng thuộc tính $connection:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The connection name for the model.
     *
     * @var string
     */
    protected $connection = 'connection-name';
}

Giá trị thuộc tính mặc định

Nếu bạn muốn định nghĩa giá trị mặc định cho một số thuộc tính của model, bạn có thể định nghĩa thuộc tính $attributes trên model của bạn:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The model's default values for attributes.
     *
     * @var array
     */
    protected $attributes = [
        'delayed' => false,
    ];
}

Lấy ra Model

Khi bạn đã tạo một model và bảng cơ sở dữ liệu được liên kết với model đó, bạn có thể bắt đầu truy xuất dữ liệu từ cơ sở dữ liệu của bạn. Hãy nghĩ về mỗi model Eloquent như là một query builder cho phép bạn truy vấn vào bảng cơ sở dữ liệu được liên kết với model đó. Ví dụ:

<?php

$flights = App\Flight::all();

foreach ($flights as $flight) {
    echo $flight->name;
}

Adding Additional Constraints

Phương thức all của Eloquent sẽ trả về tất cả các bản ghi có trong bảng của model. Vì mỗi model Eloquent đóng vai trò là một query builder, nên bạn cũng có thể thêm các ràng buộc cho các truy vấn của bạn, và sau đó sử dụng phương thức get để lấy ra kết quả:

$flights = App\Flight::where('active', 1)
               ->orderBy('name', 'desc')
               ->take(10)
               ->get();

{tip} Vì các model Eloquent là các query builder, nên bạn nên xem lại tất cả các phương thức có sẵn trên query builder. Bạn có thể sử dụng bất kỳ phương thức nào có trong các truy vấn Eloquent của bạn.

Refreshing Models

Bạn có thể refresh các model bằng cách sử dụng các phương thức freshrefresh. Phương thức fresh sẽ lấy ra một model mới từ cơ sở dữ liệu. Instance model hiện tại sẽ không bị ảnh hưởng:

$flight = App\Flight::where('number', 'FR 900')->first();

$freshFlight = $flight->fresh();

Phương thức refresh sẽ tái tạo lại model hiện tại bằng cách sử dụng dữ liệu mới từ cơ sở dữ liệu. Ngoài ra, tất cả các quan hệ đã được load cũng sẽ bị refresh:

$flight = App\Flight::where('number', 'FR 900')->first();

$flight->number = 'FR 456';

$flight->refresh();

$flight->number; // "FR 900"

Collection

Đối với các phương thức Eloquent như allget để lấy nhiều bản ghi, một instance của Illuminate\Database\Eloquent\Collection sẽ được trả về. Class Collection cung cấp nhiều phương thức hữu ích để làm việc với các bản ghi Eloquent của bạn:

$flights = $flights->reject(function ($flight) {
    return $flight->cancelled;
});

Bạn cũng có thể chạy một vòng lặp cho collection giống như đối với một mảng:

foreach ($flights as $flight) {
    echo $flight->name;
}

Phân kết quả

Nếu bạn cần xử lý hàng ngàn bản ghi Eloquent, hãy sử dụng phương thức chunk. Phương thức chunk sẽ lấy ra một đoạn "chunk" của model Eloquent, đưa chúng vào một Closure để xử lý. Sử dụng phương thức chunk sẽ bảo toàn bộ nhớ khi làm việc với các truy vấn mà có nhiều bản ghi:

Flight::chunk(200, function ($flights) {
    foreach ($flights as $flight) {
        //
    }
});

Tham số đầu tiên được truyền cho phương thức là số lượng bản ghi mà bạn muốn nhận vào cho mỗi đoạn "chunk". Closure sẽ được truyền thông qua tham số thứ hai, và nó sẽ được gọi cho mỗi đoạn được lấy từ cơ sở dữ liệu. Các truy vấn cơ sở dữ liệu sẽ được thực hiện để truy xuất từng đoạn của bản ghi và được chuyển vào cho Closure.

Using Cursors

Phương thức cursor cho phép bạn lặp qua các bản ghi trong cơ sở dữ liệu của bạn bằng một con trỏ, nó sẽ chỉ thực hiện một truy vấn duy nhất. Khi xử lý lượng lớn dữ liệu, phương thức cursor có thể được sử dụng để giảm đáng kể việc sử dụng bộ nhớ RAM của bạn:

foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
    //
}

Lấy ra một Model / một thống kê

Ngoài việc truy xuất tất cả các bản ghi có trong một bảng, bạn cũng có thể truy xuất một bản ghi bằng cách sử dụng phương thức find hoặc first. Thay vì trả về một tập hợp các model, thì các phương thức này sẽ trả về một instance model duy nhất:

// Retrieve a model by its primary key...
$flight = App\Flight::find(1);

// Retrieve the first model matching the query constraints...
$flight = App\Flight::where('active', 1)->first();

Bạn cũng có thể gọi phương thức find với một mảng các khóa chính, sẽ trả về một tập hợp các bản ghi khớp với mảng khoá chính đó:

$flights = App\Flight::find([1, 2, 3]);

Not Found Exceptions

Thỉnh thoảng bạn có thể muốn đưa ra một ngoại lệ nếu không tìm thấy model. Điều này đặc biệt hữu ích trong các route hoặc controller. Các phương thức findOrFailfirstOrFail sẽ lấy ra kết quả đầu tiên của truy vấn; tuy nhiên, nếu không tìm thấy kết quả nào, một Illuminate\Database\Eloquent\ModelNotFoundException sẽ được đưa ra:

$model = App\Flight::findOrFail(1);

$model = App\Flight::where('legs', '>', 100)->firstOrFail();

Nếu ngoại lệ không được xử lý, một HTTP response 404 sẽ tự động được gửi về cho người dùng. Bạn sẽ không cần phải viết đoạn code kiểm tra để trả về response 404 khi sử dụng các phương thức này:

Route::get('/api/flights/{id}', function ($id) {
    return App\Flight::findOrFail($id);
});

Lấy ra một thống kê

Bạn cũng có thể sử dụng các phương thức count, sum, max, và các phương thức thống kê khác được cung cấp bởi query builder. Các phương thức này trả về giá trị thay vì một instance model:

$count = App\Flight::where('active', 1)->count();

$max = App\Flight::where('active', 1)->max('price');

Thêm và cập nhật Model

Thêm

Để tạo một bản ghi mới vào trong cơ sở dữ liệu, hãy tạo một instance model mới, set các thuộc tính có trên model đó, và sau đó gọi phương thức save:

<?php

namespace App\Http\Controllers;

use App\Flight;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class FlightController extends Controller
{
    /**
     * Create a new flight instance.
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // Validate the request...

        $flight = new Flight;

        $flight->name = $request->name;

        $flight->save();
    }
}

Trong ví dụ này, chúng ta đã gán tham số name trong HTTP request cho một thuộc tính name của instance model App\Flight. Khi chúng ta gọi phương thức save, một bản ghi sẽ được thêm vào cơ sở dữ liệu. Các timestamp created_atupdate_at cũng sẽ tự động được set khi gọi phương thức save, do đó bạn không cần phải set chúng.

Cập nhật

Phương thức save cũng có thể được sử dụng để cập nhật một model đã tồn tại trong cơ sở dữ liệu. Để cập nhật một model, bạn nên truy xuất nó ra, set lại bất kỳ các thuộc tính nào mà bạn muốn cập nhật và sau đó gọi phương thức save. Timestamp update_at sẽ tự động được cập nhật lại theo nó, do đó bạn không cần phải tự phải set lại giá trị cho nó:

$flight = App\Flight::find(1);

$flight->name = 'New Flight Name';

$flight->save();

Mass Updates

Cập nhật cũng có thể được thực hiện đối với một số lượng lớn các model tương ứng với một câu lệnh truy vấn duy nhất. Trong ví dụ này, tất cả các flight 'đang hoạt động' và có 'điểm đến' là San Diego sẽ bị đánh dấu là delay:

App\Flight::where('active', 1)
          ->where('destination', 'San Diego')
          ->update(['delayed' => 1]);

Phương thức update yêu cầu một mảng gồm các cặp: tên cột và giá trị cần được cập nhật.

{note} Khi chạy một mass update thông qua Eloquent, thì các event của model như saving, saved, updating, và updated sẽ không được kích hoạt. Điều này là do các model đã không được lấy ra khi chạy một mass update.

Mass Assignment

Bạn cũng có thể sử dụng phương thức create để lưu một model mới với chỉ một dòng code duy nhất. Model được thêm vào và sẽ được trả về cho bạn từ phương thức đó. Tuy nhiên, trước khi làm như vậy, bạn sẽ cần phải khai báo thuộc tính fillable hoặc guarded trên model, vì mặc định, tất cả các model Eloquent đều được bảo vệ để chống lại việc mass-assignment.

Lỗ hổng mass-assignment xảy ra khi người dùng truyền một tham số HTTP mà bạn không yêu cầu thông qua một request và các tham số này thay đổi giá trị một cột trong cơ sở dữ liệu của bạn mà bạn không mong muốn. Ví dụ: kẻ xấu có thể gửi một tham số is_admin thông qua một request HTTP, sau đó được truyền vào phương thức create trong model của bạn, cho phép người đó có thể nâng quyền lên quyền admin.

Vì vậy, để bắt đầu, bạn nên định nghĩa các thuộc tính mà bạn muốn mass assignable. Bạn có thể làm điều này bằng cách sử dụng thuộc tính $fillable trên model. Ví dụ: hãy tạo thuộc tính name trong model Flight có thể được sử dụng để mass assignable:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['name'];
}

Khi chúng ta đã tạo cho các thuộc tính có thể được sử dụng để mass assignable, thì chúng ta có thể sử dụng phương thức create để thêm một bản ghi mới vào trong cơ sở dữ liệu. Phương thức create sẽ trả về instance model đã được lưu:

$flight = App\Flight::create(['name' => 'Flight 10']);

Nếu bạn đã có một instance model, bạn có thể sử dụng phương thức fill để thêm vào model một mảng các thuộc tính:

$flight->fill(['name' => 'Flight 22']);

Guarding Attributes

Trong khi $fillable đóng vai trò là một "danh sách trắng" cho các thuộc tính có thể được sử dụng để mass assignable, thì bạn cũng có thể chọn sử dụng $guarded. Thuộc tính $guarded sẽ chứa một mảng các thuộc tính mà bạn không muốn được sử dụng cho mass assignable. Tất cả các thuộc tính khác không có trong mảng này sẽ được sử dụng cho mass assignable. Vì vậy, thuộc tính $guarded giống như là một "danh sách đen". Một điều quan trọng là bạn nên sử dụng $fillable hoặc $guarded - nhưng không phải là cả hai. Trong ví dụ dưới đây, tất cả các thuộc tính ngoại trừ price sẽ được sử dụng cho mass assignable:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The attributes that aren't mass assignable.
     *
     * @var array
     */
    protected $guarded = ['price'];
}

Nếu bạn muốn làm cho tất cả các thuộc tính đều có thể được sử dụng mass assignable, bạn có thể định nghĩa thuộc tính $guarded là một mảng trống:

/**
 * The attributes that aren't mass assignable.
 *
 * @var array
 */
protected $guarded = [];

Các phương thức tạo khác

firstOrCreate/ firstOrNew

Có hai phương thức khác mà bạn có thể sử dụng để tạo các model thông qua cách mass assigning thuộc tính là: firstOrCreatefirstOrNew. Phương thức firstOrCreate sẽ thử xác định bản ghi đó đã có trong cơ sở dữ liệu hay chưa bằng cách sử dụng các cặp giá trị gồm: cột và giá trị. Nếu không thể tìm thấy model trong cơ sở dữ liệu, một bản ghi sẽ được thêm vào với các thuộc tính từ tham số đầu tiên, và các thuộc tính tùy chọn trong tham số thứ hai.

Phương thức firstOrNew cũng giống như phương thức firstOrCreate cũng sẽ cố gắng thử xác định một bản ghi có trong cơ sở dữ liệu có khớp với các thuộc tính đã cho hay không. Tuy nhiên, nếu không tìm thấy model, một instance model mới sẽ được trả về. Lưu ý rằng model được trả về bởi firstOrNew vẫn chưa được lưu vào trong cơ sở dữ liệu. Bạn sẽ cần phải gọi phương thức save để lưu nó:

// Retrieve flight by name, or create it if it doesn't exist...
$flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);

// Retrieve flight by name, or create it with the name, delayed, and arrival_time attributes...
$flight = App\Flight::firstOrCreate(
    ['name' => 'Flight 10'],
    ['delayed' => 1, 'arrival_time' => '11:30']
);

// Retrieve by name, or instantiate...
$flight = App\Flight::firstOrNew(['name' => 'Flight 10']);

// Retrieve by name, or instantiate with the name, delayed, and arrival_time attributes...
$flight = App\Flight::firstOrNew(
    ['name' => 'Flight 10'],
    ['delayed' => 1, 'arrival_time' => '11:30']
);

updateOrCreate

Bạn cũng có thể gặp các tình huống mà bạn muốn cập nhật một model nếu có hoặc tạo một model mới nếu không tồn tại. Laravel cung cấp một phương thức updateOrCreate để thực hiện điều này với chỉ trong một bước. Giống như phương thức firstOrCreate, updateOrCreate sẽ lưu model vào cơ sở dữ liệu, do đó bạn sẽ không cần phải gọi đến save():

// If there's a flight from Oakland to San Diego, set the price to $99.
// If no matching model exists, create one.
$flight = App\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99, 'discounted' => 1]
);

Xoá Model

Để xóa một model, hãy gọi phương thức delete trên một instance model đó:

$flight = App\Flight::find(1);

$flight->delete();

Deleting An Existing Model By Key

Trong ví dụ trên, chúng ta đang lấy một model từ cơ sở dữ liệu trước khi gọi phương thức delete. Tuy nhiên, nếu bạn biết khóa chính của model, bạn có thể xóa trực tiếp model này mà không cần phải truy xuất nó ra bằng cách gọi phương thức destroy. Ngoài một khóa chính làm tham số của nó ra, phương thức destroy cũng sẽ chấp nhận nhiều khóa chính cùng một lúc như một mảng khóa chính hoặc một collection khóa chính:

App\Flight::destroy(1);

App\Flight::destroy(1, 2, 3);

App\Flight::destroy([1, 2, 3]);

App\Flight::destroy(collect([1, 2, 3]));

Deleting Models By Query

Bạn cũng có thể chạy một câu lệnh xóa trên một tập các model. Trong ví dụ này, chúng ta sẽ xóa tất cả các flight có đánh dấu là không hoạt động. Giống như mass update, mass delete cũng sẽ không kích hoạt bất kỳ event nào của model khi các model bị xóa:

$deletedRows = App\Flight::where('active', 0)->delete();

{note} Khi thực hiện câu lệnh mass delete thông qua Eloquent, các event model như là deletingdeleted sẽ không được kích hoạt cho các model đã bị xóa. Điều này là do các model đã không được lấy ra khi thực hiện câu lệnh xóa.

Soft Delete

Ngoài việc xóa các bản ghi ra khỏi cơ sở dữ liệu của bạn, Eloquent cũng có thể sử dụng "soft delete" cho các model. Khi các model bị soft delete, thì chúng sẽ không thực sự bị xóa ra khỏi cơ sở dữ liệu của bạn. Thay vào đó, một thuộc tính deleted_at sẽ được set vào model và được thêm vào trong cơ sở dữ liệu. Nếu một model có giá trị deleted_at khác null, model đó đã bị soft delete. Để bật soft delete cho một model, hãy sử dụng trait Illuminate\Database\Eloquent\SoftDeletes trên model:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Flight extends Model
{
    use SoftDeletes;
}

{tip} Trait SoftDeletes sẽ tự động cast thuộc tính deleted_at thành một instance DateTime / Carbon cho bạn.

Bạn cũng cần thêm cột deleted_at vào bảng cơ sở dữ liệu của bạn. Schema builder của Laravel có chứa một phương thức helper để tạo cột này:

Schema::table('flights', function (Blueprint $table) {
    $table->softDeletes();
});

Bây giờ, khi bạn gọi phương thức delete trên model, cột deleted_at sẽ được set thành ngày giờ của hiện tại. Và khi truy vấn một model mà có sử dụng soft delete, thì các model mà đã bị soft delete thì sẽ bị tự động loại khỏi ra tất cả các kết quả truy vấn.

Để xác định xem một instance model đã cho có bị soft delete hay chưa, hãy sử dụng phương thức trashed:

if ($flight->trashed()) {
    //
}

Query Model Soft Deleted

Including Soft Deleted Models

Như đã lưu ý ở trên, các model bị soft delete sẽ bị tự động loại ra khỏi tất cả các kết quả truy vấn. Tuy nhiên, bạn có thể buộc các model bị soft delete xuất hiện trong kết quả bằng cách sử dụng phương thức withTrashed trong câu truy vấn:

$flights = App\Flight::withTrashed()
                ->where('account_id', 1)
                ->get();

Phương thức withTrashed cũng có thể được sử dụng cho các query quan hệ:

$flight->history()->withTrashed()->get();

Retrieving Only Soft Deleted Models

Phương thức onlyTrashed sẽ chỉ truy xuất vào các model đã bị soft deleted:

$flights = App\Flight::onlyTrashed()
                ->where('airline_id', 1)
                ->get();

Restoring Soft Deleted Models

Thỉnh thoảng bạn có thể muốn "un-delete" một model đã bị soft deleted. Để khôi phục lại model đã bị soft deleted thành trạng thái hoạt động, hãy sử dụng phương thức restore trên một instance model đó:

$flight->restore();

Bạn cũng có thể sử dụng phương thức restore trong một truy vấn để nhanh chóng khôi phục nhiều model. Một lần nữa, giống như các hành động "mass" khác, điều này sẽ không kích hoạt bất kỳ event nào của model khi các model được khôi phục:

App\Flight::withTrashed()
        ->where('airline_id', 1)
        ->restore();

Giống như phương thức withTrashed, phương thứcrestore cũng có thể được sử dụng trên quan hệ:

$flight->history()->restore();

Permanently Deleting Models

Thỉnh thoảng bạn cũng có thể cần phải loại bỏ vĩnh viễn một model ra khỏi cơ sở dữ liệu của bạn. Để xóa vĩnh viễn một model bị soft deleted ra khỏi cơ sở dữ liệu, hãy sử dụng phương thức forceDelete:

// Force deleting a single model instance...
$flight->forceDelete();

// Force deleting all related models...
$flight->history()->forceDelete();

Query Scope

Global Scope

Global scope cho phép bạn thêm các ràng buộc cho tất cả các truy vấn của một model nhất định. Chức năng soft delete của Laravel cũng sử dụng global scope để chỉ lấy ra các model "non-deleted" ra khỏi cơ sở dữ liệu. Viết global scope của riêng bạn có thể cung cấp một cách thuận tiện và dễ dàng để đảm bảo rằng mọi truy vấn cho một model nhất định đều có được các ràng buộc nhất định.

Writing Global Scopes

Viết một global scope rất đơn giản. Định nghĩa một class implement từ interface Illuminate\Database\Eloquent\Scope. Interface này yêu cầu bạn implement một phương thức: apply. Phương thức apply có thể thêm các ràng buộc where cho các truy vấn khi cần:

<?php

namespace App\Scopes;

use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class AgeScope implements Scope
{
    /**
     * Apply the scope to a given Eloquent query builder.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return void
     */
    public function apply(Builder $builder, Model $model)
    {
        $builder->where('age', '>', 200);
    }
}

{tip} Nếu global scope của bạn đang thêm các cột vào trong câu lệnh select, thì bạn nên sử dụng phương thức addSelect thay vì select. Điều này sẽ ngăn việc bạn vô tình thay thế lệnh select hiện tại của truy vấn.

Applying Global Scopes

Để gán một global scope cho một model, bạn nên ghi đè phương thức boot của một model và sử dụng phương thức addGlobalScope:

<?php

namespace App;

use App\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope(new AgeScope);
    }
}

Sau khi thêm scope, một truy vấn tới User::all() sẽ tạo ra SQL như sau:

select * from `users` where `age` > 200

Anonymous Global Scopes

Eloquent cũng cho phép bạn định nghĩa global scope bằng cách sử dụng Closures, điều này đặc biệt hữu ích cho các scope đơn giản không cần phải tạo một class riêng:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class User extends Model
{
    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope('age', function (Builder $builder) {
            $builder->where('age', '>', 200);
        });
    }
}

Removing Global Scopes

Nếu bạn muốn xóa một global trong cho một truy vấn nhất định, bạn có thể sử dụng phương thức withoutGlobalScope. Phương thức chấp nhận tên class của global scope làm tham số duy nhất của nó:

User::withoutGlobalScope(AgeScope::class)->get();

Hoặc, nếu bạn đã định nghĩa global scope dùng Closure:

User::withoutGlobalScope('age')->get();

Nếu bạn muốn xóa một vài hoặc thậm chí là tất cả các global scope, bạn cũng có thể sử dụng phương thức withoutGlobalScopes:

// Remove all of the global scopes...
User::withoutGlobalScopes()->get();

// Remove some of the global scopes...
User::withoutGlobalScopes([
    FirstScope::class, SecondScope::class
])->get();

Bạn cũng có thể xóa một a global scope ra khỏi các quan hệ:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    /**
     * Get the creator of the post.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function creator()
    {
        return $this->belongsTo(User::class)->withoutGlobalScopes();
    }
}

Local Scope

Local scope cho phép bạn xác định các nhóm ràng buộc chung mà bạn có thể dễ dàng sử dụng lại trong suốt qua trình xử lý của application của bạn. Ví dụ: bạn có thể cần thường xuyên truy xuất tất cả người dùng được coi là "popular". Để định nghĩa một scope, hãy set tiền tố scope cho tên phương thức trong model Eloquent.

Scope sẽ luôn phải trả về một instance query builder:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Scope a query to only include popular users.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopePopular($query)
    {
        return $query->where('votes', '>', 100);
    }

    /**
     * Scope a query to only include active users.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeActive($query)
    {
        return $query->where('active', 1);
    }
}

Utilizing A Local Scope

Khi một scope đã được định nghĩa xong, bạn có thể gọi phương thức scope khi truy vấn model. Tuy nhiên, bạn không cần phải ghi tiền tố scope khi gọi phương thức. Bạn thậm chí có thể kết hợp nó với các scope khác, ví dụ:

$users = App\User::popular()->active()->orderBy('created_at')->get();

Việc kết hợp nhiều scope cho model Eloquent thông qua truy vấn or có thể yêu cầu sử dụng lệnh callback Closure:

$users = App\User::popular()->orWhere(function (Builder $query) {
    $query->active();
})->get();

Tuy nhiên, vì điều này có thể phức tạp, Laravel cung cấp một phương thức "higher order" là orWhere cho phép bạn kết hợp các scope này với nhau một cách thuận tiện mà không cần sử dụng Closures:

$users = App\User::popular()->orWhere->active()->get();

Dynamic Scopes

Thỉnh thoảng bạn cũng có thể muốn định nghĩa một scope nhận vào các tham số. Để bắt đầu, chỉ cần thêm các tham số bổ sung vào trong scope của bạn. Các tham số scope cần được định nghĩa sau tham số $query:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Scope a query to only include users of a given type.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @param  mixed  $type
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeOfType($query, $type)
    {
        return $query->where('type', $type);
    }
}

Bây giờ, bạn có thể truyền tham số khi gọi scope:

$users = App\User::ofType('admin')->get();

So sánh Model

Thỉnh thoảng bạn có thể cần xác định xem hai model có "giống nhau" hay không. Phương thức is có thể được sử dụng để xác minh hai model đó có cùng khóa chính,cùng bảng và cùng kết nối cơ sở dữ liệu:

if ($post->is($anotherPost)) {
    //
}

Event

Các eloquent model sẽ kích hoạt một số event, cho phép bạn hook đến các chỗ khác trong vòng đời của một model: retrieved, creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored. Event cho phép bạn dễ dàng thực thi code mỗi khi một class model cụ thể nào đó được lưu hoặc được cập nhật vào trong cơ sở dữ liệu. Mỗi event sẽ nhận về một instance của model đó thông qua phương thức khởi tạo của event đó.

Event retrieved sẽ được kích hoạt khi một model được lấy ra khỏi cơ sở dữ liệu. Khi một model mới được lưu vào lần đầu tiên, các event creatingcreated sẽ kích hoạt. Nếu một model đã có trong cơ sở dữ liệu và phương thức save được gọi, thì các event updatingupdated sẽ kích hoạt. Tuy nhiên, trong cả hai trường hợp trên, thì các event saving / saved cũng sẽ kích hoạt.

{note} Khi bạn cập nhật một loạt dữ liệu thông qua Eloquent, thì các event của model như savedupdated sẽ không được kích hoạt cho các model đó. Điều này là do các model không thực sự được lấy ra khi bạn chạy các cập nhật đó.

Để bắt đầu, hãy định nghĩa một thuộc tính $dispatchesEvents trên model Eloquent của bạn để nối các thời điểm khác nhau trong vòng đời của model Eloquent đó vào các event classes của bạn:

<?php

namespace App;

use App\Events\UserSaved;
use App\Events\UserDeleted;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The event map for the model.
     *
     * @var array
     */
    protected $dispatchesEvents = [
        'saved' => UserSaved::class,
        'deleted' => UserDeleted::class,
    ];
}

Sau khi định nghĩa và ánh xạ các event Eloquent của bạn, bạn có thể sử dụng event listener để xử lý các event đó.

Observer

Defining Observers

Nếu bạn đang listen nhiều event trên một model, bạn có thể sử dụng các observer để nhóm tất cả các listen của bạn vào trong một class duy nhất. Các class observer có tên phương thức chính là tên các event Eloquent mà bạn muốn listen. Mỗi phương thức này nhận vào một model làm tham số duy nhất của chúng. Lệnh Artisan make:Observer là cách dễ nhất để tạo một class observer mới:

php artisan make:observer UserObserver --model=User

Lệnh này sẽ lưu file observer mới vào trong thư mục App/Observers của bạn. Nếu thư mục này không tồn tại, Artisan sẽ tạo nó cho bạn. Class observer mới của bạn sẽ trông giống như sau:

<?php

namespace App\Observers;

use App\User;

class UserObserver
{
    /**
     * Handle the User "created" event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function created(User $user)
    {
        //
    }

    /**
     * Handle the User "updated" event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function updated(User $user)
    {
        //
    }

    /**
     * Handle the User "deleted" event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function deleted(User $user)
    {
        //
    }
}

Để đăng ký một observer, hãy sử dụng phương thức observe trên model mà bạn muốn observe. Bạn có thể đăng ký observer trong phương thức boot của một trong những service provider của bạn. Trong ví dụ này, chúng ta sẽ đăng ký observer trong AppServiceProvider:

<?php

namespace App\Providers;

use App\User;
use App\Observers\UserObserver;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        User::observe(UserObserver::class);
    }
}
Redis Relationships
© 2023 by Logo page doc-vn