Cách Sử Dụng Mô Hình Cơ Sở Dữ Liệu (Phong cách Laravel)
Mô hình webman dựa trên Eloquent ORM. Mỗi bảng cơ sở dữ liệu đều có một "mô hình" tương ứng để tương tác với bảng đó. Bạn có thể sử dụng mô hình để truy vấn dữ liệu trong bảng và chèn các bản ghi mới vào bảng.
Trước khi bắt đầu, hãy đảm bảo rằng bạn đã cấu hình kết nối cơ sở dữ liệu trong config/database.php
.
Lưu ý: Để Eloquent ORM hỗ trợ các trình quan sát mô hình, cần phải nhập thêm
composer require "illuminate/events"
Ví dụ
Ví dụ về Mô Hình Cơ Sở Dữ Liệu
<?php
namespace app\model;
use support\Model;
class User extends Model
{
/**
* Tên bảng liên kết với mô hình
*
* @var string
*/
protected $table = 'user';
/**
* Định nghĩa lại khóa chính, mặc định là id
*
* @var string
*/
protected $primaryKey = 'uid';
/**
* Cho biết liệu có tự động duy trì dấu thời gian hay không
*
* @var bool
*/
public $timestamps = false;
}
Tên Bảng
Bạn có thể chỉ định bảng dữ liệu tùy chỉnh bằng cách định nghĩa thuộc tính table trên mô hình:
class User extends Model
{
/**
* Tên bảng liên kết với mô hình
*
* @var string
*/
protected $table = 'user';
}
Khóa Chính
Eloquent cũng giả định rằng mỗi bảng dữ liệu đều có một cột khóa chính có tên là id. Bạn có thể định nghĩa thuộc tính $primaryKey được bảo vệ để ghi đè quy ước.
class User extends Model
{
/**
* Định nghĩa lại khóa chính, mặc định là id
*
* @var string
*/
protected $primaryKey = 'uid';
}
Eloquent giả định rằng khóa chính là một số nguyên tự tăng, điều này có nghĩa là theo mặc định khóa chính sẽ tự động chuyển đổi thành kiểu int. Nếu bạn muốn sử dụng khóa chính không tự tăng hoặc không phải số, thì cần phải đặt thuộc tính công khai $incrementing thành false.
class User extends Model
{
/**
* Cho biết liệu khóa chính của mô hình có tự tăng hay không
*
* @var bool
*/
public $incrementing = false;
}
Nếu khóa chính của bạn không phải là số nguyên, bạn cần thiết lập thuộc tính $keyType được bảo vệ trên mô hình thành string:
class User extends Model
{
/**
* Kiểu của ID tự tăng.
*
* @var string
*/
protected $keyType = 'string';
}
Dấu Thời Gian
Theo mặc định, Eloquent mong đợi bảng dữ liệu của bạn có các cột created_at và updated_at. Nếu bạn không muốn Eloquent tự động quản lý hai cột này, hãy đặt thuộc tính $timestamps trong mô hình thành false:
class User extends Model
{
/**
* Cho biết liệu có tự động duy trì dấu thời gian hay không
*
* @var bool
*/
public $timestamps = false;
}
Nếu bạn cần tùy chỉnh định dạng dấu thời gian, hãy đặt thuộc tính $dateFormat trong mô hình của bạn. Thuộc tính này quyết định cách lưu trữ thuộc tính ngày tháng trong cơ sở dữ liệu, cũng như định dạng của mô hình khi được tuần tự hóa thành mảng hoặc JSON:
class User extends Model
{
/**
* Định dạng lưu trữ dấu thời gian
*
* @var string
*/
protected $dateFormat = 'U';
}
Nếu bạn cần tùy chỉnh tên cột lưu trữ dấu thời gian, bạn có thể thiết lập giá trị của các hằng số CREATED_AT và UPDATED_AT trong mô hình:
class User extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'last_update';
}
Kết Nối Cơ Sở Dữ Liệu
Theo mặc định, mô hình Eloquent sẽ sử dụng kết nối cơ sở dữ liệu mặc định được cấu hình trong ứng dụng của bạn. Nếu bạn muốn chỉ định một kết nối khác cho mô hình, hãy thiết lập thuộc tính $connection:
class User extends Model
{
/**
* Tên kết nối của mô hình
*
* @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 mô hình, bạn có thể định nghĩa thuộc tính $attributes trên mô hình:
class User extends Model
{
/**
* Giá trị mặc định của mô hình.
*
* @var array
*/
protected $attributes = [
'delayed' => false,
];
}
Truy Vấn Mô Hình
Khi bạn đã tạo mô hình và bảng cơ sở dữ liệu liên kết với nó, bạn có thể truy vấn dữ liệu từ cơ sở dữ liệu. Hãy tưởng tượng mỗi mô hình Eloquent như một trình xây dựng truy vấn mạnh mẽ, bạn có thể sử dụng nó để truy vấn nhanh hơn với các bảng dữ liệu liên kết. Ví dụ:
$users = app\model\User::all();
foreach ($users as $user) {
echo $user->name;
}
Mẹo: Bởi vì mô hình Eloquent cũng là một trình xây dựng truy vấn, nên bạn cũng nên đọc Trình Xây Dựng Truy Vấn để biết tất cả các phương thức có sẵn. Bạn có thể sử dụng những phương thức này trong truy vấn Eloquent.
Ràng Buộc Thêm
Phương thức all của Eloquent sẽ trả về tất cả các kết quả trong mô hình. Vì mỗi mô hình Eloquent đều hoạt động như một trình xây dựng truy vấn, bạn cũng có thể thêm điều kiện truy vấn và sau đó sử dụng phương thức get để lấy kết quả truy vấn:
$users = app\model\User::where('name', 'like', '%tom')
->orderBy('uid', 'desc')
->limit(10)
->get();
Tải Lại Mô Hình
Bạn có thể sử dụng phương thức fresh và refresh để tải lại mô hình. Phương thức fresh sẽ truy xuất lại mô hình từ cơ sở dữ liệu. Các thể hiện mô hình hiện có sẽ không bị ảnh hưởng:
$user = app\model\User::where('name', 'tom')->first();
$fresh_user = $user->fresh();
Phương thức refresh sử dụng dữ liệu mới từ cơ sở dữ liệu để gán lại giá trị cho mô hình hiện có. Hơn nữa, các mối quan hệ đã được tải cũng sẽ được tải lại:
$user = app\model\User::where('name', 'tom')->first();
$user->name = 'jerry';
$user = $user->fresh();
$user->name; // "tom"
Tập Hợp
Các phương thức all và get của Eloquent có thể truy vấn nhiều kết quả, trả về một thể hiện Illuminate\Database\Eloquent\Collection
. Lớp Collection
cung cấp rất nhiều hàm trợ giúp để xử lý kết quả Eloquent:
$users = $users->reject(function ($user) {
return $user->disabled;
});
Sử Dụng Con Trỏ
Phương thức cursor cho phép bạn sử dụng con trỏ để duyệt qua cơ sở dữ liệu, nó chỉ thực hiện một truy vấn duy nhất. Khi xử lý một lượng lớn dữ liệu, phương thức cursor có thể giảm đáng kể việc sử dụng bộ nhớ:
foreach (app\model\User::where('sex', 1)->cursor() as $user) {
//
}
cursor trả về một thể hiện Illuminate\Support\LazyCollection
. Bộ sưu tập lười cho phép bạn sử dụng hầu hết các phương thức bộ sưu tập trong Laravel, và mỗi lần chỉ tải một mô hình vào bộ nhớ:
$users = app\model\User::cursor()->filter(function ($user) {
return $user->id > 500;
});
foreach ($users as $user) {
echo $user->id;
}
Các Truy Vấn Con
Eloquent cung cấp hỗ trợ truy vấn con nâng cao, bạn có thể sử dụng một câu truy vấn duy nhất để lấy thông tin từ các bảng liên quan. Chẳng hạn, giả sử chúng ta có một bảng đích destinations
và một bảng chuyến bay đến các đích đó có tên flights
. Bảng flights
chứa một trường arrival_at
, biểu thị thời gian chuyến bay đến đích.
Sử dụng chức năng truy vấn con với các phương thức select và addSelect, chúng ta có thể một câu truy vấn để lấy toàn bộ các đích destinations
, cùng với tên của chuyến bay cuối cùng đến từng đích:
use app\model\Destination;
use app\model\Flight;
return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
])->get();
Sắp Xếp Theo Truy Vấn Con
Ngoài ra, hàm orderBy trong trình xây dựng truy vấn cũng hỗ trợ truy vấn con. Chúng ta có thể sử dụng tính năng này để sắp xếp tất cả các đích theo thời gian chuyến bay cuối cùng đến đích. Tương tự, điều này có thể thực hiện chỉ với một truy vấn duy nhất đến cơ sở dữ liệu:
return Destination::orderByDesc(
Flight::select('arrived_at')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
)->get();
Truy Vấn Một Mô Hình / Tập Hợp
Ngoài việc truy vấn tất cả các bản ghi từ bảng dữ liệu, bạn có thể sử dụng các phương thức find, first hoặc firstWhere để truy vấn một bản ghi duy nhất. Những phương thức này trả về một thể hiện mô hình đơn lẻ thay vì trả về tập hợp mô hình:
// Tìm một mô hình bằng khóa chính...
$flight = app\model\Flight::find(1);
// Tìm mô hình đầu tiên phù hợp với điều kiện truy vấn...
$flight = app\model\Flight::where('active', 1)->first();
// Tìm mô hình đầu tiên phù hợp với điều kiện truy vấn một cách nhanh chóng...
$flight = app\model\Flight::firstWhere('active', 1);
Bạn cũng có thể sử dụng mảng khóa chính làm tham số gọi phương thức find, nó sẽ trả về tập hợp các bản ghi khớp:
$flights = app\model\Flight::find([1, 2, 3]);
Đôi khi bạn có thể muốn thực hiện một hành động khác nếu không tìm thấy giá trị khi tìm kiếm kết quả đầu tiên. Phương thức firstOr sẽ trả về giá trị đầu tiên khi tìm thấy kết quả, nếu không, nó sẽ thực hiện callback đã cho. Giá trị trả về của callback sẽ là giá trị trả về của phương thức firstOr:
$model = app\model\Flight::where('legs', '>', 100)->firstOr(function () {
// ...
});
Phương thức firstOr cũng chấp nhận mảng cột để truy vấn:
$model = app\model\Flight::where('legs', '>', 100)
->firstOr(['id', 'legs'], function () {
// ...
});
Ngoại Lệ "Không Tìm Thấy"
Đôi khi bạn muốn ném ra một ngoại lệ khi không tìm thấy mô hình. Điều này rất hữu ích trong các controller và route. Các phương thức findOrFail và firstOrFail sẽ truy xuất kết quả đầu tiên của truy vấn, nếu không tìm thấy, sẽ ném ra ngoại lệ Illuminate\Database\Eloquent\ModelNotFoundException
:
$model = app\model\Flight::findOrFail(1);
$model = app\model\Flight::where('legs', '>', 100)->firstOrFail();
Truy Vấn Tập Hợp
Bạn cũng có thể sử dụng các phương thức count, sum và max được cung cấp bởi trình xây dựng truy vấn, cùng với các hàm tập hợp khác để thao tác với tập hợp. Những phương thức này chỉ trả về các giá trị vô hướng thích hợp thay vì một thể hiện mô hình:
$count = app\model\Flight::where('active', 1)->count();
$max = app\model\Flight::where('active', 1)->max('price');
Chèn
Để thêm một bản ghi mới vào cơ sở dữ liệu, trước tiên hãy tạo một thể hiện mô hình mới, thiết lập các thuộc tính cho thể hiện, sau đó gọi phương thức save:
<?php
namespace app\controller;
use app\model\User;
use support\Request;
use support\Response;
class FooController
{
/**
* Thêm một bản ghi mới vào bảng người dùng
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
// Xác thực yêu cầu
$user = new User;
$user->name = $request->get('name');
$user->save();
}
}
Các dấu thời gian created_at và updated_at sẽ được thiết lập tự động (khi thuộc tính $timestamps trong mô hình là true), không cần phải gán giá trị thủ công.
Cập Nhật
Phương thức save cũng có thể được sử dụng để cập nhật một mô hình đã tồn tại trong cơ sở dữ liệu. Để cập nhật mô hình, bạn cần truy xuất nó, thiết lập các thuộc tính cần cập nhật và sau đó gọi phương thức save. Tương tự, dấu thời gian updated_at sẽ được cập nhật tự động, vì vậy cũng không cần gán giá trị thủ công:
$user = app\model\User::find(1);
$user->name = 'jerry';
$user->save();
Cập Nhật Hàng Loạt
app\model\User::where('uid', '>', 10)
->update(['name' => 'tom']);
Kiểm Tra Sự Thay Đổi Thuộc Tính
Eloquent cung cấp các phương thức isDirty, isClean và wasChanged để kiểm tra trạng thái bên trong của mô hình và xác định cách thức thay đổi các thuộc tính của nó từ khi được tải lần đầu.
Phương thức isDirty xác định xem có thuộc tính nào đã thay đổi kể từ khi mô hình được tải không. Bạn có thể truyền tên thuộc tính cụ thể để xác định xem thuộc tính cụ thể đó có bị bẩn hay không. Phương thức isClean ngược lại với isDirty, nó cũng chấp nhận tham số thuộc tính tùy chọn:
$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 xác định xem có thuộc tính nào đã thay đổi trong chu kỳ yêu cầu hiện tại khi mô hình được lưu lần cuối. Bạn cũng có thể truyền tên thuộc tính để xem thuộc tính cụ thể đó đã được thay đổi hay chưa:
$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
Gán Nhiều Giá Trị
Bạn cũng có thể sử dụng phương thức create để lưu mô hình mới. Phương thức này sẽ trả về thể hiện mô hình. Tuy nhiên, trước khi sử dụng, bạn cần chỉ định thuộc tính fillable hoặc guarded trên mô hình, vì tất cả các mô hình Eloquent mặc định không thể thực hiện gán nhiều giá trị.
Khi người dùng truyền các tham số HTTP không mong đợi qua yêu cầu, và tham số đó thay đổi các trường trong cơ sở dữ liệu mà bạn không muốn thay đổi, sẽ xảy ra lỗ hổng gán nhiều giá trị. Ví dụ: người dùng độc hại có thể truyền tham số is_admin qua yêu cầu HTTP, sau đó đưa nó vào phương thức create, có thể cho phép người dùng nâng cấp thành quản trị viên.
Vì vậy, trước khi bắt đầu, bạn nên xác định các thuộc tính nào trên mô hình có thể được gán nhiều giá trị. Bạn có thể thực hiện điều này bằng cách sử dụng thuộc tính $fillable trên mô hình. Ví dụ: cho phép thuộc tính name của mô hình Flight có thể được gán nhiều giá trị:
<?php
namespace app\model;
use support\Model;
class Flight extends Model
{
/**
* Các thuộc tính có thể được gán nhiều giá trị.
*
* @var array
*/
protected $fillable = ['name'];
}
Khi chúng ta đã thiết lập các thuộc tính có thể gán nhiều giá trị, chúng ta có thể sử dụng phương thức create để chèn dữ liệu mới vào cơ sở dữ liệu. Phương thức create sẽ trả về thể hiện mô hình được lưu:
$flight = app\model\Flight::create(['name' => 'Flight 10']);
Nếu bạn đã có một thể hiện mô hình, bạn có thể truyền một mảng cho phương thức fill để gán giá trị:
$flight->fill(['name' => 'Flight 22']);
$fillable có thể được coi như "danh sách trắng" cho gán nhiều giá trị, bạn cũng có thể sử dụng thuộc tính $guarded để thực hiện. Thuộc tính $guarded chứa mảng các thuộc tính không được phép gán nhiều giá trị. Điều này có nghĩa là, $guarded trên thực tế sẽ giống như "danh sách đen". Lưu ý: bạn chỉ có thể sử dụng một trong hai thuộc tính $fillable hoặc $guarded, không thể sử dụng đồng thời. Ví dụ dưới đây cho thấy, tất cả các thuộc tính đều có thể được gán nhiều giá trị ngoại trừ thuộc tính price:
<?php
namespace app\model;
use support\Model;
class Flight extends Model
{
/**
* Các thuộc tính không thể được gán nhiều giá trị.
*
* @var array
*/
protected $guarded = ['price'];
}
Nếu bạn muốn tất cả các thuộc tính đều có thể được gán nhiều giá trị, bạn có thể định nghĩa $guarded là một mảng rỗng:
/**
* Các thuộc tính không thể được gán nhiều giá trị.
*
* @var array
*/
protected $guarded = [];
Các Phương Thức Tạo Khác
firstOrCreate/ firstOrNew
Dưới đây có hai phương thức bạn có thể sử dụng để gán nhiều giá trị: firstOrCreate và firstOrNew. Phương thức firstOrCreate sẽ khớp dữ liệu trong cơ sở dữ liệu dựa trên các cặp khóa / giá trị được cung cấp. Nếu không tìm thấy mô hình trong cơ sở dữ liệu, nó sẽ chèn một bản ghi có thuộc tính của tham số đầu tiên và thuộc tính tùy chọn của tham số thứ hai.
Phương thức firstOrNew tương tự như phương thức firstOrCreate, nó sẽ cố gắng tìm kiếm một bản ghi trong cơ sở dữ liệu dựa trên thuộc tính được cung cấp. Tuy nhiên, nếu phương thức firstOrNew không tìm thấy mô hình tương ứng, nó sẽ trả về một thể hiện mô hình mới. Lưu ý rằng thể hiện mô hình được trả về từ firstOrNew chưa được lưu vào cơ sở dữ liệu, bạn cần gọi phương thức save để lưu nó:
// Truy xuất chuyến bay qua tên, nếu không tồn tại sẽ tạo mới...
$flight = app\model\Flight::firstOrCreate(['name' => 'Flight 10']);
// Truy xuất chuyến bay qua tên, hoặc sử dụng tên và các thuộc tính delayed và arrival_time để tạo...
$flight = app\model\Flight::firstOrCreate(
['name' => 'Flight 10'],
['delayed' => 1, 'arrival_time' => '11:30']
);
// Truy xuất chuyến bay qua tên, nếu không tồn tại sẽ tạo ra một thể hiện...
$flight = app\model\Flight::firstOrNew(['name' => 'Flight 10']);
// Truy xuất chuyến bay qua tên, hoặc sử dụng tên và các thuộc tính delayed và arrival_time để tạo một thể hiện mô hình...
$flight = app\model\Flight::firstOrNew(
['name' => 'Flight 10'],
['delayed' => 1, 'arrival_time' => '11:30']
);
Bạn cũng có thể gặp phải tình huống mà bạn muốn cập nhật mô hình hiện có hoặc tạo một mô hình mới nếu nó không tồn tại. Phương thức updateOrCreate sẽ thực hiện điều này trong một lần. Tương tự như phương thức firstOrCreate, updateOrCreate sẽ lưu mô hình, do đó không cần gọi save():
// Nếu có chuyến bay từ Oakland đến San Diego, giá sẽ là 99 đô la.
// Nếu không tìm thấy mô hình phù hợp, một mô hình mới sẽ được tạo ra.
$flight = app\model\Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);
Xóa Mô Hình
Bạn có thể gọi phương thức delete trên thể hiện mô hình để xóa mô hình đó:
$flight = app\model\Flight::find(1);
$flight->delete();
Xóa Mô Hình Qua Khóa Chính
app\model\Flight::destroy(1);
app\model\Flight::destroy(1, 2, 3);
app\model\Flight::destroy([1, 2, 3]);
app\model\Flight::destroy(collect([1, 2, 3]));
Xóa Mô Hình Qua Truy Vấn
$deletedRows = app\model\Flight::where('active', 0)->delete();
Sao Chép Mô Hình
Bạn có thể sử dụng phương thức replicate để sao chép một thể hiện mới chưa được lưu vào cơ sở dữ liệu. Phương thức này rất hữu ích khi các thể hiện mô hình chia sẻ 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();
So Sánh Mô Hình
Đôi khi bạn có thể cần xác định xem hai mô hình có "giống nhau" không. Phương thức is có thể được sử dụng để nhanh chóng kiểm tra hai mô hình có cùng khóa chính, bảng và kết nối cơ sở dữ liệu hay không:
if ($post->is($anotherPost)) {
//
}
Trình Quan Sát Mô Hình
Xem tham khảo Sự Kiện và Trình Quan Sát Mô Hình trong Laravel
Lưu ý: Để Eloquent ORM hỗ trợ trình quan sát mô hình, cần phải nhập thêm composer require "illuminate/events"
<?php
namespace app\model;
use support\Model;
use app\observer\UserObserver;
class User extends Model
{
public static function boot()
{
parent::boot();
static::observe(UserObserver::class);
}
}