详解Laravel之模型关联预加载

下面由laravel教程栏目给大家介绍laravel之模型关联预加载,希望对需要的朋友有所帮助!

详解Laravel之模型关联预加载

Laravel学习笔记之模型关联预加载

说明:本文主要说明Laravel Eloquent的延迟预加载(Eager Loading),使用延迟预加载来减少MySQL查询次数。同时,作者会将开发过程中的一些截图和代码黏上去,提高阅读效率。

备注:现在有4张表:商家表merchants、商家电话表phones、商家拥有的店铺shops表和店铺里的商品表products。并且关系是:

[    'merchants_phones' => 'one-to-one',    'merchants_shops'  => 'one-to-many',    'shops_products'   => 'one-to-many',]

现在要求做出一个页面以列表形式显示每个店铺,每个店铺块包含店铺信息如标题、包含店铺商家信息如姓名和电话、包含拥有的商品信息如介绍和价格。看看有没有预加载会有什么不同。

开发环境:Laravel5.1+MAMP+PHP7+MySQL5.5

先写个店铺列表页

1.先装上开发插件三件套(具体可参考:Laravel学习笔记之Seeder填充数据小技巧)
不管咋样,先装上开发插件三件套:

composer require barryvdh/laravel-debugbar --devcomposer require barryvdh/laravel-ide-helper --devcomposer require mpociot/laravel-test-factory-helper --dev//config/app.php/** *Develop Plugin */        BarryvdhDebugbarServiceProvider::class,MpociotLaravelTestFactoryHelperTestFactoryHelperServiceProvider::class,BarryvdhLaravelIdeHelperIdeHelperServiceProvider::class,

2.写上表字段、表关联和测试数据填充器Seeder
依次输入指令:

php artisan make:model Merchant -mphp artisan make:model Phone -mphp artisan make:model Shop -mphp artisan make:model Product -m

写上表字段和表关联:

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');    }}/** * AppMerchant * * @property integer $id * @property string $username * @property string $email * @property string $first_name * @property string $last_name * @property CarbonCarbon $created_at * @property CarbonCarbon $updated_at * @property-read AppPhone $phone * @property-read IlluminateDatabaseEloquentCollection|AppShop[] $shops * @method static IlluminateDatabaseQueryBuilder|AppMerchant whereId($value) * @method static IlluminateDatabaseQueryBuilder|AppMerchant whereUsername($value) * @method static IlluminateDatabaseQueryBuilder|AppMerchant whereEmail($value) * @method static IlluminateDatabaseQueryBuilder|AppMerchant whereFirstName($value) * @method static IlluminateDatabaseQueryBuilder|AppMerchant whereLastName($value) * @method static IlluminateDatabaseQueryBuilder|AppMerchant whereCreatedAt($value) * @method static IlluminateDatabaseQueryBuilder|AppMerchant whereUpdatedAt($value) * @mixin Eloquent */class Merchant extends Model{    /**     * @return IlluminateDatabaseEloquentRelationsHasOne     */    public function phone()    {        return $this->hasOne(Phone::class, 'merchant_id');    }    /**     * @return IlluminateDatabaseEloquentRelationsHasMany     */    public function shops()    {        return $this->hasMany(Shop::class, 'merchant_id');    }}/** * AppPhone * * @property integer $id * @property integer $number * @property integer $merchant_id * @property CarbonCarbon $created_at * @property CarbonCarbon $updated_at * @property-read AppMerchant $merchant * @method static IlluminateDatabaseQueryBuilder|AppPhone whereId($value) * @method static IlluminateDatabaseQueryBuilder|AppPhone whereNumber($value) * @method static IlluminateDatabaseQueryBuilder|AppPhone whereMerchantId($value) * @method static IlluminateDatabaseQueryBuilder|AppPhone whereCreatedAt($value) * @method static IlluminateDatabaseQueryBuilder|AppPhone whereUpdatedAt($value) * @mixin Eloquent */class Phone extends Model{    /**     * @return IlluminateDatabaseEloquentRelationsBelongsTo     */    public function merchant()    {        return $this->belongsTo(Merchant::class, 'merchant_id');    }}/** * AppProduct * * @property integer $id * @property string $name * @property string $short_desc * @property string $long_desc * @property float $price * @property integer $shop_id * @property CarbonCarbon $created_at * @property CarbonCarbon $updated_at * @property-read IlluminateDatabaseEloquentCollection|AppShop[] $shop * @method static IlluminateDatabaseQueryBuilder|AppProduct whereId($value) * @method static IlluminateDatabaseQueryBuilder|AppProduct whereName($value) * @method static IlluminateDatabaseQueryBuilder|AppProduct whereShortDesc($value) * @method static IlluminateDatabaseQueryBuilder|AppProduct whereLongDesc($value) * @method static IlluminateDatabaseQueryBuilder|AppProduct wherePrice($value) * @method static IlluminateDatabaseQueryBuilder|AppProduct whereShopId($value) * @method static IlluminateDatabaseQueryBuilder|AppProduct whereCreatedAt($value) * @method static IlluminateDatabaseQueryBuilder|AppProduct whereUpdatedAt($value) * @mixin Eloquent */class Product extends Model{    /**     * @return IlluminateDatabaseEloquentRelationsBelongsTo     */    public function shop()    {        return $this->belongsTo(Shop::class, 'shop_id');    }}/** * AppShop * * @property integer $id * @property string $name * @property string $slug * @property string $site * @property integer $merchant_id * @property CarbonCarbon $created_at * @property CarbonCarbon $updated_at * @property-read IlluminateDatabaseEloquentCollection|AppMerchant[] $merchant * @property-read IlluminateDatabaseEloquentCollection|AppProduct[] $products * @method static IlluminateDatabaseQueryBuilder|AppShop whereId($value) * @method static IlluminateDatabaseQueryBuilder|AppShop whereName($value) * @method static IlluminateDatabaseQueryBuilder|AppShop whereSlug($value) * @method static IlluminateDatabaseQueryBuilder|AppShop whereSite($value) * @method static IlluminateDatabaseQueryBuilder|AppShop whereMerchantId($value) * @method static IlluminateDatabaseQueryBuilder|AppShop whereCreatedAt($value) * @method static IlluminateDatabaseQueryBuilder|AppShop whereUpdatedAt($value) * @mixin Eloquent */class Shop extends Model{    /**     * @return IlluminateDatabaseEloquentRelationsBelongsTo     */    public function merchant()    {        return $this->belongsTo(Merchant::class, 'merchant_id');    }    /**     * @return IlluminateDatabaseEloquentRelationsHasMany     */    public function products()    {        return $this->hasMany(Product::class, 'shop_id');    }}

别忘了利用下开发三件套输入指令:

php artisan ide-helper:generatephp artisan ide-helper:modelsphp artisan test-factory-helper:generate

表的关系如图:

df49f7bcdf0cecf399a0f9daa27dd12.png

文心大模型 文心大模型

百度飞桨-文心大模型 ERNIE 3.0 文本理解与创作

文心大模型 56 查看详情 文心大模型

然后写Seeder,可以参考Laravel学习笔记之Seeder填充数据小技巧:

php artisan make:seeder MerchantTableSeederphp artisan make:seeder PhoneTableSeederphp artisan make:seeder ShopTableSeederphp artisan make:seeder ProductTableSeederclass MerchantTableSeeder extends Seeder{    /**     * Run the database seeds.     *     * @return void     */    public function run()    {        $faker = FakerFactory::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' => CarbonCarbon::now()->toDateTimeString(),                'updated_at' => CarbonCarbon::now()->toDateTimeString()            ];        }        DB::table('merchants')->insert($datas);    }}class PhoneTableSeeder extends Seeder{    /**     * Run the database seeds.     *     * @return void     */    public function run()    {        $faker        = FakerFactory::create();        $merchant_ids = AppMerchant::lists('id')->toArray();        $datas        = [];        foreach (range(1, 20) as $key => $value) {            $datas[]  = [                'number'      => $faker->randomNumber() ,                'merchant_id' => $faker->randomElement($merchant_ids) ,                'created_at'  => CarbonCarbon::now()->toDateTimeString(),                'updated_at'  => CarbonCarbon::now()->toDateTimeString()            ];        }        DB::table('phones')->insert($datas);    }}class ShopTableSeeder extends Seeder{    /**     * Run the database seeds.     *     * @return void     */    public function run()    {        $faker        = FakerFactory::create();        $merchant_ids = AppMerchant::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'   => CarbonCarbon::now()->toDateTimeString(),                'updated_at'   => CarbonCarbon::now()->toDateTimeString()            ];        }        DB::table('shops')->insert($datas);    }}class ProductTableSeeder extends Seeder{    /**     * Run the database seeds.     *     * @return void     */    public function run()    {        $faker    = FakerFactory::create();        $shop_ids = AppShop::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'        =>  CarbonCarbon::now()->toDateTimeString() ,                'updated_at'        =>  CarbonCarbon::now()->toDateTimeString()            ];        }        DB::table('products')->insert($datas);    }}php artisan db:seed

3.写个简单View视图
(1)用Repository Pattern来组织代码

//app/Repositorynamespace AppRepository;interface ShopRepositoryInterface{    public function all();}//app/Repository/Eloquentnamespace AppRepositoryEloquent;use AppRepositoryShopRepositoryInterface;use AppShop;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.phpclass 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                    Bootstrap Template                    html,body{            width: 100%;            height: 100%;        }        *{            margin: 0;            border: 0;        }    

@yield('content')

//resources/views/shop/index.blade.php@extends('shop.layout')@section('content')
    @foreach($shops as $shop)
  • Store:{{$shop->name}}

    Member:{{$shop->merchant->first_name.' '.$shop->merchant->last_name}} {{--这里数组取电话号码--}} Phone:{{$shop->merchant->phone['number']}}
      @foreach($shop->products as $product)
    • Name:{{$product->name}}

      Desc:{{$product->short_desc}}

      Price:{{$product->price}}

      {{-- {!! Debugbar::info('products:'.$product->id) !!}--}}
    • @endforeach
  • @endforeach
@endsection//路由Route::get('/eagerload', 'ShopController@all');

(2)Debugbar查看程序执行数据
d777dc41ca87ce95aed7da74438ba3c.png

可以看到,执行了121次query,耗时38.89ms,效率很低,仔细观察每一个statement就发现这是先扫描shops表,再根据shops中每一个merchant_id去查找merchants表,查找products表也是这样,又有很多次query,这是N+1查找问题。

预加载查询

(1)嵌套预加载
Eloquent在通过属性访问关联数据时是延迟加载的,就是只有该关联数据只有在通过属性访问它时才会被加载。在查找上层模型时可以通过预加载关联数据,避免N+1问题。而且,使用预加载超级简单。
只需修改一行:

//app/Repository/Eloquent/ShopRepository    public function all()    {        // TODO: Implement all() method.//        $shops = $this->shop->all();        //通过`点`语法嵌套预加载,多种关联就写对应的关联方法        //Shop这个Model里关联方法是Merchant()和Products(),Merchant Model里关联方法是Phone()        $shops = $this->shop->with(['merchant.phone', 'products'])->get();        return $shops;    }

不需要修改其他代码,再看Debugbar里的查询:
af843e3594f856dc5e66997f968e425.png

It is working!!!

发现:只有4个query,耗时3.58ms,效率提高很多。把原来的N+1这种query改造成了where..in..这种query,效率提高不少。可以用EXPLAIN来查看SQL语句的执行计划。

(2)预加载条件限制
还可以对预加载进行条件限制,如对products进行预先排序,代码也很好修改,只需:

//app/Repository/Eloquent/ShopRepositorypublic 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;    }

通过加个限制条件,就等于在预加载products时SQL语句上加个排序。截图就不截取了。

总结:关联模型预加载的确是个有意思的功能,效率提高不少。最近都在瞎研究,遇到好玩的东西再分享出来吧,到时见。

以上就是详解Laravel之模型关联预加载的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/483823.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫

关于作者

上一篇 2025年11月8日 12:31:59
下一篇 2025年11月8日 12:32:36

相关推荐

发表回复

登录后才能评论
关注微信