Laravel ORM Model 的預(yù)定義屬性

緣起

后端開(kāi)發(fā)的基本操作就是處理數(shù)據(jù) -- "增刪改查 / CURD", 而 Laravel 框架的"對(duì)象關(guān)系映射 (ORM = Object Relationship Mapping)" 為解決數(shù)據(jù)操作中的痛點(diǎn)和癢點(diǎn)提供了便捷的解決方案.


屬性

$table 屬性 -- 自定義關(guān)聯(lián)的數(shù)據(jù)表

既然叫"對(duì)象關(guān)系映射", 則意味著有明確的對(duì)應(yīng)關(guān)系和約定.

Laravel 約定數(shù)據(jù)表的表名是 Model 名的復(fù)數(shù), 比如 User Model 對(duì)應(yīng)的是 users 表, Order Model 對(duì)應(yīng)的是 orders 表.
如果需要自定義, 可以通過(guò)覆寫(xiě) $table 屬性來(lái)指定表名:

protected $table = 'yourTableName';

$primaryKey 屬性 -- 自定義主鍵

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

如果想設(shè)置其他字段做為主鍵,可以通過(guò)覆寫(xiě) $primaryKey 屬性來(lái)自定義.

protected $primaryKey = 'uid';

$timestamp 屬性 -- 數(shù)據(jù)的時(shí)間戳屬性

數(shù)據(jù)的可追溯性是非常重要的. 所以 Laravel 遷移文件默認(rèn)帶時(shí)間戳:

$table->timestamps();

所以通過(guò)遷移文件生成的數(shù)據(jù)表默認(rèn)帶 create_atupdated_at 字段, 在添加數(shù)據(jù)和更新數(shù)據(jù)時(shí)會(huì)分別自動(dòng)更新這兩個(gè)字段.

這兩個(gè)字段是 MySQL 的 datetime 類(lèi)型, 即這種樣式: 2015-08-05 07:27:09.
但是個(gè)人覺(jué)得從 MySQL 檢索優(yōu)化的角度來(lái)說(shuō), int 型的 Unix 時(shí)間戳比 datetime 類(lèi)型速度要快, 所以這樣設(shè)置:

use Illuminate\Database\Eloquent\Model;

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

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

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

PS. 如果不想使用 timestamp, 可以將其關(guān)閉:

protected $timestamps = FALSE;

$casts 屬性 -- 轉(zhuǎn)化數(shù)據(jù)類(lèi)型

PHP 擅長(zhǎng)處理數(shù)組, 而前后端交互通常用 JSON, 所以常見(jiàn)的場(chǎng)景是我們希望用"數(shù)組"處理數(shù)據(jù)和存儲(chǔ)數(shù)據(jù), 但是希望讀取出來(lái)的是 JSON 格式.

這種場(chǎng)景下就可以使用 $casts 屬性, 實(shí)現(xiàn)取出數(shù)據(jù)時(shí)自動(dòng)轉(zhuǎn)化為 JSON 數(shù)據(jù):

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

還有一種使用場(chǎng)景, 是把存儲(chǔ)的 1 和 0, 在取出時(shí)自動(dòng)轉(zhuǎn)化為 true 和 false:

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

**注意: **
$cast 并沒(méi)有真的改變存儲(chǔ)的數(shù)據(jù)類(lèi)型,而是取出數(shù)據(jù)時(shí)暫時(shí)轉(zhuǎn)換成設(shè)定的數(shù)據(jù)類(lèi)型;


$attributes 屬性 -- 默認(rèn)值

給數(shù)據(jù)庫(kù)里的一個(gè)字段設(shè)置默認(rèn)值.

protected $attributes = [
    'goods_ids' => '[]', //可以配合 $casts, 取出數(shù)據(jù)時(shí)自動(dòng)轉(zhuǎn)化為 JSON
    'category_ids' => '[]',
    'display_order' => 0,
];

注意:
不能寫(xiě)成 'goods_ids' => []. 而必須給[]加上引號(hào); 上次因?yàn)闆](méi)有加引號(hào), 存儲(chǔ)數(shù)據(jù)時(shí)莫名出現(xiàn)多個(gè)空數(shù)據(jù)(暫時(shí)還未弄清楚原因).


$dates 屬性 - 強(qiáng)大的時(shí)間類(lèi)

時(shí)間數(shù)據(jù)經(jīng)常面臨"格式化"的問(wèn)題, 比如"Unix時(shí)間戳"和"可讀時(shí)間格式"的轉(zhuǎn)化.

$date 屬性可以解決這個(gè)問(wèn)題.
設(shè)置成這個(gè)屬性的時(shí)間數(shù)據(jù)可以自動(dòng)轉(zhuǎn)化為 Carbon 類(lèi)的對(duì)象, 從而使用 Carbon 的方法來(lái)處理時(shí)間.

Carbon 類(lèi)的方法很強(qiáng)大, 大家可以深入研究一下Carbon源碼 或者是查看 Laravel 中的 Carbon 類(lèi) (/vendor/nesbot/carbon/src/Carbon/Carbon.php).
比如, 把"可讀時(shí)間格式"(比如 2017-04-30 12:00) 轉(zhuǎn)化為 "Unix 時(shí)間戳":

$model->deleted_at->timestamp

$guarded 和 $fillable 屬性 -- 限制寫(xiě)入數(shù)據(jù)庫(kù)的數(shù)據(jù)

因?yàn)?Eloquent 模型默認(rèn)對(duì)批量賦值(Mass Assignment)進(jìn)行保護(hù). 這規(guī)則要求使用 create() 或者 update() 方法批量插入或者更新屬性時(shí), 需要先設(shè)置 $guarded$fillable 屬性.

設(shè)置后, 在批量寫(xiě)入數(shù)據(jù)庫(kù)時(shí), 不光會(huì)篩掉數(shù)據(jù)表沒(méi)有的字段, 也會(huì)篩選掉 $guarded$fillable 中限制的字段.

  • $guarded 是"黑名單", 寫(xiě)入這里的字段, 表示不可以被賦值; 如果所有字段都可以寫(xiě)入數(shù)據(jù)庫(kù), 可以這樣寫(xiě) protected $guarded = [''], 表示沒(méi)有需要被 "guarded/保護(hù)" 的字段.
  • $fillable 是"白名單", 寫(xiě)入這里的字段, 表示只有在這些聲明的字段可以被寫(xiě)入數(shù)據(jù)庫(kù);

注意:
一個(gè) model 只能使用其中一個(gè)屬性, 而不是一起使用.
一般來(lái)說(shuō), 因?yàn)檫@兩個(gè)屬性的適用場(chǎng)景是剔除非法賦值的數(shù)據(jù), 所以$guarded 使用的頻率高一些。


$hidden 和 $visible 屬性 -- 設(shè)置數(shù)據(jù)的可見(jiàn)性

比如像 password 這種字段,是不希望在讀取后呈現(xiàn)給用戶看到的,那么可以把它隱藏:

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

注意:

  • $guarded & $fillable 屬性一樣, 一個(gè) model 只能使用 $hidden$visible 其中一個(gè)屬性, 而不是同時(shí)使用.
    一般來(lái)說(shuō), 由于這兩個(gè)屬性的適用場(chǎng)景是"隱藏?cái)?shù)據(jù)", 所以 $hidden 使用的頻率高一些。
  • 對(duì)于"關(guān)聯(lián)查詢"
    • 如果要隱藏整張關(guān)聯(lián)表的字段,需要在 $hidden 中填寫(xiě)"表間關(guān)系的方法"(比如 hasManyPost)
    • 如果要隱藏關(guān)聯(lián)表里的部分字段,則需要到關(guān)聯(lián)表的 model 里去設(shè)置 $hidden / $visible 屬性.

$appends 屬性 -- 添加屬性

開(kāi)發(fā) API 接口時(shí), 前端經(jīng)常會(huì)要求提供一些數(shù)據(jù)表沒(méi)有的字段, 這時(shí)候, 就需要使用 $appends 屬性了. 在查詢數(shù)據(jù)庫(kù)返回的數(shù)據(jù)中, 手動(dòng)增加新的數(shù)據(jù):

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

這時(shí), 查詢 users 表的數(shù)據(jù)時(shí), 就多了一個(gè) is_admin 的數(shù)據(jù).


$deleted_at 屬性 -- 軟刪除

數(shù)據(jù)是寶貴的, 而硬盤(pán)存儲(chǔ)的成本非常低廉, 所以刪除數(shù)據(jù)時(shí)一般只是添加刪除標(biāo)識(shí)而并不真的刪除數(shù)據(jù).

Laravel 提供了"軟刪除"方案, 使用 deleted_at 字段保存數(shù)據(jù)的刪除時(shí)間. 查詢數(shù)據(jù)時(shí), 被軟刪除的數(shù)據(jù)將會(huì)自動(dòng)從查詢結(jié)果中排除.

想要使用"軟刪除", 需要這樣設(shè)置:

  • 1.創(chuàng)建數(shù)據(jù)表時(shí)在"遷移文件"中加入 deleted_at 字段:
public function up()
{
    Schema::table('flights', function ($table) {
        $table->softDeletes();
    });
}

或者是創(chuàng)建遷移文件在已有的數(shù)據(jù)表中添加 deleted_at 字段:

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

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'];
}

如果想要查詢出這些被刪除的數(shù)據(jù)時(shí), 只要加上 withTrashed() 方法即可.


參考文章


文章歷史

  • 2017/04/30 (第一次發(fā)布)
  • 2017/05/03 潤(rùn)色
  • 2017/06/03 潤(rùn)色
  • 2017/06/14 潤(rùn)色

如果我的文章對(duì)你有用, 希望給些改進(jìn)的建議, 或者打個(gè)"喜歡" _

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容