Laravel 6.x 非同期通信(Ajax) 【JavaScript】 【jQuery】 【axios】 【Vue.js】 各記述方法 ～事前準備編～

#制作環境

Windows 10
Laravel : 6.18.35
Laravel/ui : 1.0
Laravel-mix : 5.0.1
Bootstrap : 4.0.0
axios : 0.19
Vue : 2.5.17
XAMPP
PHP : 7.4.3
Visual Studio Code

#はじめに

この記事はプログラミングをはじめたばかりの素人が、できたことをメモするのに利用しています。
内容には誤りがあるかもしれません。

#関連記事

[Laravel 6.x 非同期通信(Ajax) 【JavaScript】 【jQuery】 【axios】 【Vue.js】 各記述方法 ～JavaScript編～](https://qiita.com/Charry/items/f605cbb686e8c0796450)
[Laravel 6.x 非同期通信(Ajax) 【JavaScript】 【jQuery】 【axios】 【Vue.js】 各記述方法 ～jQuery編～](https://qiita.com/Charry/items/74c66aea1a82070a6d3d)
[Laravel 6.x 非同期通信(Ajax) 【JavaScript】 【jQuery】 【axios】 【Vue.js】 各記述方法 ～JavaScript × axios編～](https://qiita.com/Charry/items/ffd6db72de2c2be96e43)
[Laravel 6.x 非同期通信(Ajax) 【JavaScript】 【jQuery】 【axios】 【Vue.js】 各記述方法 ～jQuery × axios編～](https://qiita.com/Charry/items/0241c1b6a903c42cd9de)
[Laravel 6.x 非同期通信(Ajax) 【JavaScript】 【jQuery】 【axios】 【Vue.js】 各記述方法 ～Vue.js × axios編～](https://qiita.com/Charry/items/7e932ce730a4d4c7e737)
[Laravel 6.x 非同期通信(Ajax) 【JavaScript】 【jQuery】 【axios】 【Vue.js】 各記述方法 ～JavaScript × FetchAPI編～](https://qiita.com/Charry/items/8297146146a6b729c8f6)
[Laravel 6.x 非同期通信(Ajax) 【JavaScript】 【jQuery】 【axios】 【Vue.js】 各記述方法 ～jQuery × FetchAPI編～](https://qiita.com/Charry/items/9f22f61e5ef455b83ecb)

#作成するもの

商品の登録ができるだけの簡単なページを作成します。
その際業者コードを入力すると、非同期通信(Ajax)で仕入れ先の名前が自動で入力できるようにします。
ただし商品の登録時に業者コードが入力されていなくても、仕入れ先が手動で入力されていれば問題なく登録できるものとします。

非同期通信の記述を学ぶのが主な目的の為、最低限必要だと思う記述しかしていません。

#完成イメージ

![image.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/760252/ee8eaa3e-55d8-1535-0f2b-882901b64364.jpeg)

登録した商品は下に一覧で表示される。
やりたい事は、業者コード(4桁)を入力すると仕入れ先が自動で反映されるようにする。

![image2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/760252/07a5f012-950a-aef3-12a2-8a8b39cc91fd.jpeg)

#はじめる前に

当方の環境では`Bootstrap`と`Vue.js`を事前に`laravel/ui`でインストールし、`Laravel-mix`で読み込むようにしてあります。
インストールは事前に行っておいて下さい。
CDNを利用される場合は、`<script src="{{ mix('js/app.js') }}"></script>`の部分をCDNで置き換えてください。

#事前準備

非同期通信の処理の前に、必要なデータベースのテーブルやフォームを作成します。
<font color="red">※仕入れ先を新規登録するフォームの作成はしません。
また、事前準備の内容についてはあまり詳しく触れません。</font>

##データベース

データベースには以下の3つのテーブルを作成します。

<font color="blue">**items テーブル**</font>
商品用のテーブルです。

|id|name|supplier|created_at|updated_at|
|:---|:---|:---|:---|:---|
|1|りんご|(株)くだもの|2020-11-06 18:21:21|2020-11-06 18:21:21|
|2|きゅうり|(株)やさい|2020-11-06 18:21:21|2020-11-06 18:21:21|
|3|牛肉|(株)肉|2020-11-06 18:21:21|2020-11-06 18:21:21|
|4|サンマ|魚組合|2020-11-06 18:21:21|2020-11-06 18:21:21|
|5|グレープフルーツ|(株)くだもの|2020-11-06 18:21:21|2020-11-06 18:21:21|
|6|ヒラメ|魚組合|2020-11-06 18:21:21|2020-11-06 18:21:21|
|7|豚肉|(株)肉|2020-11-06 18:21:21|2020-11-06 18:21:21|
|8|ラム肉|(株)肉|2020-11-06 18:21:21|2020-11-06 18:21:21|
|9|白菜|(株)やさい|2020-11-06 18:21:21|2020-11-06 18:21:21|

<font color="blue">**prices テーブル**</font>
価格のテーブル

|id|item_id|price|created_at|updated_at|
|:---|:---|:---|:---|:---|
|1|1|180|2020-11-06 18:21:21|2020-11-06 18:21:21|
|2|2|98|2020-11-06 18:21:21|2020-11-06 18:21:21|
|3|3|580|2020-11-06 18:21:21|2020-11-06 18:21:21|
|4|4|240|2020-11-06 18:21:21|2020-11-06 18:21:21|
|5|5|145|2020-11-06 18:21:21|2020-11-06 18:21:21|
|6|6|280|2020-11-06 18:21:21|2020-11-06 18:21:21|
|7|7|480|2020-11-06 18:21:21|2020-11-06 18:21:21|
|8|8|780|2020-11-06 18:21:21|2020-11-06 18:21:21|
|9|9|280|2020-11-06 18:21:21|2020-11-06 18:21:21|

<font color="blue">**traders テーブル**</font>
仕入れ先業者のテーブルです。

|id|trader_code|trader_name|created_at|updated_at|
|:---|:---|:---|:---|:---|
|1|1001|(株)くだもの|2020-11-06 18:21:21|2020-11-06 18:21:21|
|2|1002|(株)やさい|2020-11-06 18:21:21|2020-11-06 18:21:21|
|3|1003|(株)肉|2020-11-06 18:21:21|2020-11-06 18:21:21|
|4|1004|魚組合|2020-11-06 18:21:21|2020-11-06 18:21:21|

<font color="red">itemsとpricesは1対1の関係なので、本来1つにまとめればいいと思いますが、練習の為に1対1のままにしてあります。</font>

##マイグレーションファイルの作成

プロジェクトのディレクトリでターミナルを起動し、以下を実行してください。

```PHP
php artisan make:migration create_items_table
```

続けて、

```PHP
php artisan make:migration create_prices_table
```

続けて、

```PHP
php artisan make:migration create_traders_table
```

完了後、`database>migrations`内の`xxxx_xx_xxx_xxxxxx_create_items_table.php`を開き、以下のように編集します。

```PHP:create_items_table.php
    public function up()
    {
        Schema::create('items', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name')->comment('商品名');
            $table->string('supplier')->comment('仕入れ先');
            $table->timestamps();
        });
    }
```

次に同じディレクトリ内の`xxxx_xx_xxx_xxxxxx_create_prices_table.php`を開き、以下のように編集します。

```PHP:create_prices_table.php
    public function up()
    {
        Schema::create('prices', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('item_id')->comment('商品ID');
            $table->foreign('item_id')->references('id')->on('items')->onDelete('cascade');
            $table->unsignedBigInteger('price')->comment('価格');
            $table->timestamps();
        });
    }
```

次に同じディレクトリ内の`xxxx_xx_xxx_xxxxxx_create_traders_table.php`を開き、以下のように編集します。

```PHP:create_traders_table.php
    public function up()
    {
        Schema::create('traders', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('trader_code')->unique()->index()->comment('業者コード');
            $table->string('trader_name')->comment('業者名');
            $table->timestamps();
        });
    }
```

##モデルの作成

ターミナルを起動し、以下を実行してください。

```PHP
php artisan make:model Models/Item
```

<font color="red">※モデルはModelsフォルダの中に作成するようにしています。</font>

続けて、

```PHP
php artisan make:model Models/Price
```

続けて、

```PHP
php artisan make:model Models/Trader
```

完了後、`app>Models`内の`Item.php`を開き以下のように編集します。

```PHP:Item.php
use Illuminate\Database\Eloquent\Relations\HasOne;

class Item extends Model
{
    protected $fillable = ['name', 'supplier'];

    public function price(): HasOne
    {
        return $this->hasOne('App\Models\price');
    }
}
```

次に`Price.php`を開き以下のように編集します。

```PHP:Price.php
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Price extends Model
{
    protected $fillable = ['price'];


    public function item(): BelongsTo
    {
        return $this->belongsTo('App\Models\item')->withTimestamps();
    }
}
```

次に`Trader.php`を開き以下のように編集します。

```PHP:Trader.php
class Trader extends Model
{
    protected $fillable = ['trader_name', 'trader_code'];
}
```

##ビューの作成

`resources>views`内に新しく`index.blade.php`を作成し、内容を以下のように編集します。

```PHP:index.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <link rel="stylesheet" href="{{ mix('css/app.css') }}">
    <title>テスト</title>
</head>
<body>

    <div id="app">
        <div class="container">

            <h1 class="mt-5 mb-3">商品登録</h1>

            @if (session('success'))
                <p class="text-primary">{{ session('success') }}</p>
            @endif

            @if (count($errors) > 0)
                <div>
                    <ul>
                        @foreach ($errors->all() as $error)
                            <li class="text-danger">{{ $error }}</li>
                        @endforeach
                    </ul>
                </div>
            @endif

            <form action="/form" method="post">
                @csrf
                <div>
                    <label>商品名: <input type="text" name="name"></label>
                </div>

                <div>
                    <label>価格: <input type="text" name="price"></label>
                </div>

                <div>
                    <label>業者コード: <input type="text" id="code"></label>
                    <label>仕入れ先: <input type="text" id="supplier" name="supplier"></label>
                </div>

                <button class="btn btn-primary" type="submit">登録</button>

            </form>

            <h1 class="mt-5">商品一覧</h1>

            <table class="table">

                <thead class="thead-dark">
                    <tr>
                        <th scope="col">商品名</th>
                        <th scope="col">価格</th>
                        <th scope="col">仕入れ先</th>
                    </tr>
                </thead>

                <tbody>
                    @foreach ($itemTable as $item)
                    <tr>
                        <td>{{ $item->name }}</td>
                        <td>{{ $item->price->price }}</td>
                        <td>{{ $item->supplier }}</td>
                    </tr>
                    @endforeach
                </tbody>

            </table>

        </div>
    </div>
    <script src="{{ mix('js/app.js') }}"></script>

</body>
</html>
```

##コントローラの作成

ターミナルで以下を実行してください。

```PHP
php artisan make:cotroller FormController
```

完了後、app>Http>Controllers内のFormController.phpを開き、以下のように編集します。

```PHP:FormController.php
use App\Models\Item;
use App\Models\Trader;
use App\Http\Requests\TestFormRequest;
use DB;

class FormController extends Controller
{
    public function index()
    {
        $itemTable = Item::with('price')->get();
        return view('index', compact('itemTable'));
    }

    public function post(TestFormRequest $request)
    {
        DB::transaction(function () use ($request) {

            $form = $request->all();
            unset($form['_token']);

            $itemTable = new Item;
            $itemTable->fill($form)->save();

            $priceData = ['price' => $form['price']];

            $itemTable->price()->create($priceData);
        });

        return redirect('/form')->with('success', '★★商品を登録しました★★');
    }
}
```

###<font color="red">コントローラの記述について(2020/12/05追記)</font>

コントローラの以下の記述ですが、

```PHP:FormController.php
$itemTable = Item::with('price')->get();
```

`->get()`を`->first()`とした方が、スクリプトの記述を少しだけシンプルに書くことができます。

<font color="red">**変更後**</font>

```PHP:FormController.php
$itemTable = Item::with('price')->first();
```

記述を上記に変更することで、スクリプトの指定の記述を以下のように変更することができます。

<font color="blue">**変更部分**</font>

```JavaScript
data[0].trader_name
```

<font color="red">**変更後**</font>

```JavaScript
data.trader_name
```

`[0]`がいらなくなります。


####->get()と->first()の違い

`->get()`だと複数の値を取得することを想定しているため、たとえ１つのレコードのデータを取得したい場合でも、複数のデータ取得と同じ形でデータが取得されます。


<font color="blue">**イメージ**</font>

取得されるデータが複数(かなり簡易的に記述してます)

```PHP
[0] => {取得されたデータ}
[1] => {取得されたデータ}
[2] => {取得されたデータ}
[3] => {取得されたデータ}
```

取得されるデータが１つ

```PHP
[0] => {取得されたデータ}
```

この様に[0]番目(1番目)に取得されたデータ、[1]番目(2番目)に取得されたデータと個別に分かれる形になり、1レコードのデータを取得する際は何番目([])のデータなのかを指定するか、foreach等で1レコード単位で抜き出す必要があります。
これは例え1レコード分のデータでも同じです。
その為valueに値を代入する際に、[0]番目の値を代入するように指定してます。


`->first()`だと抜き出した最初の値だけを取得するので、1レコードのデータを取得したらそこで終了、つまり最初から1つしか取得しないので、[]番目に取得されたデータという指定が必要ありません。

<font color="blue">**イメージ**</font>

取得されるデータは最初から1つ(かなり簡易的に記述してます)

```PHP
{取得されたデータ}
```

1レコードのデータを取得する際は`->first()`で取得する方がいいです。

##フォームリクエストの作成

バリデーションの為にフォームリクエストを作成します。

ターミナルで以下を実行してください。

```PHP
php artisan make:request TestFormRequest
```

完了後、`app>Http>Requests`内の`TestFormRequest.php`を開き、以下のように記述します。

```PHP:TestFormRequest.php
class TestFormRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name'     => 'required',
            'price'    => 'required|integer|regex:/^[\d]+$/',
            'supplier' => 'required',
        ];
    }

    public function messages()
    {
        return [
            'name.required'  => ':attributeは必ず入力してください。',
            'price.required' => ':attributeは必ず入力してください。',
            'price.integer'  => ':attributeは整数を入力してください。',
            'price.regex'    => ':attributeは整数を入力してください。',
            'supplier'       => ':attributeは必ず入力してください。',
        ];
    }

    public function attributes()
    {
        return [
            'name'     => '商品名',
            'price'    => '価格',
            'supplier' => '仕入れ先',
        ];
    }
}
```



##ルーティングの作成

`routes`内の`web.php`を開き、以下のように記述します。

```PHP:web.php
Route::get('/form', 'FormController@index')->name('index');
Route::post('/form', 'FormController@post')->name('post');
```

##確認

`/form`にアクセスし、商品が登録できるかやってみてください。
商品が登録され、下の一覧に表示されればOKです。
まだ仕入れ先の自動入力は実装していないので、仕入れ先は手動で入力してください。
また、仕入れ先を4桁(1001)で直接登録しておいてください。

次回につづく・・・