概述
发行说明 - Laravel
对于所有 Laravel 发行版本,BUG 修复的期限为 18 个月,安全修复的期限为 2 年。对于包括 Lumen 在内的所有额外的库,只有最新的版本才会得到 BUG 修复。
本文基于 Laravel 8.x 版本
版本 | PHP (*) | 发行时间 | Bug 修复截止时间 | 安全修复截止时间 |
---|
6 (LTS) | 7.2 - 8.0 | 2019 年 9 月 3 日 | 2022 年 1 月 25 日 | 2022 年 9 月 6 日 |
7 | 7.2 - 8.0 | 2020 年 3 月 3 日 | 2020 年 10 月 6 日 | 2021 年 3 月 3 日 |
8 | 7.3 - 8.1 | 2020 年 9 月 8 日 | 2022 年 7 月 26 日 | 2023 年 1 月 24 日 |
9 | 8.0 - 8.1 | 2022 年 2 月 8 日 | 2023 年 8 月 8 日 | 2024 年 2 月 8 日 |
10 | 8.1 | 2023 年 2 月 7 日 | 2024 年 8 月 7 日 | 2025 年 2 月 7 日 |
请求路由
路由 |《Laravel 8.x》
常规路由
1
2
3
4
5
6
7
8
9
10
11
| Route::get('foo', function () {
return 'Hello World';
});
Route::get('/user', 'UserController@index');
Route::match(['get', 'post'], '/', function () {
//
});
Route::get('config/list','Common\Config@list');
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| // 路由命名,控制器中跳转至指定的路由
Route::get('user/profile', 'UserProfileController@show')->name('profile');
// 生成 URL...
$url = route('profile');
// 控制器中生成重定向...
return redirect()->route('profile');
// 传参方式
Route::get('user/{id}/profile', function ($id) {
//
})->name('profile');
$url = route('profile', ['id' => 1]);
|
路由分组
1
2
3
4
5
6
7
8
9
10
11
| Route::prefix('admin')->group(function () {
Route::get('users', function () {
// 匹配包含 「/admin/users」 的 URL
});
});
Route::name('admin.')->group(function () {
Route::get('users', function () {
// 指定路由名为 「admin.users」... (不影响请求get路由)
})->name('users');
});
|
1
2
3
4
| // {注意} 回退路由应始终是你应用程序注册的最后一个路由。
Route::fallback(function () {
// 类似 404 页面,匹配不到任何路由的时候走到该方法
});
|
路由中间键
路由 |《Laravel 8.x》
1
2
3
4
5
6
| // 限流方式
Route::middleware('auth:api', 'throttle:60,1')->group(function () {
Route::get('/user', function () {
//
});
});
|
控制器中获取请求内容
接收请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| // 无值情况 $name === null
// 检索 body 和 url 中参数,两则都有的情况下 body 内容优先。
$name = $request->input('name');
// 获取请求数据数组
$input = $request->all();
// 只取指定的参数,不存在的值 === null,key 依然存在
/*
?name=2&age=10
array:2 [
"name" => "2"
"a1" => null
]
*/
$input = $request->all(['name','a1']);
// 只取指定的参数,不存在的值不会有 key,有值并且是空字符串时候,值 === null
/*
?name=2&age=10&a1=
array:2 [
"name" => "2"
"a1" => null
]
?name=2&age=10
array:1 [
"name" => "2"
]
*/
$input = $request->only(['name','a1']);
|
获取上传的文件
请求 |《Laravel 8.x》
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| $file = $request->file('photo');
$file = $request->photo;
// 判断是否为文件
if ($request->hasFile('photo')) {
//
}
// 获取上传临时文件路径
// "/tmp/phpxXRXuy"
$path = $request->photo->path();
// 获取扩展名
// "png"
$extension = $request->photo->extension();
// 获取文件原始文件名
// "Snipaste_2021-09-24_10-09-55.png"
$name = $request->photo->->getClientOriginalName();
|
表单校验
表单验证 |《Laravel 8.x》
快速校验
1
2
3
4
| $validatedData = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
|
手动创建验证器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| $validator = Validator::make(request()->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
if ($validator->fails()) {
//array:2 [
// 0 => "title"
// 1 => "body"
//]
$keys = $validator->errors()->keys(); // 错误的全部 key
// "标题 不能为空。" 第一个错误信息
dd($validator->errors()->first());
}
|
表单验证 |《Laravel 8.x》
1
2
3
4
5
6
7
| // 自定义错误信息
// :attribute 占位符会被验证字段的实际名称替换
$messages = [
'required' => 'The :attribute field is required.',
];
$validator = Validator::make($input, $rules, $messages);
|
控制器可用校验规则
表单验证 |《Laravel 8.x》
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| [
// 验证的字段必须存在于输入数据中,而不是空。
// 空字符串、空数组、无效上传文件、值为 null 都不能过校验。
'key' => 'required',
// 验证字段必须包含在给定的值列表中。
'type' => 'required|in:1,2,3,4,5',
// 校验参数范围 1< page_size < 1000 (整数)
'page_size' => 'required|integer|between:1,1000',
// 校验参数范围 1< float < 1000 (可为小数)
'float' => 'required|numeric|between:1,1000',
// 校验字符个数 必须介于 1 - 2 个字符之间。
'body' => 'required|between:1,2',
// 正则表达式匹配,修饰符
// 不通过提示 "mp4 格式不正确。"
'mp4' => 'regex:/^\w+\.mp4$/i',
]
|
collect 集合
集合 |《Laravel 8.x》
创建集合
1
| $collection = collect([1, 2, 3]);
|
扩展集合
1
2
3
4
5
6
7
8
9
10
11
| Collection::macro('toUpper', function () {
return $this->map(function ($value) {
return Str::upper($value);
});
});
$collection = collect(['first', 'second']);
$upper = $collection->toUpper();
// ['FIRST', 'SECOND']
|
all 获取底层数组
1
2
3
4
| // 注意:只有第一层会变成数组,其他嵌套对象保持原样。
collect([1, 2, 3])->all();
// [1, 2, 3]
|
toArray 集合转数组
1
2
3
4
5
6
7
8
9
10
11
| // 注意:toArray 也会将所有集合的嵌套对象转换为数组。如果你想获取原数组,可以使用 all 方法。
$collection = collect(['name' => 'Desk', 'price' => 200]);
$collection->toArray();
/*
[
"name" => "Desk"
"price" => 200
]
*/
|
get 取键的集合项
1
2
3
4
5
6
7
8
9
10
11
12
| $collection = collect(['name' => 'taylor', 'framework' => 'laravel']);
$value = $collection->get('name');
// taylor
$collection = collect(['name' => 'taylor', 'framework' => 'laravel']);
$value = $collection->get('foo', 'default-value');
// default-value
$collection->get('email', function () {
return 'default-value';
});
// default-value
|
chunk 将集合拆开成块
1
2
3
4
5
6
7
| $collection = collect([1, 2, 3, 4, 5, 6, 7]);
$chunks = $collection->chunk(4);
$chunks->toArray();
// [[1, 2, 3, 4], [5, 6, 7]]
|
collapse 二维转一维
1
2
3
4
5
6
7
| $collection = collect([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
$collapsed = $collection->collapse();
$collapsed->all();
// [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
each 循环集合项只做循环
1
2
3
4
5
6
| // 只做循环操作,不会改变原始集合内容
$collection->each(function ($item, $key) {
if (/* some condition */) {
return false; // 中断循环
}
});
|
map 遍历生成新集合
1
2
3
4
5
6
7
8
9
10
| $collection = collect([1, 2, 3, 4, 5]);
// 创建一个新集合
$multiplied = $collection->map(function ($item, $key) {
return $item * 2;
});
$multiplied->all();
// [2, 4, 6, 8, 10]
|
1
2
3
4
5
6
7
8
9
| $collection = collect([1, 2, 3, 4, 5]);
$collection->transform(function ($item, $key) {
return $item * 2;
});
$collection->all();
// [2, 4, 6, 8, 10]
|
filter 保留满足条件项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| $collection = collect([1, 2, 3, 4]);
$filtered = $collection->filter(function ($value, $key) {
return $value > 2;
});
$filtered->all();
// [3, 4]
// 未提供回调函数 过滤空值
$collection = collect([1, 2, 3, null, false, '', 0, []]);
$collection->filter()->all();
// [1, 2, 3]
|
reject 丢弃满足条件项
1
2
3
4
5
6
7
8
9
| $collection = collect([1, 2, 3, 4]);
$filtered = $collection->reject(function ($value, $key) {
return $value > 2;
});
$filtered->all();
// [1, 2]
|
first 取满足条件的第一个
1
2
3
4
5
6
7
8
9
| collect([1, 2, 3, 4])->first(function ($value, $key) {
return $value > 2;
});
// 3
collect([1, 2, 3, 4])->first();
// 1
|
implode 集合转字符串
1
2
3
4
5
6
7
8
9
10
11
12
| $collection = collect([
['account_id' => 1, 'product' => 'Desk'],
['account_id' => 2, 'product' => 'Chair'],
]);
$collection->implode('product', ', ');
// Desk, Chair
collect([1, 2, 3, 4, 5])->implode('-');
// '1-2-3-4-5'
|
join 集合转字符串
1
2
3
4
5
| collect(['a', 'b', 'c'])->join(', '); // 'a, b, c'
collect(['a', 'b', 'c'])->join(', ', ', and '); // 'a, b, and c'
collect(['a', 'b'])->join(', ', ' and '); // 'a and b'
collect(['a'])->join(', ', ' and '); // 'a'
collect([])->join(', ', ' and '); // ''
|
isEmpty 判断空集合
1
2
3
| collect([])->isEmpty();
// true
|
keyBy 指定键值为键
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| $collection = collect([
['product_id' => 'prod-100', 'name' => 'Desk'],
['product_id' => 'prod-200', 'name' => 'Chair'],
]);
$keyed = $collection->keyBy('product_id');
$keyed->all();
/*
[
'prod-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],
'prod-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],
]
*/
// 使用回调函数
$keyed = $collection->keyBy(function ($item) {
return strtoupper($item['product_id']);
});
$keyed->all();
/*
[
'PROD-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],
'PROD-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],
]
*/
|
keys 返回集合键
1
2
3
4
5
6
7
8
9
10
| $collection = collect([
'prod-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],
'prod-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],
]);
$keys = $collection->keys();
$keys->all();
// ['prod-100', 'prod-200']
|
merge 合并集合
1
2
3
4
5
6
7
8
9
10
| // 带 key 值的项合并,同名 key 后面的覆盖前面的项。
$collection = collect(['product_id' => 1, 'price' => 100]);
$merged = $collection->merge(['price' => 200, 'discount' => false]);
$merged->all();
// ['product_id' => 1, 'price' => 200, 'discount' => false]
$collection = collect(['Desk', 'Chair']);
$merged = $collection->merge(['Bookcase', 'Door']);
$merged->all();
// ['Desk', 'Chair', 'Bookcase', 'Door']
|
union 合并集合
1
2
3
4
5
6
7
8
9
| // 带 key 值的项合并,同名 key 后面的忽略。
$collection = collect([1 => ['a'], 2 => ['b']]);
$union = $collection->union([3 => ['c'], 1 => ['b']]);
$union->all();
// [1 => ['a'], 2 => ['b'], 3 => ['c']]
// 根据数组的 key 进行合并
collect([1, 2, 3])->union([4, 5, 6, 7, 8])->all();
//[1, 2, 3, 7, 8]
|
only 获取指定键的项
1
2
3
4
5
6
7
8
| // 不存在的 Key 不会在结果中展示
$collection = collect(['product_id' => 1, 'name' => 'Desk', 'price' => 100, 'discount' => false]);
$filtered = $collection->only(['product_id', 'name', 'date']);
$filtered->all();
// ['product_id' => 1, 'name' => 'Desk']
|
pluck 获取指定键值
1
2
3
4
5
6
7
8
9
10
11
12
| // 将指定键的值组成一个数组
$collection = collect([
['product_id' => 'prod-100', 'name' => 'Desk'],
['product_id' => 'prod-200', 'name' => 'Chair'],
]);
$plucked = $collection->pluck('name');
$plucked->all();
// ['Desk', 'Chair']
// 可另外指定第二个参数,当成新集合的键。(如果有重复项,则取数组最后的项)
$plucked = $collection->pluck('name', 'product_id');
$plucked->all();
// ['prod-100' => 'Desk', 'prod-200' => 'Chair']
|
values 重置键
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| $collection = collect([
10 => ['product' => 'Desk', 'price' => 200],
11 => ['product' => 'Desk', 'price' => 200]
]);
$values = $collection->values();
$values->all();
/*
[
0 => ['product' => 'Desk', 'price' => 200],
1 => ['product' => 'Desk', 'price' => 200],
]
*/
|
push 追加元素
1
2
3
4
| $collection = collect([1, 2, 3, 4]);
$collection->push(5);
$collection->all();
// [1, 2, 3, 4, 5]
|
put 指定键值对追加
1
2
3
4
| $collection = collect(['product_id' => 1, 'name' => 'Desk']);
$collection->put('price', 100);
$collection->all();
// ['product_id' => 1, 'name' => 'Desk', 'price' => 100]
|
sortBy 根据二维数组内排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| $collection = collect([
['name' => 'Desk', 'price' => 200],
['name' => 'Chair', 'price' => 100],
['name' => 'Bookcase', 'price' => 150],
]);
$sorted = $collection->sortBy('price');
$sorted->values()->all();
/*
[
['name' => 'Chair', 'price' => 100],
['name' => 'Bookcase', 'price' => 150],
['name' => 'Desk', 'price' => 200],
]
*/
// 自定义排序方式
$collection = collect([
['name' => 'Desk', 'colors' => ['Black', 'Mahogany']],
['name' => 'Chair', 'colors' => ['Black']],
['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']],
]);
$sorted = $collection->sortBy(function ($product, $key) {
return count($product['colors']);
});
$sorted->values()->all();
/*
[
['name' => 'Chair', 'colors' => ['Black']],
['name' => 'Desk', 'colors' => ['Black', 'Mahogany']],
['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']],
]
*/
|
数据库
原生 SQL
快速入门 |《Laravel 8.x》
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| $users = DB::connection('foo')->select(...);
$pdo = DB::connection()->getPdo();
$users = DB::select('select * from users where active = ?', [1]);
$results = DB::select('select * from users where id = :id', ['id' => 1]);
DB::insert('insert into users (id, name) values (?, ?)', [1, 'Dayle']);
// 返回受该语句影响的行数
$affected = DB::update('update users set votes = 100 where name = ?', ['John']);
// 返回受该语句影响的行数
$deleted = DB::delete('delete from users');
// 执行不需要任何返回值的 sql
DB::statement('drop table users');
|
数据库事务
1
2
3
4
5
6
7
8
9
10
11
12
| // 自动事务
DB::transaction(function () {
DB::table('users')->update(['votes' => 1]);
DB::table('posts')->delete();
});
// 手动处理事务
DB::beginTransaction(); // 开始
DB::rollBack(); // 回滚
DB::commit(); // 提交
// Tip:DB facade 的事务方法同样适用于 查询构造器 和 Eloquent ORM 。
|
查询构造器
普通结果查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| // 查询获取所有行
$users = DB::table('users')->get();
// 查询单行
$user = DB::table('users')->where('name', 'John')->first();
// 查询单值
$email = DB::table('users')->where('name', 'John')->value('email');
// 查询单列值的集合
$titles = DB::table('roles')->pluck('title');
$roles = DB::table('roles')->pluck('title', 'name'); // 值 - 键
foreach ($roles as $name => $title) {
echo $title;
}
// 分块结果
DB::table('users')->orderBy('id')->chunk(100, function ($users) {
foreach ($users as $user) {
// return false; // 中断继续获取分块结果
}
});
// 更新型分块 - 根据 ID 来处理结果。(查询条件有被更新操作)
DB::table('users')->where('active', false)
->chunkById(100, function ($users) {
foreach ($users as $user) {
DB::table('users')
->where('id', $user->id)
->update(['active' => true]);
}
});
|
聚合查询
1
2
3
4
| $users = DB::table('users')->count();
$price = DB::table('orders')->max('price');
$price = DB::table('orders')->where('finalized', 1)->avg('price');
DB::table('orders')->where('finalized', 1)->exists();
|
select 指定列、去重
1
2
3
4
5
6
| $users = DB::table('users')->select('name', 'email as user_email')->get();
$users = DB::table('users')->distinct()->get(); // 排重
// 新增字段
$query = DB::table('users')->select('name');
$users = $query->addSelect('age')->get();
|
原生表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| $users = DB::table('users')
->select(DB::raw('count(*) as user_count, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();
$orders = DB::table('orders')
->selectRaw('price * ? as price_with_tax', [1.0825])
->get();
$orders = DB::table('orders')
->whereRaw('price > IF(state = "TX", ?, 100)', [200])
->get();
$orders = DB::table('orders')
->select('department', DB::raw('SUM(price) as total_sales'))
->groupBy('department')
->havingRaw('SUM(price) > ?', [2500])
->get();
$orders = DB::table('orders')
->orderByRaw('updated_at - created_at DESC')
->get();
|
Join
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| // inner join
$users = DB::table('users')
->join('contacts', 'users.id', '=', 'contacts.user_id')
->join('orders', 'users.id', '=', 'orders.user_id')
->select('users.*', 'contacts.phone', 'orders.price')
->get();
// Left Join / Right Join
$users = DB::table('users')
->leftJoin('posts', 'users.id', '=', 'posts.user_id')
->get();
$users = DB::table('users')
->rightJoin('posts', 'users.id', '=', 'posts.user_id')
->get();
// 高级 join 用法
DB::table('users')
->join('contacts', function ($join) {
$join->on('users.id', '=', 'contacts.user_id')->orOn(...);
})
->get();
DB::table('users')
->join('contacts', function ($join) {
$join->on('users.id', '=', 'contacts.user_id')
->where('contacts.user_id', '>', 5);
})
->get();
// 子连接查询
$latestPosts = DB::table('posts')
->select('user_id', DB::raw('MAX(created_at) as last_post_created_at'))
->where('is_published', true)
->groupBy('user_id');
$users = DB::table('users')
// 接收三个参数:子查询、表别名、定义关联字段的闭包
->joinSub($latestPosts, 'latest_posts', function ($join) {
$join->on('users.id', '=', 'latest_posts.user_id');
})->get();
|
union
1
2
3
4
5
6
7
| $first = DB::table('users')
->whereNull('first_name');
$users = DB::table('users')
->whereNull('last_name')
->union($first)
->get();
|
where
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| $users = DB::table('users')->where('votes', '=', 100)->get();
$users = DB::table('users')->where('votes', 100)->get();
$users = DB::table('users')->where('votes', '>=', 100)->get();
$users = DB::table('users')->where('votes', '<>', 100)->get();
$users = DB::table('users')->where('name', 'like', 'T%')->get();
$users = DB::table('users')->where([
['status', '=', '1'],
['subscribed', '<>', '1'],
])->get();
$users = DB::table('users')->whereBetween('votes', [1, 100])->get();
$users = DB::table('users')->whereIn('id', [1, 2, 3])->get();
// 比较两个字段相等
$users = DB::table('users')->whereColumn('first_name', 'last_name')->get();
$users = DB::table('users')->whereColumn([
['first_name', '=', 'last_name'],
['updated_at', '>', 'created_at'],
])->get();
// 子查询
$users = DB::table('users')->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})->get();
/*
select * from users
where exists (
select 1 from orders where orders.user_id = users.id
)
*/
|
where json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| // 仅支持 MySQL 5.7+ JSON 类型支持的数据库
$users = DB::table('users')
->where('options->language', 'en')
->get();
$users = DB::table('users')
->where('preferences->dining->meal', 'salad')
->get();
$users = DB::table('users')
->whereJsonContains('options->languages', 'en')
->get();
$users = DB::table('users')
->whereJsonContains('options->languages', ['en', 'de'])
->get();
$users = DB::table('users')
->whereJsonLength('options->languages', 0)
->get();
$users = DB::table('users')
->whereJsonLength('options->languages', '>', 1)
->get();
|
order group limit offset
1
2
3
4
5
6
7
8
9
10
11
12
| $users = DB::table('users')
->orderBy('name', 'desc') // 降序
->get();
$users = DB::table('users')
->groupBy('first_name', 'status')
->having('account_id', '>', 100)
->get();
// 通常使用 chunk 就行
$users = DB::table('users')->skip(10)->take(5)->get();
$users = DB::table('users')->offset(10)->limit(5)->get();
|
when 条件语句
1
2
3
4
5
6
7
8
9
10
11
12
| // 第一个参数为 true 才会丢给闭包
$role = $request->input('role');
$users = DB::table('users')->when($role, function ($query, $role) {
return $query->where('role_id', $role);
})->get();
// 第三个参数是第一个参数为 false 时执行的
$sortBy = null;
$users = DB::table('users')->when($sortBy, function ($query, $sortBy) {
return $query->orderBy($sortBy);
}, function ($query) {
return $query->orderBy('name');
})->get();
|
insert
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| DB::table('users')->insert(
['email' => 'john@example.com', 'votes' => 0]
);
DB::table('users')->insert([
['email' => 'taylor@example.com', 'votes' => 0],
['email' => 'dayle@example.com', 'votes' => 0]
]);
// insertOrIgnore 方法用于忽略重复插入记录到数据库的错误:
DB::table('users')->insertOrIgnore([
['id' => 1, 'email' => 'taylor@example.com'],
['id' => 2, 'email' => 'dayle@example.com']
]);
// 插入并获取自增 ID
$id = DB::table('users')->insertGetId(
['email' => 'john@example.com', 'votes' => 0]
);
|
update
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| $affected = DB::table('users')
->where('id', 1)
->update(['votes' => 1]);
// 第一个参数用于查询,存在则更新不存在则插入。
DB::table('users')
->updateOrInsert(
['email' => 'john@example.com', 'name' => 'John'],
['votes' => '2']
);
// 更新 json
$affected = DB::table('users')
->where('id', 1)
->update(['options->enabled' => true]);
// 自增自减
DB::table('users')->increment('votes');
DB::table('users')->increment('votes', 5);
DB::table('users')->decrement('votes');
DB::table('users')->decrement('votes', 5);
// 操作过程更新指定字段
DB::table('users')->increment('votes', 1, ['name' => 'John']);
|
delete
1
2
3
| DB::table('users')->delete();
DB::table('users')->where('votes', '>', 100)->delete();
DB::table('users')->truncate(); // 清空表并重置自增 ID
|
分页
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| $users = DB::table('users')->paginate(15); // size ,会自动检测 page
$users = DB::table('users')->simplePaginate(15); // 简单分页,只有上一页下一页
$results->count(); // 获取当前页数据的数量。
$results->currentPage(); // 获取当前页页码。
$results->firstItem(); // 获取结果集中第一条数据的结果编号。
$results->getOptions(); // 获取分页器选项。
$results->getUrlRange($start, $end); // 创建分页 URL 的范围。
$results->hasMorePages(); // 是否有多页。
$results->items(); // 获取当前页的所有项。
$results->lastItem(); // 获取结果集中最后一条数据的结果编号。
$results->lastPage(); // 获取最后一页的页码。(在 simplePaginate 无效)。
$results->nextPageUrl(); // 获取下一页的 URL。
$results->onFirstPage(); // 当前页是否为第一页。
$results->perPage(); // 每页的数据条数。
$results->previousPageUrl(); // 获取前一页的 URL。
$results->total(); // 数据总数(在 simplePaginate 无效)。
$results->url($page); // 获取指定页的 URL。
|
ORM
快速入门 |《Laravel 8.x》
创建 model
1
2
3
4
5
| php artisan make:model Flight
// 同时生成数据库迁移
php artisan make:model Flight --migration
php artisan make:model Flight -m
|
Eloquent 模型约定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| protected $table = 'my_flights'; // 表名
protected $primaryKey = 'flight_id'; // 重定义主键,默认主键为 id
public $timestamps = false; // 不需要自动维护 created_at updated_at
protected $dateFormat = 'U'; // 模型日期格式设置为时间戳,默认为 string 类型时间
const CREATED_AT = 'creation_date'; // 重新设置创建日期字段名
const UPDATED_AT = 'last_update'; // 重新设置修改日期字段名
protected $connection = 'connection-name'; // 选择指定连
protected $attributes = [ // 设置默认属性
'delayed' => false,
];
protected $fillable = ['name']; // 可以被批量赋值的属性。
protected $guarded = ['price']; // 不可批量赋值的属性。
protected $guarded = []; // 所有属性都可以批量赋值
protected $hidden = ['password']; // 隐藏某些字段
protected $casts = [ // 属性类型转换
'birthday' => 'date:Y-m-d',
'joined_at' => 'datetime:Y-m-d H:00',
'is_admin' => 'boolean',
'options' => 'array', // 数据库中 JSON 使用时自动转换成数组,存入时候,数组也会自动转成 json
];
|
分块结果
1
2
3
4
5
| Flight::chunk(200, function ($flights) {
foreach ($flights as $flight) {
//
}
});
|
游标
1
2
3
4
5
6
7
8
9
10
11
| // 允许你使用游标遍历数据库,它只执行一次查询。
foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
//
}
$users = App\User::cursor()->filter(function ($user) {
return $user->id > 500;
});
foreach ($users as $user) {
echo $user->id;
}
|
查询单条数据
1
2
3
4
5
6
7
8
9
10
| // 通过主键检索一个模型...
$flight = App\Flight::find(1);
$flights = App\Flight::find([1, 2, 3]);
// 检索符合查询限制的第一个模型...
$flight = App\Flight::where('active', 1)->first();
// 未找到产生一个异常
$model = App\Flight::findOrFail(1);
$model = App\Flight::where('legs', '>', 100)->firstOrFail();
|
插入更新数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // 插入数据,会自动维护创建时间和更新时间
$flight = new Flight;
$flight->name = $request->name;
$flight->save(); // 插入
// 更新操作,自动维护更新时间
$flight = App\Flight::find(1);
$flight->name = 'New Flight Name';
$flight->save();
// 批量更新,(通过 Eloquent 批量更新时, 更新的模型不会触发 saved 和 updated 事件。)
App\Flight::where('active', 1)
->where('destination', 'San Diego')
->update(['delayed' => 1]);
// 批量赋值,注意:需定义 $fillable 或 $guarded
$flight = App\Flight::create(['name' => 'Flight 10']);
$flight->fill(['name' => 'Flight 22']); // 通过已有模型
// 其他创建方法
|
firstOrCreate firstOrNew
试图找到记录(或创建该记录,如果找不到)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // 通过 name 来查找航班,不存在则创建... (直接插入数据)
$flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);
// 通过 name 查找航班,不存在则使用 name、 delayed 和 arrival_time 属性创建...
$flight = App\Flight::firstOrCreate(
['name' => 'Flight 10'], // 查询条件
['delayed' => 1, 'arrival_time' => '11:30'] // 修改内容
);
// 总结:如果第一个参数找到记录后,直接返回数据(不会根据第二个参数进行修改)。
// 通过 name 查找航班,不存在则创建一个实例... (只是生成一个实例)
$flight = App\Flight::firstOrNew(['name' => 'Flight 10']);
// 通过 name 查找航班,不存在则使用 name 和 delayed 属性创建一个实例...
$flight = App\Flight::firstOrNew(
['name' => 'Flight 10'],
['delayed' => 1, 'arrival_time' => '11:30']
);
|
updateOrCreate
期望使用所提供的数据更新现有记录(如果尚未存在,则创建该记录)。
1
2
3
4
5
6
7
| // 如果没匹配到存在的模型,则创建一个。
$flight = App\Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);
// 通过现有的全部数据覆盖库中的数据,(如果通过第一个参数找到值则做更新操作,未找到则做创建操作)
|
delete
1
2
3
4
5
6
7
8
9
10
11
12
| // 通过模型删除
$flight = App\Flight::find(1);
$flight->delete();
// 通过主键删除
App\Flight::destroy(1);
App\Flight::destroy(1, 2, 3);
App\Flight::destroy([1, 2, 3]);
App\Flight::destroy(collect([1, 2, 3]));
// 通过查询删除,(批量删除不会为删除的模型启动任何模型事件)
$deletedRows = App\Flight::where('active', 0)->delete();
|
查询作用域
快速入门 |《Laravel 8.x》
全局作用域
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| <?php
// 编写一个类
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class AgeScope implements Scope
{
/**
* 把约束加到 Eloquent 查询构造中。
*
* @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);
}
}
<?php
// 然后在 model 里面应用
namespace App;
use App\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 模型的「启动」方法
*
* @return void
*/
protected static function boot()
{
parent::boot();
static::addGlobalScope(new AgeScope);
}
}
// 添加作用域后,对 User::all() 的查询会生成以下 SQL 查询语句:
// select * from `users` where `age` > 200
|
匿名作用域
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| <?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class User extends Model
{
/**
* 模型的「启动」方法
*
* @return void
*/
protected static function boot()
{
parent::boot();
static::addGlobalScope('age', function (Builder $builder) {
$builder->where('age', '>', 200);
});
}
}
|
取消全局作用域
1
2
3
4
5
6
7
8
9
| User::withoutGlobalScope(AgeScope::class)->get();
User::withoutGlobalScope('age')->get();
// 取消所有的全局作用域...
User::withoutGlobalScopes()->get();
// 取消部分全局作用域...
User::withoutGlobalScopes([
FirstScope::class, SecondScope::class
])->get();
|
本地作用域
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| <?php
// 模型方法添加 scope 前缀。
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 只查询受欢迎的用户的作用域
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopePopular($query)
{
return $query->where('votes', '>', 100);
}
/**
* 只查询 active 用户的作用域
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeActive($query)
{
return $query->where('active', 1);
}
}
|
1
2
3
4
5
6
| $users = App\User::popular()->active()->orderBy('created_at')->get();
$users = App\User::popular()->orWhere(function (Builder $query) {
$query->active();
})->get();
// 允许高阶方法,不使用闭包方式
$users = App\User::popular()->orWhere->active()->get();
|
动态作用域
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| <?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 将查询作用域限制为仅包含给定类型的用户
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param mixed $type
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeOfType($query, $type)
{
return $query->where('type', $type);
}
}
// $users = App\User::ofType('admin')->get();
|
模型关联
定义关联
hasOne 一对一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| <?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 获取用户的任务记录
*/
public function task()
{
// 假设一个用户一条任务
return $this->hasOne('App\Model\UserTask'); // 默认情况下 UserTask 模型中有 user_id
// return $this->hasOne('App\Model\UserTask', 'foreign_key'); // 额外指定关系 key
// return $this->hasOne('App\Model\UserTask', 'foreign_key', 'local_key'); // 额外指定 User key
}
}
|
1
2
3
4
5
6
| $model = User::find(1)->task;
/*
实际会执行两条 SQL,先查询出来结果,然后取到用户 ID 值再去查询 user_task 表的记录,limit 1 条记录
select * from `user` where `user`.`id` = ? limit 1
select * from `user_task` where `user_task`.`user_id` = ? and `user_task`.`user_id` is not null limit 1
*/
|
belongsTo 一对一反向
1
2
3
4
5
6
7
8
9
10
| // 其实和一对一类似,只不过是通过 task 记录来找到用户的资料。
return $this->belongsTo('App\Model\User');
// 如果 task_user 中,user_id 不是外键,则需要指定foreign_key
// 取方法名然后加上 _id 当外键
return $this->belongsTo('App\Model\User', 'foreign_key');
// 如果父表不是 id 则指定 other_key
return $this->belongsTo('App\Model\User', 'foreign_key', 'other_key');
|
hasMany 一对多
1
2
| // 一个用户多条任务场景
return $this->hasMany('App\Model\UserTask');
|
1
2
3
4
5
6
7
8
9
10
11
| $model = User::find(1)->task;
/*
实际会执行两条 SQL,先查询出来结果,然后取到用户 ID 值再去查询 user_task 表的记录
select * from `user` where `user`.`id` = ? limit 1
select * from `user_task` where `user_task`.`user_id` = ? and `user_task`.`user_id` is not null
*/
$model = User::find(1)->task()->where('create_ymd', '20210927')->first();
/*
select * from `user` where `user`.`id` = ? limit 1
select * from `user_task` where `user_task`.`user_id` = ? and `user_task`.`user_id` is not null and `create_ymd` = ? limit 1
*/
|
belongsTo 一对多反向
1
2
3
4
5
6
7
| // 只知道一条任务,需要查到该任务的人信息
return $this->belongsTo('App\Model\User');
return $this->belongsTo('App\Model\User', 'foreign_key');
return $this->belongsTo('App\Model\User', 'foreign_key', 'other_key');
// 通过一条任务记录,获取用户状态值
$userStatus = UserTask::find(1)->user->status;
|
belongsToMany 多对多
一个用户可以拥有多种角色,同时这些角色也被多个用户拥有。
定义这种关系需要三张表,users(用户表)、roles(角色表)、role_user(角色绑定关系表)该表命名由关联的两个模型按照字母顺序排序来,切该表包含了 user_id 和 role_id 字段。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| <?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 用户拥有的角色
*/
public function roles()
{
return $this->belongsToMany('App\Role');
// 指定关联表
// return $this->belongsToMany('App\Role', 'role_user');
// 指定关联表外键,第三个参数是次模型中的外键名,第四个参数是连接表中外键名。
// return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');
}
}
|
1
2
3
4
5
6
7
8
| // 获取该用户的所有角色
$user = App\User::find(1);
foreach ($user->roles as $role) {
//
}
$roles = App\User::find(1)->roles()->orderBy('name')->get();
|
belongsToMany 多对多反向
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| <?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
/**
* 拥有此角色的用户
*/
public function users()
{
// 除了引入模型不同,其他都与正向连接相同
return $this->belongsToMany('App\User');
}
}
|
获取多对多中间表字段
1
2
3
4
5
6
7
8
9
10
| $user = App\User::find(1);
foreach ($user->roles as $role) {
echo $role->pivot->created_at; // 获取关系绑定时间
}
// 中间表默认 pivot 对象值包含模型关联主键,需要额外字段需要关联时指出。
return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');
// 中间表自动维护 created_at 和 updated_at 字段。
return $this->belongsToMany('App\Role')->withTimestamps();
|
自定义中间表模型
模型关联 |《Laravel 8.x》
多态关联
模型关联 |《Laravel 8.x》
一个模型中关联多个父级表,如图片表中的图片,可以属于用户,也可以属于某篇文章。
查询关联
模型关联 |《Laravel 8.x》
预加载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| $books = App\Book::with('author')->get();
foreach ($books as $book) {
echo $book->author->name;
}
/*
select * from books
select * from authors where id in (1, 2, 3, 4, 5, ...)
*/
$books = App\Book::with(['author', 'publisher'])->get(); // 多关联
$books = App\Book::with('author.contacts')->get(); // 嵌套预加载
$books = App\Book::with('author:id,name')->get(); // 预加载指定参数列
$books = App\Book::without('author')->get(); // 去除预加载
protected $with = ['author']; // 模型中添加默认预加载
// 预加载添加约束
$users = App\User::with(['posts' => function ($query) {
$query->where('title', 'like', '%first%');
}])->get();
$users = App\User::with(['posts' => function ($query) {
$query->orderBy('created_at', 'desc');
}])->get();
|
插入更新关联模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
| // 为指定文章添加一条新的评论
$comment = new App\Comment(['message' => 'A new comment.']);
$post = App\Post::find(1);
$post->comments()->save($comment);
// 为指定文章添加多条评论
$post = App\Post::find(1);
$post->comments()->saveMany([
new App\Comment(['message' => 'A new comment.']),
new App\Comment(['message' => 'Another comment.']),
]);
// 递归保存模型和关联数据
$post = App\Post::find(1);
$post->comments[0]->message = 'Message';
$post->comments[0]->author->name = 'Author Name';
$post->push();
// create 可以接受一个数组,需要配置批量赋值相关数据
$post = App\Post::find(1);
$comment = $post->comments()->create([
'message' => 'A new comment.',
]);
$post = App\Post::find(1);
// 批量创建
$post->comments()->createMany([
[
'message' => 'A new comment.',
],
[
'message' => 'Another new comment.',
],
]);
// 更新 belongsTo 关联
$account = App\Account::find(10);
$user->account()->associate($account);
$user->save();
// 默认模型,用于查询不到关联模型的时候给出一个默认值
/**
* 获取帖子的作者。
*/
public function user()
{
return $this->belongsTo('App\User')->withDefault();
}
/**
* 获取帖子的作者。
*/
public function user()
{
return $this->belongsTo('App\User')->withDefault([
'name' => 'Guest Author',
]);
}
/**
* 获取帖子的作者。
*/
public function user()
{
return $this->belongsTo('App\User')->withDefault(function ($user, $post) {
$user->name = 'Guest Author';
});
}
|
综合话题
1
2
3
4
5
6
7
8
9
10
| // 查询原生 sql 执行语句
DB::connection('read')->enableQueryLog();
// 执行 sql 查询
dd(DB::connection('read')->getQueryLog());
\DB::enableQueryLog();
dd(\DB::getQueryLog());
\Illuminate\Support\Facades\DB::enableQueryLog();
dd(\Illuminate\Support\Facades\DB::getQueryLog());
|
1
2
3
4
5
| // 缓存系统
$value = cache('key');
cache(['key' => 'value'], $seconds);
cache(['key' => 'value'], now()->addMinutes(10));
|
参考地址