您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)Laravel之模型關(guān)聯(lián)預(yù)加載的示例分析,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
Laravel學(xué)習(xí)筆記之模型關(guān)聯(lián)預(yù)加載
說明:本文主要說明Laravel Eloquent的延遲預(yù)加載(Eager Loading),使用延遲預(yù)加載來減少MySQL查詢次數(shù)。同時(shí),作者會(huì)將開發(fā)過程中的一些截圖和代碼黏上去,提高閱讀效率。
備注:現(xiàn)在有4張表:商家表merchants、商家電話表phones、商家擁有的店鋪shops表和店鋪里的商品表products。并且關(guān)系是:
[ 'merchants_phones' => 'one-to-one', 'merchants_shops' => 'one-to-many', 'shops_products' => 'one-to-many', ]
現(xiàn)在要求做出一個(gè)頁面以列表形式顯示每個(gè)店鋪,每個(gè)店鋪塊包含店鋪信息如標(biāo)題、包含店鋪商家信息如姓名和電話、包含擁有的商品信息如介紹和價(jià)格??纯从袥]有預(yù)加載會(huì)有什么不同。
開發(fā)環(huán)境:Laravel5.1+MAMP+PHP7+MySQL5.5
1.先裝上開發(fā)插件三件套
(具體可參考:Laravel學(xué)習(xí)筆記之Seeder填充數(shù)據(jù)小技巧)
不管咋樣,先裝上開發(fā)插件三件套:
composer require barryvdh/laravel-debugbar --dev composer require barryvdh/laravel-ide-helper --dev composer require mpociot/laravel-test-factory-helper --dev //config/app.php /** *Develop Plugin */ Barryvdh\Debugbar\ServiceProvider::class, Mpociot\LaravelTestFactoryHelper\TestFactoryHelperServiceProvider::class, Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
2.寫上表字段、表關(guān)聯(lián)和測(cè)試數(shù)據(jù)填充器Seeder
依次輸入指令:
php artisan make:model Merchant -m php artisan make:model Phone -m php artisan make:model Shop -m php artisan make:model Product -m
寫上表字段和表關(guān)聯(lián):
class CreateMerchantsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('merchants', function (Blueprint $table) { $table->increments('id'); $table->string('username')->unique(); $table->string('email')->unique(); $table->string('first_name'); $table->string('last_name'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('merchants'); } } class CreatePhonesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('phones', function (Blueprint $table) { $table->increments('id'); $table->integer('number')->unsigned(); $table->integer('merchant_id')->unsigned(); $table->timestamps(); $table->foreign('merchant_id') ->references('id') ->on('merchants') ->onUpdate('cascade') ->onDelete('cascade'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('phones', function($table){ $table->dropForeign('merchant_id'); // Drop foreign key 'user_id' from 'posts' table }); Schema::drop('phones'); } } class CreateShopsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('shops', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('slug')->unique(); $table->string('site'); $table->integer('merchant_id')->unsigned(); $table->timestamps(); $table->foreign('merchant_id') ->references('id') ->on('merchants') ->onUpdate('cascade') ->onDelete('cascade'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('shops', function($table){ $table->dropForeign('merchant_id'); // Drop foreign key 'user_id' from 'posts' table }); Schema::drop('shops'); } } class CreateProductsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('products', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->text('short_desc'); $table->text('long_desc'); $table->double('price'); $table->integer('shop_id')->unsigned(); $table->timestamps(); $table->foreign('shop_id') ->references('id') ->on('shops') ->onUpdate('cascade') ->onDelete('cascade'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('products', function($table){ $table->dropForeign('shop_id'); // Drop foreign key 'user_id' from 'posts' table }); Schema::drop('products'); } } /** * App\Merchant * * @property integer $id * @property string $username * @property string $email * @property string $first_name * @property string $last_name * @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $updated_at * @property-read \App\Phone $phone * @property-read \Illuminate\Database\Eloquent\Collection|\App\Shop[] $shops * @method static \Illuminate\Database\Query\Builder|\App\Merchant whereId($value) * @method static \Illuminate\Database\Query\Builder|\App\Merchant whereUsername($value) * @method static \Illuminate\Database\Query\Builder|\App\Merchant whereEmail($value) * @method static \Illuminate\Database\Query\Builder|\App\Merchant whereFirstName($value) * @method static \Illuminate\Database\Query\Builder|\App\Merchant whereLastName($value) * @method static \Illuminate\Database\Query\Builder|\App\Merchant whereCreatedAt($value) * @method static \Illuminate\Database\Query\Builder|\App\Merchant whereUpdatedAt($value) * @mixin \Eloquent */ class Merchant extends Model { /** * @return \Illuminate\Database\Eloquent\Relations\HasOne */ public function phone() { return $this->hasOne(Phone::class, 'merchant_id'); } /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function shops() { return $this->hasMany(Shop::class, 'merchant_id'); } } /** * App\Phone * * @property integer $id * @property integer $number * @property integer $merchant_id * @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $updated_at * @property-read \App\Merchant $merchant * @method static \Illuminate\Database\Query\Builder|\App\Phone whereId($value) * @method static \Illuminate\Database\Query\Builder|\App\Phone whereNumber($value) * @method static \Illuminate\Database\Query\Builder|\App\Phone whereMerchantId($value) * @method static \Illuminate\Database\Query\Builder|\App\Phone whereCreatedAt($value) * @method static \Illuminate\Database\Query\Builder|\App\Phone whereUpdatedAt($value) * @mixin \Eloquent */ class Phone extends Model { /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function merchant() { return $this->belongsTo(Merchant::class, 'merchant_id'); } } /** * App\Product * * @property integer $id * @property string $name * @property string $short_desc * @property string $long_desc * @property float $price * @property integer $shop_id * @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $updated_at * @property-read \Illuminate\Database\Eloquent\Collection|\App\Shop[] $shop * @method static \Illuminate\Database\Query\Builder|\App\Product whereId($value) * @method static \Illuminate\Database\Query\Builder|\App\Product whereName($value) * @method static \Illuminate\Database\Query\Builder|\App\Product whereShortDesc($value) * @method static \Illuminate\Database\Query\Builder|\App\Product whereLongDesc($value) * @method static \Illuminate\Database\Query\Builder|\App\Product wherePrice($value) * @method static \Illuminate\Database\Query\Builder|\App\Product whereShopId($value) * @method static \Illuminate\Database\Query\Builder|\App\Product whereCreatedAt($value) * @method static \Illuminate\Database\Query\Builder|\App\Product whereUpdatedAt($value) * @mixin \Eloquent */ class Product extends Model { /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function shop() { return $this->belongsTo(Shop::class, 'shop_id'); } } /** * App\Shop * * @property integer $id * @property string $name * @property string $slug * @property string $site * @property integer $merchant_id * @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $updated_at * @property-read \Illuminate\Database\Eloquent\Collection|\App\Merchant[] $merchant * @property-read \Illuminate\Database\Eloquent\Collection|\App\Product[] $products * @method static \Illuminate\Database\Query\Builder|\App\Shop whereId($value) * @method static \Illuminate\Database\Query\Builder|\App\Shop whereName($value) * @method static \Illuminate\Database\Query\Builder|\App\Shop whereSlug($value) * @method static \Illuminate\Database\Query\Builder|\App\Shop whereSite($value) * @method static \Illuminate\Database\Query\Builder|\App\Shop whereMerchantId($value) * @method static \Illuminate\Database\Query\Builder|\App\Shop whereCreatedAt($value) * @method static \Illuminate\Database\Query\Builder|\App\Shop whereUpdatedAt($value) * @mixin \Eloquent */ class Shop extends Model { /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function merchant() { return $this->belongsTo(Merchant::class, 'merchant_id'); } /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function products() { return $this->hasMany(Product::class, 'shop_id'); } }
別忘了利用下開發(fā)三件套輸入指令:
php artisan ide-helper:generate php artisan ide-helper:models php artisan test-factory-helper:generate
表的關(guān)系如圖:
然后寫Seeder,可以參考Laravel學(xué)習(xí)筆記之Seeder填充數(shù)據(jù)小技巧:
php artisan make:seeder MerchantTableSeeder php artisan make:seeder PhoneTableSeeder php artisan make:seeder ShopTableSeeder php artisan make:seeder ProductTableSeeder class MerchantTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $faker = Faker\Factory::create(); $datas = []; foreach (range(1, 20) as $key => $value) { $datas[] = [ 'username' => $faker->userName , 'email' => $faker->safeEmail , 'first_name' => $faker->firstName , 'last_name' => $faker->lastName , 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), 'updated_at' => \Carbon\Carbon::now()->toDateTimeString() ]; } DB::table('merchants')->insert($datas); } } class PhoneTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $faker = Faker\Factory::create(); $merchant_ids = \App\Merchant::lists('id')->toArray(); $datas = []; foreach (range(1, 20) as $key => $value) { $datas[] = [ 'number' => $faker->randomNumber() , 'merchant_id' => $faker->randomElement($merchant_ids) , 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), 'updated_at' => \Carbon\Carbon::now()->toDateTimeString() ]; } DB::table('phones')->insert($datas); } } class ShopTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $faker = Faker\Factory::create(); $merchant_ids = \App\Merchant::lists('id')->toArray(); $datas = []; foreach (range(1, 40) as $key => $value) { $datas[] = [ 'name' => $faker->name , 'slug' => $faker->slug , 'site' => $faker->word , 'merchant_id' => $faker->randomElement($merchant_ids) , 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), 'updated_at' => \Carbon\Carbon::now()->toDateTimeString() ]; } DB::table('shops')->insert($datas); } } class ProductTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $faker = Faker\Factory::create(); $shop_ids = \App\Shop::lists('id')->toArray(); $datas = []; foreach (range(1, 30) as $key => $value) { $datas[] = [ 'name' => $faker->name , 'short_desc' => $faker->text , 'long_desc' => $faker->text , 'price' => $faker->randomFloat() , 'shop_id' => $faker->randomElement($shop_ids) , 'created_at' => \Carbon\Carbon::now()->toDateTimeString() , 'updated_at' => \Carbon\Carbon::now()->toDateTimeString() ]; } DB::table('products')->insert($datas); } } php artisan db:seed
3.寫個(gè)簡(jiǎn)單View視圖
(1)用Repository Pattern來組織代碼
//app/Repository namespace App\Repository; interface ShopRepositoryInterface { public function all(); } //app/Repository/Eloquent namespace App\Repository\Eloquent; use App\Repository\ShopRepositoryInterface; use App\Shop; class ShopRepository implements ShopRepositoryInterface { /** * @var Shop */ public $shop; public function __construct(Shop $shop) { $this->shop = $shop; } public function all() { // TODO: Implement all() method. $shops = $this->shop->all(); return $shops; } } //app/provider/ShopRepositoryServiceProvider //php artisan make:provider ShopRepositoryServiceProvider /** * Register the application services. * * @return void */ public function register() { $this->app->bind(ShopRepositoryInterface::class, ShopRepository::class); } //app/Http/Controllers/ShopController.php class ShopController extends Controller { /** * @var ShopRepositoryInterface */ public $shop; /** * ShopController constructor. * @param ShopRepositoryInterface $shopRepositoryInterface */ public function __construct(ShopRepositoryInterface $shopRepositoryInterface) { $this->shop = $shopRepositoryInterface; } public function all() { $shops = $this->shop->all(); return view('shop.index', compact('shops')); } } //視圖 //resources/views/shop/layout.blade.php <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3個(gè)meta標(biāo)簽*必須*放在最前面,任何其他內(nèi)容都*必須*跟隨其后! --> <title>Bootstrap Template</title> <!-- 新 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css"> <style> html,body{ width: 100%; height: 100%; } *{ margin: 0; border: 0; } </style> </head> <body> <p class="container"> <p class="row"> <p class="col-xs-12 col-md-12"> @yield('content') </p> </p> </p> <!-- jQuery文件。務(wù)必在bootstrap.min.js 之前引入 --> <script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script> <!-- 最新的 Bootstrap 核心 JavaScript 文件 --> <script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> <script> </script> </body> </html> //resources/views/shop/index.blade.php @extends('shop.layout') @section('content') <ul class="list-group"> @foreach($shops as $shop) <li class="list-group-item" style="margin-top: 10px"> <h2><strong style="color: darkred">Store:</strong>{{$shop->name}}</h2> <span><strong style="color: orangered">Member:</strong>{{$shop->merchant->first_name.' '.$shop->merchant->last_name}}</span> {{--這里數(shù)組取電話號(hào)碼--}} <span><strong style="color: orangered">Phone:</strong>{{$shop->merchant->phone['number']}}</span> <ul class="list-group"> @foreach($shop->products as $product) <li class="list-group-item"> <h4><strong style="color: red">Name:</strong>{{$product->name}}</h4> <h5><strong style="color: red">Desc:</strong>{{$product->short_desc}}</h5> <h5><strong style="color: red">Price:</strong>{{$product->price}}</h5> {{-- {!! Debugbar::info('products:'.$product->id) !!}--}} </li> @endforeach </ul> </li> @endforeach </ul> @endsection //路由 Route::get('/eagerload', 'ShopController@all');
(2)Debugbar查看程序執(zhí)行數(shù)據(jù)
可以看到,執(zhí)行了121次query,耗時(shí)38.89ms,效率很低,仔細(xì)觀察每一個(gè)statement就發(fā)現(xiàn)這是先掃描shops表,再根據(jù)shops中每一個(gè)merchant_id去查找merchants表,查找products表也是這樣,又有很多次query,這是N+1查找問題。
(1)嵌套預(yù)加載
Eloquent在通過屬性訪問關(guān)聯(lián)數(shù)據(jù)時(shí)是延遲加載
的,就是只有該關(guān)聯(lián)數(shù)據(jù)只有在通過屬性訪問它時(shí)才會(huì)被加載。在查找上層模型時(shí)可以通過預(yù)加載關(guān)聯(lián)數(shù)據(jù),避免N+1問題。而且,使用預(yù)加載超級(jí)簡(jiǎn)單。
只需修改一行:
//app/Repository/Eloquent/ShopRepository public function all() { // TODO: Implement all() method. // $shops = $this->shop->all(); //通過`點(diǎn)`語法嵌套預(yù)加載,多種關(guān)聯(lián)就寫對(duì)應(yīng)的關(guān)聯(lián)方法 //Shop這個(gè)Model里關(guān)聯(lián)方法是Merchant()和Products(),Merchant Model里關(guān)聯(lián)方法是Phone() $shops = $this->shop->with(['merchant.phone', 'products'])->get(); return $shops; }
不需要修改其他代碼,再看Debugbar里的查詢:
It is working!!!
發(fā)現(xiàn):只有4個(gè)query,耗時(shí)3.58ms,效率提高很多。把原來的N+1這種query改造成了where..in..
這種query,效率提高不少??梢杂肊XPLAIN來查看SQL語句的執(zhí)行計(jì)劃。
(2)預(yù)加載條件限制
還可以對(duì)預(yù)加載進(jìn)行條件限制,如對(duì)products進(jìn)行預(yù)先排序,代碼也很好修改,只需:
//app/Repository/Eloquent/ShopRepository public function all() { // TODO: Implement all() method. // $shops = $this->shop->all(); // $shops = $this->shop->with(['merchant.phone', 'products'])->get(); $shops = $this->shop->with(['members.phone', 'products'=>function($query){ // $query->orderBy('price', 'desc'); $query->orderBy('price', 'asc'); }])->get(); return $shops; }
通過加個(gè)限制條件,就等于在預(yù)加載products時(shí)SQL語句上加個(gè)排序。
關(guān)于“Laravel之模型關(guān)聯(lián)預(yù)加載的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。