優(yōu)化Laravel數(shù)據(jù)庫查詢的18個技巧【推薦】

下面由laravel教程欄目帶大家介紹關(guān)于優(yōu)化laravel數(shù)據(jù)庫查詢的18個技巧【推薦】,希望對大家有所幫助!

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

如果應(yīng)用運行緩慢或存在大量數(shù)據(jù)庫查詢,請按照以下性能優(yōu)化提示來縮短應(yīng)用的加載時間。

1. 檢索大型數(shù)據(jù)集

本提示主要側(cè)重于提高處理大型數(shù)據(jù)集時應(yīng)用的內(nèi)存使用率。

處理大的集合時,分組檢索結(jié)果處理,而不是一次性檢索處理。

如下展示了從 posts 表檢索數(shù)據(jù)的過程。

$posts = Post::all(); // 使用 eloquent $posts = DB::table('posts')->get(); // 使用查詢構(gòu)造器  foreach ($posts as $post){  // 處理 posts 操作 }

上面的例子會從 posts 表檢索所有的記錄并處理。如果這個表達到了 100 多萬行呢?內(nèi)存將很快被耗盡。

為了避免在處理大型數(shù)據(jù)集時出現(xiàn)問題,我們可以檢索結(jié)果子集并按照下面的方式處理它們。

選項 1: 使用 chunk

// 當(dāng)使用 eloquent 時 $posts = Post::chunk(100, function($posts){     foreach ($posts as $post){      // Process posts     } });  // 當(dāng)使用查詢構(gòu)造器時 $posts = DB::table('posts')->chunk(100, function ($posts){     foreach ($posts as $post){      // Process posts     } });

以上例子從 posts 表中檢索 100 條記錄對其進行處理,另外再檢索 100 條記錄進行處理。此迭代將繼續(xù),直到處理完所有記錄。

這種方法將創(chuàng)建更多的數(shù)據(jù)庫查詢,但內(nèi)存效率會更高。 通常, 大型數(shù)據(jù)集的處理應(yīng)該再后臺進行。因此,可以在后臺運行時進行更多查詢,以避免在處理大型數(shù)據(jù)集時耗盡內(nèi)存。

選項 2: 使用游標(biāo)

// 使用 eloquent foreach (Post::cursor() as $post){    // 處理單個 post }  // 使用 query 構(gòu)建器 foreach (DB::table('posts')->cursor() as $post){    // 處理單個 post }

示例進行單個數(shù)據(jù)庫查詢,檢索表的所有記錄,一個接一個一個處理 Eloquent 模型。這種方式僅查詢一次數(shù)據(jù)庫,得到全部 posts 。 但使用?php 生成器?優(yōu)化內(nèi)存使用。

什么情況使用這個呢?

這能夠在應(yīng)用層極大地優(yōu)化內(nèi)存使用,由于我們檢索表的所有數(shù)據(jù),數(shù)據(jù)庫內(nèi)存占用任然很高。

在數(shù)據(jù)庫內(nèi)存較多,應(yīng)用內(nèi)存較少的時候,建議使用游標(biāo)。然而,如果你的數(shù)據(jù)庫沒有足夠的內(nèi)存,最好使用 chunks 。

選項 3: 使用 chunkById

// 使用 eloquent $posts = Post::chunkById(100, function($posts){     foreach ($posts as $post){      // 處理 posts     } });  // 使用 query 構(gòu)造器 $posts = DB::table('posts')->chunkById(100, function ($posts){     foreach ($posts as $post){      // 處理 posts     } });

chunk?和?chunkById?最大的區(qū)別是 chunk 通過offset?和?limit 檢索數(shù)據(jù)。然而
chunkById?通過id?字段檢索結(jié)構(gòu)。id 字段通常是整型字段,而且它也是自增字段。

chunk?和?chunkById?的查詢?nèi)缦隆?/p>

chunk

select * from posts offset 0 limit 100
select * from posts offset 101 limit 100

chunkById

select * from posts order by id asc limit 100
select * from posts where id > 100 order by id asc limit 100

通常,查詢使用 limit 和 offset 是較慢的,盡量避免使用。本文?詳細介紹使用 offset 的問題。

chunkById 使用 id 整型字段,通過 where clause 查詢,這樣會更快。

什么時候使用 chunkById ?

  • 當(dāng)數(shù)據(jù)庫存在自增 主鍵?的時候使用。

2. 選擇合適的列

通常從數(shù)據(jù)庫檢索數(shù)據(jù)時,會像下面這樣做。

$posts = Post::find(1); // 使用 eloquent $posts = DB::table('posts')->where('id','=',1)->first(); // 使用 query 構(gòu)建器

上面的代碼會得到如下的查詢

select * from posts where id = 1 limit 1

select * 表示從表中查出所有列。
當(dāng)需要所有列時,這沒有問題。

然而,僅需要指定的列(id,title)時,只需要像下面這樣檢索那些列。

$posts = Post::select(['id','title'])->find(1); // 使用 eloquent $posts = DB::table('posts')->where('id','=',1)->select(['id','title'])->first(); // 使用 query 構(gòu)建器

上面代碼得到如下查詢

select id,title from posts where id = 1 limit 1

3. 當(dāng)需要數(shù)據(jù)庫表的一兩個列時

這點主要關(guān)注對檢索結(jié)果的處理時間。這不影響實際的查詢時間。

如我上面提到的,檢索指定的列,可以這樣做

$posts = Post::select(['title','slug'])->get(); // 使用 eloquent $posts = DB::table('posts')->select(['title','slug'])->get(); // 使用 query 構(gòu)建器

執(zhí)行上面的代碼,它會在幕后執(zhí)行以下操作。

  • 執(zhí)行?select title, slug from posts?查詢
  • 檢索出的每一行對應(yīng)一個 Post?模型對象(對 PHP 對象)(query 構(gòu)建器得到標(biāo)準(zhǔn)的 PHP 對象)
  • 為 Post?模型生成 collection
  • 返回 collection

訪問數(shù)據(jù)

foreach ($posts as $post){     // $post 是 Post 模型或  php 標(biāo)準(zhǔn)對象     $post->title;     $post->slug; }

上面的方式有額外的開銷,為每一行創(chuàng)建 Post?模型,并為這些對象創(chuàng)建一個集合。如果的確需要 Post?模型實例而不是數(shù)據(jù),這是最正確的做法。

但如果您只需要兩個值時,則可以執(zhí)行以下操作:

$posts = Post::pluck('title', 'slug'); // 使用 eloquent 時 $posts = DB::table('posts')->pluck('title','slug'); // 使用查詢構(gòu)造器時

當(dāng)上面代碼被執(zhí)行時,它在幕后會執(zhí)行以下操作。

  • 對數(shù)據(jù)庫執(zhí)行 select title, slug from posts 查詢
  • 創(chuàng)建一個數(shù)組,其中會以 title 作為 數(shù)組值,slug 作為 數(shù)組鍵
  • 返回數(shù)組 ( 數(shù)組格式:[ slug => title, slug => title ] )

要訪問結(jié)果,我們可以這么做

foreach ($posts as $slug => $title){     // $title 是 post 的 title     // $slug 是 post 的 slug }

如果您想檢索一列,您可以這么做

$posts = Post::pluck('title'); // 使用 eloquent 時 $posts = DB::table('posts')->pluck('title'); // 使用查詢構(gòu)造器時 foreach ($posts as  $title){     // $title 是 post 的 title }

上面的方式消除了每一行 Post?對象的創(chuàng)建。這將降低查詢結(jié)果處理的內(nèi)存和時間消耗。

建議在新代碼中使用上述方式。個人感覺不值得花時間遵循上面的提示重構(gòu)代碼。
重構(gòu)代碼,最好是在要處理大的數(shù)據(jù)集或者是比較閑的時候

4. 使用查詢代替 collection 來統(tǒng)計行數(shù)

統(tǒng)計表的行數(shù),通常這樣做

$posts = Post::all()->count(); // 使用 eloquent $posts = DB::table('posts')->get()->count(); // 使用查詢構(gòu)造器

這將生成以下查詢

select * from posts

上述方法將從表中檢索所有行。將它們加載到 collection 對象中并計算結(jié)果。當(dāng)數(shù)據(jù)表中的行較少時,這可以正常工作。但隨著表的增長,內(nèi)存很快就會耗盡。

與上述方法不同,我們可以直接計算數(shù)據(jù)庫本身的總行數(shù)。

$posts = Post::count(); // 使用 eloquent 時 $posts = DB::table('posts')->count(); // 使用查詢構(gòu)造器時

這將生成以下查詢

select count(*) from posts

在 sql 中計算行數(shù)是一個緩慢的過程,當(dāng)數(shù)據(jù)庫表中有多行時性能會很差。最好盡量避免計算行數(shù)。

5. 通過即時加載關(guān)系避免 n + 1查詢

這條建議你可能聽說過無數(shù)次了。所以我會盡可能簡短。讓我們假設(shè)您有以下場景

class PostController extends Controller {     public function index()     {         $posts = Post::all();         return view('posts.index', ['posts' => $posts ]);     } }
// posts/index.blade.php 文件  @foreach($posts as $post)     <li>         <h3>{{ $post->title }}</h3>         <p>Author: {{ $post->author->name }}</p>     </li> @endforeach

上面的代碼是檢索所有的帖子,并在網(wǎng)頁上顯示帖子標(biāo)題和作者,假設(shè)帖子模型關(guān)聯(lián)作者。

執(zhí)行以上代碼將導(dǎo)致運行以下查詢。

select * from posts // 假設(shè)返回5條數(shù)據(jù) select * from authors where id = { post1.author_id } select * from authors where id = { post2.author_id } select * from authors where id = { post3.author_id } select * from authors where id = { post4.author_id } select * from authors where id = { post5.author_id }

如上,1 條查詢來檢索帖子,5 條查詢來檢索帖子的作者(假設(shè)有 5 篇帖子)。因此對于每篇帖子,都會進行一個單獨的查詢來檢索它的作者。

所以如果有 N 篇帖子,將會產(chǎn)生 N+1 條查詢(1 條查詢檢索帖子,N 條查詢檢索每篇帖子的作者)。這常被稱作 N+1 查詢問題。

避免這個問題,可以像下面這樣預(yù)加載帖子的作者。

$posts = Post::all(); // Avoid doing this $posts = Post::with(['author'])->get(); // Do this instead

執(zhí)行上面的代碼得到下面的查詢:

select * from posts // Assume this query returned 5 posts select * from authors where id in( { post1.author_id }, { post2.author_id }, { post3.author_id }, { post4.author_id }, { post5.author_id } )

6. 預(yù)加載嵌套關(guān)系

從上面的例子,考慮作者歸屬于一個組,同時需要顯示組的名字的情況。因此在 blade 文件中,可以按下面這樣做。

@foreach($posts as $post)     <li>         <h3>{{ $post->title }}</h3>         <p>Author: {{ $post->author->name }}</p>         <p>Author's Team: {{ $post->author->team->name }}</p>     </li> @endforeach

接著

$posts = Post::with(['author'])->get();

得到下面的查詢:

select * from posts // Assume this query returned 5 posts select * from authors where id in( { post1.author_id }, { post2.author_id }, { post3.author_id }, { post4.author_id }, { post5.author_id } ) select * from teams where id = { author1.team_id } select * from teams where id = { author2.team_id } select * from teams where id = { author3.team_id } select * from teams where id = { author4.team_id } select * from teams where id = { author5.team_id }

如上,盡管預(yù)加載了 authors? 關(guān)系,仍然產(chǎn)生了大量的查詢。這是因為沒有預(yù)加載 authors 上的 team 關(guān)系。

通過下面這樣來解決這個它。

$posts = Post::with(['author.team'])->get();

執(zhí)行得到下面的查詢。

select * from posts // Assume this query returned 5 posts select * from authors where id in( { post1.author_id }, { post2.author_id }, { post3.author_id }, { post4.author_id }, { post5.author_id } ) select * from teams where id in( { author1.team_id }, { author2.team_id }, { author3.team_id }, { author4.team_id }, { author5.team_id } )

通過預(yù)加載嵌套關(guān)系,可以將查詢數(shù)從 11 減到 3。

7. 如果僅需要 id 時,別預(yù)加載 belongsTo 關(guān)系

想象一下,有 posts?和?authors 兩張表。帖子表有 author_id?列歸屬作者表。

為了得到帖子的作者 id,通常這樣做

$post = Post::findOrFail(<post id>); $post->author->id;

執(zhí)行得到兩個查詢。

select * from posts where id = <post id> limit 1 select * from authors where id = <post author id> limit 1

然而,可以直接通過下面方式得到作者 id 。

$post = Post::findOrFail(<post id>); $post->author_id; // 帖子表有存放作者 id 的 author_id 列

什么時候采取上面的方式?

采取上的方式,需要確保帖子關(guān)聯(lián)的作者在作者表始終存在。

8. 避免使用不必要的查詢

很多時候,一些數(shù)據(jù)庫查詢是不必要的??纯聪旅娴睦印?/p>

<?php  class PostController extends Controller {     public function index()     {         $posts = Post::all();         $private_posts = PrivatePost::all();         return view('posts.index', ['posts' => $posts, 'private_posts' => $private_posts ]);     } }

上面代碼是從兩張不同的表(posts,?private_posts)檢索數(shù)據(jù),然后傳到視圖中。
視圖文件如下。

// posts/index.blade.php  @if( request()->user()->isAdmin() )     <h2>Private Posts</h2>     <ul>         @foreach($private_posts as $post)             <li>                 <h3>{{ $post->title }}</h3>                 <p>Published At: {{ $post->published_at }}</p>             </li>         @endforeach     </ul> @endif  <h2>Posts</h2> <ul>     @foreach($posts as $post)         <li>             <h3>{{ $post->title }}</h3>             <p>Published At: {{ $post->published_at }}</p>         </li>     @endforeach </ul>

正如你上面看到的,$private_posts 僅對 管理員 用戶可見,其他用戶都無法看到這些帖子。

問題是,當(dāng)我們在做

$posts = Post::all(); $private_posts = PrivatePost::all();

我們進行兩次查詢。一次從 posts 表獲取記錄,另一次從 private_posts 表獲取記錄。

private_posts 表的記錄僅 管理員用戶 可見。但我們?nèi)栽诓樵円詸z索所有用戶記錄,即使它們不可見。

我們可以調(diào)整邏輯,避免額外的查詢。

$posts = Post::all(); $private_posts = collect(); if( request()->user()->isAdmin() ){     $private_posts = PrivatePost::all(); }

將邏輯更改為上述內(nèi)容后,我們對管理員用戶進行了兩次查詢,并對其他用戶進行了一次查詢。

9. 合并相似的查詢

我們有時需要進行查詢以同一個表中檢索不同類型的行。

$published_posts = Post::where('status','=','published')->get(); $featured_posts = Post::where('status','=','featured')->get(); $scheduled_posts = Post::where('status','=','scheduled')->get();

上述代碼正從同一個表檢索狀態(tài)不同的行。代碼將進行以下查詢。

select * from posts where status = 'published' select * from posts where status = 'featured' select * from posts where status = 'scheduled'

如您所見,它正在對同一個表進行三次不同的查詢以檢索記錄。我們可以重構(gòu)此代碼以僅進行一次數(shù)據(jù)庫查詢。

$posts =  Post::whereIn('status',['published', 'featured', 'scheduled'])->get(); $published_posts = $posts->where('status','=','published'); $featured_posts = $posts->where('status','=','featured'); $scheduled_posts = $posts->where('status','=','scheduled');
select * from posts where status in ( 'published', 'featured', 'scheduled' )

上面的代碼生成一個查詢來檢索全部特定狀態(tài)的帖子,通過狀態(tài)為返回的帖子創(chuàng)建不同的 collections 。三個不同的狀態(tài)的變量由一個查詢生成。

10. 為常查詢的列添加索引

如果查詢中含有 where?條件作用于 string?類型的?column ,最好給這列添加索引。通過這列的查詢將會快很多。

$posts = Post::where('status','=','published')->get();

上面例子,我們對 status 列添加 where 條件來查詢。可以通過下面這樣的數(shù)據(jù)庫遷移來優(yōu)化查詢。

Schema::table('posts', function (Blueprint $table) {    $table->index('status'); });

11. ?使用 simplePaginate 而不是 Paginate

分頁結(jié)果時,我們通常會這樣做

$posts = Post::paginate(20);

這將進行兩次查詢,第一次檢索分頁結(jié)果,第二次表中計算表中的總行數(shù)。對表中的行數(shù)進行計數(shù)是一個緩慢的操作,會對查詢性能產(chǎn)生負面影響。

那么為什么 laravel 會計算總行數(shù)呢?

為了生成分頁連接,Laravel 會計算總行數(shù)。因此,當(dāng)生成分頁連接時,您可以預(yù)先知道會有多少頁,以及過去的頁碼是多少。

另一方面,執(zhí)行 simplePaginate 不會計算總行數(shù),查詢會比 paginate 方法快得多。但您將無法知道最后一個頁碼并無法跳轉(zhuǎn)到不同的頁面。

如果您的數(shù)據(jù)庫表有很多行,最好避免使用 paginate,而是使用 simplePaginate。

$posts = Post::paginate(20); // 為所有頁面生成分頁鏈接 $posts = Post::simplePaginate(20); // 僅生成上一頁和下一頁的分頁鏈接

什么時候使用分頁和簡單分頁

查看下面的比較表,確定是分頁還是簡單分頁適合您

paginate / simplePaginate
數(shù)據(jù)庫表只有很少行,并且不會變大 paginate / simplePaginate
數(shù)據(jù)庫表有很多行,并且增長很快 simplePaginate
必須提供用戶選項以跳轉(zhuǎn)到特定頁面 paginate
必須向用戶顯示結(jié)果總數(shù) paginate
不主動使用分頁鏈接 simplePaginate
UI/UX 不會影響從切換編號分頁鏈接到下一個/上一個分頁鏈接 simplePaginate
使用“加載更多”按鈕或“無限滾動”分頁 simplePaginate

12. 避免使用前導(dǎo)通配符(LIKE 關(guān)鍵字)

當(dāng)嘗試查詢匹配特性模式的結(jié)果時,我們通常會使用

select * from table_name where column like %keyword%

上述查詢導(dǎo)致全表掃描。如果我們知道出現(xiàn)在列值開頭的關(guān)鍵字,我們會查詢以下結(jié)果。

select * from table_name where column like keyword%

13. 避免 where 子句使用 SQL 函數(shù)

最好避免在 where 子句中使用 SQL 函數(shù),因為它們會導(dǎo)致全表掃描。 讓我們看下面的例子。要根據(jù)特定的時間查詢結(jié)果,我們通常會這樣做

$posts = POST::whereDate('created_at', '>=', now() )->get();

這將導(dǎo)致類似的于下面的查詢

select * from posts where date(created_at) >= 'timestamp-here'

上面的查詢將導(dǎo)致全表掃描,因為在計算日期函數(shù)之前,不會應(yīng)用 where 條件。

我們可以重構(gòu)這個函數(shù),以避免使用如下的 date sql 函數(shù)

$posts = Post::where('created_at', '>=', now() )->get();
select * from posts where created_at >= 'timestamp-here'

14. 避免在表中添加過多的列

最好限制表中列的總數(shù)。可以利用像 mysql 這樣的關(guān)系數(shù)據(jù)庫將具有如此多列的表拆分為多個表??梢允褂盟鼈兊闹麈I和外鍵將它們連接在一起。

向表中添加太多列會增加單個記錄的長度,并且會減慢表掃描的速度。在執(zhí)行 select * 查詢時,最終會檢索到一些實際上并不需要的列。

15. 將帶有文本數(shù)據(jù)的單獨列輸入到它們自己的表中

這個技巧來自個人經(jīng)驗,并不是設(shè)計數(shù)據(jù)庫表的標(biāo)準(zhǔn)方法。我建議只有當(dāng)您的表有太多的記錄或者會快速增長時才遵循這個技巧。

如果一個表有存儲大量數(shù)據(jù)的列(例如: 數(shù)據(jù)類型為 TEXT 的列) ,那么最好將它們分離到它們自己的表中,或者分離到一個不經(jīng)常被詢問的表中。

當(dāng)表中有包含大量數(shù)據(jù)的列時,單個記錄的大小會變得非常大。我個人觀察到它影響了我們其中一個項目的查詢時間。

假設(shè)您有一個名為 posts 的表,其中包含一列 內(nèi)容,用于存儲博客文章內(nèi)容。博客文章的內(nèi)容將是真正的巨大和經(jīng)常的時候,你需要這個數(shù)據(jù)只有當(dāng)一個人正在查看這個特定的博客文章。

所以,在數(shù)據(jù)表中有大量文章記錄的時候,將這些長文本字段(大字段)分離到單獨的表中將會徹底的改善查詢性能。

16. 從表中查詢最新記錄的最佳實踐

當(dāng)需要從一個數(shù)據(jù)表中查詢最新的記錄行時,通常我們會這么做:

$posts = Post::latest()->get(); // or $posts = Post::orderBy('created_at', 'desc')->get();

上面的查詢方式將會產(chǎn)生如下 sql 語句:

select * from posts order by created_at desc

這種查詢方式基本上都是按照 created_at 字段做降序排列來給查詢結(jié)果排序的。由于 created_at 字段是字符串類型的數(shù)據(jù),所以用這種方式對查詢結(jié)果進行排序通常會更慢。(譯者注:MySQL 的 TIMESTAMP 類型字段是以 UTC 格式存儲數(shù)據(jù)的,形如 20210607T152000Z,所以 created_at 字段確實是字符串類型的數(shù)據(jù))。

如果你的數(shù)據(jù)表中使用了自增長的 id 字段作為主鍵,那么大多數(shù)情況下,最新的數(shù)據(jù)記錄行的 id 字段值也是最大的。因為 id 字段不僅是一個整形數(shù)據(jù)的字段,而且也是一個主鍵字段,所以基于 id 字段對查詢結(jié)果進行排序會更快。所以查詢最新記錄的最佳實踐如下:

$posts = Post::latest('id')->get(); // or $posts = Post::orderBy('id', 'desc')->get();

該方法會產(chǎn)生如下 sql 語句

select * from posts order by id desc

17. 優(yōu)化 MySQL 的數(shù)據(jù)插入操作

為了更快地從數(shù)據(jù)庫查詢數(shù)據(jù),我們已經(jīng)為 select 方法做了很多優(yōu)化。 大多數(shù)情況下,我們只需要為查詢方法進行優(yōu)化就可以滿足性能要求了。 但是很多時候我們還需要為『插入』和『更新』(insert 和 update)方法進行優(yōu)化。所以我給大家推薦一篇有趣的文章optimizing mysql inserts,這篇文章將有助于優(yōu)化緩慢的『插入』和『更新』操作。

18. 檢查和優(yōu)化查詢方法

在 Laravel 框架中,優(yōu)化數(shù)據(jù)查詢并沒有完全通用的辦法。你只能盡量搞清楚下面這些問題:你的程序是如何運行的、進行了多少個數(shù)據(jù)庫查詢操作、有多少查詢操作是真正必要的。所以請檢查你的應(yīng)用產(chǎn)生的查詢操作,這將有助于你確定并減少數(shù)據(jù)查詢操作的總量。

有很多工具可以輔助你檢查每個頁面產(chǎn)生的查詢方法:

注意:?不推薦在生產(chǎn)環(huán)境下使用這些工具。在生產(chǎn)環(huán)境使用這些工具將會降低你的應(yīng)用性能,并且會讓未經(jīng)授權(quán)的用戶獲取到程序的敏感信息。

  • Laravel Debugbar?– Laravel Debugbar 有一個 database選項卡,點擊該選項卡將會展示你打開一個頁面時應(yīng)用程序執(zhí)行的所有查詢語句。你可以瀏覽應(yīng)用的每個頁面并查看每個頁面用到的查詢語句。
  • Clockwork?– Clockwork 與 Laravel Debugbar 一樣,只不過 Clockwork 不會在你的網(wǎng)站上注入一個工具欄,你可以在『開發(fā)者工具窗口』( developer tools window ),或者通過打開 url /yourappurl/clockwork 進入一個單獨的頁面來查看應(yīng)用的調(diào)試信息。
  • Laravel Telescope?– Laravel Telescope 是一個專為開發(fā) Laravel 應(yīng)用而提供的十分優(yōu)秀的調(diào)試工具。一旦你安裝了 Laravel Telescope,便可以通過訪問 yourappurl/telescope 地址進入它的儀表盤頁面。在 telescope 的儀表盤界面,點擊打開 queries 標(biāo)簽頁,這個頁面將會展示你的應(yīng)用執(zhí)行過的所有 MySQL 查詢語句。

原文地址:https://laravel-news.com/18-tips-to-optimize-your-laravel-database-queries

譯文地址:https://learnku.com/laravel/t/61384

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點贊9 分享