Laravel入門 Laraveでデータベースを扱う3つの方法(第6回)

2021-01-01
6 min read

Laravel でデータベース操作を簡単なサンプルを通して学びます。

データベース接続

Laravel で RDB を扱う方法は、大きく分けて 3 つあります。

  • Query Builder(クエリビルダ)
  • Eloquent (エレクエント) ORM
  • SQL クエリ

それぞれの方法でテーブルからデータを取得する方法をみていきましょう。

クエリビルダ

クエリビルダを使えば、データベースへの問い合わせを簡易に行えます。 たとえば、下記のクエリビルダでは、tasks テーブルのすべての行を取得します。

todo/app/Http/Controllers/BasicController.php の showTasksA メソッド (ソースは、Slackにて配布)

public function showTasksA(Request $request) {

    $tasks = DB::table('tasks')->get();
    return view('show-tasks', compact('tasks'));

}

生の SQL では、SELECT * FROM tasksという文を記述することになります。一方、クエリビルダは、SQL 文ではなく、メソッドにてデータベースへの問い合わを行います。 SQL を使いこなしている方々にとっては、回りくどく思われる事でしょう。もちろん SQL 文を直接発行する方法もあります。

DB::tableというのは、Laravle の特徴であるファサードが使われています。

DB ファサードの実体は、Illuminate\Database\DatabaseManagerになります。

そして、tableというメソッドは下記のクラスのものになります。 \Illuminate\Database\Connection

戻り値は\Illuminate\Database\Query\Builderになります。 API仕様を見れば、SQL に相当するメソッドがいくつもあることがみてとれます。 部分的に生 SQL を使用するためのメソッドもあります。

また、下記のようにメソッドチェインで where 句をつけることができます。

DB::table('tasks')->where('status',1)->get();

最後に get()メソッドを呼ぶことで SQL を発行します。

戻り値は連想配列ではなくcollectionになります。 collectionの子要素は、PHP のstdClassオブジェクトになります。

Tinker で確認してみます。

$ php artisan tinker
Psy Shell v0.10.4 (PHP 7.4.0 — cli) by Justin Hileman
>>> $result = DB::table('tasks')->get();
=> Illuminate\Support\Collection {#3305
     all: [#3314
         +"id": 2,
         +"title": "サンフランシスコ旅行",
         +"status": 1,
         +"description": "サンノゼ空港からパロ・アルトへの行き方を調べる",
         +"created_at": null,
         +"updated_at": null,
       },
      ],
   }
>>> $result[1] instanceof stdClass
=> true
>>>exit
Exit:  Goodbye

クエリビルダの公式ドキュメントの日本語訳は下記にあります。

https://readouble.com/laravel/7.x/ja/queries.html

Eloquent ORM

Eloquent ORMは、Active Record パターンを実装した ORMです。 Active Record パターンは、Enterpise Application Patterns の一種で、テーブルとクラスを 1 対 1 で紐付けたものです。 Active Record パターンの実装はいくつかあり、Ruby 言語の Web フレームワークである Ruby on Rails の ORMが有名です。

定義 https://www.martinfowler.com/eaaCatalog/activeRecord.html

定義訳 https://bliki-ja.github.io/pofeaa/ActiveRecord/

ORMというものは、オブジェクト指向言語と RDB の間を仲介するものです。 オブジェクト指向言語からすると、データはオブジェクト。RDB では、データを表として扱うので両者の間に隔たりができます。 その隔たりを解消してくれるのが ORMです。 しかし、ORMにも限界があリます。効率よく使うには、SQL の知識が不可欠です。

Eloquent ORMを使っていきます。

モデルクラスの生成

モデルはテーブルに対して 1 対 1 に対応したものです。

tasksテーブルに対応したtaskモデルを作ります。

モデルのひな型は、下記のコマンドで作れます。

$ php artisan make:model <モデル名>

ちなみにモデルの名称は、単数形で、テーブルの名称が複数形とするのが Laravel の標準です。

複数形を調べるには、Str::plural を使用しhます。

$ php artisan tinker
>>> echo Str::plural('task');
tasks⏎

Laravel の標準機能は、上記のコマンドでモデルのテンプレートを生成してくれますが、中身は空のため実装が必要となってきます。

テーブルがすでにある場合は、テーブルからモデルを生成します。生成ツールとして下記を使用します。 https://github.com/krlove/eloquent-model-generator

モデル生成ツールのインストール

$ cd /var/www/html/todo
$ composer require krlove/eloquent-model-generator --dev

モデル生成

$ php artisan krlove:generate:model Task

app 以下に Task.php が できています。

app
└── Task.php

データベースの内容を表示

Eloquent ORM でのタスクの表示のソースは下記になります。 クエリビルダーでは、DB::table('tasks')->get() と記述していたところが、Task::all() となります。

todo/app/Http/Controllers/BasicController.php の showTaskB メソッド (ソースは、Slackにて配布)

    public function showTaskB(Request $request) {

        $tasks = Task::all();
        return view('show-tasks', compact('tasks'));

    }

Illuminate/Database/Eloquent/Modelstaticメソッドであるallの呼び出しています。 戻り値は、Illuminate\Database\Eloquent\Collectionが返されます。 子要素はApp\Taskになります。

tinkerで戻り値を確認します。

$ php artisan tinker
>>> App\Task::all()
=> Illuminate\Database\Eloquent\Collection {#4099
     all: [
       App\Task {#3632
         id: 2,
         title: "サンフランシスコ旅行",
         status: 1,
         description: "サンノゼ空港からパロ・アルトへの行き方を調べる",
         created_at: null,
         updated_at: null,
       },
     ],
   }
>>>exit
Exit:  Goodbye

Eloquent ORMの公式ドキュメントの日本語訳は下記にあります。

https://readouble.com/laravel/7.x/ja/eloquent.html

SQLクエリ

複雑なテーブル構成の場合、Eloquentどころか、Query Builderでも問い合わせがつらくなります。 特に業務システムなんてものは、そもそも複雑で、生の SQL で記述した方がベターとなることが多いです。 そういう場合は、下記のように SQL 文を発行します。

public function showTaskC(Request $request) {

    $states = DB::select('SELECT * from tasks');
    return view('show-tasks', compact('states'));

}

select メソッドの引数は、SQL 文といっても、文字列です。 長い SQL 文の場合は、別ファイルに記述して読み込ませるようにするということもできます。

select メソッドの戻り値は配列になります。

tinkerで戻り値を確認します。

$ php artisan tinker
>>> DB::select('SELECT * from tasks')
=> [
     {#3326
       +"id": 2,
       +"title": "サンフランシスコ旅行",
       +"status": 1,
       +"description": "サンノゼ空港からパロ・アルトへの行き方を調べる",
       +"created_at": null,
       +"updated_at": null,
     },
    ]
>>> $result instanceof Illuminate\Support\Collection
=> false
>>> is_array($result)
=> true
>>> $result[1] instanceof stdClass
=> true
>>>exit
Exit:  Goodbye

Query BuilderEloquentとは戻り値が異なることに注意してください。 Query BuilderEloquentは、Illuminate\Support\Collectionを返すので、後続の処理方法が異なってくることに注意してください 配列の子要素は、PHP のstdClassオブジェクトになります。

Eloquent modelhydrateメソッドを使えば、モデルに変換ができます。

$ php artisan tinker
>>> $result = DB::select('SELECT * from tasks')
>>> App\Task::hydrate($result)
Illuminate\Database\Eloquent\Collection {#4318
     all: [
       App\Task {#4319
         id: 1,
         title: "bitcoinの半減期を調べる",
         status: 2,
         description: "bitcoinの半減期と価格の相関性を調べる",
         created_at: null,
         updated_at: null,
       },
 >>>exit
 Exit:  Goodbye

また、SQL 文に引数を渡す事もできます。

$results = DB::select('select * from tasks where id = :id', ['id' => 1]);

この引数の渡し方は、SQL インジェクションを防ぐために提供されています。 下記のような SQL 文の書き方は、推奨されません。

DB::select("select * from tasks where id = $id");
DB::select('select * from tasks where id =' . $id);

SQLクエリの公式ドキュメントの日本語訳は下記にあります。

https://readouble.com/laravel/7.x/ja/database.html