Laravel ORM Model 的預定義屬性

weixin_33724059發表於2017-04-30
4686061-d1c3b5d7d8b9dba7.png

緣起

後端開發的基本操作就是處理資料 -- "增刪改查 / CURD", 而 Laravel 框架的"物件關係對映 (ORM = Object Relationship Mapping)" 為解決資料操作中的痛點和癢點提供了便捷的解決方案.


屬性

$table 屬性 -- 自定義關聯的資料表

既然叫"物件關係對映", 則意味著有明確的對應關係和約定.

Laravel 約定資料表的表名是 Model 名的複數, 比如 User Model 對應的是 users 表, Order Model 對應的是 orders 表.
如果需要自定義, 可以通過覆寫 $table 屬性來指定表名:

protected $table = 'yourTableName';

$primaryKey 屬性 -- 自定義主鍵

Laravel 約定每張表都有整型的 id 欄位做為自增主鍵.

如果想設定其他欄位做為主鍵,可以通過覆寫 $primaryKey 屬性來自定義.

protected $primaryKey = 'uid';

$timestamp 屬性 -- 資料的時間戳屬性

資料的可追溯性是非常重要的. 所以 Laravel 遷移檔案預設帶時間戳:

$table->timestamps();

所以通過遷移檔案生成的資料表預設帶 create_atupdated_at 欄位, 在新增資料和更新資料時會分別自動更新這兩個欄位.

這兩個欄位是 MySQL 的 datetime 型別, 即這種樣式: 2015-08-05 07:27:09.
但是個人覺得從 MySQL 檢索優化的角度來說, int 型的 Unix 時間戳比 datetime 型別速度要快, 所以這樣設定:

use Illuminate\Database\Eloquent\Model;

class PosterSubScribeModel extends Model
{
    protected $table = 'subscriber';
    protected $guarded = [''];

    /**
     * 獲取當前Unix時間戳
     * @return int
     */
    public function freshTimestamp()
    {
        return time();
    }

    /**
     * 避免轉換Unix時間戳為時間字串
     *
     * @param \DateTime|int $value
     * @return \DateTime|int
     */
    public function fromDateTime($value)
    {
        return $value;
    }
}

PS. 如果不想使用 timestamp, 可以將其關閉:

protected $timestamps = FALSE;

$casts 屬性 -- 轉化資料型別

PHP 擅長處理陣列, 而前後端互動通常用 JSON, 所以常見的場景是我們希望用"陣列"處理資料和儲存資料, 但是希望讀取出來的是 JSON 格式.

這種場景下就可以使用 $casts 屬性, 實現取出資料時自動轉化為 JSON 資料:

namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
    protected $casts = [
        'my_array_data' => 'json',
    ];
}

還有一種使用場景, 是把儲存的 1 和 0, 在取出時自動轉化為 true 和 false:

class User extends Model
{
    protected $casts = [
        'options' => 'json',
        'status' => 'bool',
    ];
}

**注意: **
$cast 並沒有真的改變儲存的資料型別,而是取出資料時暫時轉換成設定的資料型別;


$attributes 屬性 -- 預設值

給資料庫裡的一個欄位設定預設值.

protected $attributes = [
    'goods_ids' => '[]', //可以配合 $casts, 取出資料時自動轉化為 JSON
    'category_ids' => '[]',
    'display_order' => 0,
];

注意:
不能寫成 'goods_ids' => []. 而必須給[]加上引號; 上次因為沒有加引號, 儲存資料時莫名出現多個空資料(暫時還未弄清楚原因).


$dates 屬性 - 強大的時間類

時間資料經常面臨"格式化"的問題, 比如"Unix時間戳"和"可讀時間格式"的轉化.

$date 屬性可以解決這個問題.
設定成這個屬性的時間資料可以自動轉化為 Carbon 類的物件, 從而使用 Carbon 的方法來處理時間.

Carbon 類的方法很強大, 大家可以深入研究一下Carbon原始碼 或者是檢視 Laravel 中的 Carbon 類 (/vendor/nesbot/carbon/src/Carbon/Carbon.php).
比如, 把"可讀時間格式"(比如 2017-04-30 12:00) 轉化為 "Unix 時間戳":

$model->deleted_at->timestamp

$guarded 和 $fillable 屬性 -- 限制寫入資料庫的資料

因為 Eloquent 模型預設對批量賦值(Mass Assignment)進行保護. 這規則要求使用 create() 或者 update() 方法批量插入或者更新屬性時, 需要先設定 $guarded$fillable 屬性.

設定後, 在批量寫入資料庫時, 不光會篩掉資料表沒有的欄位, 也會篩選掉 $guarded$fillable 中限制的欄位.

  • $guarded 是"黑名單", 寫入這裡的欄位, 表示不可以被賦值; 如果所有欄位都可以寫入資料庫, 可以這樣寫 protected $guarded = [''], 表示沒有需要被 "guarded/保護" 的欄位.
  • $fillable 是"白名單", 寫入這裡的欄位, 表示只有在這些宣告的欄位可以被寫入資料庫;

注意:
一個 model 只能使用其中一個屬性, 而不是一起使用.
一般來說, 因為這兩個屬性的適用場景是剔除非法賦值的資料, 所以$guarded 使用的頻率高一些。


$hidden 和 $visible 屬性 -- 設定資料的可見性

比如像 password 這種欄位,是不希望在讀取後呈現給使用者看到的,那麼可以把它隱藏:

  • 黑名單 $hidden 的寫法
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
    protected $hidden = ['password'];
}
  • 白名單 $visible 的寫法
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
    protected $visible = ['first_name', 'last_name']; //排除 password 欄位
}

注意:

  • $guarded & $fillable 屬性一樣, 一個 model 只能使用 $hidden$visible 其中一個屬性, 而不是同時使用.
    一般來說, 由於這兩個屬性的適用場景是"隱藏資料", 所以 $hidden 使用的頻率高一些。
  • 對於"關聯查詢"
    • 如果要隱藏整張關聯表的欄位,需要在 $hidden 中填寫"表間關係的方法"(比如 hasManyPost)
    • 如果要隱藏關聯表裡的部分欄位,則需要到關聯表的 model 裡去設定 $hidden / $visible 屬性.

$appends 屬性 -- 新增屬性

開發 API 介面時, 前端經常會要求提供一些資料表沒有的欄位, 這時候, 就需要使用 $appends 屬性了. 在查詢資料庫返回的資料中, 手動增加新的資料:

namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
    public function getIsAdminAttribute()
    {
         return 'yes';
    }
     protected $appends = ['is_admin'];
}

這時, 查詢 users 表的資料時, 就多了一個 is_admin 的資料.


$deleted_at 屬性 -- 軟刪除

資料是寶貴的, 而硬碟儲存的成本非常低廉, 所以刪除資料時一般只是新增刪除標識而並不真的刪除資料.

Laravel 提供了"軟刪除"方案, 使用 deleted_at 欄位儲存資料的刪除時間. 查詢資料時, 被軟刪除的資料將會自動從查詢結果中排除.

想要使用"軟刪除", 需要這樣設定:

  • 1.建立資料表時在"遷移檔案"中加入 deleted_at 欄位:
public function up()
{
    Schema::table('flights', function ($table) {
        $table->softDeletes();
    });
}

或者是建立遷移檔案在已有的資料表中新增 deleted_at 欄位:

public function up()
{
    Schema::table('poster', function (Blueprint $table) {
        $table->integer('deleted_at')->nullable()->comment('刪除時間');
    });
}

public function down()
{
    Schema::table('poster', function (Blueprint $table) {
        $table->dropColumn('deleted_at');
    });
}
  • 2.引入 SoftDeletes 的 trait, 並宣告 deleted_at 欄位是 $dates 屬性:
namespace App;

use app\common\models\BaseModel;
use Illuminate\Database\Eloquent\SoftDeletes;

class Poster extends BaseModel
{
    use SoftDeletes;

    protected $dates = ['deleted_at'];
}

如果想要查詢出這些被刪除的資料時, 只要加上 withTrashed() 方法即可.


參考文章


文章歷史

  • 2017/04/30 (第一次釋出)
  • 2017/05/03 潤色
  • 2017/06/03 潤色
  • 2017/06/14 潤色

如果我的文章對你有用, 希望給些改進的建議, 或者打個"喜歡" _

相關文章