Laravel 因可编写出干净,可用可调试的代码而为广大的 PHP 开发者所熟知。它一样也支持许许多多的功能,有时却未能在文档中体现,或者因为某种缘由它们出现过又被移除了。php
我已经在生产环境中使用 Laravel 2 年了,从中我学到如何把代码变得更好,从我首次使用它以来我都充分发掘它的优点。接下来我将向你展现一些可能对你在用 Laravel 写代码时颇有帮助的奥义之招。laravel
Laravel 有一种很是棒的方式来使用 查询构造器 编写查询。就像这样:git
$orders = Order::where('status', 'delivered')->where('paid', true)->get();
复制代码
很不错。这让我专一于编写更友好的代码而不是 SQL 语句。但若是用 本地范围 ,咱们可让这行代码变得更好些。github
当查询数据时, 本地范围 容许咱们建立本身的 查询构造器 链式方法。举个例子,取代 ->where()
,咱们能够用更简洁的 ->delivered()
和 ->paid()
。数据库
首先在 Order
模型,咱们加入一些方法:跨域
class Order extends Model
{
...
public function scopeDelivered($query) {
return $query->where('status', 'delivered');
}
public function scopePaid($query) {
return $query->where('paid', true);
}
}
复制代码
当声明本地范围时,你应该使用 scope[Something]
来命名。这样 Laravel 便会知道这是一个本地范围而且能够在查询构造器中使用。请确保你在方法中传入了第一个参数 $query
,也就是由 Laravel 自动注入的查询构造器实例。数组
$orders = Order::delivered()->paid()->get();
复制代码
对于可接受额外参数的查询,你可使用动态范围。每一个范围都容许你传入额外的参数。缓存
class Order extends Model
{
...
public function scopeStatus($query, string $status) {
return $query->where('status', $status);
}
}
$orders = Order::status('delivered')->paid()->get();
复制代码
在本文的后面,你会知道为何数据库字段应该使用 蛇形命名
,但这里有第一个缘由:Laravel 默认用 where[Something]
来替换 scope[Something]
。因此做为 scopeStatus
范围的代替,你能够这样作:bash
Order::whereStatus('delivered')->paid()->get();
复制代码
对于 where[Something]
,Laravel 会搜索 蛇形命名
版本的数据库字段。若是你的数据库中有个 status
字段,你能够用上面那个例子。若是有个 shipping_status
字段,你能够用:app
Order::whereShippingStatus('delivered')->paid()->get();
复制代码
由你决定!
Laravel 提供了一种优秀的方式来验证表单提交的数据。若是你须要它,不论是 POST 仍是 GET 请求,它均可以验证。
在控制器中,你能够这样作:
public function store(Request $request)
{
$validatedData = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
// 若是这篇博客的内容无效……
}
复制代码
可是当控制器中已经有不少代码时,再把验证表单数据的代码加进去就会显得很凌乱。你想尽量地减小控制器的代码 —— 至少这是我在控制器中写不少逻辑时想到的第一件事。
Laravel 提供了一种很萌的方式来验证表单请求,那就是建立并使用专门的 请求类 而不是用原始的 Request
。你只须要建立你的请求类:
php artisan make:request StoreBlogPost
复制代码
在 app/Http/Requests/
目录中能够找到你刚建立的请求类:
class StoreBlogPostRequest extends FormRequest
{
public function authorize()
{
return $this->user()->can('create.posts');
}
public function rules()
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
];
}
}
复制代码
如今,你应该用新建立的 App\Http\Requests\StoreBlogPostRequest
来代替原先的 Illuminate\Http\Request
类:
use App\Http\Requests\StoreBlogPostRequest;
public function store(StoreBlogPostRequest $request)
{
// 若是这篇博客的内容无效……
}
复制代码
请求类中的 authorize()
方法应返回一个布尔值。若是返回了 false
,它会抛出一个 403
异常,请确保你在 app/Exceptions/Handler.php
的 render()
方法中捕获了这个异常:
public function render($request, Exception $exception)
{
if ($exception instanceof \Illuminate\Auth\Access\AuthorizationException) {
//
}
return parent::render($request, $exception);
}
复制代码
请求类中还有一个 messages()
方法,当验证失败时,它会返回一个包含了错误信息的数组:
class StoreBlogPostRequest extends FormRequest
{
public function authorize()
{
return $this->user()->can('create.posts');
}
public function rules()
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
];
}
public function messages()
{
return [
'title.required' => 'The title is required.',
'title.unique' => 'The post title already exists.',
...
];
}
}
复制代码
@if ($errors->any())
@foreach ($errors->all() as $error)
{{ $error }}
@endforeach
@endif
复制代码
若是你想获得某个字段的验证信息,你能够这样作(当这个字段验证经过时 $errors->has()
会返回一个 false
):
<input type="text" name="title" />
@if ($errors->has('title'))
<label class="error">{{ $errors->first('title') }}</label>
@endif
复制代码
构建查询时,可使用已有的魔术范围:
created_at
倒序查询:User::latest()->get();
复制代码
User::latest('last_login_at')->get();
复制代码
ORDER BY RAND()
)User::inRandomOrder()->get();
复制代码
你是否曾经为了获取更多的信息而在查询语句中使用大量的 join 操做?即便在使用查询构造器的状况下,编写这样的 SQL 语句也是困难的,可是数据模型已经使用 关联关系 来实现一样的功能。因为文档提供了太多的信息,所以刚开始时你可能对关联关系并不熟悉,可是这些内容能够帮助你更好的理解事物的运行原理,同时让你的程序运行得更加顺畅。
经过 这里 查询关联关系的文档。
Laravel 的任务 是后台运行程序必用的功能强大的工具。
任务系统可以帮助你实现,在执行上述这些任务时,减小你的用户的应用加载时间。这些任务能够被放进命名的队列,它们可以被安排优先级,Laravel 几乎在全部可能的地方都实现了队列:不管在后台执行一些 PHP 任务,或者发送消息,或者广播事件,队列都在这些场景中出现。
你能够在 这里 查询队列的文档。
在使用队列时,我喜欢使用 Laravel Horizon ,由于它很容易安装,它可以经过 Supervisor 工具或者配置文件实现后台运行,同时我可以告诉 Horizon 我但愿每一个队列产生多少个进程。
Laravel 从一开始就教给你变量和方法应使用像 $camelCase
camelCase()
这样的小驼峰命名而数据库字段应使用像 snake_case
这样的蛇形命名。为何呢?由于这有助于咱们构造更好的 访问器。
访问器是能够直接在模型中构造的自定义字段。若是咱们的数据库包含了 first_name
、last_name
、age
这几个字段,咱们能够增长一个叫作 name
的自定义字段来把 first_name
和 last_name
拼接起来。别担忧,这个 name
不会被写入到数据库。它只是某个模型的自定义属性。全部的访问器,和 范围 同样,都有自定义命名语法:getSomethingAttribute
:
class User extends Model
{
...
public function getNameAttribute(): string
{
return $this->first_name.' '.$this->last_name;
}
}
复制代码
当使用 $user->name
,访问器会返回拼接好的字符串。
默认状况下,用 dd($user)
是看不到 name
属性的,可是经过 $appends
变量咱们可使它一直可用:
class User extends Model
{
protected $appends = [
'name',
];
...
public function getNameAttribute(): string
{
return $this->first_name.' '.$this->last_name;
}
}
复制代码
如今每次 dd($user)
,咱们均可以看到 name
了。(不过仍然,这个属性不是从数据库取得的,而是每次使用时将 first_name
和 last_name
拼接获得的)。
要注意下,若是你数据库里已经有 name
这个字段了,那状况就会有点不同:$appends
数组里的 name
元素就不须要了,而后访问器须要传入一个参数,这个参数就是数据库中的 name
(也就是说咱们用不着再使用 $this
了)。
举个例子,咱们也许想用 ucfirst()
来使名字的首字母转为大写:
class User extends Model
{
protected $appends = [
//
];
...
public function getFirstNameAttribute($firstName): string
{
return ucfirst($firstName);
}
public function getLastNameAttribute($lastName): string
{
return ucfirst($lastName);
}
}
复制代码
如今当咱们用 $user->first_name
,它会返回一个首字母大写的字符串。
因为这个特性,数据库字段最好是用 snake_case
这种蛇形命名。
我喜欢把与模型相关的静态数据存放在模型文件中。让咱们一块儿来看一下。
不要像下面这样:
BettingOdds.php
class BettingOdds extends Model
{
...
}
复制代码
config/bettingOdds.php
return [
'sports' => [
'soccer' => 'sport:1',
'tennis' => 'sport:2',
'basketball' => 'sport:3',
...
],
];
复制代码
使用下面的方式访问:
config('bettingOdds.sports.soccer');
复制代码
我更喜欢这样作:
BettingOdds.php
class BettingOdds extends Model
{
protected static $sports = [
'soccer' => 'sport:1',
'tennis' => 'sport:2',
'basketball' => 'sport:3',
...
];
}
复制代码
而后访问它们:
BettingOdds::$sports['soccer'];
复制代码
为何这样?由于这样有益于后续操做:
class BettingOdds extends Model
{
protected static $sports = [
'soccer' => 'sport:1',
'tennis' => 'sport:2',
'basketball' => 'sport:3',
...
];
public function scopeSport($query, string $sport)
{
if (! isset(self::$sports[$sport])) {
return $query;
}
return $query->where('sport_id', self::$sports[$sport]);
}
}
复制代码
如今咱们可使用范围查询:
BettingOdds::sport('soccer')->get();
复制代码
在过去,咱们一般以一种原始的方式使用数组:
$fruits = ['apple', 'pear', 'banana', 'strawberry'];
foreach ($fruits as $fruit) {
echo 'I have '. $fruit;
}
复制代码
如今,咱们可使用一种高级的方法(译者注:集合的方式)处理数组中的数据。咱们能够过滤、转换、遍历和修改数组中数据:
$fruits = collect($fruits);
$fruits = $fruits->reject(function ($fruit) {
return $fruit === 'apple';
})->toArray();
['pear', 'banana', 'strawberry']
复制代码
想要了解细节, 请查看 集合的文档.
当使用 查询构造器时,->get()
方法返回一个 Collection
实例。但要注意别搞混了 Collection
和 Query
builder:
orderBy()
, where()
,等等。->get()
方法以后,数据被获取到,内存空间被消耗。它返回一个 Collection
实例。某些查询构造器不可用或者说可用可是方法名不一样,关于这些请查阅 全部集合的方法。若是你能在 Query Builder
层次过滤数据,就去作吧!不要依赖于等到结果 Collection
实例返回时再过滤---你将会消耗更多的内存空间。 使用 Limit 限制结果条数,在 DB 层使用索引来加快查询。
以下是一些我在用的扩展包:
如下是我(原文做者)编写的一些扩展包:
若是你有更多关于 Laravel 的问题,若是你须要运维方面的帮助,或者只是想说声 谢谢
,你能够在 Twitter @rennokki 上找到我!
转自 PHP / Laravel 开发者社区 laravel-china.org/topics/2216…