<p><img src="/static/imghw/default1.png" data-src="https://img.php.cn/upload/article/000/000/000/174119929536701.jpg" class="lazy" alt="Learn to master Query Scopes in Laravel"></p>
<p>在構(gòu)建Laravel應(yīng)用程序時(shí),您可能需要編寫具有約束條件的查詢,這些約束條件在整個(gè)應(yīng)用程序中的多個(gè)地方使用。也許您正在構(gòu)建一個(gè)多租戶應(yīng)用程序,並且您必須不斷向查詢中添加<code>where</code>約束以按用戶的團(tuán)隊(duì)進(jìn)行篩選?;蛘?,也許您正在構(gòu)建一個(gè)博客,並且您必須不斷向查詢中添加<code>where</code>約束以篩選博客文章是否已發(fā)布。 </p>
<p>在Laravel中,我們可以使用查詢範(fàn)圍來幫助我們將這些約束條件整潔地保存在一個(gè)地方並重複使用。 </p>
<p>在本文中,我們將研究局部查詢範(fàn)圍和全局查詢範(fàn)圍。我們將學(xué)習(xí)兩者之間的區(qū)別,如何創(chuàng)建您自己的查詢範(fàn)圍,以及如何編寫它們的測試。 </p>
<p>在閱讀完本文後,您應(yīng)該能夠自信地在Laravel應(yīng)用程序中使用查詢範(fàn)圍。 </p>
<h1 id="什麼是查詢範(fàn)圍">什麼是查詢範(fàn)圍? </h1>
<hr>
<p>查詢範(fàn)圍允許您以可重用的方式在Eloquent查詢中定義約束條件。它們通常定義為Laravel模型上的方法,或者作為實(shí)現(xiàn)<code>IlluminateDatabaseEloquentScope</code>接口的類。 </p>
<p>它們不僅非常適合在一個(gè)地方定義可重用的邏輯,而且還可以通過將復(fù)雜的查詢約束隱藏在簡單的函數(shù)調(diào)用之後來使您的代碼更具可讀性。 </p>
<p>查詢範(fàn)圍分為兩種類型:</p>
<ul>
<li>局部查詢範(fàn)圍 - 您必須手動(dòng)將這些範(fàn)圍應(yīng)用於您的查詢。 </li>
<li>全局查詢範(fàn)圍 - 默認(rèn)情況下,這些範(fàn)圍會應(yīng)用於模型上的所有查詢,前提是已註冊該查詢。 </li>
</ul>
<p>如果您曾經(jīng)使用過Laravel內(nèi)置的“軟刪除”功能,您可能已經(jīng)在不知不覺中使用了查詢範(fàn)圍。 Laravel使用局部查詢範(fàn)圍為您提供模型上的<code>withTrashed</code>和<code>onlyTrashed</code>等方法。它還使用全局查詢範(fàn)圍自動(dòng)向模型上的所有查詢添加<code>whereNull('deleted_at')</code>約束,以便默認(rèn)情況下查詢中不會返回軟刪除的記錄。 </p>
<p>讓我們來看看如何在Laravel應(yīng)用程序中創(chuàng)建和使用局部查詢範(fàn)圍和全局查詢範(fàn)圍。 </p>
<h1 id="局部查詢範(fàn)圍">局部查詢範(fàn)圍</h1>
<hr>
<p>局部查詢範(fàn)圍定義為Eloquent模型上的方法,允許您定義可以手動(dòng)應(yīng)用於模型查詢的約束條件。 </p>
<p>假設(shè)我們正在構(gòu)建一個(gè)具有管理面板的博客應(yīng)用程序。在管理面板中,我們有兩個(gè)頁面:一個(gè)用於列出已發(fā)布的博客文章,另一個(gè)用於列出未發(fā)布的博客文章。 </p>
<p>我們假設(shè)博客文章是使用<code>AppModelsArticle</code>模型訪問的,並且數(shù)據(jù)庫表具有一個(gè)可為空的<code>published_at</code>列,用於存儲博客文章的發(fā)佈時(shí)間。如果<code>published_at</code>列在過去,則該博客文章被認(rèn)為已發(fā)布。如果<code>published_at</code>列在未來或?yàn)?lt;code>null</code>,則該博客文章被認(rèn)為未發(fā)布。 </p>
<p>要獲取已發(fā)布的博客文章,我們可以編寫如下查詢:</p>
<pre class="brush:php;toolbar:false"><code>use App\Models\Article;
$publishedPosts = Article::query()
->where('published_at', '<', now())
->get();</code></pre>
<p>要獲取未發(fā)布的博客文章,我們可以編寫如下查詢:</p>
<pre class="brush:php;toolbar:false"><code>use App\Models\Article;
use Illuminate\Contracts\Database\Eloquent\Builder;
$unpublishedPosts = Article::query()
->where(function (Builder $query): void {
$query->whereNull('published_at')
->orWhere('published_at', '>', now());
})
->get();</code></pre>
<p>上面的查詢並不特別複雜。但是,假設(shè)我們在整個(gè)應(yīng)用程序中的多個(gè)地方使用它們。隨著出現(xiàn)次數(shù)的增加,我們犯錯(cuò)或忘記在一個(gè)地方更新查詢的可能性越來越大。例如,開發(fā)人員可能會意外地使用<code>>=</code>而不是<code><</code>來查詢已發(fā)布的博客文章?;蛘?,確定博客文章是否已發(fā)布的邏輯可能會更改,我們需要更新所有查詢。 </p>
<p>這就是查詢範(fàn)圍非常有用的地方。因此,讓我們通過在<code>AppModelsArticle</code>模型上創(chuàng)建局部查詢範(fàn)圍來整理我們的查詢。 </p>
<p>局部查詢範(fàn)圍是通過創(chuàng)建一個(gè)以<code>scope</code>開頭並以範(fàn)圍的預(yù)期名稱結(jié)尾的方法來定義的。例如,名為<code>scopePublished</code>的方法將在模型上創(chuàng)建一個(gè)<code>published</code>範(fàn)圍。該方法應(yīng)該接受一個(gè)<code>IlluminateContractsDatabaseEloquentBuilder</code>實(shí)例並返回一個(gè)<code>IlluminateContractsDatabaseEloquentBuilder</code>實(shí)例。 </p>
<p>我們將這兩個(gè)範(fàn)圍都添加到<code>AppModelsArticle</code>模型中:</p>
<pre class="brush:php;toolbar:false"><code>declare(strict_types=1);
namespace App\Models;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
final class Article extends Model
{
public function scopePublished(Builder $query): Builder
{
return $query->where('published_at', '<', now());
}
public function scopeNotPublished(Builder $query): Builder
{
return $query->where(function (Builder $query): Builder {
return $query->whereNull('published_at')
->orWhere('published_at', '>', now());
});
}
// ...
}</code>
</p>
<p>正如我們在上面的示例中看到的,我們將<code>where</code>約束從之前的查詢移動(dòng)到了兩個(gè)單獨(dú)的方法中:<code>scopePublished</code>和<code>scopeNotPublished</code>。我們現(xiàn)在可以在我們的查詢中像這樣使用這些範(fàn)圍:</p>
<pre class="brush:php;toolbar:false"><code>use App\Models\Article;
$publishedPosts = Article::query()
->published()
->get();
$unpublishedPosts = Article::query()
->notPublished()
->get();</code></pre>
<p>在我個(gè)人看來,我發(fā)現(xiàn)這些查詢更容易閱讀和理解。這也意味著如果我們將來需要使用相同約束條件編寫任何查詢,我們可以重複使用這些範(fàn)圍。 </p>
<h1 id="全局查詢範(fàn)圍">全局查詢範(fàn)圍</h1>
<hr>
<p>全局查詢範(fàn)圍執(zhí)行與局部查詢範(fàn)圍類似的功能。但是,它不是在逐個(gè)查詢的基礎(chǔ)上手動(dòng)應(yīng)用,而是自動(dòng)應(yīng)用於模型上的所有查詢。 </p>
<p>正如我們前面提到的,Laravel內(nèi)置的“軟刪除”功能使用了<code>IlluminateDatabaseEloquentSoftDeletingScope</code>全局查詢範(fàn)圍。此範(fàn)圍會自動(dòng)向模型上的所有查詢添加<code>whereNull('deleted_at')</code>約束。如果您有興趣了解其底層工作原理,可以在這裡查看GitHub上的源代碼。 </p>
<p>例如,假設(shè)您正在構(gòu)建一個(gè)具有管理面板的多租戶博客應(yīng)用程序。您可能只想允許用戶查看屬於其團(tuán)隊(duì)的文章。因此,您可能會編寫如下查詢:</p>
<pre class="brush:php;toolbar:false"><code>use App\Models\Article;
$publishedPosts = Article::query()
->where('published_at', '<', now())
->get();</code></pre>
<p>此查詢很好,但很容易忘記添加<code>where</code>約束。如果您正在編寫另一個(gè)查詢並忘記添加約束,則最終會在您的應(yīng)用程序中出現(xiàn)一個(gè)錯(cuò)誤,該錯(cuò)誤將允許用戶與不屬於其團(tuán)隊(duì)的文章進(jìn)行交互。當(dāng)然,我們不希望發(fā)生這種情況! </p>
<p>為了防止這種情況,我們可以創(chuàng)建一個(gè)全局範(fàn)圍,我們可以將其自動(dòng)應(yīng)用於我們所有<code>AppModelArticle</code>模型查詢。 </p>
<h3 id="如何創(chuàng)建全局查詢範(fàn)圍">#如何創(chuàng)建全局查詢範(fàn)圍</h3>
<p>讓我們創(chuàng)建一個(gè)全局查詢範(fàn)圍,該範(fàn)圍按<code>team_id</code>列過濾所有查詢。 </p>
<p>請注意,為了本文的目的,我們保持示例簡單。在實(shí)際應(yīng)用程序中,您可能希望使用更強(qiáng)大的方法來處理用戶未經(jīng)身份驗(yàn)證或用戶屬於多個(gè)團(tuán)隊(duì)等情況。但現(xiàn)在,讓我們保持簡單,以便我們可以專注於全局查詢範(fàn)圍的概念。 </p>
<p>我們將首先在終端中運(yùn)行以下Artisan命令:</p>
<pre class="brush:php;toolbar:false"><code>use App\Models\Article;
use Illuminate\Contracts\Database\Eloquent\Builder;
$unpublishedPosts = Article::query()
->where(function (Builder $query): void {
$query->whereNull('published_at')
->orWhere('published_at', '>', now());
})
->get();</code></pre>
<p>這應(yīng)該已經(jīng)創(chuàng)建了一個(gè)新的<code>app/Models/Scopes/TeamScope.php</code>文件。我們將對此文件進(jìn)行一些更新,然後查看完成的代碼:</p>
<pre class="brush:php;toolbar:false"><code>declare(strict_types=1);
namespace App\Models;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
final class Article extends Model
{
public function scopePublished(Builder $query): Builder
{
return $query->where('published_at', '<', now());
}
public function scopeNotPublished(Builder $query): Builder
{
return $query->where(function (Builder $query): Builder {
return $query->whereNull('published_at')
->orWhere('published_at', '>', now());
});
}
// ...
}</code></pre>
<p>在上面的代碼示例中,我們可以看到我們有一個(gè)新的類,它實(shí)現(xiàn)了<code>IlluminateDatabaseEloquentScope</code>接口並具有一個(gè)名為<code>apply</code>的單個(gè)方法。這就是我們定義要應(yīng)用於模型查詢的約束條件的方法。 </p>
<p>我們的全局範(fàn)圍現(xiàn)在可以使用了。我們可以將其添加到任何我們想要將查詢範(fàn)圍縮小到用戶團(tuán)隊(duì)的模型中。 </p>
<p>讓我們將其應(yīng)用於<code>AppModelsArticle</code>模型。 </p>
<h3 id="應(yīng)用全局查詢範(fàn)圍">#應(yīng)用全局查詢範(fàn)圍</h3>
<p>有多種方法可以將全局範(fàn)圍應(yīng)用於模型。第一種方法是在模型上使用<code>IlluminateDatabaseEloquentAttributesScopedBy</code>屬性:</p>
<pre class="brush:php;toolbar:false"><code>use App\Models\Article;
$publishedPosts = Article::query()
->published()
->get();
$unpublishedPosts = Article::query()
->notPublished()
->get();</code></pre>
<p>另一種方法是在模型的<code>booted</code>方法中使用<code>addGlobalScope</code>方法:</p>
<pre class="brush:php;toolbar:false"><code>use App\Models\Article;
$articles = Article::query()
->where('team_id', Auth::user()->team_id)
->get();</code></pre>
<p>這兩種方法都將<code>where('team_id', Auth::user()->team_id)</code>約束應(yīng)用於<code>AppModelsArticle</code>模型上的所有查詢。 </p>
<p>這意味著您現(xiàn)在可以編寫查詢,而無需擔(dān)心按<code>team_id</code>列進(jìn)行過濾:</p>
<pre class="brush:php;toolbar:false"><code>php artisan make:scope TeamScope</code></pre>
<p>如果我們假設(shè)用戶屬於<code>team_id</code>為<code>1</code>的團(tuán)隊(duì),則將為上面的查詢生成以下SQL:</p>
<pre class="brush:php;toolbar:false"><code>declare(strict_types=1);
namespace App\Models\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Support\Facades\Auth;
final readonly class TeamScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*/
public function apply(Builder $builder, Model $model): void
{
$builder->where('team_id', Auth::user()->team_id);
}
}</code></pre>
<p>這很酷,對吧? ! </p>
<h3 id="匿名全局查詢範(fàn)圍">#匿名全局查詢範(fàn)圍</h3>
<p>定義和應(yīng)用全局查詢範(fàn)圍的另一種方法是使用匿名全局範(fàn)圍。 </p>
<p>讓我們更新我們的<code>AppModelsArticle</code>模型以使用匿名全局範(fàn)圍:</p>
<pre class="brush:php;toolbar:false"><code>declare(strict_types=1);
namespace App\Models;
use App\Models\Scopes\TeamScope;
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
use Illuminate\Database\Eloquent\Model;
#[ScopedBy(TeamScope::class)]
final class Article extends Model
{
// ...
}</code></pre>
<p>在上面的代碼示例中,我們使用了<code>addGlobalScope</code>方法在模型的<code>booted</code>方法中定義匿名全局範(fàn)圍。 <code>addGlobalScope</code>方法接受兩個(gè)參數(shù):</p>
<ul>
<li>範(fàn)圍的名稱 - 如果您需要在查詢中忽略它,則可以使用此名稱來引用範(fàn)圍</li>
<li>範(fàn)圍約束 - 定義要應(yīng)用於查詢的約束的閉包</li>
</ul>
<p>與其他方法一樣,這會將<code>where('team_id', Auth::user()->team_id)</code>約束應(yīng)用於<code>AppModelsArticle</code>模型上的所有查詢。 </p>
<p>根據(jù)我的經(jīng)驗(yàn),匿名全局範(fàn)圍不如在單獨(dú)的類中定義全局範(fàn)圍常見。但了解它們可用是很有好處的,以備不時(shí)之需。 </p>
<h3 id="忽略全局查詢範(fàn)圍">#忽略全局查詢範(fàn)圍</h3>
<p>有時(shí)您可能希望編寫一個(gè)不使用已應(yīng)用於模型的全局查詢範(fàn)圍的查詢。例如,您可能正在構(gòu)建一個(gè)需要包含所有記錄的報(bào)表或分析查詢,而不管全局查詢範(fàn)圍如何。 </p>
<p>如果是這種情況,您可以使用兩種方法之一來忽略全局範(fàn)圍。 </p>
<p>第一種方法是<code>withoutGlobalScopes</code>。如果未向其傳遞任何參數(shù),此方法允許您忽略模型上的所有全局範(fàn)圍:</p>
<pre class="brush:php;toolbar:false"><code>use App\Models\Article;
$publishedPosts = Article::query()
->where('published_at', '<', now())
->get();</code></pre>
<p>或者,如果您只想忽略給定的一組全局範(fàn)圍,您可以將範(fàn)圍名稱傳遞給<code>withoutGlobalScopes</code>方法:</p>
<pre class="brush:php;toolbar:false"><code>use App\Models\Article;
use Illuminate\Contracts\Database\Eloquent\Builder;
$unpublishedPosts = Article::query()
->where(function (Builder $query): void {
$query->whereNull('published_at')
->orWhere('published_at', '>', now());
})
->get();</code></pre>
<p>在上面的示例中,我們忽略了<code>AppModelsScopesTeamScope</code>和另一個(gè)名為<code>another_scope</code>的虛構(gòu)匿名全局範(fàn)圍。 </p>
<p>或者,如果您只想忽略單個(gè)全局範(fàn)圍,可以使用<code>withoutGlobalScope</code>方法:</p>
<pre class="brush:php;toolbar:false"><code>declare(strict_types=1);
namespace App\Models;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
final class Article extends Model
{
public function scopePublished(Builder $query): Builder
{
return $query->where('published_at', '<', now());
}
public function scopeNotPublished(Builder $query): Builder
{
return $query->where(function (Builder $query): Builder {
return $query->whereNull('published_at')
->orWhere('published_at', '>', now());
});
}
// ...
}</code></pre>
<h3 id="全局查詢範(fàn)圍注意事項(xiàng)">#全局查詢範(fàn)圍注意事項(xiàng)</h3>
<p>務(wù)必記住,全局查詢範(fàn)圍僅應(yīng)用於通過模型進(jìn)行的查詢。如果您使用<code>IlluminateSupportFacadesDB</code>外觀編寫數(shù)據(jù)庫查詢,則不會應(yīng)用全局查詢範(fàn)圍。 </p>
<p>例如,假設(shè)您編寫了此查詢,您希望它只抓取屬於登錄用戶的團(tuán)隊(duì)的文章:</p>
<pre class="brush:php;toolbar:false"><code>use App\Models\Article;
$publishedPosts = Article::query()
->published()
->get();
$unpublishedPosts = Article::query()
->notPublished()
->get();</code></pre>
<p>在上面的查詢中,即使在<code>AppModelsArticle</code>模型上定義了<code>AppModelsScopesTeamScope</code>全局查詢範(fàn)圍,也不會應(yīng)用該範(fàn)圍。因此,您需要確保在數(shù)據(jù)庫查詢中手動(dòng)應(yīng)用約束條件。 </p>
<h1 id="測試局部查詢範(fàn)圍">測試局部查詢範(fàn)圍</h1>
<hr>
<p>既然我們已經(jīng)學(xué)習(xí)瞭如何創(chuàng)建和使用查詢範(fàn)圍,那麼我們將研究如何為它們編寫測試。 </p>
<p>有多種方法可以測試查詢範(fàn)圍,您選擇的方法可能取決於您的個(gè)人喜好或您正在編寫的範(fàn)圍的內(nèi)容。例如,您可能希望為範(fàn)圍編寫更多單元樣式的測試?;蛘?,您可能希望編寫更多集成樣式的測試,這些測試會在諸如控制器之類的上下文中測試範(fàn)圍。 </p>
<p>就我個(gè)人而言,我喜歡混合使用兩者,這樣我就可以確信範(fàn)圍正在添加正確的約束,並且範(fàn)圍實(shí)際上正在查詢中使用。 </p>
<p>讓我們從前面的示例<code>published</code>和<code>notPublished</code>範(fàn)圍開始,並為它們編寫一些測試。我們將需要編寫兩個(gè)不同的測試(每個(gè)範(fàn)圍一個(gè)):</p>
<ul>
<li>一個(gè)測試檢查<code>published</code>範(fàn)圍只返回已發(fā)布的文章。 </li>
<li>一個(gè)測試檢查<code>notPublished</code>範(fàn)圍只返回未發(fā)布的文章。 </li>
</ul>
<p>讓我們看看這些測試,然後討論正在做的事情:</p>
<pre class="brush:php;toolbar:false"><code>use App\Models\Article;
$publishedPosts = Article::query()
->where('published_at', '<', now())
->get();</code></pre>
<p>我們可以在上面的測試文件中看到,我們首先在<code>setUp</code>方法中創(chuàng)建一些數(shù)據(jù)。我們創(chuàng)建了兩篇已發(fā)布的文章、一篇未安排的文章和一篇已安排的文章。 </p>
<p>然後是一個(gè)測試(<code>only_published_articles_are_returned</code>),它檢查<code>published</code>範(fàn)圍只返回已發(fā)布的文章。還有一個(gè)測試(<code>only_not_published_articles_are_returned</code>),它檢查<code>notPublished</code>範(fàn)圍只返回未發(fā)布的文章。 </p>
<p>通過這樣做,我們現(xiàn)在可以確信我們的查詢範(fàn)圍正在按預(yù)期應(yīng)用約束條件。 </p>
<h1 id="在控制器中測試範(fàn)圍">在控制器中測試範(fàn)圍</h1>
<hr>
<p>正如我們提到的,測試查詢範(fàn)圍的另一種方法是在控制器中使用的上下文中測試它們。雖然範(fàn)圍的隔離測試可以幫助斷言範(fàn)圍正在向查詢添加正確的約束,但它實(shí)際上並沒有測試範(fàn)圍是否按預(yù)期在應(yīng)用程序中使用。例如,您可能忘記向控制器方法中的查詢添加<code>published</code>範(fàn)圍。 </p>
<p>通過編寫斷言在控制器方法中使用範(fàn)圍時(shí)返回正確數(shù)據(jù)的測試,可以捕獲這些類型的錯(cuò)誤。 </p>
<p>讓我們以具有多租戶博客應(yīng)用程序的示例為例,並為列出文章的控制器方法編寫一個(gè)測試。我們假設(shè)我們有一個(gè)非常簡單的控制器方法,如下所示:</p>
<pre class="brush:php;toolbar:false"><code>use App\Models\Article;
use Illuminate\Contracts\Database\Eloquent\Builder;
$unpublishedPosts = Article::query()
->where(function (Builder $query): void {
$query->whereNull('published_at')
->orWhere('published_at', '>', now());
})
->get();</code></pre>
<p>我們假設(shè)<code>AppModelsArticle</code>模型已應(yīng)用我們的<code>AppModelsScopesTeamScope</code>。 </p>
<p>我們將要斷言只返回屬於用戶團(tuán)隊(duì)的文章。測試用例可能如下所示:</p>
<pre class="brush:php;toolbar:false"><code>declare(strict_types=1);
namespace App\Models;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
final class Article extends Model
{
public function scopePublished(Builder $query): Builder
{
return $query->where('published_at', '<', now());
}
public function scopeNotPublished(Builder $query): Builder
{
return $query->where(function (Builder $query): Builder {
return $query->whereNull('published_at')
->orWhere('published_at', '>', now());
});
}
// ...
}</code></pre>
<p>在上面的測試中,我們正在創(chuàng)建兩個(gè)團(tuán)隊(duì)。然後,我們創(chuàng)建一個(gè)屬於團(tuán)隊(duì)一的用戶。我們?yōu)閳F(tuán)隊(duì)一創(chuàng)建 3 篇文章,為團(tuán)隊(duì)二創(chuàng)建 2 篇文章。然後,我們充當(dāng)用戶並向列出文章的控制器方法發(fā)出請求??刂破鞣椒☉?yīng)該只返回屬於團(tuán)隊(duì)一的 3 篇文章,因此我們通過比較文章的 ID 來斷言只返回這些文章。 </p>
<p>這意味著我們可以確信全局查詢範(fàn)圍正在控制器方法中按預(yù)期使用。 </p>
<h1 id="結(jié)論">結(jié)論</h1>
<hr>
<p>在本文中,我們學(xué)習(xí)了局部查詢範(fàn)圍和全局查詢範(fàn)圍。我們學(xué)習(xí)了它們之間的區(qū)別,如何創(chuàng)建和使用它們,以及如何為它們編寫測試。 </p>
<p>希望您現(xiàn)在應(yīng)該能夠自信地在Laravel應(yīng)用程序中使用查詢範(fàn)圍。 </p>
以上是學(xué)會在Laravel中掌握查詢範(fàn)圍的詳細(xì)內(nèi)容。更多資訊請關(guān)注PHP中文網(wǎng)其他相關(guān)文章!