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.
Để 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
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
{
//
}
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';
}
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';
}
Mặc định, Eloquent sẽ yêu cầu các cột created_at
và updated_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_AT
và UPDATED_AT
trong model của bạn:
<?php
class Flight extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'last_update';
}
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';
}
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,
];
}
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;
}
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.
Bạn có thể refresh các model bằng cách sử dụng các phương thức fresh
và refresh
. 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"
Đối với các phương thức Eloquent như all
và get
để 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;
}
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.
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) {
//
}
Phương thức cursor
sẽ trả về một instance Illuminate\Support\LazyCollection
. Lazy collections cho phép bạn sử dụng nhiều phương thức có sẵn trên các collection Laravel cở bản trong khi chỉ load một model duy nhất vào bộ nhớ tại một thời điểm:
$users = App\User::cursor()->filter(function ($user) {
return $user->id > 500;
});
foreach ($users as $user) {
echo $user->id;
}
Eloquent cũng cung cấp hỗ trợ các truy vấn con nâng cao, cho phép bạn lấy thông tin từ các bảng liên quan trong một truy vấn duy nhất. Ví dụ, hãy tưởng tượng rằng chúng ta có một bảng các chuyến bay destinations
và một bảng flights
đến các destination. Bảng flights
chứa một cột arrived_at
cho biết thời điểm chuyến bay đến destination.
Sử dụng chức năng truy vấn phụ có trong phương thức select
và addSelect
, chúng ta có thể lấy ra tất cả các destinations
và tên của chuyến bay đã đến điểm đến đó gần đây nhất chỉ bằng một câu lệnh truy vấn duy nhất:
use App\Destination;
use App\Flight;
return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
])->get();
Ngoài ra, hàm orderBy
của query builder cũng hỗ trợ các truy vấn con. Chúng ta có thể sử dụng chức năng này để sắp xếp tất cả các điểm đến dựa trên thời điểm chuyến bay cuối cùng đến điểm đến đó. Một lần nữa, điều này có thể được thực hiện chỉ trong một truy vấn duy nhất đối với cơ sở dữ liệu:
return Destination::orderByDesc(
Flight::select('arrived_at')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
)->get();
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
, first
, hoặc firstWhere
. 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();
// Shorthand for retrieving the first model matching the query constraints...
$flight = App\Flight::firstWhere('active', 1);
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]);
Thỉnh thoảng bạn có thể muốn lấy ra kết quả đầu tiên của một truy vấn hoặc thực hiện một số hành động khác nếu không tìm thấy kết quả nào. Phương thức firstOr
sẽ trả về kết quả đầu tiên được tìm thấy hoặc nếu không tìm thấy kết quả nào, sẽ thực hiện callback đã cho. Kết quả của callback sẽ được coi là kết quả của phương thức firstOr
:
$model = App\Flight::where('legs', '>', 100)->firstOr(function () {
// ...
});
Phương thức firstOr
cũng chấp nhận một mảng tên cột để lấy ra:
$model = App\Flight::where('legs', '>', 100)
->firstOr(['id', 'legs'], function () {
// ...
});
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 findOrFail
và firstOrFail
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);
});
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');
Để 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\Http\Controllers\Controller;
use App\Flight;
use Illuminate\Http\Request;
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_at
và update_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.
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();
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.
Eloquent cung cấp các phương thức isDirty
, isClean
và wasChanged
để kiểm tra xem trạng thái của model của bạn và xác định các thuộc tính của model đã bị thay đổi như thế nào so với khi chúng được load ra lần đầu tiên.
Phương thức isDirty
sẽ xác định xem có bất kỳ thuộc tính nào bị thay đổi kể từ khi model được load hay không. Bạn có thể truyền vào một tên thuộc tính cụ thể để xác định xem thuộc tính đó có bị thay đổi hay không. Phương thức isClean
sẽ ngược lại với phương thức isDirty
và cũng chấp nhận một tùy chọn tham số thuộc tính:
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->isDirty(); // true
$user->isDirty('title'); // true
$user->isDirty('first_name'); // false
$user->isClean(); // false
$user->isClean('title'); // false
$user->isClean('first_name'); // true
$user->save();
$user->isDirty(); // false
$user->isClean(); // true
Phương thức wasChanged
sẽ xác định xem đã có bất kỳ thuộc tính nào bị thay đổi khi model được lưu vào lần cuối trong request hiện tại hay không. Bạn cũng có thể truyền tên một thuộc tính để xem liệu thuộc tính đó có bị thay đổi hay không:
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->save();
$user->wasChanged(); // true
$user->wasChanged('title'); // true
$user->wasChanged('first_name'); // false
Phương thức getOriginal
sẽ trả về một mảng chứa các thuộc tính ban đầu của model bất kể có thay đổi nào từ khi model được load. Bạn có thể truyền vào tên của một thuộc tính cụ thể để nhận về giá trị ban đầu của một thuộc tính đó:
$user = User::find(1);
$user->name; // John
$user->email; // [email protected]
$user->name = "Jack";
$user->name; // Jack
$user->getOriginal('name'); // John
$user->getOriginal(); // Array of original attributes...
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']);
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 = [];
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à: firstOrCreate
và firstOrNew
. 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]
);
Để 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();
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]));
{note} Phương thức
destroy
sẽ load từng model và gọi phương thứcdelete
trên từng model đó để kích hoạt các eventdeleting
vàdeleted
.
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à
deleting
vàdeleted
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.
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ínhdeleted_at
thành một instanceDateTime
/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:
public function up()
{
Schema::table('flights', function (Blueprint $table) {
$table->softDeletes();
});
}
public function down()
{
Schema::table('flights', function (Blueprint $table) {
$table->dropSoftDeletes();
});
}
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()) {
//
}
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();
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();
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();
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();
Bạn có thể tạo một bản sao chưa lưu của một instance model bằng cách sử dụng phương thức replicate
. Điều này đặc biệt hữu ích khi bạn có các instance model dùng chung nhiều thuộc tính giống nhau:
$shipping = App\Address::create([
'type' => 'shipping',
'line_1' => '123 Example Street',
'city' => 'Victorville',
'state' => 'CA',
'postcode' => '90001',
]);
$billing = $shipping->replicate()->fill([
'type' => 'billing'
]);
$billing->save();
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.
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\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
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.
Để gán một global scope cho một model, bạn nên ghi đè phương thức booted
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 "booted" method of the model.
*
* @return void
*/
protected static function booted()
{
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
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\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booted" method of the model.
*
* @return void
*/
protected static function booted()
{
static::addGlobalScope('age', function (Builder $builder) {
$builder->where('age', '>', 200);
});
}
}
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();
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);
}
}
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();
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();
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)) {
//
}
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 creating
và created
sẽ kích hoạt. Các event updating
/ updated
sẽ kích hoạt khi một model đang tồn tại có sửa đổi và gọi đến phương thức save
. Các event saving
/ saved
sẽ kích hoạt khi một model mới được tạo hoặc cập nhậ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ư
saved
,updated
,deleting
, vàdeleted
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 hoặc xoá bỏ.
Để 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\UserDeleted;
use App\Events\UserSaved;
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 đó.
Thay vì sử dụng các class event tùy biến, bạn có thể đăng ký một Closures để được chạy khi các event model khác nhau được kích hoạt. Thông thường, bạn nên đăng ký các Closures này trong phương thức booted
của model của bạn:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booted" method of the model.
*
* @return void
*/
protected static function booted()
{
static::created(function ($user) {
//
});
}
}
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)
{
//
}
/**
* Handle the User "forceDeleted" event.
*
* @param \App\User $user
* @return void
*/
public function forceDeleted(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\Observers\UserObserver;
use App\User;
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);
}
}
Đôi khi bạn có thể muốn tạm thời "tắt" tất cả các event do một model kích hoạt. Bạn có thể làm được điều này bằng cách sử dụng phương thức withoutEvents
. Phương thức withoutEvents
chấp nhận một Closure làm tham số duy nhất của nó. Bất kỳ code nào được chạy trong Closure này sẽ không kích hoạt bất kỳ event nào của model. Ví dụ: code sau sẽ tìm và xóa một instance App\User
mà không kích hoạt bất kỳ event model nào. Bất kỳ giá trị nào được trả về bởi Closure cũng là giá trị sẽ được trả về bởi phương thức withoutEvents
:
use App\User;
$user = User::withoutEvents(function () use () {
User::findOrFail(1)->delete();
return User::find(2);
});
entry