CRUD Service
XinAdmin provides an abstract BaseService base class that encapsulates common CRUD operations, data validation, and query building functionality, allowing developers to quickly build business service layers.
Basic Usage
Create a service class that extends BaseService and define the $model property pointing to the corresponding Eloquent model:
<?php
namespace App\Services;
use App\Models\User;
class UserService extends BaseService
{
protected string $model = User::class;
}
After extending, the service class automatically has the following methods:
Search Field Configuration
$searchField - Precise Search Fields
Define fields that support searching and their query operators:
protected array $searchField = [
'status' => '=', // Exact match
'age' => '>', // Greater than
'score' => '>=', // Greater than or equal
'price' => '<', // Less than
'stock' => '<=', // Less than or equal
'code' => '<>', // Not equal
'name' => 'like', // Fuzzy search (both sides)
'email' => 'afterLike', // Post fuzzy search (prefix match)
'phone' => 'beforeLike', // Pre fuzzy search (suffix match)
'created_at' => 'date', // Date query
'updated_at' => 'betweenDate', // Date range query
];
Operator Description:
$quickSearchField - Quick Search Fields
Define fields that support keyword search for global fuzzy search:
protected array $quickSearchField = ['name', 'email', 'phone'];
When the frontend passes the keywordSearch parameter, it will perform fuzzy matching across all quick search fields:
// Generated SQL
WHERE (name LIKE '%keyword%' OR email LIKE '%keyword%' OR phone LIKE '%keyword%')
Query Parameters
The query() method supports the following request parameters:
filter - Filter Conditions
Used for multi-value filtering, supports JSON string or array format:
{
"filter": {
"status": [1, 2],
"type": ["admin", "user"]
}
}
Generated SQL:
WHERE status IN (1, 2) AND type IN ('admin', 'user')
keywordSearch - Keyword Search
Performs global fuzzy search in fields defined by $quickSearchField:
{
"keywordSearch": "John"
}
sorter - Sorting
Supports single field sorting, values are ascend (ascending) or descend (descending):
{
"sorter": {
"created_at": "descend"
}
}
Search Field Parameters
Pass corresponding parameters based on fields defined in $searchField:
{
"status": 1,
"name": "John",
"created_at": "2024-01-15",
"updated_at": ["2024-01-01", "2024-01-31"]
}
Data Validation
Define Validation Rules
Override the rules() method to define validation rules:
protected function rules(): array
{
return [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'password' => 'required|string|min:6|confirmed',
'status' => 'integer|in:0,1',
];
}
Define Validation Messages
Override the messages() method to define custom error messages:
protected function messages(): array
{
return [
'name.required' => 'Name is required',
'email.required' => 'Email is required',
'email.email' => 'Invalid email format',
'email.unique' => 'Email already in use',
'password.min' => 'Password must be at least 6 characters',
];
}
Distinguish Create and Update Validation
Use the isUpdate() method to check if it's an update request:
protected function rules(): array
{
$rules = [
'name' => 'required|string|max:255',
'status' => 'integer|in:0,1',
];
if ($this->isUpdate()) {
$rules['email'] = 'required|email|unique:users,email,' . request()->route('id');
} else {
$rules['email'] = 'required|email|unique:users,email';
$rules['password'] = 'required|string|min:6';
}
return $rules;
}
Complete Example
<?php
namespace App\Services;
use App\Models\User;
use App\Services\BaseService;
class UserService extends BaseService
{
protected string $model = User::class;
protected array $searchField = [
'status' => '=',
'role_id' => '=',
'name' => 'like',
'email' => 'like',
'created_at' => 'betweenDate',
];
protected array $quickSearchField = ['name', 'email', 'phone'];
protected function rules(): array
{
$rules = [
'name' => 'required|string|max:255',
'email' => 'required|email',
'phone' => 'nullable|string|max:20',
'status' => 'integer|in:0,1',
'role_id' => 'required|exists:roles,id',
];
if ($this->isUpdate()) {
$rules['email'] .= '|unique:users,email,' . request()->route('id');
} else {
$rules['email'] .= '|unique:users,email';
$rules['password'] = 'required|string|min:6';
}
return $rules;
}
protected function messages(): array
{
return [
'name.required' => 'Name is required',
'email.required' => 'Email is required',
'email.email' => 'Invalid email format',
'email.unique' => 'Email already in use',
'password.required' => 'Password is required',
'password.min' => 'Password must be at least 6 characters',
'role_id.required' => 'Please select a role',
'role_id.exists' => 'Role does not exist',
];
}
}
Tree Data
The getTreeData() method is used to build tree-structured data:
public function tree(): array
{
$list = $this->model::query()
->orderBy('sort', 'asc')
->get()
->toArray();
return $this->getTreeData($list);
}
Data Requirements:
- Data must contain
id and parent_id fields
- Root node's
parent_id defaults to 0
- Generated child nodes are stored in the
children field
Exception Handling
BaseService uses RepositoryException to throw exceptions:
Exceptions are caught by the global exception handler and return unified JSON responses.
Extending Methods
You can extend or override parent methods in subclasses:
class UserService extends BaseService
{
public function query(array $params): array
{
$query = $this->model::query()->with(['role', 'department']);
$result = $this->buildSearch($params, $query)
->paginate($params['pageSize'] ?? 10)
->toArray();
return $result;
}
public function create(array $data): bool
{
$data['password'] = bcrypt($data['password']);
return parent::create($data);
}
}