作成日:2021/04/12 更新日:2022/06/24

#9 LaravelでAjaxのデータを登録する方法

①以前のデータの確認

続いて新規登録処理の実装に移りましょう。
以前作成した「index.php」の下記の部分です。
スクラッチで作ったPHPの一部です。

/* 移動したようその座標をデータベースへ登録 */
if(!empty($_POST['left'])){
  try{
    $sql  = 'UPDATE `sortable` SET `left_x` = :LEFT, `top_y` = :TOP WHERE `id` = :NUMBER';
    $stmt = $dbh->prepare($sql);
    $stmt->bindValue(':LEFT'  , $_POST['left'], PDO::PARAM_INT);
    $stmt->bindValue(':TOP'   , $_POST['top'],  PDO::PARAM_INT);
    $stmt->bindValue(':NUMBER', $_POST['id'],   PDO::PARAM_INT);
    $stmt->execute();
  } catch (PDOException $e) {
    echo $e->getMessage();
  }
}
PHPでデータを送信するロジック部分

<script>
$(function(){
  $('.drag').draggable({
    containment:'#drag-area',
    cursor:'move',
    opacity:0.6,
    scroll:true,
    zIndex:10,
    /* ==========STOP処理====================================== */
    stop:function(event, ui){
      var myNum  = $(this).data('num');
      var myLeft = (ui.offset.left - $('#drag-area').offset().left);
      var myTop  = (ui.offset.top  - $('#drag-area').offset().top);
      /* ==========AJAX通信================= */
      $.ajax({
        type:'POST',
        url :'http://localhost:8001/',
        data: {
          id  :myNum,
          left:myLeft,
          top :myTop
        }
      }).done(function(){
         console.log('成功');
      }).fail(function(XMLHttpRequest, textStatus, errorThrown){
         console.log(XMLHttpRequest.status);
         console.log(textStatus);
         console.log(errorThrown);
      });
      /* ==========/AJAX通信================= */
        console.log("左: " + myLeft);
        console.log("上: " + myTop);
    }
    /* ==========/STOP処理====================================== */
  });
});
</script>
js部分

②Ajaxの修正とCSRF対策について。main.jsの修正

Ajaxの通信先をLaravelのアプリケーションに変更する必要があります。

$.ajaxSetup({  /* ①-追加 */
  headers: {
    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
  }
});
$.ajax({
  type:'POST',
  url: '/update',  /* ②-変更 */
  data: {
    id  :myNum,
    left:myLeft,
    top :myTop
  }
}).done(function(){
main.jsを上記の様に修正しましょう。

①.ajaxのリクエスト通信にheadersを追加し、CSRFに関する記述をする

CSRFクロス サイト リクエスト フォージェリとは悪意のある攻撃(エクスプロイト)のことです。
LaravelではPOSTされたデータやHTMLフォームから来たデータがCSRF攻撃だった場合に、それを防ぐ仕組みが用意されています。
TOKENトークンとは暗号鍵やワンタイムパスワードなどを生成する仕組みのことです。
POSTやHTMLフォームからデータを受ける場合は、隠しCSRFトークンフィールドをmetaタグやフォームに埋め込み、データの有効性をチェックするようにしましょう。

urlの値を「/update」へ変更する

以前は「localhost:8001」を指定していた部分です。あとでルーティングファイル「web.php」を修正し、「/update」が呼ばれたらControllerの更新処理を呼び出すように設定します。

Ajaxのheadersに渡すためのCSRFトークンをHTMLに記述する


<meta name="csrf-token" content="{{ csrf_token() }}">
sortable.blade.php内のmetaタグ、content属性にcsrf_token()メソッドを入れておきます。

③Controllerに更新処理を追記する。

コントローラーのSortableController.phpに記述してあるregister()メソッドの下に、
新たに update()メソッド を追記します。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Sortable;

class SortableController extends Controller
{
    public function index(){
      $sortable = Sortable::orderBy('id', 'asc')->get();

      return view('sortable', [
        'sortables' => $sortable
      ]);
    }

    public function register(Request $request){
      $sortable = new Sortable;
      $sortable->name = $request->inputName;
      $sortable->save();

      return redirect('/');
    }
    /* ↓↓↓ここに追記↓↓↓ */
    public function update(Request $request){
      $inputs   = $request->all();
      $s        = new Sortable;
      $sortable = $s->find($inputs['id']);
      $sortable->left_x = $inputs['left'];
      $sortable->top_y  = $inputs['top'];
      $sortable->save();

      return redirect('/');
    }
}
手順としては
・Ajax通信で仮想サーバへ飛んできたHTTPRequestの内容を取得
・requestの中からidを取り出し、テーブルから該当するレコードを探しに行く
・該当するレコードのleft_xとtop_yカラムの値を更新し、更新登録を完了する。
・初期画面へリダイレクトする。

▼解説

$inputs = $request->all();
$inputsにAjax通信で飛んできたrequestの内容を代入する。
Ajaxで送信した内容はHTTPRequestという形でwebサーバ(MAMPの仮想サーバ)に入ってきます。
そのrequestの中に、Ajaxで格納したdata(idやleft_x、top_yの座標)が入っているので、その値を取得するために上記の様な処理を行います。

$sortable = $s->find($inputs['id']);
idをキーに更新対象のレコードを特定する。
find()メソッド$sortableの中から該当するidを持っているレコードを探します。
※「$sortable」はModelの「Sortable.php」の内容を持っています。

$sortable->left_x = $inputs['left'];
$sortable->left_y = $inputs['top'];
$sortable->save();
データベース内の更新対象カラム「left_x」「top_y」に、更新する値を渡しsave()メソッドで更新登録する。

$sortable = $s->find($inputs['id']);
初期画面へリダイレクトする。

④ルーティングファイルweb.phpを修正


<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', 'SortableController@index');
Route::post('/register', 'SortableController@register');
Route::post('/update', 'SortableController@update');
URLで「/update」が呼ばれたときに、Controllerのupdateメソッド()を呼び出すようにします。 /updateというURLは先程記述した、Ajax通信が発生する際に呼ばれるURLです。
マウスで移動したとき、console.logで”成功”が出れば完成です。

完成したLarabelアプリの図解

【前提】
  MAMP、Homebrew、Composer、VS Code、ブラウザ をインストールしておく
【初期設定】
 ・開発環境フォルダを作成
 ・フォルダに対しMAMPのhttpd-vhosts.confでバーチャルホストを設定
 ・ターミナルでComposerを使ってLaravel本体をインストールする
 ・Laravelの.envファイルをMAMP環境に編集
 ・Laravelの/config/database.phpを.envの内容に編集
 ・phpMyAdminでデータベースを作成
 ・ターミナルを使ってartisanコマンドでLaravelのマイグレーションファイルを作成
 ・Laravelの/database/migration/にあるマイグレーションファイルを編集
 ・ターミナルを使ってartisanコマンドでマイグレーションを実行(DBにテーブルができる)

#10 Laravelでアプリを作ってみよう

応用課題として自分用の「タスク管理ツール」を作成してみましょう。

レイアウト部分のHTMLとCSSは用意しました。後ほど使うのでダウンロードしておきましょう。
HTML&CSSデータ ダウンロード
まずは学習してきた流れ通り、Laravelプロジェクトを作成するところから始めましょう。
ここでのLaravelのバージョンは8.75.0、PHPは7.4.1とします。

前準備

開発ディレクトリを作成


「8027-MyTASK」という名称にしました

開発ディレクトリにLaravelプロジェクト「MyTASK」を作成

ComposerでLalavelをインストールしましょう。
ここではプロジェクト名を「MyTASK」にしました。

cd 8027-MyTASK [enterkey]
composer create-project laravel/laravel MyTASK --prefer-dist

開発ディレクトリをMAMPでバーチャルホストを設定

MAMPでポート番号のバーチャルホストを設定します。#5 Laravelの環境構築を参考にしましょう。
設定後にMAMPを再起動はお忘れなく。

Listen 8027
<virtualhost *:8027>
  DocumentRoot "/Users/ユーザ名/job/stg/8027-MyTASK/MyTASK/public"
</virtualhost>

Laravel環境設定

①.envファイルのデータベース情報の設定
②/config/database.phpで.env情報を設定
Laravelの環境設定を変更する .env ファイルを参考にしましょう。

DB_HOST=localhost
DB_PORT=8889
DB_DATABASE=cbc_mytask
DB_USERNAME=root
DB_PASSWORD=root
もし、DB名を間違えて起動した場合「php : Laravel SQLSTATE [HY000] [1049]不明なデータベース」と出ることがあります。その場合は、

$ php artisan config:cache
で、リフレッシュしてもう一度アクセスしてみましょう。

データベースを作成し、マイグレーションを使ってテーブル、カラムを作成

ここではデータベースcbc_mytaskを作成し、
テーブル名をtasks、カラムにidとタスクを入れるtaskというカラムを作りました。

データベース名を「cbc_mytask」とします。

$ php artisan make:migration create_tasks_table --create=tasks
マイグレーション設定のコマンドを実行。

public function up()
{
  Schema::create('tasks', function (Blueprint $table) {
      $table->integer('id')->autoIncrement();
      $table->text('task')->nullable(false);
  });
}
マイグレーションファイルでは、idにはオートインクリメント、メモを入れるテキストデータをtaskという名前のカラムを作ります。

$ php artisan migrate
マイグレーション実行

データベースが作成できました

Modelを作る

#7 ③データを取得するモデルSortable.phpを作るを参照してみてください。
ここではモデル名をTaskとしました。

$ php artisan make:model Task
アルチザンコマンドでTaskモデル(Task.php)を/app/Models/ディレクトリに作ります。
ファイルの編集ですが今回も日付記録のカラムがないので、Task.php内のTaskクラスに、タイムスタンプfalseの設定を追記しておきましょう。

public $timestamps = false;  /* 追加 */

Controllerを作る

コントローラーの理解

今回のアプリで行う処理は主に3つです。
1.初期表示(SELECT)・・・テーブルから全てのデータを取得し、タスク表示箇所へ反映する。
2.登録(INSERT)・・・入力されたタスク名をテーブルに登録する。
3.完了(DELETE)・・・完了ボタンが押されたら、そのタスクに該当するレコードをテーブルから削除する。(物理削除する)

「3.完了」の動作は、ajax通信で実装してください。通信方法はPOSTにしてください。
(DELETEという通信方法もありますが、今回はPOSTで作りましょう)

また、完了ボタンを押下した際に、本当にそのタスクを完了してよいかの確認ができる様に、アラート機能をjsで実装をしてください。 ajaxの通信が始まる前に記述しましょう。googleでjs 確認ダイアログ コンファームなどのキーワードで検索してみましょう。
いいえが押された場合は、削除処理が進まないようにします。

コントローラーを作る

TaskController.phpファイルを作ります。

$ php artisan make:controller TaskController
アルチザンコマンドで/app/Http/Controllers/ディレクトリにTaskController.phpを作ります。

コントローラーに表示、登録、削除のメソッドを作成する

TaskControllerクラス内で、テーブル全件取得と、登録、削除のメソッドを作成します。

class TaskController extends Controller
{
  public function テーブル全件取得メソッド(){
    処理
  }
  public function 登録メソッド(){
    処理
  }
  public function 削除メソッド(){
    処理
  }
}
登録のメソッドは#7 ④コントローラーのSortableController.phpを作るを参照しましょう。
削除のメソッドはLaravelマニュアル モデル削除 を見ながら作りましょう。
またgoogleなどの検索エンジンで「Laravel 削除メソッド」で探してみましょう。

削除するときは、データベースを取得しているクラス「Task」にアクセスし、特定のidを特定して、削除するメソッドを実行します。 クラスにはスコープ演算子を使ってアクセスしてみましょう。

Class::静的プロパティ(変数)、Class::静的メソッド(関数)
「::(ダブルコロン)」はスコープ演算子
Class外部からClassをスコープ(範囲指定)して静的なプロパティ、メソッドにアクセスする。

(例)
class Aisatsu
{
  public static $hello = 'おはよう';
  public static function bye(){
    return 'ばいばい';
  }
}

echo Aisatsu::$hello;
echo Aisatsu::bye();
答え

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Task; /* 名前空間を設定します */

class TaskController extends Controller
{
  public function index(){                      /* indexメソッドを追加します */
    $task = Task::orderBy('id', 'asc')->get();  /* Taskクラスを使ってtasksテーブル全件取得を指示 */

    /* task.blade.phpに対し(第1引数)、taskカラムのデータをtasksに渡す(第2引数) */
    return view('task', [
      'tasks' => $task
    ]);
  }

  public function register(Request $request){
    $task = new Task;
    $task->task = $request->task;
    $task->save();
    return redirect('/');
  }

  public function delete(Request $request){
    $task = Task::find($request->id);  /*Taskクラスを使って、ajaxで渡ってきたidをセットする*/
    $task->delete();                   /*指定されたidを削除する*/
    /* $task = Task::find($request->id)->delete(); //これでも可 */
    return redirect('/');
  }
}

web.phpでルーティングを設定する

/routes/web.phpでルートURLが呼ばれたら、Controllerのindex()メソッドの処理を実行させます。
#7 ⑤ルーティングのweb.phpを修正するを参考にしましょう。

/* Route::get('/', function () {
     return view('welcome');
}); */
Route::get('/',          'App\Http\Controllers\TaskController@index');
Route::post('/register', 'App\Http\Controllers\TaskController@register');
Route::post('/delete',   'App\Http\Controllers\TaskController@delete');

bladeテンプレートでViewを作る

ダウンロードした過去制作のindex.phpを編集しながら、3つの機能ごとにファイルを分割しよう

機能の部品であるregists.blade.phpとmyTasks.blade.phpはlayoutsディレクトリに入れましょう。
テンプレートファイルtask.blade.phpは/resources/views/ディレクトリに入れましょう。
CSSやJSファイルは公開フォルダの/public/ディレクトリ内に入れます。
tasks.blade.phpに2つの機能ファイルを@includeさせましょう。

登録する部分は、index.htmlの「formタグ」部分をコピーして、#8 ②作ったregistrations.blade.phpを参考に、 作ってみましょう。
表示する部分は、index.htmlの「ループさせて表示させる」部分を探し出して、 #7 ②作ったdrags.blade.phpを参考に、作ってみましょう。
cssやjsを読み込ませる際にも{{ asset(***/***.***) }}で読み込ませましょう。
答え

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="csrf-token" content="{{ csrf_token() }}">
  <title>MyTasks</title>
  <link href="{{ asset('css/reset.css')}}" rel="stylesheet">
  <link href="{{ asset('css/style.css')}}" rel="stylesheet">
</head>
<body>
  <div id="wrap" class="wrap">

    <div class="task">
      <!-- タスク作成エリア -->
      <div class="task__new">
        <h2>新しいタスクを作成</h2>
        @include('layout.regists')
      </div>

      <!-- タスク一覧表示エリア -->
      <div class="task__area">
        <h2>わたしのタスク一覧</h2>
        <ul class="task__area-list">
          @include('layout.myTasks')
        </ul>
      </div>
    </div>

  </div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js"></script>
<script src="{{ asset('js/main.js')}}"></script>
</body>
</html>
/resources/views/task.blade.php


<form action="{{ url( 'register' )}}" method="POST">
  {{ csrf_field() }}
  <input type="text" name="task" maxlength="30" placeholder="タスクを入力してください">
  <input type="submit" value="追加">
</form>
/resources/views/layout/regists.blade.php


@foreach($tasks as $task)
  <li>
    <p>{{ $task->task }}</p>
    <button class="delete_btn" data-id="{{ $task->id }}">完了</button>
  </li>
@endforeach
/resources/views/layout/myTasks.blade.php

ajaxの通信ロジックを作る


$(function(){
  $('.delete_btn').on('click', function(){
    var Id = $(this).data('id');
    /*console.log(Id);*/
    if(confirm("タスクを完了してよいでしょうか?")){
      /* ==========AJAX通信================= */
      $.ajaxSetup({
        headers: {
          'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        }
      });
      $.ajax({
        type:'POST',
        url: '/delete',
        data: {
          id: Id
        }
      }).done(function(){
         /*console.log('成功');*/
         location.reload();
      }).fail(function(XMLHttpRequest, textStatus, errorThrown){
         console.log(XMLHttpRequest.status);
         console.log(textStatus);
         console.log(errorThrown);
      });
      /* ==========/AJAX通信================= */
    }
  });
});

実際に動かしてみよう!

Laravelのデバッグバーをインストールする

Controllerの設定あたりでエラーが出ることが増えると思います。 そんなときはLaravelのデバッグツールを使ってエラーの原因を探ることができます。

$ composer require barryvdh/laravel-debugbar
アプリフォルダである「MyTASK」まで移動しcomposerでインストールします。

APP_DEBUG=true
.envファイルのAPP_DEBUGの値がtrueにっていることを確認。

$ php artisan config:cache
表示されないときはキャッシュをクリアしてみましょう。

また、Laravel8のマニュアル を見て基本的な動作を習得しましょう。