Eloquent Model
File:
packages/skilldo/framework/src/Database/Eloquent/Model.php
Namespace:SkillDo\Database\Eloquent\Model
Tài liệu tham khảo: Laravel Eloquent
1. Eloquent Model là gì?
Eloquent là một ORM (Object-Relational Mapper) - giúp bạn làm việc với bảng Database thông qua các đối tượng PHP (class) thay vì viết SQL thuần.
Class SkillDo\Database\Eloquent\Model là phiên bản tùy chỉnh của Eloquent được SkillDo CMS v8 xây dựng lại (không kế thừa Illuminate\Database\Eloquent\Model).
Traits tích hợp sẵn trong Model (namespace SkillDo\Traits\Eloquent\*):
- ModelStatic (các phương thức static:
all()...) - ModelMeta (đọc/ghi bảng metadata:
getMeta,addMeta,updateMeta,deleteMeta) - ModelEvent (Hooks tự động:
saving,saved,deleted...) - HasGlobalScopes (
addGlobalScope) - HasUniqueIds (nền tảng cho
HasUuids/HasUlids— primary key UUID/ULID) - HasRelationships (
hasOne,hasMany,belongsTo,belongsToMany)
Traits tùy chọn (tự khai báo use khi cần):
- SoftDeletes (Xóa mềm với cột
trashkiểu số 0/1) - ModelRoute (Tự động quản lý URL/Slug qua bảng
routes) - ModelLanguage (Đa ngôn ngữ qua bảng
language)
2. Tạo Model cho Plugin
Bước 1: Khai Báo Class Model
Tạo file đặt trong app/Models/ của Plugin:
<?php
namespace MyPlugin\Models;
use SkillDo\Database\Eloquent\Model;
class Booking extends Model
{
// Tên bảng trong database (nếu bỏ qua sẽ suy ra từ tên class dạng snake_case số nhiều)
protected string $table = 'bookings';
// Khai báo kiểu dữ liệu cho các cột ĐẶC BIỆT (đặc điểm riêng của SkillDo Model, khác Laravel)
// Lưu ý: KHÔNG cần liệt kê hết — hệ thống tự đọc toàn bộ cột + default
// từ schema database (cache key `table_columns_{table}`).
// Chỉ khai báo cột cần kiểu xử lý đặc biệt: wysiwyg, image, json, array...
// hoặc cần default khác với database.
protected array $columns = [
'note' => ['wysiwyg'],
'options' => ['json'],
'status' => ['int', 1], // [kiểu, giá_trị_mặc_định]
];
}
Bước 2: Đăng Ký Alias (Tùy Chọn)
Mở file bootstrap/config.php của Plugin và đăng ký class alias qua SkillDo\AliasLoader (cú pháp giống cách CMS đăng ký SkillDo\Model\* trong CmsServiceProvider::aliases()):
// Trong bootstrap/config.php
\SkillDo\AliasLoader::getInstance()->alias('Booking', \MyPlugin\Models\Booking::class);
// Sau đó có thể dùng
$bookings = Booking::where('status', 1)->get();
3. Các Thao Tác CRUD Cơ Bản
3.1 Lấy Dữ Liệu (Read)
use MyPlugin\Models\Booking;
// Lấy tất cả
$bookings = Booking::all();
// Lấy bản ghi với điều kiện
$activeBookings = Booking::where('status', 1)->get();
// Lấy một bản ghi theo ID
$booking = Booking::find(5);
echo $booking->name;
// Lấy bản ghi đầu tiên khớp điều kiện
$booking = Booking::where('phone', '0901234567')->first();
// Đếm số lượng
$count = Booking::where('status', 1)->count();
[!WARNING]
Model::get()gọi TĨNH khác hẳn Laravel. TraitModelStaticđịnh nghĩa lạiget()ở dạng static:static function get($id = 0)
{
if(is_numeric($id)) return static::query()->find($id);
return static::query()->first();
}Nghĩa là:
Cách gọi Trả về Booking::get()MỘT bản ghi đầu tiên (tương đương first()) — không phải danh sáchBooking::get(5)Bản ghi có id = 5 (tương đương find(5))Booking::all()Collection tất cả bản ghi Booking::where(...)->get()Collection (đây là get()của Query Builder, hoạt động như Laravel)Muốn lấy danh sách mà không có điều kiện, dùng
Booking::all()hoặcBooking::query()->get().
Các static helper riêng của SkillDo
Ngoài API Eloquent chuẩn, trait ModelStatic bổ sung:
| Method | Mô tả |
|---|---|
all() | Collection tất cả bản ghi |
get($id = 0) | Một bản ghi (xem cảnh báo trên) |
create($data) | Thêm mới, trả về int|string id hoặc SKD_Error |
insert($data, $oldObject = null) | Có primary key trong $data thì update, không có thì create |
inserts($data) | Insert hàng loạt qua Query Builder (không bắn model event) |
updateBatch($values, $index = null, $raw = false) | Cập nhật nhiều bản ghi trong một câu lệnh |
delete($id = 0) | Xóa cứng |
3.2 Thêm Mới (Create)
Chỉ có một cách thêm mới: dùng create() — trả về ID vừa insert (int|string) hoặc SKD_Error nếu thất bại:
$bookingId = Booking::create([
'name' => 'Nguyễn Văn A',
'phone' => '0901234567',
'service_id' => 3,
'status' => 1,
'note' => 'Yêu cầu đặc biệt...',
]);
if (is_skd_error($bookingId)) {
// Xử lý lỗi
echo $bookingId->first();
} else {
echo "Tạo thành công, ID = " . $bookingId;
}
Lưu ý (khác Laravel): KHÔNG thể tạo bản ghi mới bằng cách
new Booking()rồi gọisave(). Phương thứcsave()của SkillDo Model chỉ dành cho UPDATE — nó trả vềfalsengay nếu model chưa có primary key (empty($this->getKey())) hoặc không có thay đổi (!isDirty()).
3.3 Cập Nhật (Update)
Cách 1: Lấy bản ghi rồi sửa
$booking = Booking::find(5);
$booking->status = 2;
$booking->note = 'Đã xác nhận';
$booking->save(); // UPDATE — yêu cầu model đã có ID; trả về ID nếu thành công, false nếu không có gì thay đổi
Cách 2: Mass update qua Query Builder
Booking::where('status', 0)
->where('created', '<', '2026-01-01')
->update(['status' => 3]); // Cập nhật tất cả khớp điều kiện
3.4 Xóa (Delete)
delete() luôn là xóa cứng (xóa thật khỏi DB) — kể cả khi model dùng trait SoftDeletes (xóa mềm dùng trash(), xem mục 7). Khi xóa thành công, hệ thống tự dọn route (ModelRoute), bản dịch (ModelLanguage) và metadata liên quan, rồi trả về mảng các ID đã xóa (hoặc false nếu không xóa được gì).
// Lấy bản ghi rồi xóa
$booking = Booking::find(5);
$booking->delete();
// Xóa theo điều kiện (hàng loạt)
Booking::where('status', 3)->delete();
// Xóa theo ID cụ thể
Booking::whereIn('id', [1, 2, 3])->delete();
4. Model Events (Hooks Tự Động)
Model của SkillDo hỗ trợ các hook lắng nghe sự kiện được khai báo trong boot(). Đây là tính năng rất mạnh để tự động hóa logic khi lưu/xóa dữ liệu.
class Booking extends Model
{
protected string $table = 'bookings';
protected static function boot(): void
{
parent::boot();
// Chạy TRƯỚC khi lưu (cả insert lẫn update)
static::saving(function (Booking $booking) {
// Chuẩn hóa số điện thoại trước khi lưu
$booking->phone = preg_replace('/\D/', '', $booking->phone);
});
// Chạy SAU khi lưu thành công
static::saved(function (Booking $booking, $action) {
// $action = 'add' (thêm mới) | 'update' (cập nhật)
if ($action === 'add') {
// Gửi email thông báo đặt lịch mới
// NotificationService::sendBookingConfirmation($booking);
}
});
// Chạy SAU khi xóa thành công
static::deleted(function (Booking $booking, $listIdRemove, $objects) {
// Dọn dẹp dữ liệu liên quan
\Illuminate\Support\Facades\DB::table('booking_services')
->whereIn('booking_id', $listIdRemove)
->delete();
});
}
}
Danh sách Events hỗ trợ (trait SkillDo\Traits\Eloquent\ModelEvent):
| Event | Thời điểm kích hoạt |
|---|---|
saving | Trước khi lưu (Insert hoặc Update) |
saved | Sau khi lưu thành công — callback nhận ($model, $action) với $action = 'add'|'update' |
creating | Trước khi Insert mới |
created | Sau khi Insert thành công |
updating | Trước khi Update (mass update qua builder) |
updated | Sau khi Update thành công |
deleting | Trước khi Delete — callback nhận ($model, $listId, $objects) |
deleted | Sau khi Delete thành công — callback nhận ($model, $listIdRemove, $objects) |
retrieved | Sau khi bản ghi được load từ DB (get()/first()) |
trashing / trashed | Trước / sau khi xóa mềm bằng trash() (cần trait SoftDeletes) |
restoring / restored | Trước / sau khi khôi phục bằng restore() (cần trait SoftDeletes) |
booting / booted | Khi model boot lần đầu / sau khi khởi tạo instance |
columnsCreated / rulesCreated | Sau khi build xong danh sách cột / rules từ schema |
setQueryBuilding / setQueryBuilt | Khi query builder của model đang/đã được khởi tạo (dùng để tùy biến query mặc định) |