Laravel已是众所周知的“优雅”、“简洁”、“实用”、“敏捷”、“特性丰富”等的代名词,尤为是对PHP开发者来讲。固然了,也正是由于它特性过于“丰富”,致使不少特性并无列在文档里,或者曾经列出来过,可是出于某些缘由,后来又移除了。不少时候,咱们给新手学习建议的时候,都不会建议他直接去看文档,由于文档体系的展开,每每并非新手能hold住的。咱们每每会推荐他们从一些过来人的、真正优雅规范的教程开始上手,上手之后,再在实际当中自行查阅文档。php
本文里,将列出一些截至laravel 5.7的最佳实践与使用建议,不论新手与否,相信均可以帮你将laravel用得更好,将laravel的优雅进行到底,同时更充分地利用起laravel的特性来。前端
$orders = Order::where('status', 'delivered')->where('paid', true)->get();
复制代码
咱们常常会写些相似的逻辑,可是当条件查询啥的多了之后,也会变得繁琐,并且期间的不少条件限定与查询,就不能复用,每次都得重复写,这个时候local scopes就登场了。local scopes你能够理解成本地的查询域,或者更直白一些,就是一个经常使用的查询条件片断。咱们能够在model里这样:laravel
class Order extends Model
{
...
public function scopeDelivered($query) {
return $query->where('status', 'delivered');
}
public function scopePaid($query) {
return $query->where('paid', true);
}
}
复制代码
能够看到这些查询域,都是以scope开头的,后面接上你想要的名字,而后在调用的时候,只须要调用scope后面的实际名字便可:数据库
$orders = Order::delivered()->paid()->get();
复制代码
因此这个查询域,就是laravel query builder的一个个片断,或者说数据库条件查询的片断,而后它们能够拼接起来,让咱们终端的代码更加简洁直观。bash
固然,咱们能够更进一步,给这个查询域传上参数,造成动态的条件查询,好比下面这样:app
class Order extends Model
{
...
public function scopeStatus($query, string $status) {
return $query->where('status', $status);
}
}
$orders = Order::status('delivered')->paid()->get();
复制代码
在不少不那么规范的教程里,你常常会看到以下的代码:框架
public function store(Request $request)
{
$validatedData = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
// The blog post is valid...
}
复制代码
不规范的缘由,就是他们把全部能想到的东西,都丢在controller里就完事了,但这样触犯的忌讳就太多了,违背的原则也就太多了,更重要的,随着项目的进行,很容易让代码变成大花脸,变得愈来愈无法维护和扩展,更不用说别人甚至本身回头阅读了。那么上面的这个例子,就是让controller去管了它本不该该负责的数据验证逻辑,而这块,laravel有更优雅的方式来解放controller,也便是咱们的自定义form request:less
php artisan make:request StoreBlogPost
复制代码
执行了之后,在app/Http/Requests/
文件夹下,就会多出下面这么个单独的类:dom
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',
];
}
}
复制代码
这样了之后,在controller里,就不须要传Request
了,而是传上咱们刚才定义的这个StoreBlogPostRequest
:异步
use App\Http\Requests\StoreBlogPostRequest;
public function store(StoreBlogPostRequest $request)
{
// 到这儿的时候,post的相关数据就已经验证好了
}
复制代码
固然在这个自定义的request类里,咱们能够作的文章不止这一点,好比咱们还能够具体定义每个字段验证失败时的错误信息,这个能够经过定义messages()
方法来实现,咱们也能够将request类里的错误信息,在blade当中去相应地返回。这些呢,在咱们的《Laravel5.7优雅实战入门:第二版》课程里都已经详细说了,这里就实在懒得啰嗦了。
created_at
一栏,进行降序排列(‘desc’)User::latest()->get();
复制代码
User::latest('last_login_at')->get();
复制代码
User::inRandomOrder()->get();
复制代码
true
时,才执行相应查询// 假设用户在news页面,想经过下面的url进行最新消息的排序
// mydomain.com/news?sort=new
User::when($request->query('sort'), function ($query, $sort) {
if ($sort == 'new') {
return $query->latest();
}
return $query;
})->get();
复制代码
与这个when()
方法相对应的,还有一个unless()
, 它是when()
的反面,也即只有为false的时候,才执行某个查询
时至今日,你是否还在写一些大段的、难写难读又难复用的SQL查询语句呢?好比为了获取更多的信息,使用了大量的join
。即便你利用laravel的Query Builder,这些语句也很是难写,每次写一个是否得花半天呢?并且若是你不是那么懂,原本一个很快的查询,可能让你写糟糕了,反而很是很是慢。
可是若是你懂得laravel里的Model关系,懂得ORM这种更现代的查询方式,那么每每几乎没有什么难的查询,写的时候根本无需动脑,并且不少逻辑均可以复用,而且避免了因不懂背后原理而致使性能反而降低的问题。
这一方面,咱们pilishen.com已经说得足够多了,好比能够看看咱们以前的一篇相关帖子《laravel框架中的Model操做数据库 , 相比DB类有什么明显的优越性吗?》
固然这些,在咱们的《Laravel5.7优雅实战入门:第二版》课程里也都详尽演示了。
原来咱们课程里老说,“高性能离不开异步,异步离不开队列”。这方面,laravel的队列job,在处理耗时的、能够放在后台运行的任务上,是一个强大的、必需要掌握的工具。
想发送邮件?队列job。 想发送广播消息?队列job。 想进行较多的图片操做?队列job。 。。。
相似的耗时操做,队列可让你的前台用户无需等待,背后处理便可。这期间,你甚至能够设置队列的频道或名字,设置优先级,设置超时时间,设置重试次数,等等等等。
假设你只有first_name
和 last_name
两个字段,而后你想着每次都能轻松地取得user的全名name,那么能够:
class User extends Model
{
...
public function getNameAttribute(): string
{
return $this->first_name.' '.$this->last_name;
}
}
复制代码
这样当你获取$user->name
时,就能自动拼接起全名来。默认的这个$user->name
属性,并不会自动附加到你的user实例上,也即这个时候你dd($user)
,并不会显示出name
属性来,那么怎么样让这个name
属性自动附加到$user
实例上呢?能够以下:
class User extends Model
{
protected $appends = [
'name',
];
...
public function getNameAttribute(): string
{
return $this->first_name.' '.$this->last_name;
}
}
复制代码
这个时候,每次咱们dd($user)
,或者在blade视图以及js前端调用中,就天然会带上这个拼接出来的属性了,固然了,这个属性,并无实际存在数据库中。
假设有个BettingOdds.php
:
class BettingOdds extends Model
{
...
}
复制代码
可能有的人会将一些静态内容放到好比说config/bettingOdds.php
中:
return [
'sports' => [
'soccer' => 'sport:1',
'tennis' => 'sport:2',
'basketball' => 'sport:3',
...
],
];
复制代码
而后呢,获取的时候这样config(’bettingOdds.sports.soccer’);
,可是这样并很差,存在一些隐患,不如直接放到model当中:
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;
}
复制代码
之前,咱们都习惯直接用array来操做相关数据,可是在laravel里,更优雅的方式是多使用collection及相关方法
$fruits = collect($fruits);
$fruits = $fruits->reject(function ($fruit) {
return $fruit === 'apple';
})->toArray();
['pear', 'banana', 'strawberry']
复制代码
这样咱们就能够方便地利用上一些filter、map、transform、reject等等方法,既简单,又优雅
咱们知道laravel的Query Builders,当在它后面最终调用->get()
方法时,返回的是一个集合Collection实例。可是这里,不要将单纯的Collection和Query builder混淆起来:
->get()
或first()
这些方法前,咱们实际上并无获取任何数据,好比当咱们用orderBy()
、where()
这些查询方法时->get()
或first()
方法时,数据才真的去获取,相应的内存才真的占用,而后返回一个集合实例因此,若是能在Query Builder的环节,就进行数据的过滤或条件限定,那就要在这一环节作。而不要说先获取了数据,返回了Collection实例之后,再用Collection相关的方法去过滤或操做,这样的话耗费的内存资源就容易多得多。固然了,这期间也要限定好查询,利用好数据库索引,关于数据库索引,若是你还不够精通,记得看咱们的国际IT专场:《每一个程序猿必须且必定要懂的“数据库索引”》