オブジェクト リレーショナル マッピング (ORM) を使用すると、データの操作が驚くほど簡単になります。オブジェクト指向の方法でデータ間の関係を定義すると、関連するモデル データのクエリが簡単になるため、開発者は基礎となるデータの呼び出しに注意を払う必要がありません。
ORM の標準的なデータ最適化では、関連データを積極的にロードします。いくつかのサンプル関係を設定し、その後、即時読み込みと非即時読み込みでクエリがどのように変化するかを段階的に説明します。私はコードを直接使用して実験するのが好きで、いくつかの例を見て積極的な読み込みがどのように機能するかを説明します。これは、クエリを最適化する方法を理解するのにさらに役立ちます。
はじめに
基本的なレベルでは、ORM は関連するモデル データの読み込みが「遅い」です。しかし、ORM はどのようにしてユーザーの意図を知るのでしょうか?モデルをクエリした後は、関連するモデルのデータを実際に使用することはできません。クエリが最適化されていないことは、「N 1」問題として知られています。オブジェクトを使用してクエリを表すと、知らず知らずのうちにクエリを実行している可能性があります。
データベースから 100 個のオブジェクトを受け取り、各レコードに 1 つの関連付けられたモデル (つまり、belongsTo) があると想像してください。 ORM を使用すると、デフォルトで 101 個のクエリが発生します。元の 100 レコードに対して 1 つのクエリが発生し、モデル オブジェクトの関連データにアクセスする場合はレコードごとに追加のクエリが発生します。疑似コードで、公開されたすべての投稿の公開著者をリストしたいとします。一連の投稿 (各投稿には作成者がいます) から、次のように作成者名のリストを取得できます:
$posts = Post::published()->get(); // 一次查询 $authors = array_map(function($post) { // 生成对作者模型的查询 return $post->author->name; }, $posts);
すべての投稿者が必要であることをモデルに伝えているわけではないので、毎回個々の投稿から取得します。モデル インスタンス 作成者の名前を取得するときに、別のクエリが発生します。
プリロード
前述したように、ORM は関連付けの読み込みを「怠惰」に行います。関連するモデル データを使用する場合は、即時読み込みを使用して 101 クエリを 2 クエリに減らすことができます。モデルに何をロードしたいかを伝えるだけです。
以下は、プリロードを使用した Rails Active Record ガイドの例です。ご覧のとおり、この概念は Laravel の積極的な読み込みの概念と非常に似ています。
# Rails posts = Post.includes(:author).limit(100) # Laravel $posts = Post::with('author')->limit(100)->get();
より広い視点から探求することで、より理解が深まることがわかりました。 Active Record のドキュメントには、このアイデアの共感をさらに高めるのに役立ついくつかの例が記載されています。
Laravel の Eloquent ORM
Eloquent と呼ばれる Laravel の ORM は、モデルを簡単にプリロードでき、ネストされたリレーショナル モデルもプリロードできます。 Post モデルを例として、Laravel プロジェクトで積極的な読み込みを使用する方法を学びましょう。
このプロジェクト ビルドを使用して、最後にいくつかのプリロード例をさらに詳しく見ていきます。
ビルド
データベースの移行、モデル、データベース シードを構築して、プリロードを体験してみましょう。この手順を進めたい場合は、データベースにアクセスでき、基本的な Laravel のインストールが完了していることを前提としています。
Laravel インストーラーを使用して、新しいプロジェクトを作成します。
laravel new blog-example
データベースと選択内容に従って .env ファイルを編集します。
次に、ネストされた関係をプリロードできるように 3 つのモデルを作成します。この例は単純なので、積極的な読み込みに集中できます。インデックスや外部キー制約など、使用する可能性のあるものは省略しています。
php artisan make:model -m Post php artisan make:model -m Author php artisan make:model -m Profile
-m フラグは、テーブル スキーマの作成に使用されるモデルで使用する移行を作成します。
データ モデルには次の関連付けがあります:
Post ->belongsTo ->Author
Author ->hasMany ->Post
Author -> hasOne -> Profile
Migration
各データ テーブルのプロファイル構造を作成しましょう。 Laravel は新しいテーブルに対して down() メソッドを自動的に追加するため、up() メソッドのみを追加しました。これらの移行ファイルは、database/migrations/ ディレクトリに配置されます。
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreatePostsTable extends Migration { /** * 执行迁移 * * @return void */ public function up() { Schema::create('posts', function (Blueprint $table) { $table->increments('id'); $table->unsignedInteger('author_id'); $table->string('title'); $table->text('body'); $table->timestamps(); }); } /** * 回滚迁移 * * @return void */ public function down() { Schema::dropIfExists('posts'); } }
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateAuthorsTable extends Migration { /** * 执行迁移 * * @return void */ public function up() { Schema::create('authors', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->text('bio'); $table->timestamps(); }); } /** * 回滚迁移 * * @return void */ public function down() { Schema::dropIfExists('authors'); } }
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateProfilesTable extends Migration { /** * 执行迁移 * * @return void */ public function up() { Schema::create('profiles', function (Blueprint $table) { $table->increments('id'); $table->unsignedInteger('author_id'); $table->date('birthday'); $table->string('city'); $table->string('state'); $table->string('website'); $table->timestamps(); }); } /** * 回滚迁移 * * @return void */ public function down() { Schema::dropIfExists('profiles'); } }
Model
モデルの関連付けを定義し、積極的な読み込みを使用してさらに実験を実行する必要があります。 php 職人の make:model コマンドを実行すると、モデル ファイルが作成されます。
最初のモデルは app/Post.php です:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { public function author() { return $this->belongsTo(Author::class); } }
次に、app\Author.php モデルには 2 つの関係があります:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Author extends Model { public function profile() { return $this->hasOne(Profile::class); } public function posts() { return $this->hasMany(Post::class); } }
モデルと移行を通じて、次のことができます。移行を実行し、シード モデル データのプリロードを続行します。
php artisan migrate Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table Migrating: 2017_08_04_042509_create_posts_table Migrated: 2017_08_04_042509_create_posts_table Migrating: 2017_08_04_042516_create_authors_table Migrated: 2017_08_04_042516_create_authors_table Migrating: 2017_08_04_044554_create_profiles_table Migrated: 2017_08_04_044554_create_profiles_table
データベースを見ると、作成されたすべてのデータ テーブルが表示されます。
ファクトリ モデル
クエリ ステートメントを実行するには、クエリを提供するための偽のデータを作成する必要があります。いくつかのファクトリ モデルを追加して、これらを使用しましょうデータベースはテスト データを提供します。
database/factories/ModelFactory.php ファイルを開き、次の 3 つのファクトリ モデルを既存のユーザー ファクトリ モデル ファイルに追加します。
/** @var \Illuminate\Database\Eloquent\Factory $factory */ $factory->define(App\Post::class, function (Faker\Generator $faker) { return [ 'title' => $faker->sentence, 'author_id' => function () { return factory(App\Author::class)->create()->id; }, 'body' => $faker->paragraphs(rand(3,10), true), ]; }); /** @var \Illuminate\Database\Eloquent\Factory $factory */ $factory->define(App\Author::class, function (Faker\Generator $faker) { return [ 'name' => $faker->name, 'bio' => $faker->paragraph, ]; }); $factory->define(App\Profile::class, function (Faker\Generator $faker) { return [ 'birthday' => $faker->dateTimeBetween('-100 years', '-18 years'), 'author_id' => function () { return factory(App\Author::class)->create()->id; }, 'city' => $faker->city, 'state' => $faker->state, 'website' => $faker->domainName, ]; });
これらのファクトリ モデルは、必要なデータの一部を簡単に入力できます。クエリを実行したり、リレーショナル モデルに必要なデータを作成および生成するために使用することもできます。
database/seeds/DatabaseSeeder.php ファイルを開き、次の内容を DatabaseSeeder::run() メソッドに追加します。
public function run() { $authors = factory(App\Author::class, 5)->create(); $authors->each(function ($author) { $author ->profile() ->save(factory(App\Profile::class)->make()); $author ->posts() ->saveMany( factory(App\Post::class, rand(20,30))->make() ); }); }
你创建了五个 author 并遍历循环每一个 author ,创建和保存了每个 author 相关联的 profile 和 posts (每个 author 的 posts 的数量在 20 和 30 个之间)。
我们已经完成了迁移、模型、工厂模型和数据库填充的创建工作,将它们组合起来可以以重复的方式重新运行迁移和数据库填充:
php artisan migrate:refresh php artisan db:seed
你现在应该有一些已经填充的数据,可以在下一章节使用它们。注意在 Laravel 5.5 版本中包含一个 migrate:fresh 命令,它会删除表,而不是回滚迁移并重新应用它们。
尝试使用预加载
现在我们的前期工作终于已经完成了。 我个人认为最好的可视化方式就是将查询结果记录到 storage/logs/laravel.log 文件当中查看。
要把查询结果记录到日志中,有两种方式。第一种,可以开启 MySQL 的日志文件,第二种,则是使用 Eloquent 的数据库调用来实现。通过 Eloquent 来实现记录查询语句的话,可以将下面的代码添加到 app/Providers/AppServiceProvider.php boot () 方法当中:
namespace App\Providers; use DB; use Log; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { DB::listen(function($query) { Log::info( $query->sql, $query->bindings, $query->time ); }); } // ... }
我喜欢把这个监听器封装在配置检查的时候,以便可以控制记录查询日志的开关。你也可以从 Laravel Debugbar 获取到更多相关的信息。
首先,尝试一下在不使用预加载模型的时候,会发生什么情况。清除你的 storage/log/laravel.log 文件当中的内容然后运行 "tinker" 命令:
php artisan tinker >>> $posts = App\Post::all(); >>> $posts->map(function ($post) { ... return $post->author; ... }); >>> ...
这个时候检查你的 laravel.log 文件,你会发现一堆查询作者的查询语句:
[2017-08-04 06:21:58] local.INFO: select * from `posts` [2017-08-04 06:22:06] local.INFO: select * from `authors` where `authors`.`id` = ? limit 1 [1] [2017-08-04 06:22:06] local.INFO: select * from `authors` where `authors`.`id` = ? limit 1 [1] [2017-08-04 06:22:06] local.INFO: select * from `authors` where `authors`.`id` = ? limit 1 [1] ....
然后,再次清空 laravel.log 文件,, 这次使用 with() 方法来用预加载查询作者信息:
php artisan tinker >>> $posts = App\Post::with('author')->get(); >>> $posts->map(function ($post) { ... return $post->author; ... }); ...
这次你应该看到了,只有两条查询语句。一条是对所有帖子进行查询,以及对帖子所关联的作者进行查询:
[2017-08-04 07:18:02] local.INFO: select * from `posts` [2017-08-04 07:18:02] local.INFO: select * from `authors` where `authors`.`id` in (?, ?, ?, ?, ?) [1,2,3,4,5]
如果你有多个关联的模型,你可以使用一个数组进行预加载的实现:
$posts = App\Post::with(['author', 'comments'])->get();
在 Eloquent 中嵌套预加载
嵌套预加载来做相同的工作。在我们的例子中,每个作者的 model 都有一个关联的个人简介。因此,我们将针对每个个人简介来进行查询。
清空 laravel.log 文件,来做一次尝试:
php artisan tinker >>> $posts = App\Post::with('author')->get(); >>> $posts->map(function ($post) { ... return $post->author->profile; ... }); ...
你现在可以看到七个查询语句,前两个是预加载的结果。然后,我们每次获取一个新的个人简介时,就需要来查询所有作者的个人简介。
通过预加载,我们可以避免嵌套在模型关联中的额外的查询。最后一次清空 laravel.log 文件并运行一下命令:
>>> $posts = App\Post::with('author.profile')->get(); >>> $posts->map(function ($post) { ... return $post->author->profile; ... });
现在,总共有三个查询语句:
[2017-08-04 07:27:27] local.INFO: select * from `posts` [2017-08-04 07:27:27] local.INFO: select * from `authors` where `authors`.`id` in (?, ?, ?, ?, ?) [1,2,3,4,5] [2017-08-04 07:27:27] local.INFO: select * from `profiles` where `profiles`.`author_id` in (?, ?, ?, ?, ?) [1,2,3,4,5]
懒人预加载
你可能只需要收集关联模型的一些基础的条件。在这种情况下,可以懒惰地调用关联数据的一些其他查询:
php artisan tinker >>> $posts = App\Post::all(); ... >>> $posts->load('author.profile'); >>> $posts->first()->author->profile; ...
你应该只能看到三条查询,并且是在调用 $posts->load() 方法后。
总结
希望你能了解到更多关于预加载模型的相关知识,并且了解它是如何在更加深入底层的工作方式。 预加载文档 是非常全面的,我希望额外的一些代码实现可以帮助您更好的优化关联查询。
以上がLaravelのN+1問題解決策の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

本篇文章给大家带来了关于laravel的相关知识,其中主要介绍了关于单点登录的相关问题,单点登录是指在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统,下面一起来看一下,希望对大家有帮助。

本篇文章给大家带来了关于laravel的相关知识,其中主要介绍了关于Laravel的生命周期相关问题,Laravel 的生命周期从public\index.php开始,从public\index.php结束,希望对大家有帮助。

在laravel中,guard是一个用于用户认证的插件;guard的作用就是处理认证判断每一个请求,从数据库中读取数据和用户输入的对比,调用是否登录过或者允许通过的,并且Guard能非常灵活的构建一套自己的认证体系。

laravel中asset()方法的用法:1、用于引入静态文件,语法为“src="{{asset(‘需要引入的文件路径’)}}"”;2、用于给当前请求的scheme前端资源生成一个url,语法为“$url = asset('前端资源')”。

本篇文章给大家带来了关于laravel的相关知识,其中主要介绍了关于使用中间件记录用户请求日志的相关问题,包括了创建中间件、注册中间件、记录用户访问等等内容,下面一起来看一下,希望对大家有帮助。

本篇文章给大家带来了关于laravel的相关知识,其中主要介绍了关于中间件的相关问题,包括了什么是中间件、自定义中间件等等,中间件为过滤进入应用的 HTTP 请求提供了一套便利的机制,下面一起来看一下,希望对大家有帮助。

在laravel中,fill方法是一个给Eloquent实例赋值属性的方法,该方法可以理解为用于过滤前端传输过来的与模型中对应的多余字段;当调用该方法时,会先去检测当前Model的状态,根据fillable数组的设置,Model会处于不同的状态。

laravel路由文件在“routes”目录里。Laravel中所有的路由文件定义在routes目录下,它里面的内容会自动被框架加载;该目录下默认有四个路由文件用于给不同的入口使用:web.php、api.php、console.php等。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

PhpStorm Mac バージョン
最新(2018.2.1)のプロフェッショナル向けPHP統合開発ツール

Dreamweaver Mac版
ビジュアル Web 開発ツール

SecLists
SecLists は、セキュリティ テスターの究極の相棒です。これは、セキュリティ評価中に頻繁に使用されるさまざまな種類のリストを 1 か所にまとめたものです。 SecLists は、セキュリティ テスターが必要とする可能性のあるすべてのリストを便利に提供することで、セキュリティ テストをより効率的かつ生産的にするのに役立ちます。リストの種類には、ユーザー名、パスワード、URL、ファジング ペイロード、機密データ パターン、Web シェルなどが含まれます。テスターはこのリポジトリを新しいテスト マシンにプルするだけで、必要なあらゆる種類のリストにアクセスできるようになります。

DVWA
Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、

MinGW - Minimalist GNU for Windows
このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。
