如何讓我們的模型更簡潔

playmaker發表於2022-02-17

我們平時可能在應用中建立了大量的模型。這些模型的程式碼量總是隨著大量的訪問器 / 修改器、增加的 Scope、甚至各種關聯關係而擴大。例如:

// Models/Article.php
class Article extends Model
{
   /**
     * 狀態
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeStatus($query, $status)
    {
        return $query->where('status', $status);
    }

    /**
     * 關聯使用者
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    //...... 
    //甚至訪問器 / 修改器
}

// usage of the scope
Article::status($status)->get();

正如您可能想象的那樣,這些方法會在一段時間後導致模型臃腫,您可以通過以下方法使模型變得更簡潔。

一、擴充套件 Eloquent Builder

1、編寫自己的 Eloquent Builder

可以建立您自己的 Eloquent Builder 並將其繫結到您的模型。這可以通過建立一個擴充套件 Eloquent Builder 的類來完成。首先讓我們從建立一個 ArticleBuilder 開始。將它放在哪裡並不重要,但我傾向於在 App 名稱空間中為它建立一個目錄 App\EloquentBuilders

<?php

declare(strict_types=1);

namespace App\EloquentBuilders;

use Illuminate\Database\Eloquent\Builder;

class ArticleBuilder extends Builder
{
    public function published(): self
    {
        return $this->whereNotNull('published_at');
    }
}

如您所見,它使用與以前相同的方法,因為作用域在後臺使用查詢構建器!

2、註冊全新的 Eloquent Builder

現在剩下的就是將我們的自定義查詢構建器繫結到模型中。通過覆蓋 newEloquentBuilder 方法來完成。覆蓋它後,您可以刪除任何舊 Scope 。您的最終結果將如下所示:

<?php

declare(strict_types=1);

namespace App\Models;

use App\EloquentBuilders\ArticleBuilder;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    use HasFactory;

    public function newEloquentBuilder($query): Builder
    {
        return new ArticleBuilder($query);
    }
}

// usage of the scope
Article::published()->get(); 

二、在Eloquent 模型中使用 Trait (推薦)

例如:常見的訂單模型為例,可以這樣定義目錄結構:

├── Order/ 
    ├── Traits/ 
        ├── Relationship/  //定義關聯關係
            ├── OrderRelationship.php
        ├── Scope/ //定義Scope
            ├── OrderScope.php 
    ├── Order.php //定義模型

訂單模型 Order.php:

<?php

namespace App\Models\Order;

use Illuminate\Database\Eloquent\Model;
use App\Models\Order\Traits\Relationship\OrderRelationship;
use App\Models\Order\Traits\Scope\OrderScope;

class Order extends Model
{
    use OrderRelationship,
        OrderScope,

    //......
}

// usage of the scope
Order::status($status)->get();

根據需要定義關聯關係 Traits\Relationship\OrderRelationship.php

<?php

namespace App\Models\Order\Traits\Relationship;

use App\Models\User\User;

trait OrderRelationship
{
     /**
     * 使用者
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    //......
}

根據需要定義 Scope Traits\Scope\OrderScope.php

<?php

namespace App\Models\Order\Traits\Scope;

trait OrderScope
{

    /**
     * 訂單狀態
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeStatus($query, $status)
    {
        return $query->where('status', $status);
    }

    //......
}

最後,功能方面沒有任何改變,但您的模型變得更加簡潔。
如果有好用的處理方法,請在評論中說出你的想法。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章