Laravelでデータベースを操作する(クエリビルダ編)


# 目次
Laravelの記事一覧は下記
[PHPフレームワークLaravelの使い方](https://qiita.com/toontoon/items/c4d0371e504c37f6576e)

# Laravelバージョン
動作確認はLaravel Framework 7.19.1で行っています

# 前提条件
[eclipseでLaravel開発環境を構築する。デバッグでブレークポイントをつけて止める。(WindowsもVagrantもdockerも)](https://qiita.com/toontoon/items/76fa7f8845b20776077c)
本記事は上記が完了している前提で書かれています
プロジェクトの作成もapacheの設定も上記で行っています

[Laravelで入力値エラーチェック(validate)を実装する](https://qiita.com/toontoon/items/abf5c52eb0c3fe11003c)
[Laravelでフラッシュデータ(直後のHTTPリクエストの間だけセッションに保存されるデータ)を使う](https://qiita.com/toontoon/items/eb9de17f65c198112f24)
本記事は上記の内容を理解している前提で書かれています

[LaravelでDIを使う](https://qiita.com/toontoon/items/ebf53e4053be6f663730)
本記事は上記で作成したフォルダとファイルを使用します

[Laravelでデータベースを扱う準備をする](https://qiita.com/toontoon/items/740b9575d2a323e93c0d)
[Laravelでテーブル作成](https://qiita.com/toontoon/items/2948a722a0ae7da2fb33)
[Laravelで初期データ投入](https://qiita.com/toontoon/items/f081f7f9f1667233480a)
本記事は上記ので作成したデータベースとレコードを使用します

# サービスクラス作成
(1) /sample/app/Services/InterfacesフォルダにTable2Service.php作成

```Table2Service.php
<?php
namespace App\Services\Interfaces;

interface Table2Service
{
    public function __construct();

    public function select($id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col);
    public function insert($table1_id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col);
    public function update($id, $table1_id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col);
    public function delete($id);

}
```

(2) /sample/app/Services/ImplフォルダにTable2ServiceImpl.php作成

```App\Services\Impl\Table2ServiceImpl.php
<?php
namespace App\Services\Impl;

use App\Services\Interfaces\Table2Service;
use Illuminate\Support\Facades\DB;

class Table2ServiceImpl implements Table2Service
{

    public function __construct()
    {

    }

    public function select($id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col)
    {

        $db = DB::table('table2')
                    ->leftJoin('table1', 'table2.table1_id', '=', 'table1.id')
                    ->leftJoin('table3', 'table2.id', '=', 'table3.table2_id')
                    ->select([
                        'table1.id            as table1_id',
                        'table1.varchar_col   as table1_varchar_col',
                        'table1.int_col       as table1_int_col',
                        'table1.datetime_col  as table1_datetime_col',
                        'table1.date_col      as table1_date_col',
                        'table1.time_col      as table1_time_col',
                        'table2.id            as table2_id',
                        'table2.varchar_col   as table2_varchar_col',
                        'table2.int_col       as table2_int_col',
                        'table2.datetime_col  as table2_datetime_col',
                        'table2.date_col      as table2_date_col',
                        'table2.time_col      as table2_time_col',
                        'table3.id            as table3_id',
                        'table3.varchar_col   as table3_varchar_col',
                        'table3.int_col       as table3_int_col',
                        'table3.datetime_col  as table3_datetime_col',
                        'table3.date_col      as table3_date_col',
                        'table3.time_col      as table3_time_col',
                    ]);

        if (!is_null($id)) {
            $db->where('table2.id', '=', $id);
        }
        if (!is_null($varchar_col)) {
            $db->where('table2.varchar_col', 'like', '%'. addcslashes($varchar_col, '\\_%') . '%');
        }
        if (!is_null($int_col)) {
            $db->where('table2.int_col', '=', $int_col);
        }
        if (!is_null($datetime_col)) {
            $db->where('table2.datetime_col', '=', $datetime_col);
        }
        if (!is_null($date_col)) {
            $db->where('table2.date_col', '=', $date_col);
        }
        if (!is_null($time_col)) {
            $db->where('table2.time_col', '=', $time_col);
        }

        $recordList = $db->get();

        return $recordList;

    }

    public function insert($table1_id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col)
    {
        $id = DB::table('table2')->insertGetId(
            [
                'table1_id' => $table1_id,
                'varchar_col' => $varchar_col,
                'int_col' => $int_col,
                'datetime_col' => $datetime_col,
                'date_col' => $date_col,
                'time_col' => $time_col,
            ]
        );

        return $id;

    }

    public function update($id, $table1_id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col)
    {

        $affected = DB::table('table2')
                       ->where('id', '=', $id)
                       ->update([
                           'table1_id' => $table1_id,
                           'varchar_col' => $varchar_col,
                           'int_col' => $int_col,
                           'datetime_col' => $datetime_col,
                           'date_col' => $date_col,
                           'time_col' => $time_col,
                       ]);

        return $affected;

    }

    public function delete($id)
    {
        $affected = DB::table('table2')
                      ->where('id', '=', $id)
                      ->delete();

        return $affected;

    }

}
```

これがデータベースにselect、insert、update、deleteしている処理になります
コードを見ればどんなSQLが発行されるかわかると思います。説明も不要でしょう
ここで使用していないメソッドは下記で確認できます
[Laravel 7.x データベース：クエリビルダ](https://readouble.com/laravel/7.x/ja/queries.html)
また、トランザクションを使用したい場合は下記でできます
[Laravel 7.x データベース：利用開始 データベーストランザクション](https://readouble.com/laravel/7.x/ja/database.html#database-transactions)

(3) /sample/tests/Services/ImplフォルダにTable2ServiceImpl.php作成

```Tests\Services\Impl\Table2ServiceImpl.php
<?php
namespace Tests\Services\Impl;

use App\Services\Interfaces\Table2Service;

class Table2ServiceImpl implements Table2Service
{

    public function __construct()
    {
    }

    public function select($id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col)
    {
    }

    public function insert($table1_id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col)
    {
    }

    public function update($id, $table1_id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col)
    {
    }

    public function delete($id)
    {
    }

}
```

# DI登録
(1) /sample/app/Providers/DiServiceProvider.phpに下記を追記
`use App\Services\Interfaces\Table2Service;`

(2) /sample/app/Providers/DiServiceProvider.phpのregisterメソッドに下記を追記
`app()->singleton(Table2Service::class, $prefix . 'Table2ServiceImpl');`

DiServiceProvider.phpは[LaravelでDIを使う](https://qiita.com/toontoon/items/ebf53e4053be6f663730)で作成したファイルです
[LaravelでDIを使う](https://qiita.com/toontoon/items/ebf53e4053be6f663730)でconfig/app.phpのprovidersに登録してあります

# フォームリクエストの作成
 /sample/app/Http/Requests/Table2Request.php作成

```Table2Request.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class Table2Request extends FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        $method = $this->getMethod();

        $idRequire = 'nullable';
        $url = $this->url();
        $urlArray = explode("/", $url);
        $urlTail = array_pop($urlArray);
        if ($method === 'POST' && ($urlTail === 'update-query-builder' || $urlTail === 'delete-query-builder')) {
            $idRequire = 'required';
        }

        return [
            'id'               => [$idRequire, 'numeric', 'max:18446744073709551615'],
            'table1_id'        => ['nullable', 'numeric', 'max:18446744073709551615'],
            'varchar_col'      => ['nullable', 'max:255'],
            'int_col'          => ['nullable', 'integer', 'max:2147483647'],
            'datetime_col'     => ['nullable', 'date_format:Y-m-d H:i:s'],
            'date_col'         => ['nullable', 'date_format:Y-m-d'],
            'time_col'         => ['nullable', 'date_format:H:i:s'],
        ];

    }
}
```

# Controllerにメソッド追加
(1) /sample/app/Http/Controllers/SampleController.phpにuse文を追記
`use App\Http\Requests\Table2Request;`
`use App\Services\Interfaces\Table2Service;`

(2) /sample/app/Http/Controllers/SampleController.phpにselectQueryBuilderメソッド、insertQueryBuilderメソッド、updateQueryBuilderメソッド、deleteQueryBuilderメソッドを追記

```SampleController.php
    public function selectQueryBuilder(Table2Request $request, Table2Service $table2Service)
    {
        if (is_null($request->session()->get('errors'))) {
            $request->flash();
        }

        $id = $request->input('id');
        $varchar_col = $request->input('varchar_col');
        $int_col =  $request->input('int_col');
        $datetime_col =  $request->input('datetime_col');
        $date_col =  $request->input('date_col');
        $time_col =  $request->input('time_col');

        $recordList  = [];
        if ($request->getMethod() === 'POST') {
            $recordList = $table2Service->select($id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col);
        }

        $data = [
            'recordList' => $recordList
        ];

        return view('sample.select', $data);
    }

    public function insertQueryBuilder(Table2Request $request, Table2Service $table2Service)
    {
        if (is_null($request->session()->get('errors'))) {
            $request->flash();
        }

        $table1_id = $request->input('table1_id');
        $varchar_col = $request->input('varchar_col');
        $int_col =  $request->input('int_col');
        $datetime_col =  $request->input('datetime_col');
        $date_col =  $request->input('date_col');
        $time_col =  $request->input('time_col');

        $id  = null;
        if ($request->getMethod() === 'POST') {
            $id = $table2Service->insert($table1_id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col);
        }

        $data = [
            'id' => $id
        ];

        return view('sample.insert', $data);
    }

    public function updateQueryBuilder(Table2Request $request, Table2Service $table2Service)
    {
        if (is_null($request->session()->get('errors'))) {
            $request->flash();
        }

        $id = $request->input('id');
        $table1_id = $request->input('table1_id');
        $varchar_col = $request->input('varchar_col');
        $int_col =  $request->input('int_col');
        $datetime_col =  $request->input('datetime_col');
        $date_col =  $request->input('date_col');
        $time_col =  $request->input('time_col');

        $affected  = null;
        if ($request->getMethod() === 'POST') {
            $affected = $table2Service->update($id, $table1_id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col);
        }

        $data = [
            'affected' => $affected
        ];

        return view('sample.update', $data);
    }

    public function deleteQueryBuilder(Table2Request $request, Table2Service $table2Service)
    {
        if (is_null($request->session()->get('errors'))) {
            $request->flash();
        }

        $id = $request->input('id');

        $affected  = null;
        if ($request->getMethod() === 'POST') {
            $affected = $table2Service->delete($id);
        }

        $data = [
            'affected' => $affected
        ];

        return view('sample.delete', $data);
    }
```

先ほど作成したフォームリクエストを使って入力値を受け取り、先ほど作成したサービスクラスでデータベースアクセス処理を行っています
この記事では入力画面と完了画面を同じメソッドで処理していますが、これはメソッドを分けているとサンプルコードが長くなり、記事が見にくくなるので1つのメソッドで処理しているだけです
実際の開発では`if ($request->getMethod() === 'POST')`というif文など使わずにメソッドを分けましょう

(3) /sample/routes/web.phpに下記を追記
`Route::match(['get', 'post'],'sample/select-query-builder', 'SampleController@selectQueryBuilder');`
`Route::match(['get', 'post'],'sample/insert-query-builder', 'SampleController@insertQueryBuilder');`
`Route::match(['get', 'post'],'sample/update-query-builder', 'SampleController@updateQueryBuilder');`
`Route::match(['get', 'post'],'sample/delete-query-builder', 'SampleController@deleteQueryBuilder');`

# viewの作成
(1) /sample/resources/views/sample/select.blade.phpファイル作成

```select.blade.php
<html>
    <head>
        <title>sample</title>
        <style>
        .sample-table {
            border-collapse:collapse;
            white-space: nowrap;
            border: 1px solid #000000;
        }
        .sample-table thead {
            background-color: #33CCFF;
            color: #FFFFFF;
            font-weight: bold;
        }
        .sample-table td {
            padding-left:10px;
            padding-right:10px;
            border: 1px solid #000000;
        }
        </style>
    </head>
    <body>

        <form action="{{ url('sample/select-query-builder') }}" method="post">
            @csrf
            @error('id')
                @foreach ($errors->get('id') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>id<input type="text" name="id" value="{{ old('id') }}"></div>
            @error('varchar_col')
                @foreach ($errors->get('varchar_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>varchar_col<input type="text" name="varchar_col" value="{{ old('varchar_col') }}"></div>
            @error('int_col')
                @foreach ($errors->get('int_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>int_col<input type="text" name="int_col" value="{{ old('int_col') }}"></div>
            @error('datetime_col')
                @foreach ($errors->get('datetime_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>datetime_col<input type="text" name="datetime_col" value="{{ old('datetime_col') }}"></div>
            @error('date_col')
                @foreach ($errors->get('date_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>date_col<input type="text" name="date_col" value="{{ old('date_col') }}"></div>
            @error('time_col')
                @foreach ($errors->get('time_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>time_col<input type="text" name="time_col" value="{{ old('time_col') }}"></div>
            <input type="submit" >
        </form>

        <br>

        <table class="sample-table">
            <thead>
                <tr>
                    <td colspan=6>table1</td>
                    <td colspan=6>table2</td>
                    <td colspan=6>table3</td>
                </tr>
                <tr>
                    <td>id</td>
                    <td>varchar_col</td>
                    <td>int_col</td>
                    <td>datetime_col</td>
                    <td>date_col</td>
                    <td>time_col</td>
                    <td>id</td>
                    <td>varchar_col</td>
                    <td>int_col</td>
                    <td>datetime_col</td>
                    <td>date_col</td>
                    <td>time_col</td>
                    <td>id</td>
                    <td>varchar_col</td>
                    <td>int_col</td>
                    <td>datetime_col</td>
                    <td>date_col</td>
                    <td>time_col</td>
                </tr>
            </thead>
            <tbody>
                @foreach ($recordList as $record)
                <tr>
                    <td>{{ $record->table1_id }}</td>
                    <td>{{ $record->table1_varchar_col }}</td>
                    <td>{{ $record->table1_int_col }}</td>
                    <td>{{ $record->table1_datetime_col }}</td>
                    <td>{{ $record->table1_date_col }}</td>
                    <td>{{ $record->table1_time_col }}</td>
                    <td>{{ $record->table2_id }}</td>
                    <td>{{ $record->table2_varchar_col }}</td>
                    <td>{{ $record->table2_int_col }}</td>
                    <td>{{ $record->table2_datetime_col }}</td>
                    <td>{{ $record->table2_date_col }}</td>
                    <td>{{ $record->table2_time_col }}</td>
                    <td>{{ $record->table3_id }}</td>
                    <td>{{ $record->table3_varchar_col }}</td>
                    <td>{{ $record->table3_int_col }}</td>
                    <td>{{ $record->table3_datetime_col }}</td>
                    <td>{{ $record->table3_date_col }}</td>
                    <td>{{ $record->table3_time_col }}</td>
                </tr>
                @endforeach
            </tbody>
        </table>


    </body>
</html>
```

(2) /sample/resources/views/sample/insert.blade.phpファイル作成

```insert.blade.php
<html>
    <head>
        <title>sample</title>
    </head>
    <body>

        <form action="{{ url('sample/insert-query-builder') }}" method="post">
            @csrf
            @error('table1_id')
                @foreach ($errors->get('table1_id') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>table1_id<input type="text" name="table1_id" value="{{ old('table1_id') }}"></div>
            @error('varchar_col')
                @foreach ($errors->get('varchar_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>varchar_col<input type="text" name="varchar_col" value="{{ old('varchar_col') }}"></div>
            @error('int_col')
                @foreach ($errors->get('int_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>int_col<input type="text" name="int_col" value="{{ old('int_col') }}"></div>
            @error('datetime_col')
                @foreach ($errors->get('datetime_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>datetime_col<input type="text" name="datetime_col" value="{{ old('datetime_col') }}"></div>
            @error('date_col')
                @foreach ($errors->get('date_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>date_col<input type="text" name="date_col" value="{{ old('date_col') }}"></div>
            @error('time_col')
                @foreach ($errors->get('time_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>time_col<input type="text" name="time_col" value="{{ old('time_col') }}"></div>
            <input type="submit" >
        </form>

        <br>

        @isset($id)
            <div>新規id:{{$id}}</div>
        @endif

    </body>
</html>
```

(3) /sample/resources/views/sample/update.blade.phpファイル作成

```update.blade.php
<html>
    <head>
        <title>sample</title>
    </head>
    <body>

        <form action="{{ url('sample/update-query-builder') }}" method="post">
            @csrf
            @error('id')
                @foreach ($errors->get('id') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>id<input type="text" name="id" value="{{ old('id') }}"></div>
            @error('table1_id')
                @foreach ($errors->get('table1_id') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>table1_id<input type="text" name="table1_id" value="{{ old('table1_id') }}"></div>
            @error('varchar_col')
                @foreach ($errors->get('varchar_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>varchar_col<input type="text" name="varchar_col" value="{{ old('varchar_col') }}"></div>
            @error('int_col')
                @foreach ($errors->get('int_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>int_col<input type="text" name="int_col" value="{{ old('int_col') }}"></div>
            @error('datetime_col')
                @foreach ($errors->get('datetime_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>datetime_col<input type="text" name="datetime_col" value="{{ old('datetime_col') }}"></div>
            @error('date_col')
                @foreach ($errors->get('date_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>date_col<input type="text" name="date_col" value="{{ old('date_col') }}"></div>
            @error('time_col')
                @foreach ($errors->get('time_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>time_col<input type="text" name="time_col" value="{{ old('time_col') }}"></div>
            <input type="submit" >
        </form>

        <br>

        @isset($affected)
            <div>値が変更された行数:{{$affected}}</div>
        @endif

    </body>
</html>
```

(4) /sample/resources/views/sample/delete.blade.phpファイル作成

```delete.blade.php
<html>
    <head>
        <title>sample</title>
    </head>
    <body>

        <form action="{{ url('sample/delete-query-builder') }}" method="post">
            @csrf
            @error('id')
                @foreach ($errors->get('id') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>id<input type="text" name="id" value="{{ old('id') }}"></div>
            <input type="submit" >
        </form>

        <br>

        @isset($affected)
            <div>削除された行数:{{$affected}}</div>
        @endif

    </body>
</html>
```

# 動作確認
(1) `http://localhost/laravelSample/sample/insert-query-builder`

![a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238103/556cc0fb-4a8f-8ea1-e0bb-0d6063ebd22d.png)

送信ボタンをクリックするとinsertが実行されます

(2) `http://localhost/laravelSample/sample/update-query-builder`

![b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238103/5c37d60a-d23c-3f60-07ea-2b8774c7537c.png)

送信ボタンをクリックするとレコードが更新されます

(3) `http://localhost/laravelSample/sample/delete-query-builder`
![c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238103/a6d6be0e-8834-b8f8-3218-072979a27b42.png)

送信ボタンをクリックするとレコードが削除されます

(4) `http://localhost/laravelSample/sample/select-query-builder`

![d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238103/8e189335-a0a2-cd68-e0fb-6bbb87a865b9.png)

送信ボタンをクリックするとレコードがselectされます