Laravel

【Laravel】Todoアプリ作成

LaravelでTodoアプリを作成していきます。

環境構築を実施していない方はこちら

完成版はこちら

https://test.jum11.com

DockerのPHPコンテナにログイン

$ docker compose exec php bash

Laravel インストール

$ composer create-project laravel/laravel="7.*" laravel

データベースの設定

$ vi .env

Mysqlを使用する場合

DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=todo
DB_USERNAME=todo
DB_PASSWORD=todo

SQLiteを使用する場合

DB_CONNECTION=sqlite
$ cd database
$ touch database.sqlite

アプリケーションの設計

  • モデル名は「Todo」とします
  • テーブル名は「todos」になります。
  • コントローラー名は「TodosController」とします

テーブル構成

  • id
  • body:todoの内容
  • created_at
  • updated_at

※idとcreated_atとupdate_atは自動で作成されます。

実装

モデルとマイグレーションファイルの作成

$ cd .. (プロジェクトフォルダ直下に戻る)
$ php artisan make:model Todo --migration
Model created successfully.
Created Migration: 2019_12_14_015752_create_todos_table
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateTodosTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('todos', function (Blueprint $table) {
            $table->id();
+          $table->text('body');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('todos');
    }
}

不要なマイグレーションファイルの削除

$ cd database/migrations
$ls -al
total 20
drwxr-xr-x 7 root root 224 Sep 18 09:18 .
drwxr-xr-x 6 root root 192 Sep  7 14:33 ..
-rw-r--r-- 1 root root 798 Sep  7 14:33 2014_10_12_000000_create_users_table.php
-rw-r--r-- 1 root root 683 Sep  7 14:33 2014_10_12_100000_create_password_resets_table.php
-rw-r--r-- 1 root root 820 Sep  7 14:33 2019_08_19_000000_create_failed_jobs_table.php
-rw-r--r-- 1 root root 876 Sep  7 14:33 2019_12_14_000001_create_personal_access_tokens_table.php
-rw-r--r-- 1 root root 608 Sep 18 09:21 2021_09_18_091856_create_todos_table.php

$ rm  2014_10_12_000000_create_users_table.php
$ rm 2014_10_12_100000_create_password_resets_table.php
$ rm 2019_08_19_000000_create_failed_jobs_table.php
$ rm 2019_12_14_000001_create_personal_access_tokens_table.php

マイグレーションの実行

$ php artisan migrate                    
Migrating: 2021_09_18_100152_create_todos_table
Migrated:  2021_09_18_100152_create_todos_table (0.07 seconds)

ルーティングの作成

$ vi routes/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('/', function () {
//    return view('welcome');
//});

use Illuminate\Routing\Route;

Route::get('/','TodosController@index'); 
Route::resource('todos','TodosController');

コントローラーの作成

$ php artisan make:controller TodosController
$ vi 

Todosコントローラーでは、todoの一覧、新規登録処理、削除処理、更新処理を書いていきます。

<?php

namespace App\Http\Controllers;

use App\Http\Requests\TodoRequest;
use Illuminate\Http\Request;
use App\Todo;

class TodosController extends Controller
{
    public function index() {
      $todos = Todo::all();
      return view('todos.index')->with('todos',$todos);
    }

    public function store(TodoRequest $request) {
      $todo = new Todo();
      $todo->body = $request->body;
      $todo->save();
      return redirect('/');
    }

    public function destroy(todo $todo) {
        $todo->delete();
        return redirect('/');
      }

    public function edit(todo $todo) {
      return view('todos.edit')->with('todo',$todo);
    }

    public function update(TodoRequest $request,todo $todo) {
      $todo->body = $request->body;
      $todo->save();
      return redirect('/');
    }

}

リクエストバリデーターを作成

$ php artisan make:request TodoRequest
Request created successfully.
$ vi app/Http/Requests/TodoRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class TodoRequest 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 [
            'body' => 'required'
        ];
    }

    public function messages()
    {
        return [
            'body.required' => '入力してください。'
        ];
    }
}

見た目の部分(View)の作成

todo一覧ページ

$ cd resources/views
$ mkdir todos (todos関連のディレクトリを作成)
$ cd todos
$ vi index.blade.php (viewファイルの作成)
<!doctype html>
<html lang="ja">
  <head>
    <title>Jum Todoリスト</title>
  <!-- 必要なメタタグ -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
  </head>
  <body>
    <div class="container" style="margin-top:50px;">
    <h1>Todoリスト追加</h1>

    <form action='{{ url('/todos')}}' method="post">
      {{csrf_field()}}
  <div class="form-group">
    <label >やることを追加してください</label>
    <input type="text" name="body"class="form-control" placeholder="todo list" style="max-width:1000px;">
  </div>
  <button type="submit" class="btn btn-primary">追加する</button>  </form>

    <h1 style="margin-top:50px;">Todoリスト</h1>
    <table class="table table-striped" style="max-width:1000px; margin-top:20px;">
    <!-- <thead>
    <tr>
      <th></th><th></th><th></th>
    </tr>
  </thead> -->
  <tbody>
    @foreach ($todos as $todo)
    <tr>
      <td>{{$todo->body}}</td>
      <td><form action="{{ action('TodosController@edit', $todo) }}" method="post">
          {{ csrf_field() }}
          {{ method_field('get') }}
          <button type="submit" class="btn btn-primary">編集</button>
      </form>
      </td>

      <!-- 削除ボタン -->
      <td><form action="{{url('/todos', $todo->id)}}" method="post">
          {{ csrf_field() }}
          {{ method_field('delete') }}
          <button type="submit" class="btn btn-danger">削除</button>
      </form>
      </td>

      <!-- 削除した際にポップ画面で確認をする -->
      <!-- <td><a class="del" data-id="{{ $todo->id }}" href="#">削除</a>
        <form method="post" action='{{ url('/todos', $todo->id) }}' id="form_{{ $todo->id}}">
          {{ csrf_field() }}
          {{ method_field('delete') }}
        </form>
      </td> -->
    </tr>
    @endforeach
  </table>
</div>
  <!-- オプションのJavaScript -->
  <!-- 最初にjQuery、次にPopper.js、次にBootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
    <script>
    (function() {
  'use strict';

  var cmds = document.getElementsByClassName('del');
  var i;

  for (i = 0; i < cmds.length; i++) {
    cmds[i].addEventListener('click', function(e) {
      e.preventDefault();
      if (confirm('are you sure?')) {
        document.getElementById('form_' + this.dataset.id).submit();
      }
    });
  }

})();
</script>
  </body>
</html>

todo編集ページ

$ cd resources/views/todos
$ touch edit.blade.php
<!doctype html>
<html lang="ja">
  <head>
    <title>Jum Todoリスト</title>
  <!-- 必要なメタタグ -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
  </head>

  <body>
    <div class="container" style="margin-top:50px;">
    <h1>Todoリスト更新</h1>

    <form action='{{ url('/todos',$todo->id) }}' method="post">
      {{csrf_field()}}
      {{ method_field('patch')}}
  <div class="form-group">
    <label >やることを更新してください</label>
    <input type="text" name="body"class="form-control" value="{{ $todo->body }}" style="max-width:1000px;">
  </div>
  <button type="submit" class="btn btn-primary">更新する</button>
</form>

</div>
  <!-- オプションのJavaScript -->
  <!-- 最初にjQuery、次にPopper.js、次にBootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
    <script>
    (function() {
  'use strict';

  var cmds = document.getElementsByClassName('del');
  var i;

  for (i = 0; i < cmds.length; i++) {
    cmds[i].addEventListener('click', function(e) {
      e.preventDefault();
      if (confirm('are you sure?')) {
        document.getElementById('form_' + this.dataset.id).submit();
      }
    });
  }

})();
</script>
  </body>
</html>

完成です!!