国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

目錄
發(fā)現(xiàn)並防止 N 1 問題
防止訪問缺失的屬性
防止靜默丟棄屬性
啟用模型的嚴(yán)格模式
使用 UUID
將 UUID 用作主鍵
添加 UUID 字段到模型
使用 ULID
更改用於路由模型綁定的字段
更改所有路由的字段
更改單個(gè)路由的字段
使用自定義模型集合
比較模型
結(jié)論
首頁 後端開發(fā) php教程 Laravel模型提示

Laravel模型提示

Mar 05, 2025 pm 04:44 PM

Laravel Model Tips

Laravel 提供了大量強(qiáng)大的功能,有助於提升我們的開發(fā)體驗(yàn) (DX)。但是,隨著定期發(fā)布、日常工作的壓力以及大量可用功能的出現(xiàn),很容易錯(cuò)過一些鮮為人知的功能,而這些功能可以幫助改進(jìn)我們的代碼。

本文將介紹一些我最喜歡的 Laravel 模型使用技巧。希望這些技巧能幫助你編寫更簡(jiǎn)潔、更高效的代碼,並幫助你避免常見的陷阱。

發(fā)現(xiàn)並防止 N 1 問題

我們將首先介紹如何發(fā)現(xiàn)並防止 N 1 查詢問題。

當(dāng)延遲加載關(guān)聯(lián)關(guān)係時(shí),可能會(huì)出現(xiàn)常見的 N 1 查詢問題,其中 N 是運(yùn)行以獲取相關(guān)模型的查詢次數(shù)。

這是什麼意思呢?讓我們來看一個(gè)例子。假設(shè)我們要從數(shù)據(jù)庫中獲取所有帖子,遍歷它們,並訪問創(chuàng)建帖子的用戶。我們的代碼可能如下所示:

$posts = Post::all();

foreach ($posts as $post) {
    // 對(duì)帖子執(zhí)行某些操作...

    // 嘗試訪問帖子的用戶
    echo $post->user->name;
}

儘管上面的代碼看起來不錯(cuò),但它實(shí)際上會(huì)導(dǎo)致 N 1 問題。假設(shè)數(shù)據(jù)庫中有 100 個(gè)帖子。在第一行,我們將運(yùn)行單個(gè)查詢以獲取所有帖子。然後,在訪問 $post->userforeach 循環(huán)中,這將觸發(fā)一個(gè)新查詢以獲取該帖子的用戶;導(dǎo)致額外 100 個(gè)查詢。這意味著我們將總共運(yùn)行 101 個(gè)查詢。正如你所想像的那樣,這並不好!它會(huì)減慢應(yīng)用程序的速度,並給數(shù)據(jù)庫帶來不必要的壓力。

隨著代碼變得越來越複雜,功能越來越多,除非你積極尋找這些問題,否則很難發(fā)現(xiàn)這些問題。

值得慶幸的是,Laravel 提供了一個(gè)方便的 Model::preventLazyLoading() 方法,你可以使用它來幫助發(fā)現(xiàn)和防止這些 N 1 問題。此方法將指示 Laravel 在延遲加載關(guān)係時(shí)拋出異常,因此你可以確保始終熱切加載你的關(guān)係。

要使用此方法,可以將 Model::preventLazyLoading() 方法調(diào)用添加到你的 AppProvidersAppServiceProvider 類中:

namespace App\Providers;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Model::preventLazyLoading();
    }
}

現(xiàn)在,如果我們要運(yùn)行上面的代碼來獲取每個(gè)帖子並訪問創(chuàng)建該帖子的用戶,我們將看到拋出 IlluminateDatabaseLazyLoadingViolationException 異常,並顯示以下消息:

<code>嘗試在模型 [App\Models\Post] 上延遲加載 [user],但延遲加載已禁用。</code>

要解決此問題,我們可以更新代碼,在獲取帖子時(shí)熱切加載用戶關(guān)係。我們可以使用 with 方法來實(shí)現(xiàn):

$posts = Post::with('user')->get();

foreach ($posts as $post) {
    // 對(duì)帖子執(zhí)行某些操作...

    // 嘗試訪問帖子的用戶
    echo $post->user->name;
}

上面的代碼現(xiàn)在將成功運(yùn)行,並且只會(huì)觸發(fā)兩個(gè)查詢:一個(gè)用於獲取所有帖子,另一個(gè)用於獲取這些帖子的所有用戶。

防止訪問缺失的屬性

你嘗試訪問你認(rèn)為存在於模型上但不存在的字段的頻率有多高?你可能輸入錯(cuò)誤了,或者你可能認(rèn)為存在 full_name 字段,而實(shí)際上它被稱為 name。

假設(shè)我們有一個(gè) AppModelsUser 模型,具有以下字段:

  • id
  • name
  • email
  • password
  • created_at
  • updated_at

如果我們運(yùn)行以下代碼會(huì)發(fā)生什麼? :

$posts = Post::all();

foreach ($posts as $post) {
    // 對(duì)帖子執(zhí)行某些操作...

    // 嘗試訪問帖子的用戶
    echo $post->user->name;
}

假設(shè)我們?cè)谀P蜕蠜]有 full_name 訪問器,則 $name 變量將為 null。但我們不知道這是因?yàn)?full_name 字段實(shí)際上為 null,還是因?yàn)槲覀儧]有從數(shù)據(jù)庫中獲取該字段,或者因?yàn)樵撟侄尾淮嬖陟赌P椭?。正如你所想像的那樣,這可能會(huì)導(dǎo)致意想不到的行為,有時(shí)很難發(fā)現(xiàn)。

Laravel 提供了一個(gè) Model::preventAccessingMissingAttributes() 方法,你可以使用它來幫助防止此問題。此方法將在你嘗試訪問模型當(dāng)前實(shí)例上不存在的字段時(shí)指示 Laravel 拋出異常。

要啟用此功能,可以將 Model::preventAccessingMissingAttributes() 方法調(diào)用添加到你的 AppProvidersAppServiceProvider 類中:

namespace App\Providers;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Model::preventLazyLoading();
    }
}

現(xiàn)在,如果我們要運(yùn)行我們的示例代碼並嘗試訪問 AppModelsUser 模型上的 full_name 字段,我們將看到拋出 IlluminateDatabaseEloquentMissingAttributeException 異常,並顯示以下消息:

<code>嘗試在模型 [App\Models\Post] 上延遲加載 [user],但延遲加載已禁用。</code>

使用 preventAccessingMissingAttributes 的另一個(gè)好處是,它可以突出顯示我們嘗試讀取模型上存在的但可能未加載的字段的情況。例如,假設(shè)我們有以下代碼:

$posts = Post::with('user')->get();

foreach ($posts as $post) {
    // 對(duì)帖子執(zhí)行某些操作...

    // 嘗試訪問帖子的用戶
    echo $post->user->name;
}

如果我們阻止訪問缺失的屬性,則會(huì)拋出以下異常:

$user = User::query()->first();

$name = $user->full_name;

這在更新現(xiàn)有查詢時(shí)非常有用。例如,過去,你可能只需要模型中的幾個(gè)字段。但是,你可能現(xiàn)在正在更新應(yīng)用程序中的功能,並且需要訪問另一個(gè)字段。如果沒有啟用此方法,你可能不會(huì)意識(shí)到你正在嘗試訪問尚未加載的字段。

值得注意的是,preventAccessingMissingAttributes 方法已從 Laravel 文檔中刪除 (commit),但它仍然有效。我不確定刪除它的原因,但這是一個(gè)需要注意的問題。這可能表明它將來會(huì)被刪除。

(以下內(nèi)容與原文相同,為了保持一致性,我將保留原文,不再進(jìn)行改寫)

防止靜默丟棄屬性

preventAccessingMissingAttributes 類似,Laravel 提供了一個(gè) preventSilentlyDiscardingAttributes 方法,可以幫助防止更新模型時(shí)出現(xiàn)意外行為。

假設(shè)你有一個(gè) AppModelsUser 模型類,如下所示:

$posts = Post::all();

foreach ($posts as $post) {
    // 對(duì)帖子執(zhí)行某些操作...

    // 嘗試訪問帖子的用戶
    echo $post->user->name;
}

正如我們所看到的,name、emailpassword 字段都是可填充字段。但是,如果我們嘗試更新模型上不存在的字段(例如 full_name)或存在的但不可填充的字段(例如 email_verified_at)會(huì)發(fā)生什麼? :

namespace App\Providers;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Model::preventLazyLoading();
    }
}

如果我們運(yùn)行上面的代碼,full_nameemail_verified_at 字段都將被忽略,因?yàn)樗鼈儧]有被定義為可填充字段。但不會(huì)拋出錯(cuò)誤,因此我們將不知道這些字段已被靜默丟棄。

正如你所預(yù)期的那樣,這可能會(huì)導(dǎo)致應(yīng)用程序中難以發(fā)現(xiàn)的錯(cuò)誤,特別是如果你的“更新”語句中的任何其他內(nèi)容實(shí)際上都已更新。因此,我們可以使用 preventSilentlyDiscardingAttributes 方法,該方法將在你嘗試更新模型上不存在或不可填充的字段時(shí)拋出異常。

要使用此方法,可以將 Model::preventSilentlyDiscardingAttributes() 方法調(diào)用添加到你的 AppProvidersAppServiceProvider 類中:

<code>嘗試在模型 [App\Models\Post] 上延遲加載 [user],但延遲加載已禁用。</code>

上面的代碼將強(qiáng)制拋出錯(cuò)誤。

現(xiàn)在,如果我們嘗試運(yùn)行上面的示例代碼並更新用戶的 first_nameemail_verified_at 字段,則會(huì)拋出 IlluminateDatabaseEloquentMassAssignmentException 異常,並顯示以下消息:

$posts = Post::with('user')->get();

foreach ($posts as $post) {
    // 對(duì)帖子執(zhí)行某些操作...

    // 嘗試訪問帖子的用戶
    echo $post->user->name;
}

值得注意的是,preventSilentlyDiscardingAttributes 方法僅在你使用 fillupdate 等方法時(shí)才會(huì)突出顯示不可填充字段。如果你手動(dòng)設(shè)置每個(gè)屬性,它將不會(huì)捕獲這些錯(cuò)誤。例如,讓我們來看以下代碼:

$user = User::query()->first();

$name = $user->full_name;

在上面的代碼中,full_name 字段不存在於數(shù)據(jù)庫中,因此 Laravel 不會(huì)為我們捕獲它,而是在數(shù)據(jù)庫級(jí)別捕獲它。如果你使用的是 MySQL 數(shù)據(jù)庫,你會(huì)看到這樣的錯(cuò)誤:

namespace App\Providers;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Model::preventAccessingMissingAttributes();
    }
}

啟用模型的嚴(yán)格模式

如果你想使用我們前面提到的三種方法,你可以使用 Model::shouldBeStrict() 方法一次啟用它們。此方法將啟用 preventLazyLoading、preventAccessingMissingAttributespreventSilentlyDiscardingAttributes 設(shè)置。

要使用此方法,可以將 Model::shouldBeStrict() 方法調(diào)用添加到你的 AppProvidersAppServiceProvider 類中:

<code>屬性 [full_name] 不存在或未為模型 [App\Models\User] 獲取。</code>

這等同於:

$posts = Post::all();

foreach ($posts as $post) {
    // 對(duì)帖子執(zhí)行某些操作...

    // 嘗試訪問帖子的用戶
    echo $post->user->name;
}

preventAccessingMissingAttributes 方法類似,shouldBeStrict 方法已從 Laravel 文檔中刪除 (commit),但仍然有效。這可能表明它將來會(huì)被刪除。

使用 UUID

默認(rèn)情況下,Laravel 模型使用自動(dòng)遞增的 ID 作為其主鍵。但有時(shí)你可能更願(yuàn)意使用通用唯一標(biāo)識(shí)符 (UUID)。

UUID 是 128 位(或 36 個(gè)字符)的字母數(shù)字字符串,可用於唯一標(biāo)識(shí)資源。由於它們是如何生成的,因此它們與另一個(gè) UUID 衝突的可能性極低。一個(gè) UUID 示例是:1fa24c18-39fd-4ff2-8f23-74ccd08462b0。

你可能希望將 UUID 用作模型的主鍵?;蛘?,你可能希望保留自動(dòng)遞增的 ID 來定義應(yīng)用程序和數(shù)據(jù)庫中的關(guān)係,但將 UUID 用於面向公眾的 ID。使用這種方法可以通過使攻擊者更難以猜測(cè)其他資源的 ID 來增加額外的安全層。

例如,假設(shè)我們?cè)诼酚芍惺褂米詣?dòng)遞增的 ID。我們可能有一個(gè)用於訪問用戶的路由,如下所示:

namespace App\Providers;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Model::preventLazyLoading();
    }
}

如果路由不安全,攻擊者可以循環(huán)遍歷 ID(例如 - /users/1、/users/2、/users/3 等),試圖訪問其他用戶的資料。而如果我們使用 UUID,則 URL 可能更像 /users/1fa24c18-39fd-4ff2-8f23-74ccd08462b0、/users/b807d48d-0d01-47ae-8bbc-59b2acea6ed3/users/ec1dde93-c67a-4f14-8464-c0d29c95425f。正如你所想像的那樣,這些更難以猜測(cè)。

當(dāng)然,僅僅使用 UUID 並不能保護(hù)你的應(yīng)用程序,它們只是你可以採(cǎi)取的提高安全性的額外步驟。你需要確保你還使用其他安全措施,例如速率限制、身份驗(yàn)證和授權(quán)檢查。

將 UUID 用作主鍵

我們首先來看一下如何將主鍵更改為 UUID。

為此,我們需要確保我們的表有一個(gè)能夠存儲(chǔ) UUID 的列。 Laravel 提供了一個(gè)方便的 $table->uuid 方法,我們可以在遷移中使用它。

假設(shè)我們有這個(gè)創(chuàng)建 comments 表的基本遷移:

<code>嘗試在模型 [App\Models\Post] 上延遲加載 [user],但延遲加載已禁用。</code>

正如我們?cè)谶w移中看到的,我們定義了一個(gè) UUID 字段。默認(rèn)情況下,此字段將被稱為 uuid,但如果你願(yuàn)意,可以通過向 uuid 方法傳遞列名來更改它。

然後,我們需要指示 Laravel 將新的 uuid 字段用作我們的 AppModelsComment 模型的主鍵。我們還需要添加一個(gè)特性,它將允許 Laravel 為我們自動(dòng)生成 UUID。我們可以通過覆蓋模型上的 $primaryKey 屬性並使用 IlluminateDatabaseEloquentConcernsHasUuids 特性來實(shí)現(xiàn):

$posts = Post::with('user')->get();

foreach ($posts as $post) {
    // 對(duì)帖子執(zhí)行某些操作...

    // 嘗試訪問帖子的用戶
    echo $post->user->name;
}

現(xiàn)在應(yīng)該配置好模型並準(zhǔn)備使用 UUID 作為主鍵了。來看這個(gè)示例代碼:

$posts = Post::all();

foreach ($posts as $post) {
    // 對(duì)帖子執(zhí)行某些操作...

    // 嘗試訪問帖子的用戶
    echo $post->user->name;
}

我們可以在轉(zhuǎn)儲(chǔ)的模型中看到 uuid 字段已填充了 UUID。

添加 UUID 字段到模型

如果你更願(yuàn)意將自動(dòng)遞增的 ID 用於內(nèi)部關(guān)係,但將 UUID 用於面向公眾的 ID,則可以向模型添加 UUID 字段。

我們假設(shè)你的表具有 iduuid 字段。由於我們將使用 id 字段作為主鍵,因此我們不需要在模型上定義 $primaryKey 屬性。

我們可以覆蓋通過 IlluminateDatabaseEloquentConcernsHasUuids 特性提供的 uniqueIds 方法。此方法應(yīng)返回應(yīng)為其生成 UUID 的字段數(shù)組。

讓我們更新我們的 AppModelsComment 模型以包含我們稱為 uuid 的字段:

namespace App\Providers;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Model::preventLazyLoading();
    }
}

現(xiàn)在,如果我們要轉(zhuǎn)儲(chǔ)一個(gè)新的 AppModelsComment 模型,我們將看到 uuid 字段已填充了 UUID:

<code>嘗試在模型 [App\Models\Post] 上延遲加載 [user],但延遲加載已禁用。</code>

稍後我們將在本文中介紹如何更新你的模型和路由,以便在你的路由中使用這些 UUID 作為你的面向公眾的 ID。

使用 ULID

與在 Laravel 模型中使用 UUID 類似,有時(shí)你可能希望使用通用唯一詞典排序標(biāo)識(shí)符 (ULID)。

ULID 是 128 位(或 26 個(gè)字符)的字母數(shù)字字符串,可用於唯一標(biāo)識(shí)資源。一個(gè) ULID 示例是:01J4HEAEYYVH4N2AKZ8Y1736GD。

你可以像定義 UUID 字段一樣定義 ULID 字段。唯一的區(qū)別是,你應(yīng)該使用 IlluminateDatabaseEloquentConcernsHasUlids 特性,而不是更新你的模型以使用 IlluminateDatabaseEloquentConcernsHasUuids 特性。

例如,如果我們想更新我們的 AppModelsComment 模型以使用 ULID 作為主鍵,我們可以這樣做:

$posts = Post::with('user')->get();

foreach ($posts as $post) {
    // 對(duì)帖子執(zhí)行某些操作...

    // 嘗試訪問帖子的用戶
    echo $post->user->name;
}

更改用於路由模型綁定的字段

你可能已經(jīng)知道什麼是路由模型綁定。但以防萬一你不知道,讓我們快速回顧一下。

路由模型綁定允許你根據(jù)傳遞到 Laravel 應(yīng)用程序路由的數(shù)據(jù)自動(dòng)獲取模型實(shí)例。

默認(rèn)情況下,Laravel 將使用模型的主鍵字段(通常是 id 字段)進(jìn)行路由模型綁定。例如,你可能有一個(gè)用於顯示單個(gè)用戶信息的路由:

$user = User::query()->first();

$name = $user->full_name;

上面示例中定義的路由將嘗試查找數(shù)據(jù)庫中存在且具有提供的 ID 的用戶。例如,假設(shè)數(shù)據(jù)庫中存在 ID 為 1 的用戶。當(dāng)你訪問 URL /users/1 時(shí),Laravel 將自動(dòng)從數(shù)據(jù)庫中獲取 ID 為 1 的用戶,並將其傳遞給閉包函數(shù)(或控制器)以進(jìn)行操作。但是,如果數(shù)據(jù)庫中不存在具有提供的 ID 的模型,Laravel 將自動(dòng)返回 404 Not Found 響應(yīng)。

但是,有時(shí)你可能希望使用不同的字段(而不是主鍵)來定義如何從數(shù)據(jù)庫中檢索模型。

例如,正如我們前面提到的,你可能希望將自動(dòng)遞增的 ID 用作模型的主鍵用於內(nèi)部關(guān)係。但你可能希望將 UUID 用於面向公眾的 ID。在這種情況下,你可能希望使用 uuid 字段進(jìn)行路由模型綁定,而不是 id 字段。

同樣,如果你正在構(gòu)建博客,你可能希望根據(jù) slug 字段而不是 id 字段來獲取你的帖子。這是因?yàn)?slug 字段比自動(dòng)遞增的 ID 更易於閱讀且更利於 SEO。

更改所有路由的字段

如果你想定義應(yīng)用於所有路由的字段,則可以通過在模型上定義 getRouteKeyName 方法來實(shí)現(xiàn)。此方法應(yīng)返回你希望用於路由模型綁定的字段的名稱。

例如,假設(shè)我們要將 AppModelsPost 模型的所有路由模型綁定更改為使用 slug 字段而不是 id 字段。我們可以通過向我們的 Post 模型添加 getRouteKeyName 方法來實(shí)現(xiàn):

$posts = Post::all();

foreach ($posts as $post) {
    // 對(duì)帖子執(zhí)行某些操作...

    // 嘗試訪問帖子的用戶
    echo $post->user->name;
}

這意味著我們現(xiàn)在可以像這樣定義我們的路由:

namespace App\Providers;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Model::preventLazyLoading();
    }
}

當(dāng)我們?cè)L問 URL /posts/my-first-post 時(shí),Laravel 將自動(dòng)從數(shù)據(jù)庫中獲取 slugmy-first-post 的帖子,並將其傳遞給閉包函數(shù)(或控制器)以進(jìn)行操作。

更改單個(gè)路由的字段

但是,有時(shí)你可能只想更改單個(gè)路由中使用的字段。例如,你可能希望在一個(gè)路由中使用 slug 字段進(jìn)行路由模型綁定,但在所有其他路由中使用 id 字段。

我們可以通過在路由定義中使用 :field 語法來實(shí)現(xiàn)。例如,假設(shè)我們要在一個(gè)路由中使用 slug 字段進(jìn)行路由模型綁定。我們可以像這樣定義我們的路由:

<code>嘗試在模型 [App\Models\Post] 上延遲加載 [user],但延遲加載已禁用。</code>

這現(xiàn)在意味著在這個(gè)特定路由中,Laravel 將嘗試從數(shù)據(jù)庫中獲取具有提供的 slug 字段的帖子。

使用自定義模型集合

當(dāng)你使用 AppModelsUser::all() 等方法從數(shù)據(jù)庫中獲取多個(gè)模型時(shí),Laravel 通常會(huì)將它們放入 IlluminateDatabaseEloquentCollection 類的實(shí)例中。此類提供了許多用於處理返回的模型的有用方法。但是,有時(shí)你可能希望返回自定義集合類而不是默認(rèn)集合類。

你可能出於幾個(gè)原因想要?jiǎng)?chuàng)建一個(gè)自定義集合。例如,你可能想要添加一些特定於處理該類型模型的輔助方法?;蛘?,你可能希望將其用於改進(jìn)類型安全,並確保集合只包含特定類型的模型。

Laravel 使覆蓋應(yīng)返回的集合類型變得非常容易。

讓我們來看一個(gè)例子。假設(shè)我們有一個(gè) AppModelsPost 模型,當(dāng)我們從數(shù)據(jù)庫中獲取它們時(shí),我們希望將它們返回到自定義 AppCollectionsPostCollection 類的實(shí)例中。

我們可以創(chuàng)建一個(gè)新的 app/Collections/PostCollection.php 文件並像這樣定義我們的自定義集合類:

$posts = Post::all();

foreach ($posts as $post) {
    // 對(duì)帖子執(zhí)行某些操作...

    // 嘗試訪問帖子的用戶
    echo $post->user->name;
}

在上面的示例中,我們創(chuàng)建了一個(gè)新的 AppCollectionsPostCollection 類,它擴(kuò)展了 Laravel 的 IlluminateSupportCollection 類。我們還指定了此集合將只包含 AppModelsPost 類的實(shí)例,使用 docblock。這對(duì)於幫助你的 IDE 理解集合中將包含的數(shù)據(jù)類型非常有用。

然後,我們可以更新我們的 AppModelsPost 模型以返回自定義集合類的實(shí)例,方法是覆蓋 newCollection 方法,如下所示:

namespace App\Providers;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Model::preventLazyLoading();
    }
}

在這個(gè)例子中,我們獲取傳遞給 newCollection 方法的 AppModelsPost 模型數(shù)組,並返回自定義 AppCollectionsPostCollection 類的新的實(shí)例。

現(xiàn)在我們可以使用自定義集合類從數(shù)據(jù)庫中獲取我們的帖子,如下所示:

<code>嘗試在模型 [App\Models\Post] 上延遲加載 [user],但延遲加載已禁用。</code>

比較模型

我在處理項(xiàng)目時(shí)遇到的一個(gè)常見問題是如何比較模型。這通常是在授權(quán)檢查中,當(dāng)你想檢查用戶是否可以訪問資源時(shí)。

讓我們來看一些常見的陷阱,以及為什麼你可能應(yīng)該避免使用它們。

你應(yīng)該避免在檢查兩個(gè)模型是否相同的時(shí)候使用 ===。這是因?yàn)?=== 檢查在比較對(duì)象時(shí)將檢查它們是否是同一個(gè)對(duì)象的實(shí)例。這意味著即使兩個(gè)模型具有相同的數(shù)據(jù),如果它們是不同的實(shí)例,它們也不會(huì)被認(rèn)為是相同的。因此,你應(yīng)該避免這樣做,因?yàn)樗芸赡軙?huì)返回 false。

假設(shè) AppModelsComment 模型上存在 post 關(guān)係,並且數(shù)據(jù)庫中的第一個(gè)評(píng)論屬於第一個(gè)帖子,讓我們來看一個(gè)例子:

$posts = Post::with('user')->get();

foreach ($posts as $post) {
    // 對(duì)帖子執(zhí)行某些操作...

    // 嘗試訪問帖子的用戶
    echo $post->user->name;
}

你應(yīng)該也避免在檢查兩個(gè)模型是否相同的時(shí)候使用 ==。這是因?yàn)?== 檢查在比較對(duì)象時(shí)將檢查它們是否是同一類的實(shí)例,以及它們是否具有相同的屬性和值。但是,這可能會(huì)導(dǎo)致意想不到的行為。

來看這個(gè)例子:

$user = User::query()->first();

$name = $user->full_name;

在上面的示例中,== 檢查將返回 true,因?yàn)?$comment->post$post 是同一類,並且具有相同的屬性和值。但是,如果我們更改 $post 模型中的屬性使其不同會(huì)發(fā)生什麼?

讓我們使用 select 方法,以便我們只從 posts 表中獲取 idcontent 字段:

$posts = Post::all();

foreach ($posts as $post) {
    // 對(duì)帖子執(zhí)行某些操作...

    // 嘗試訪問帖子的用戶
    echo $post->user->name;
}

即使 $comment->post$post 是相同的模型,== 檢查也將返回 false,因?yàn)槟P途哂胁煌囊鸭虞d屬性。正如你所想像的那樣,這可能會(huì)導(dǎo)致一些難以追蹤的意外行為,特別是如果你已經(jīng)追溯地將 select 方法添加到查詢中,並且你的測(cè)試開始失敗。

相反,我喜歡使用 Laravel 提供的 isisNot 方法。這些方法將比較兩個(gè)模型,並檢查它們是否屬於同一類,是否具有相同的主鍵值,以及是否具有相同的數(shù)據(jù)庫連接。這是一種更安全的比較模型的方法,並將有助於減少意外行為的可能性。

你可以使用 is 方法來檢查兩個(gè)模型是否相同:

namespace App\Providers;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Model::preventLazyLoading();
    }
}

同樣,你可以使用 isNot 方法來檢查兩個(gè)模型是否不同:

<code>嘗試在模型 [App\Models\Post] 上延遲加載 [user],但延遲加載已禁用。</code>

在構(gòu)建查詢時(shí)使用 whereBelongsTo

最後一個(gè)技巧更像是個(gè)人偏好,但我發(fā)現(xiàn)它使我的查詢更易於閱讀和理解。

在嘗試從數(shù)據(jù)庫中獲取模型時(shí),你可能會(huì)發(fā)現(xiàn)自己正在編寫基於關(guān)係的過濾查詢。例如,你可能希望獲取屬於特定用戶和帖子的所有評(píng)論:

$posts = Post::with('user')->get();

foreach ($posts as $post) {
    // 對(duì)帖子執(zhí)行某些操作...

    // 嘗試訪問帖子的用戶
    echo $post->user->name;
}

Laravel 提供了一個(gè) whereBelongsTo 方法,你可以使用它來使你的查詢更易於閱讀(在我看來)。使用此方法,我們可以像這樣重寫上面的查詢:

$user = User::query()->first();

$name = $user->full_name;

我喜歡這種語法糖,並且覺得它使查詢更易於閱讀。這也是確保你根據(jù)正確的關(guān)係和字段進(jìn)行過濾的好方法。

你或你的團(tuán)隊(duì)可能更喜歡使用更明確的方法來編寫 where 子句。因此,這個(gè)技巧可能並不適合所有人。但我認(rèn)為只要你對(duì)你的方法保持一致,這兩種方法都很好。

結(jié)論

希望本文能向你展示一些使用 Laravel 模型的新技巧。你現(xiàn)在應(yīng)該能夠發(fā)現(xiàn)並防止 N 1 問題,防止訪問缺失的屬性,防止靜默丟棄屬性,並將主鍵類型更改為 UUID 或 ULID。你還應(yīng)該知道如何更改用於路由模型綁定的字段,指定返回的集合類型,比較模型以及在構(gòu)建查詢時(shí)使用 whereBelongsTo。

以上是Laravel模型提示的詳細(xì)內(nèi)容。更多資訊請(qǐng)關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

本網(wǎng)站聲明
本文內(nèi)容由網(wǎng)友自願(yuàn)投稿,版權(quán)歸原作者所有。本站不承擔(dān)相應(yīng)的法律責(zé)任。如發(fā)現(xiàn)涉嫌抄襲或侵權(quán)的內(nèi)容,請(qǐng)聯(lián)絡(luò)admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費(fèi)脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅(qū)動(dòng)的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費(fèi)的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費(fèi)的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強(qiáng)大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級(jí)程式碼編輯軟體(SublimeText3)

如何設(shè)置PHP時(shí)區(qū)? 如何設(shè)置PHP時(shí)區(qū)? Jun 25, 2025 am 01:00 AM

tosetTherightTimeZoneInphp,restate_default_timezone_set()functionAtthestArtofyourscriptWithavalIdidentIdentifiersuchas'america/new_york'.1.usedate_default_default_timezone_set_set()

編寫清潔和可維護(hù)的PHP代碼的最佳實(shí)踐是什麼? 編寫清潔和可維護(hù)的PHP代碼的最佳實(shí)踐是什麼? Jun 24, 2025 am 12:53 AM

寫乾淨(jìng)、易維護(hù)的PHP代碼關(guān)鍵在於清晰命名、遵循標(biāo)準(zhǔn)、合理結(jié)構(gòu)、善用註釋和可測(cè)試性。 1.使用明確的變量、函數(shù)和類名,如$userData和calculateTotalPrice();2.遵循PSR-12標(biāo)準(zhǔn)統(tǒng)一代碼風(fēng)格;3.按職責(zé)拆分代碼結(jié)構(gòu),使用MVC或Laravel式目錄組織;4.避免麵條式代碼,將邏輯拆分為單一職責(zé)的小函數(shù);5.在關(guān)鍵處添加註釋並撰寫接口文檔,明確參數(shù)、返回值和異常;6.提高可測(cè)試性,採(cǎi)用依賴注入、減少全局狀態(tài)和靜態(tài)方法。這些做法提升代碼質(zhì)量、協(xié)作效率和後期維護(hù)便利性。

如何使用PHP執(zhí)行SQL查詢? 如何使用PHP執(zhí)行SQL查詢? Jun 24, 2025 am 12:54 AM

Yes,youcanrunSQLqueriesusingPHP,andtheprocessinvolveschoosingadatabaseextension,connectingtothedatabase,executingqueriessafely,andclosingconnectionswhendone.Todothis,firstchoosebetweenMySQLiorPDO,withPDObeingmoreflexibleduetosupportingmultipledatabas

如何快速測(cè)試PHP代碼片段? 如何快速測(cè)試PHP代碼片段? Jun 25, 2025 am 12:58 AM

toquicklytestaphpcodesnippet,useanonlinephpsandboxlike3v4l.orgorphpize.onlineforinstantantantExecutionWithOutSetup; runco????delocalocallocallocallocallocallocallywithpplibycreatinga.phpfileandexecutingitviateringitviatheterminal;

如何在PHP中使用頁面緩存? 如何在PHP中使用頁面緩存? Jun 24, 2025 am 12:50 AM

PHP頁面緩存可通過減少服務(wù)器負(fù)載和加快頁面加載速度提升網(wǎng)站性能。 1.基本文件緩存通過生成靜態(tài)HTML文件並在有效期內(nèi)提供服務(wù),避免重複生成動(dòng)態(tài)內(nèi)容;2.啟用OPcache可將PHP腳本編譯為字節(jié)碼存儲(chǔ)在內(nèi)存中,提升執(zhí)行效率;3.對(duì)帶參數(shù)的動(dòng)態(tài)頁面,應(yīng)根據(jù)URL參數(shù)分別緩存,並避免緩存用戶特定內(nèi)容;4.可使用輕量級(jí)緩存庫如PHPFastCache簡(jiǎn)化開發(fā)並支持多種存儲(chǔ)驅(qū)動(dòng)。結(jié)合這些方法能有效優(yōu)化PHP項(xiàng)目的緩存策略。

如何升級(jí)PHP版本? 如何升級(jí)PHP版本? Jun 27, 2025 am 02:14 AM

升級(jí)PHP版本其實(shí)不難,但關(guān)鍵在於操作步驟和注意事項(xiàng)。以下是具體方法:1.確認(rèn)當(dāng)前PHP版本及運(yùn)行環(huán)境,使用命令行或phpinfo.php文件查看;2.選擇適合的新版本並安裝,推薦8.2或8.1,Linux用戶用包管理器安裝,macOS用戶用Homebrew;3.遷移配置文件和擴(kuò)展,更新php.ini並安裝必要擴(kuò)展;4.測(cè)試網(wǎng)站是否正常運(yùn)行,檢查錯(cuò)誤日誌確保無兼容性問題。按照這些步驟操作,大多數(shù)情況都能順利完成升級(jí)。

PHP初學(xué)者指南:當(dāng)?shù)丨h(huán)境配置的詳細(xì)說明 PHP初學(xué)者指南:當(dāng)?shù)丨h(huán)境配置的詳細(xì)說明 Jun 27, 2025 am 02:09 AM

要設(shè)置PHP開發(fā)環(huán)境,需選擇合適的工具並正確安裝配置。 ①最基礎(chǔ)的PHP本地環(huán)境需要三個(gè)組件:Web服務(wù)器(Apache或Nginx)、PHP本身和數(shù)據(jù)庫(如MySQL/MariaDB);②推薦初學(xué)者使用集成包如XAMPP或MAMP,它們簡(jiǎn)化了安裝流程,XAMPP適用於Windows和macOS,安裝後將項(xiàng)目文件放入htdocs目錄並通過localhost訪問;③MAMP適合Mac用戶,支持便捷切換PHP版本,但免費(fèi)版功能有限;④高級(jí)用戶可用Homebrew手動(dòng)安裝,在macOS/Linux系統(tǒng)中

在Linux上配置PHP開發(fā)環(huán)境的步驟 在Linux上配置PHP開發(fā)環(huán)境的步驟 Jun 30, 2025 am 01:57 AM

TosetupaPHPdevelopmentenvironmentonLinux,installPHPandrequiredextensions,setupawebserverlikeApacheorNginx,testwithaPHPfile,andoptionallyinstallMySQLandComposer.1.InstallPHPandextensionsviapackagemanager(e.g.,sudoaptinstallphpphp-mysqlphp-curlphp-mbst

See all articles