将“优雅”进行到底——laravel的最佳实践与建议

Laravel已是众所周知的“优雅”、“简洁”、“实用”、“敏捷”、“特性丰富”等的代名词,尤为是对PHP开发者来讲。固然了,也正是由于它特性过于“丰富”,致使不少特性并无列在文档里,或者曾经列出来过,可是出于某些缘由,后来又移除了。不少时候,咱们给新手学习建议的时候,都不会建议他直接去看文档,由于文档体系的展开,每每并非新手能hold住的。咱们每每会推荐他们从一些过来人的、真正优雅规范的教程开始上手,上手之后,再在实际当中自行查阅文档。php

本文里,将列出一些截至laravel 5.7的最佳实践与使用建议,不论新手与否,相信均可以帮你将laravel用得更好,将laravel的优雅进行到底,同时更充分地利用起laravel的特性来。前端

更好的阅读体验,请直接移步原文出处:www.pilishen.com/posts/pushi…

(一)将一些经常使用的数据库查询,放到local scopes中

$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();
复制代码

(二)尽量地使用自定义Form Request

在不少不那么规范的教程里,你常常会看到以下的代码:框架

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优雅实战入门:第二版》课程里都已经详细说了,这里就实在懒得啰嗦了。

(三)将一些自带的魔术查询方法用起来

  1. 按照created_at一栏,进行降序排列(‘desc’)
User::latest()->get();
复制代码
  1. 按照任意字段,降序排列查询
User::latest('last_login_at')->get();
复制代码
  1. 以随机的顺序获取数据
User::inRandomOrder()->get();
复制代码
  1. 只有当特定条件为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的时候,才执行某个查询

(四)使用Model关系,来避免大段的查询语句,甚至糟糕的查询

时至今日,你是否还在写一些大段的、难写难读又难复用的SQL查询语句呢?好比为了获取更多的信息,使用了大量的join。即便你利用laravel的Query Builder,这些语句也很是难写,每次写一个是否得花半天呢?并且若是你不是那么懂,原本一个很快的查询,可能让你写糟糕了,反而很是很是慢。

可是若是你懂得laravel里的Model关系,懂得ORM这种更现代的查询方式,那么每每几乎没有什么难的查询,写的时候根本无需动脑,并且不少逻辑均可以复用,而且避免了因不懂背后原理而致使性能反而降低的问题。

这一方面,咱们pilishen.com已经说得足够多了,好比能够看看咱们以前的一篇相关帖子《laravel框架中的Model操做数据库 , 相比DB类有什么明显的优越性吗?》

固然这些,在咱们的《Laravel5.7优雅实战入门:第二版》课程里也都详尽演示了。

(五)使用队列job来处理耗时操做

原来咱们课程里老说,“高性能离不开异步,异步离不开队列”。这方面,laravel的队列job,在处理耗时的、能够放在后台运行的任务上,是一个强大的、必需要掌握的工具。

想发送邮件?队列job。 想发送广播消息?队列job。 想进行较多的图片操做?队列job。 。。。

相似的耗时操做,队列可让你的前台用户无需等待,背后处理便可。这期间,你甚至能够设置队列的频道或名字,设置优先级,设置超时时间,设置重试次数,等等等等。

(六)善用属性获取器(accessor)与修改器(mutator)

假设你只有first_namelast_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前端调用中,就天然会带上这个拼接出来的属性了,固然了,这个属性,并无实际存在数据库中。

(七)不要将model相关的静态属性或内容,放到config当中

假设有个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();

(八)多使用集合collection,而不是原始的array来操做数据

$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混淆起来:

  1. 在Query Builder中,在你最终调用->get()first()这些方法前,咱们实际上并无获取任何数据,好比当咱们用orderBy()where()这些查询方法时
  2. 当咱们调用了->get()first()方法时,数据才真的去获取,相应的内存才真的占用,而后返回一个集合实例

因此,若是能在Query Builder的环节,就进行数据的过滤或条件限定,那就要在这一环节作。而不要说先获取了数据,返回了Collection实例之后,再用Collection相关的方法去过滤或操做,这样的话耗费的内存资源就容易多得多。固然了,这期间也要限定好查询,利用好数据库索引,关于数据库索引,若是你还不够精通,记得看咱们的国际IT专场:《每一个程序猿必须且必定要懂的“数据库索引”》

参考文章: //medium.com/@alexrenoki/pushing-laravel-further-best-tips-good-practices-for-laravel-5-7-ac97305b8cac(在该文基础上有所删减和更改)

来个人网站作个客吧,你会发现更多精彩内容的:www.pilishen.com/posts/pushi…

相关文章
相关标签/搜索