Laravel 項(xiàng)目開發(fā)規(guī)范 模型規(guī)范

2023-02-16 17:12 更新

放置位置

所有的數(shù)據(jù)模型文件,都 必須 存放在:app/Models/ 文件夾中。

命名空間:

namespace App\Models;

繼承基類

所有的 Eloquent 數(shù)據(jù)模型 都 必須 繼承統(tǒng)一的基類 App\Models\Model,此基類存放位置為 /app/Models/Model.php,內(nèi)容參考以下:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model as EloquentModel;

class Model extends EloquentModel
{
    public function scopeRecent($query)
    {
        return $query->orderBy('id', 'desc');
    }

    public function scopeOlder($query)
    {
      return $query->orderBy('id', 'asc');
    }

    public function scopeByUser($query, User $user)
    {
      return $query->where('user_id', $user->id);
    }
}

以 Photo 數(shù)據(jù)模型作為例子繼承 Model 基類:

<?php

namespace App\Models;

class Photo extends Model
{
    protected $fillable = ['id', 'user_id'];

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

命名規(guī)范

數(shù)據(jù)模型相關(guān)的命名規(guī)范:

  • 數(shù)據(jù)模型類名 必須 為「單數(shù)」, 如:App\Models\Photo
  • 類文件名 必須 為「單數(shù)」,如:app/Models/Photo.php
  • 數(shù)據(jù)庫表名字 必須 為「復(fù)數(shù)」,多個(gè)單詞情況下使用「Snake Case」 如:photos, my_photos
  • 數(shù)據(jù)庫表遷移名字 必須 為「復(fù)數(shù)」,如:2014_08_08_234417_create_photos_table.php
  • 數(shù)據(jù)填充文件名 必須 為「復(fù)數(shù)」,如:PhotosTableSeeder.php
  • 數(shù)據(jù)庫字段名 必須 為「Snake Case」,如:view_count, is_vip
  • 數(shù)據(jù)庫表主鍵 必須 為「id」
  • 數(shù)據(jù)庫表外鍵 必須 為「resource_id」,如:user_id, post_id
  • 數(shù)據(jù)模型變量 必須 為「resource_id」,如:$user_id, $post_id

模型關(guān)聯(lián)

數(shù)據(jù)關(guān)聯(lián)內(nèi)部 ?必須? 使用「resource_id」,假如 User 模型有 id 和 UUID 兩個(gè)唯一字段,其他模型關(guān)聯(lián) User ?必須? 使用 id 字段。也就是在其他模型的數(shù)據(jù)表里,使用 ?user_id? 字段。

利用 Trait 來擴(kuò)展數(shù)據(jù)模型

模型間,相同的模型邏輯,例如 User 和 Topic 都有一個(gè) settings JSON 字段,用來實(shí)現(xiàn)單個(gè)模型的設(shè)置功能,應(yīng)該 利用 Trait 來實(shí)現(xiàn)邏輯代碼。

所有模型 Traits 必須存放于 app/Models/Traits 目錄下。

注意: 業(yè)務(wù)邏輯請(qǐng)使用 ModelService 模式來組織代碼。

Repository

絕不 使用 Repository,因?yàn)槲覀儾皇窃趯?JAVA 代碼,太多封裝就成了「過度設(shè)計(jì)(Over Designed)」,極大降低了編碼愉悅感,使用 MVC 夠傻夠簡單。

代碼的可讀性,維護(hù)和開發(fā)的便捷性,直接關(guān)系到程序員開發(fā)時(shí)的愉悅感,直接影響到項(xiàng)目推進(jìn)效率和程序 Debug 速度。

關(guān)于 SQL 文件

  • 絕不 使用命令行或者 PHPMyAdmin 直接創(chuàng)建索引或表。必須 使用 數(shù)據(jù)庫遷移 去創(chuàng)建表結(jié)構(gòu),并提交版本控制器中;
  • 絕不 為了共享對(duì)數(shù)據(jù)庫更改就直接導(dǎo)出 SQL,所有修改都 必須 使用 數(shù)據(jù)庫遷移 ,并提交版本控制器中;
  • 絕不 直接向數(shù)據(jù)庫手動(dòng)寫入偽造的測試數(shù)據(jù)。必須 使用 數(shù)據(jù)填充 來插入假數(shù)據(jù),并提交版本控制器中。

作用域

Laravel 的 Model 全局作用域 允許我們?yōu)榻o定模型的所有查詢添加默認(rèn)的條件約束。

所有的全局作用域都 必須 統(tǒng)一使用 閉包定義全局作用域,如下:

/**
 * 數(shù)據(jù)模型的啟動(dòng)方法
 *
 * @return void
 */
protected static function boot()
{
    parent::boot();

    static::addGlobalScope('age', function(Builder $builder) {
        $builder->where('age', '>', 200);
    });
}

數(shù)據(jù)層無狀態(tài)

先看一段代碼,以下是 Post 模型里創(chuàng)建文章評(píng)論的方法:

    public function createComment($content)
    {
        return $this->comments()->create([
            'content' => $content,
            'user_id' => Auth::user()->id
        ]);
    }

注意 Auth::user()->id ,在數(shù)據(jù)層里使用當(dāng)前登錄用戶狀態(tài),是默認(rèn)假設(shè)這段代碼永遠(yuǎn)是在 Web 用戶請(qǐng)求下執(zhí)行的。

然而事實(shí)并非如此,有時(shí)候你可能會(huì)在命令行下觸發(fā)調(diào)用這個(gè) createComment() 方法,有時(shí)候是管理員在后臺(tái)觸發(fā),有時(shí)候是隊(duì)列里觸發(fā)。

一個(gè)最佳實(shí)踐的做法是, 絕不 在數(shù)據(jù)層里使用用戶登錄狀態(tài)信息。如果需要用戶信息,必須 將其作為依賴進(jìn)行傳參,如以上代碼可修改為:

    public function createComment($content, $user)
    {
        return $this->comments()->create([
            'content' => $content,
            'user_id' => $user->id
        ]);
    }

在有需要的地方調(diào)用時(shí),以參數(shù)傳入:

Post::createComment($content, Auth::user())

命令行書寫某些特殊邏輯時(shí),例如使用 1 號(hào)用戶的身份創(chuàng)建評(píng)論:

Post::createComment($content, User::find(1))

數(shù)據(jù)層,也就是模型里,不能跟用戶的登錄狀態(tài)掛鉤。

目錄分層

如果是一個(gè)長期維護(hù)的項(xiàng)目,必須 為模型文件按業(yè)務(wù)邏輯做分層。

一個(gè)長期維護(hù)的項(xiàng)目,很容易就會(huì)出現(xiàn)幾十上百的表,每個(gè)表對(duì)應(yīng)一個(gè) Model 文件。筆者曾維護(hù)過一個(gè)項(xiàng)目,兩百多個(gè) Model 文件,app/models 目錄完全沒法看。

如果你能預(yù)期到 Model 文件會(huì)很多,那就 必須 做好目錄劃分,按照業(yè)務(wù)邏輯來分,以 LearnKu.com 為例,app/models 的目錄結(jié)構(gòu)如下:

├── Book
│   ├── Article.php
│   └── Book.php
├── Community
│   ├── Reply.php
│   └── Topic.php
└── Project
    ├── ProjectAuthor.php
    └── Project.php

模型事件

應(yīng)該 盡量避免使用 Laravel 的 模型事件。

使用模型事件的問題在于,其職能很難界定,所有的業(yè)務(wù)邏輯都能寫到模型事件中。

而我們?cè)陧?xiàng)目中,業(yè)務(wù)邏輯代碼規(guī)都封裝到 Service 層,開發(fā)者在書寫業(yè)務(wù)邏輯代碼時(shí),就會(huì)面臨兩種選擇。

例如說 ReplyService 類的 create 方法,將創(chuàng)建評(píng)論時(shí)需要的邏輯,如發(fā)送通知給話題的作者,或者增加話題的評(píng)論數(shù)等操作,放置于此方法中,效果跟放在 ReplyObserver 中是一樣的。

不一樣的是, ReplyService 是顯示地書寫業(yè)務(wù)邏輯,代碼可讀性比模型事件更高。

模型事件另一個(gè)缺點(diǎn)就是,模型操作,附帶太多邏輯,有太多的 Side Effect,并且是隱性的。模型操作是一個(gè)使用頻率很高的功能,在有些場景中,你就想創(chuàng)建一個(gè) Reply,但是不想通知到用戶,例如說 Seed 時(shí)。雖然 Laravel 有提供模型方法讓你暫時(shí)關(guān)閉模型事件,但這在實(shí)踐中,我見過太多開發(fā)者經(jīng)常會(huì)忘記此操作。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)