20201112のlaravelに関する記事は5件です。

【Laravel】超基礎☆簡単タスク管理アプリのチュートリアル

(※ この記事は、Laravel8が出るより結構前に書いて、他のブログに載せていました。それを転載したものなので、Laravel7で解説しています。ご了承くださいませ。。(m´・ω・`)m ゴメン…)

環境:XAMPP

初めてLaravelで何かアプリを作ってみようと思って、公式ドキュメントにも載っている簡単なタスク管理アプリを作ることにしました!
自分への備忘録も兼ねて、チュートリアル的な感じで解説していこうと思います。同じく初Laravel製アプリを作ろうとしている方や、Laravel勉強中の初学者の方に参考になれば幸いです!

完成品は以下のような感じです。↓↓

pic_1

新規追加でタスクを入力すると、タスク一覧に表示されて、削除ボタンを押すと削除されます。↓↓

pic_2

それでは早速スタート!

1: プロジェクトの作成

XAMPPのインストール後、C:\xampp\htdocs に移動して、composerでプロジェクトを作成します。プロジェクト名は『basic-task』にします。

composer create-project --prefer-dist laravel/laravel basic-task

※ 因みに、現在は上記のコマンドだと最新の8系がインストールされるので、7系にする場合は、以下コマンドのようにバージョン指定が必要です。

composer create-project --prefer-dist laravel/laravel basic-task "7.*"

プロジェクトを作成したら、cd コマンドでプロジェクト内(C:\xampp\htdocs\basic-task)に移動します。因みに今回のLaravelのバージョンを確認したら下記のようになりました。

php artisan --version //Laravelバージョン確認用コマンド
Laravel Framework 7.11.0

2: データベースの作成

XAMPPでデータベースを作成します。(XAMPPでのデータベースの作り方がわからない人は、検索してみてください。) データベース名は『basic_task』にします。
データベースを作ったら、C:\xampp\htdocs\basic-task\.env ファイルに移動して、 DB_DATABASE を『basic_task』に変更します。DB_USERNAME と DB_PASSWORD はそのままにします。(XAMPP側でデータベース ユーザーを設定した人は、こちらも変更してください。)

basic-task\.env
DB_DATABASE=basic_task // ←ここ変更。
DB_USERNAME=root
DB_PASSWORD=

3: ロケールの設定

C:\xampp\htdocs\basic-task\config\app.php ファイルへ移動します。
'timezone' と 'locale' を以下のように変更します。

basic-task\config\app.php
'timezone' => 'Asia/Tokyo',
'locale' => 'ja',

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

マイグレーションファイル (データベーステーブル)を作成します。テーブル名は『tasks』にします。 --create=tasks オプションをつけると、作成したマイグレーションファイルに対応したテーブルを作ってくれます。(XAMPP側でいちいちテーブルを作らなくてもOK!)

php artisan make:migration create_tasks_table --create=tasks

5: マイグレーションファイルの更新

作成したマイグレーションファイルを更新します。
C:\xampp\htdocs\basic-task\database\migrations\2020_05_19_021913_create_tasks_table.php ファイルへ移動します。(2020_05_19_021913 の部分は、作成した日付ですので、作成日によって変わります。)今回は極シンプルなアプリということもあり、nameカラムの追加のみです。18行目あたりに $table->string('name'); だけを追記します。

basic-task\database\migrations\2020_05_19_021913_create_tasks_table.php
    public function up()
   {
       Schema::create('tasks', function (Blueprint $table) {
           $table->id();
           $table->string('name'); //ここを追記。
           $table->timestamps();
       });
   }

追記したらマイグレーションします。

php artisan migrate

tasksテーブルに、nameカラムが入っていることを確認します。

image.png

6: モデルの作成

マイグレーションファイルに対応するモデルを作ります。
モデル名は『Task』にします。

php artisan make:model Task

C:\xampp\htdocs\basic-task\app\Task.php ファイルにモデルが作成されます。

7: ルートの設定

お次はルートの設定をします。C:\xampp\htdocs\basic-task\routes\web.php ファイルへと移動します。以下3つのルートを設定します。

①トップページへアクセスしたときに表示されるページ。(getリクエスト)
②新規追加フォームで追加ボタンを押した(=submitした)時にデータを飛ばすページ(postリクエスト)
※ フォームから飛んでくるデータはRequest型で渡ってきます。その為、コールバック関数の引数には、Implicit Bindingを利用して、(Request $request) とします。この辺りがよくわからない方は、『Laravel フォーム Request Implicit Binding』等で検索してみてください。
③削除ボタンを押した時にデータを飛ばすページ(deleteリクエスト)
以上の3つです。

basic-task\routes\web.php
Route::get('/', function () {
   //
});
Route::post('/task', function (Request $request) {
   //
});
Route::delete('/task/{task}', function () {
   //
});

8: BootstrapとFont Awesomeのインストール

viewページ作成の前に、BootstrapとFont Awesomeのインストールをしておきます。まずはBootstrapのインストールから。

composerを使って、laravel/ui パッケージをインストールします。

composer require laravel/ui

Artisanコマンドを利用して、Bootstrapのスカフォールドをインストールします。

php artisan ui bootstrap

package.json の "devDependencies"箇所に、以下のようにBootstrapが追加されます。

package.json
    "devDependencies": {
       "axios": "^0.19",
       "bootstrap": "^4.0.0", //Bootstrapが追加されています。
       "cross-env": "^7.0",
       "jquery": "^3.2",
       "laravel-mix": "^5.0.1",
       "lodash": "^4.17.13",
       "popper.js": "^1.12",
       "resolve-url-loader": "^3.1.0",
       "sass": "^1.15.2",
       "sass-loader": "^8.0.0",
       "vue-template-compiler": "^2.6.11"
   },

npmでインストールします。

npm install && npm run dev

これで、C:\xampp\htdocs\basic-task\public\css\app.css ファイルにコンパイルされてBootstrapが使えるようになります!

次はFont Awesomeをインストールします。まずはnpmでインストールします。

npm install --save @fortawesome/fontawesome-free

package.json の "dependencies" の箇所に、以下のようにFont Awesomeが追加されます。

package.json
    "dependencies": {
       "@fortawesome/fontawesome-free": "^5.13.0"
   }

C:\xampp\htdocs\basic-task\resources\sass\app.scss ファイルに移動して、以下の通りに追記してインポートします。

basic-task\resources\sass\app.scss
// Font Awesome
@import '~@fortawesome/fontawesome-free/scss/fontawesome';
@import '~@fortawesome/fontawesome-free/scss/regular';
@import '~@fortawesome/fontawesome-free/scss/solid';
@import '~@fortawesome/fontawesome-free/scss/brands';

npmで実行します。

npm run production

C:\xampp\htdocs\basic-task\public\css\app.css ファイルにコンパイルされてFont Awesomeが使えるようになります!

9:viewの作成

C:\xampp\htdocs\basic-task\resources\views フォルダ内に、
『tasks.blade.php』という名前で、手動で新規ファイルを作ります。今回、極シンプルなアプリなので、viewはトップページの1ページのみです。
ひとまず、新規追加フォーム箇所を作成して表示させてみます。
コードは以下の通りです。

basic-task\resources\views\tasks.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Basic Tasks</title>
 <link type="text/css" rel="stylesheet" href="{{ mix('css/app.css') }}">
</head>
<body>
 <div class="container">
   <h3 class="my-3">タスク管理ツール</h3>
   <div class="card mb-3">
     <div class="card-header">タスク新規追加</div>
     <div class="card-body">
       <form method="POST" action="{{ url('/task') }}">
         @csrf
         <div class="form-group">
           <input type="text" name="name" class="form-control">
           @if ($errors->has('name'))
           <p class="text-danger">{{ $errors->first('name') }}</p>
           @endif
           <button type="submit" class="btn btn-outline-info mt-2"><i class="fas fa-plus fa-lg mr-2"></i>追加</button>
         </div>
       </form>
     </div>
   </div>
 </div>
</body>
</html>

ルートファイル(C:\xampp\htdocs\basic-task\routes\web.php)のgetリクエスト部分を、tasks.blade.php ファイルが表示されるように、以下の通り更新します。

basic-task\routes\web.php
Route::get('/', function () {
   return view('tasks');
});

ブラウザを更新すると、以下のように表示されるはずです。

pic_4

tasks.blade.php ファイルのコードに関して何点か説明します。(HTMLの書き方や、Bootstrapの書き方等は説明致しませんので、分からない方はご自身で調べてみてください。)
10行目辺りの以下コードは、コンパイルしたBootstrapとFont Awesomeを読み込みます。

<link type="text/css" rel="stylesheet" href="{{ mix('css/app.css') }}">

上記はmix関数を使っていますが、以下のような書き方で、asset関数を使った書き方もできます。

<link type="text/css" rel="stylesheet" href="{{ asset('css/app.css') }}">

フォーム部分について説明します。Laravelでフォームを作成する場合は、必ずformタグの下に『@csrf』をつけてください。CSRF対策の為です。これを書かないと、419|Page Expired エラー等が起きる可能性があります。

<form method="POST" action="{{ url('/task') }}">
@csrf

以下の部分はエラー変数を使っています。Laravelでは、エラーが出ると直前のページへとリダイレクトされます。その為、その時に(今回の場合だと、submitしたら '/task' に飛ばすので、その直前のページ、つまりトップページです。)定義済みエラー変数を設定することができます。

@if ($errors->has('name'))
<p class="text-danger">{{ $errors->first('name') }}</p>
@endif

<p>タグ部分を、@if ($errors->has('name')) ~ @endif で囲わずに、『<p class="text-danger">{{ $errors->first('name') }}</p>』の部分だけでも動作します。しかし、このエラー変数はどこでも使用可能な為、ifで囲わないとブラウザからソースを表示したときに、<p>タグが表示されます。(以下画像の赤枠部分です。)そこで、エラーが出た時だけ表示させるようにするには、ifで囲みます。

image.png

10:ルートの更新

まずはpostリクエストのルート設定を更新します。コードは以下の通りです。

Route::post('/task', function (Request $request) {
   request()->validate(
       [
           'name' => 'required|unique:tasks|min:3|max:255'
       ],
       [
           'name.required' => 'タスク内容を入力してください。',
           'name.unique' => 'そのタスクは既に追加されています。',
           'name.min' => '3文字以上で入力してください。',
           'name.max' => '255文字以内で入力してください。'
       ]
   );
   $task = new Task();
   $task->name = request('name');
   $task->save();
   return redirect('/');
});

request()->validate( ) の箇所は、バリデートをかけている(入力項目をチェックする)部分です。今回は、『入力必須で、ユニークであること、3文字以上であること、255文字以内であること』を設定しています。また、続けて上記のように配列を追加すると、エラーメッセージを好きなようにカスタマイズすることができます。デフォルトのメッセージは、C:\xampp\htdocs\basic-task\resources\lang\ja\validation.php ファイル内に書かれている英語表記のものです。

image.png

バリデーションの後は、コードの通りですが、以下の通りになります。
① Taskモデルのインスタンス化
\$task = new Task();
② request()ヘルパー関数を利用して、viewファイルのinputタグの名前がnameというデータを格納
\$task->name = request('name');
③ 最後に保存して、トップページにリダイレクト
\$task->save();
return redirect('/');

次は、getリクエストのルート設定も更新します。コードは以下の通りです。

Route::get('/', function () {
   return view('tasks', [
       'tasks' => App\Task::latest()->get()
   ]);
});

『トップページにアクセスしたら、'tasks'ページを表示して、'tasks'という変数を最新のものから降順にして、viewに渡しますよ』という意味です。因みに、ここでは'tasks'という変数は、viewファイルの方では『\$tasks』という表記で$マーク付きになりますので、混同しないようにしましょう。私も最初は混乱しました!ww

11: viewを完成させる

getリクエストとpostリクエストのルート設定が終わったら、viewファイルを完成させてしまいます。タスク一覧と、削除ボタンを表示する箇所です。コードは以下の通りです。(全ソースコードはこの後に載せていますので、全体像はそちらでご確認ください。)

    <div class="card">
     <div class="card-header">タスク一覧</div>
     <div class="card-body">
       @if (count($tasks) > 0)
       <table class="table table-striped">
         <tbody>
           @foreach ($tasks as $task)
           <tr>
             <td>{{ $task->name }}</td>
             <td>
               <form method="POST" action="{{ url('/task/' . $task->id) }}">
                 @csrf
                 @method('DELETE')
                 <button type="submit" class="btn btn-outline-danger" style="width: 100px;"><i class="far fa-trash-alt"></i> 削除</button>
               </form>
             </td>
           </tr>
           @endforeach
         </tbody>
       </table>
       @endif
     </div>
   </div>

上記のコードについて何点か説明します。まず@if (count(\$tasks) > 0) の箇所は、このように書くことで、タスクがあれば表示するというようにしております。@foreach (\$tasks as \$task) 箇所では、ルートで設定したように、最新のものを降順で、且つ@foreachを使うことによって、一つずつ表示するように設定しています。

12:ルートを完成させる

最後に、削除ボタンを押したときのルート設定をします。コードは以下の通りです。

Route::delete('/task/{task}', function (Task $task) {
   $task->delete();
   return redirect('/');
});

delete('/task/{task}' の、{task}にはTaskインスタンスのidが入ってきます。また、Taskインスタンスが渡ってくるので、Implicit Bindingでコールバック関数の引数には (Task \$task) を渡してあげます。この際、delete('/task/{task}' の{task} と、(Task \$task) の \$task は同じ名前にしないといけません。

以上で全行程が終了です!お疲れさまでした!
最後に、viewファイル、ルートファイル、マイグレーションファイルのソースコードを以下に載せておりますので、全体像のチェック等、参考にしてください!
それではまた第2回でお会いしましょうヽ(´ー`)ノ マタネ~。

basic-task\resources\views\tasks.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Basic Tasks</title>
 <!-- <link type="text/css" rel="stylesheet" href="{{ asset('css/app.css') }}"> -->
 <link type="text/css" rel="stylesheet" href="{{ mix('css/app.css') }}">
</head>
<body>
 <div class="container">
   <h3 class="my-3">タスク管理ツール</h3>
   <div class="card mb-3">
     <div class="card-header">タスク新規追加</div>
     <div class="card-body">
       <form method="POST" action="{{ url('/task') }}">
         @csrf
         <div class="form-group">
           <input type="text" name="name" class="form-control">
           @if ($errors->has('name'))
           <p class="text-danger">{{ $errors->first('name') }}</p>
           @endif
           <button type="submit" class="btn btn-outline-info mt-2"><i class="fas fa-plus fa-lg mr-2"></i>追加</button>
         </div>
       </form>
     </div>
   </div>
   <div class="card">
     <div class="card-header">タスク一覧</div>
     <div class="card-body">
       @if (count($tasks) > 0)
       <table class="table table-striped">
         <tbody>
           @foreach ($tasks as $task)
           <tr>
             <td>{{ $task->name }}</td>
             <td>
               <form method="POST" action="{{ url('/task/' . $task->id) }}">
                 @csrf
                 @method('DELETE')
                 <button type="submit" class="btn btn-outline-danger" style="width: 100px;"><i class="far fa-trash-alt"></i> 削除</button>
               </form>
             </td>
           </tr>
           @endforeach
         </tbody>
       </table>
       @endif
     </div>
   </div>
 </div>
</body>
</html>
basic-task\routes\web.php
<?php

use Illuminate\Support\Facades\Route;
use App\Task;

/*
|--------------------------------------------------------------------------
| 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('tasks', [
       'tasks' => App\Task::latest()->get()
   ]);
});

Route::post('/task', function (Request $request) {
   request()->validate(
       [
           'name' => 'required|unique:tasks|min:3|max:255'
       ],
       [
           'name.required' => 'タスク内容を入力してください。',
           'name.unique' => 'そのタスクは既に追加されています。',
           'name.min' => '3文字以上で入力してください。',
           'name.max' => '255文字以内で入力してください。'
       ]
   );
   $task = new Task();
   $task->name = request('name');
   $task->save();
   return redirect('/');
});

Route::delete('/task/{task}', function (Task $task) {
   $task->delete();
   return redirect('/');
});
basic-task\database\migrations\2020_05_19_021913_create_tasks_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTasksTable extends Migration
{
   /**
    * Run the migrations.
    *
    * @return void
    */
   public function up()
   {
       Schema::create('tasks', function (Blueprint $table) {
           $table->id();
           $table->string('name');
           $table->timestamps();
       });
   }
   /**
    * Reverse the migrations.
    *
    * @return void
    */
   public function down()
   {
       Schema::dropIfExists('tasks');
   }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Laravel]超基礎☆簡単タスク管理アプリのチュートリアル

(※ この記事は、Laravel8が出るより結構前に書いて、他のブログに載せていました。それを転載したものなので、Laravel7で解説しています。ご了承くださいませ。。(m´・ω・`)m ゴメン…)

環境:XAMPP

初めてLaravelで何かアプリを作ってみようと思って、公式ドキュメントにも載っている簡単なタスク管理アプリを作ることにしました!
自分への備忘録も兼ねて、チュートリアル的な感じで解説していこうと思います。同じく初Laravel製アプリを作ろうとしている方や、Laravel勉強中の初学者の方に参考になれば幸いです!

完成品は以下のような感じです。↓↓

pic_1

新規追加でタスクを入力すると、タスク一覧に表示されて、削除ボタンを押すと削除されます。↓↓

pic_2

それでは早速スタート!

1: プロジェクトの作成

XAMPPのインストール後、C:\xampp\htdocs に移動して、composerでプロジェクトを作成します。プロジェクト名は『basic-task』にします。

composer create-project --prefer-dist laravel/laravel basic-task

※ 因みに、現在は上記のコマンドだと最新の8系がインストールされるので、7系にする場合は、以下コマンドのようにバージョン指定が必要です。

composer create-project --prefer-dist laravel/laravel basic-task "7.*"

プロジェクトを作成したら、cd コマンドでプロジェクト内(C:\xampp\htdocs\basic-task)に移動します。因みに今回のLaravelのバージョンを確認したら下記のようになりました。

php artisan --version //Laravelバージョン確認用コマンド
Laravel Framework 7.11.0

2: データベースの作成

XAMPPでデータベースを作成します。(XAMPPでのデータベースの作り方がわからない人は、検索してみてください。) データベース名は『basic_task』にします。
データベースを作ったら、C:\xampp\htdocs\basic-task\.env ファイルに移動して、 DB_DATABASE を『basic_task』に変更します。DB_USERNAME と DB_PASSWORD はそのままにします。(XAMPP側でデータベース ユーザーを設定した人は、こちらも変更してください。)

basic-task\.env
DB_DATABASE=basic_task // ←ここ変更。
DB_USERNAME=root
DB_PASSWORD=

3: ロケールの設定

C:\xampp\htdocs\basic-task\config\app.php ファイルへ移動します。
'timezone' と 'locale' を以下のように変更します。

basic-task\config\app.php
'timezone' => 'Asia/Tokyo',
'locale' => 'ja',

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

マイグレーションファイル (データベーステーブル)を作成します。テーブル名は『tasks』にします。 --create=tasks オプションをつけると、作成したマイグレーションファイルに対応したテーブルを作ってくれます。(XAMPP側でいちいちテーブルを作らなくてもOK!)

php artisan make:migration create_tasks_table --create=tasks

5: マイグレーションファイルの更新

作成したマイグレーションファイルを更新します。
C:\xampp\htdocs\basic-task\database\migrations\2020_05_19_021913_create_tasks_table.php ファイルへ移動します。(2020_05_19_021913 の部分は、作成した日付ですので、作成日によって変わります。)今回は極シンプルなアプリということもあり、nameカラムの追加のみです。18行目あたりに $table->string('name'); だけを追記します。

basic-task\database\migrations\2020_05_19_021913_create_tasks_table.php
    public function up()
   {
       Schema::create('tasks', function (Blueprint $table) {
           $table->id();
           $table->string('name'); //ここを追記。
           $table->timestamps();
       });
   }

追記したらマイグレーションします。

php artisan migrate

tasksテーブルに、nameカラムが入っていることを確認します。

image.png

6: モデルの作成

マイグレーションファイルに対応するモデルを作ります。
モデル名は『Task』にします。

php artisan make:model Task

C:\xampp\htdocs\basic-task\app\Task.php ファイルにモデルが作成されます。

7: ルートの設定

お次はルートの設定をします。C:\xampp\htdocs\basic-task\routes\web.php ファイルへと移動します。以下3つのルートを設定します。

①トップページへアクセスしたときに表示されるページ。(getリクエスト)
②新規追加フォームで追加ボタンを押した(=submitした)時にデータを飛ばすページ(postリクエスト)
※ フォームから飛んでくるデータはRequest型で渡ってきます。その為、コールバック関数の引数には、Implicit Bindingを利用して、(Request $request) とします。この辺りがよくわからない方は、『Laravel フォーム Request Implicit Binding』等で検索してみてください。
③削除ボタンを押した時にデータを飛ばすページ(deleteリクエスト)
以上の3つです。

basic-task\routes\web.php
Route::get('/', function () {
   //
});
Route::post('/task', function (Request $request) {
   //
});
Route::delete('/task/{task}', function () {
   //
});

8: BootstrapとFont Awesomeのインストール

viewページ作成の前に、BootstrapとFont Awesomeのインストールをしておきます。まずはBootstrapのインストールから。

composerを使って、laravel/ui パッケージをインストールします。

composer require laravel/ui

Artisanコマンドを利用して、Bootstrapのスカフォールドをインストールします。

php artisan ui bootstrap

package.json の "devDependencies"箇所に、以下のようにBootstrapが追加されます。

package.json
    "devDependencies": {
       "axios": "^0.19",
       "bootstrap": "^4.0.0", //Bootstrapが追加されています。
       "cross-env": "^7.0",
       "jquery": "^3.2",
       "laravel-mix": "^5.0.1",
       "lodash": "^4.17.13",
       "popper.js": "^1.12",
       "resolve-url-loader": "^3.1.0",
       "sass": "^1.15.2",
       "sass-loader": "^8.0.0",
       "vue-template-compiler": "^2.6.11"
   },

npmでインストールします。

npm install && npm run dev

これで、C:\xampp\htdocs\basic-task\public\css\app.css ファイルにコンパイルされてBootstrapが使えるようになります!

次はFont Awesomeをインストールします。まずはnpmでインストールします。

npm install --save @fortawesome/fontawesome-free

package.json の "dependencies" の箇所に、以下のようにFont Awesomeが追加されます。

package.json
    "dependencies": {
       "@fortawesome/fontawesome-free": "^5.13.0"
   }

C:\xampp\htdocs\basic-task\resources\sass\app.scss ファイルに移動して、以下の通りに追記してインポートします。

basic-task\resources\sass\app.scss
// Font Awesome
@import '~@fortawesome/fontawesome-free/scss/fontawesome';
@import '~@fortawesome/fontawesome-free/scss/regular';
@import '~@fortawesome/fontawesome-free/scss/solid';
@import '~@fortawesome/fontawesome-free/scss/brands';

npmで実行します。

npm run production

C:\xampp\htdocs\basic-task\public\css\app.css ファイルにコンパイルされてFont Awesomeが使えるようになります!

9:viewの作成

C:\xampp\htdocs\basic-task\resources\views フォルダ内に、
『tasks.blade.php』という名前で、手動で新規ファイルを作ります。今回、極シンプルなアプリなので、viewはトップページの1ページのみです。
ひとまず、新規追加フォーム箇所を作成して表示させてみます。
コードは以下の通りです。

basic-task\resources\views\tasks.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Basic Tasks</title>
 <link type="text/css" rel="stylesheet" href="{{ mix('css/app.css') }}">
</head>
<body>
 <div class="container">
   <h3 class="my-3">タスク管理ツール</h3>
   <div class="card mb-3">
     <div class="card-header">タスク新規追加</div>
     <div class="card-body">
       <form method="POST" action="{{ url('/task') }}">
         @csrf
         <div class="form-group">
           <input type="text" name="name" class="form-control">
           @if ($errors->has('name'))
           <p class="text-danger">{{ $errors->first('name') }}</p>
           @endif
           <button type="submit" class="btn btn-outline-info mt-2"><i class="fas fa-plus fa-lg mr-2"></i>追加</button>
         </div>
       </form>
     </div>
   </div>
 </div>
</body>
</html>

ルートファイル(C:\xampp\htdocs\basic-task\routes\web.php)のgetリクエスト部分を、tasks.blade.php ファイルが表示されるように、以下の通り更新します。

basic-task\routes\web.php
Route::get('/', function () {
   return view('tasks');
});

ブラウザを更新すると、以下のように表示されるはずです。

pic_4

tasks.blade.php ファイルのコードに関して何点か説明します。(HTMLの書き方や、Bootstrapの書き方等は説明致しませんので、分からない方はご自身で調べてみてください。)
10行目辺りの以下コードは、コンパイルしたBootstrapとFont Awesomeを読み込みます。

<link type="text/css" rel="stylesheet" href="{{ mix('css/app.css') }}">

上記はmix関数を使っていますが、以下のような書き方で、asset関数を使った書き方もできます。

<link type="text/css" rel="stylesheet" href="{{ asset('css/app.css') }}">

フォーム部分について説明します。Laravelでフォームを作成する場合は、必ずformタグの下に『@csrf』をつけてください。CSRF対策の為です。これを書かないと、419|Page Expired エラー等が起きる可能性があります。

<form method="POST" action="{{ url('/task') }}">
@csrf

以下の部分はエラー変数を使っています。Laravelでは、エラーが出ると直前のページへとリダイレクトされます。その為、その時に(今回の場合だと、submitしたら '/task' に飛ばすので、その直前のページ、つまりトップページです。)定義済みエラー変数を設定することができます。

@if ($errors->has('name'))
<p class="text-danger">{{ $errors->first('name') }}</p>
@endif

<p>タグ部分を、@if ($errors->has('name')) ~ @endif で囲わずに、『<p class="text-danger">{{ $errors->first('name') }}</p>』の部分だけでも動作します。しかし、このエラー変数はどこでも使用可能な為、ifで囲わないとブラウザからソースを表示したときに、<p>タグが表示されます。(以下画像の赤枠部分です。)そこで、エラーが出た時だけ表示させるようにするには、ifで囲みます。

image.png

10:ルートの更新

まずはpostリクエストのルート設定を更新します。コードは以下の通りです。

Route::post('/task', function (Request $request) {
   request()->validate(
       [
           'name' => 'required|unique:tasks|min:3|max:255'
       ],
       [
           'name.required' => 'タスク内容を入力してください。',
           'name.unique' => 'そのタスクは既に追加されています。',
           'name.min' => '3文字以上で入力してください。',
           'name.max' => '255文字以内で入力してください。'
       ]
   );
   $task = new Task();
   $task->name = request('name');
   $task->save();
   return redirect('/');
});

request()->validate( ) の箇所は、バリデートをかけている(入力項目をチェックする)部分です。今回は、『入力必須で、ユニークであること、3文字以上であること、255文字以内であること』を設定しています。また、続けて上記のように配列を追加すると、エラーメッセージを好きなようにカスタマイズすることができます。デフォルトのメッセージは、C:\xampp\htdocs\basic-task\resources\lang\ja\validation.php ファイル内に書かれている英語表記のものです。

image.png

バリデーションの後は、コードの通りですが、以下の通りになります。
① Taskモデルのインスタンス化
\$task = new Task();
② request()ヘルパー関数を利用して、viewファイルのinputタグの名前がnameというデータを格納
\$task->name = request('name');
③ 最後に保存して、トップページにリダイレクト
\$task->save();
return redirect('/');

次は、getリクエストのルート設定も更新します。コードは以下の通りです。

Route::get('/', function () {
   return view('tasks', [
       'tasks' => App\Task::latest()->get()
   ]);
});

『トップページにアクセスしたら、'tasks'ページを表示して、'tasks'という変数を最新のものから降順にして、viewに渡しますよ』という意味です。因みに、ここでは'tasks'という変数は、viewファイルの方では『\$tasks』という表記で$マーク付きになりますので、混同しないようにしましょう。私も最初は混乱しました!ww

11: viewを完成させる

getリクエストとpostリクエストのルート設定が終わったら、viewファイルを完成させてしまいます。タスク一覧と、削除ボタンを表示する箇所です。コードは以下の通りです。(全ソースコードはこの後に載せていますので、全体像はそちらでご確認ください。)

    <div class="card">
     <div class="card-header">タスク一覧</div>
     <div class="card-body">
       @if (count($tasks) > 0)
       <table class="table table-striped">
         <tbody>
           @foreach ($tasks as $task)
           <tr>
             <td>{{ $task->name }}</td>
             <td>
               <form method="POST" action="{{ url('/task/' . $task->id) }}">
                 @csrf
                 @method('DELETE')
                 <button type="submit" class="btn btn-outline-danger" style="width: 100px;"><i class="far fa-trash-alt"></i> 削除</button>
               </form>
             </td>
           </tr>
           @endforeach
         </tbody>
       </table>
       @endif
     </div>
   </div>

上記のコードについて何点か説明します。まず@if (count(\$tasks) > 0) の箇所は、このように書くことで、タスクがあれば表示するというようにしております。@foreach (\$tasks as \$task) 箇所では、ルートで設定したように、最新のものを降順で、且つ@foreachを使うことによって、一つずつ表示するように設定しています。

12:ルートを完成させる

最後に、削除ボタンを押したときのルート設定をします。コードは以下の通りです。

Route::delete('/task/{task}', function (Task $task) {
   $task->delete();
   return redirect('/');
});

delete('/task/{task}' の、{task}にはTaskインスタンスのidが入ってきます。また、Taskインスタンスが渡ってくるので、Implicit Bindingでコールバック関数の引数には (Task \$task) を渡してあげます。この際、delete('/task/{task}' の{task} と、(Task \$task) の \$task は同じ名前にしないといけません。

以上で全行程が終了です!お疲れさまでした!
最後に、viewファイル、ルートファイル、マイグレーションファイルのソースコードを以下に載せておりますので、全体像のチェック等、参考にしてください!
それではまた第2回でお会いしましょうヽ(´ー`)ノ マタネ~。

basic-task\resources\views\tasks.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Basic Tasks</title>
 <!-- <link type="text/css" rel="stylesheet" href="{{ asset('css/app.css') }}"> -->
 <link type="text/css" rel="stylesheet" href="{{ mix('css/app.css') }}">
</head>
<body>
 <div class="container">
   <h3 class="my-3">タスク管理ツール</h3>
   <div class="card mb-3">
     <div class="card-header">タスク新規追加</div>
     <div class="card-body">
       <form method="POST" action="{{ url('/task') }}">
         @csrf
         <div class="form-group">
           <input type="text" name="name" class="form-control">
           @if ($errors->has('name'))
           <p class="text-danger">{{ $errors->first('name') }}</p>
           @endif
           <button type="submit" class="btn btn-outline-info mt-2"><i class="fas fa-plus fa-lg mr-2"></i>追加</button>
         </div>
       </form>
     </div>
   </div>
   <div class="card">
     <div class="card-header">タスク一覧</div>
     <div class="card-body">
       @if (count($tasks) > 0)
       <table class="table table-striped">
         <tbody>
           @foreach ($tasks as $task)
           <tr>
             <td>{{ $task->name }}</td>
             <td>
               <form method="POST" action="{{ url('/task/' . $task->id) }}">
                 @csrf
                 @method('DELETE')
                 <button type="submit" class="btn btn-outline-danger" style="width: 100px;"><i class="far fa-trash-alt"></i> 削除</button>
               </form>
             </td>
           </tr>
           @endforeach
         </tbody>
       </table>
       @endif
     </div>
   </div>
 </div>
</body>
</html>
basic-task\routes\web.php
<?php

use Illuminate\Support\Facades\Route;
use App\Task;

/*
|--------------------------------------------------------------------------
| 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('tasks', [
       'tasks' => App\Task::latest()->get()
   ]);
});

Route::post('/task', function (Request $request) {
   request()->validate(
       [
           'name' => 'required|unique:tasks|min:3|max:255'
       ],
       [
           'name.required' => 'タスク内容を入力してください。',
           'name.unique' => 'そのタスクは既に追加されています。',
           'name.min' => '3文字以上で入力してください。',
           'name.max' => '255文字以内で入力してください。'
       ]
   );
   $task = new Task();
   $task->name = request('name');
   $task->save();
   return redirect('/');
});

Route::delete('/task/{task}', function (Task $task) {
   $task->delete();
   return redirect('/');
});
basic-task\database\migrations\2020_05_19_021913_create_tasks_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTasksTable extends Migration
{
   /**
    * Run the migrations.
    *
    * @return void
    */
   public function up()
   {
       Schema::create('tasks', function (Blueprint $table) {
           $table->id();
           $table->string('name');
           $table->timestamps();
       });
   }
   /**
    * Reverse the migrations.
    *
    * @return void
    */
   public function down()
   {
       Schema::dropIfExists('tasks');
   }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Composer 2.0 is now available and you should upgradeというメッセージ

compserのアップグレード

とある案件の開発のため、新規でLaravelのインストールを実行したら
compserのアップグレードを促された件。

どうやらcomposerの1系から2系に上げなさい的なメッセージが。

You are using an outdated version of Composer. Composer 2.0 is now available and you should upgrade
訳) 古いバージョンのComposerを使用しています。 Composer 2.0が利用可能になり、アップグレードする必要があります。

そうですかという事で下記を実行

composer selfupdate
Updating to version 2.0.6 (stable channel).
   Downloading (100%)         

  [ErrorException]                                                                                      
  rename(/hoge/.cache/composer/composer-temp.phar,/usr/local/bin/composer): Permission denied 

軽くパーミッションエラーを返してくださる。
そんでこのエラーメッセージが親切そうで若干そうでもないという。

解決方法

cd ~/.cache/composer
chmod 755 composer-temp.phar
sudo mv composer-temp.phar /usr/local/bin/composer

確認

$ composer
   ______
  / ____/___  ____ ___  ____  ____  ________  _____
 / /   / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__  )  __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
                    /_/
Composer version 2.0.6 2020-11-07 11:21:17

なんて言いますか、composerって何気にこういう類の対応が多いよなぁ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel 6から7に上げたらメールが送信できなくなった

なにがおこったか

Laravel6からLaravel7にあげたらメール送信でエラー履くようになった

原因

スクリーンショット 2020-11-12 0.57.36.png

この説明を見て、一応置き換えるかと思って、7のmail.phpに置き換えました。
それだけで行けるかなと思っていましたが、7からenvのMAIL_DRIVERMAIL_MAILERに書き換わっていました。

.env
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel8 CRUD処理を使った投稿アプリを作成する その9 画像をアップロードできるようにする

目的

  • ヘッダーをベースレイアウトファイルに記載して反映する方法をまとめる

実施環境

  • 筆者の実施環境を記載する。
  • ハードウェア環境
項目 情報
OS macOS Catalina(10.15.5)
ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
プロセッサ 2 GHz クアッドコアIntel Core i5
メモリ 32 GB 3733 MHz LPDDR4
グラフィックス Intel Iris Plus Graphics 1536 MB
  • ソフトウェア環境
項目 情報 備考
PHP バージョン 7.4.8 Homebrewを用いてこちらの方法で導入→Mac HomebrewでPHPをインストールする
Laravel バージョン 8.6.0 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う
MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする

前提条件

前提情報

  • 作成するアプリ名は「laravel8_crud」とする。
  • 作成するMySQLのデータベース名は「laravel8_crud_DB」とする。
  • 下記に今回の作業のあとのソースコードのリモートリポジトリのリンクを記載する。

概要

  1. .envファイルの記載
  2. シンボリックリンクの作成
  3. マイグレーションファイルの記載とマイグレート
  4. コントローラファイルの修正
  5. ベースレイアウトファイルの作成
  6. ビューファイルの編集
  7. 確認

詳細

  1. .envファイルの記載

    1. laravel8_crudディレクトリで下記コマンドを実行して.envファイルを開く。

      $ vi .env
      
    2. 下記の内容を.envファイルに追記する。

      laravel8_crud/.env
      FILESYSTEM_DRIVER=public
      
  2. シンボリックリンクの作成

    1. laravel8_crudディレクトリで下記コマンドを実行してシンボリックリンクを作成する。

      $ php artisan storage:link
      
  3. マイグレーションファイルの記載とマイグレート

    1. laravel8_crudディレクトリで下記コマンドを実行してモデルファイルとマイグレーションファイルを作成する。

      $ php artisan make:model ContentImage -m
      
    2. laravel8_crudディレクトリで下記コマンドを実行して作成したマイグレーションファイルを開く。

      $ vi database/migrations/YYYY_MM_DD_HHMMSS_create_content_images_table.php
      
    3. up側に下記の内容を記載する。

      laravel8_crud/database/migrations/YYYY_MM_DD_HHMMSS_create_content_images_table.php
      $table->integer('content_id');
      $table->string('file_path');
      $table->string('file_name');
      
    4. 記載後の全内容を下記に記載する。

      laravel8_crud/database/migrations/YYYY_MM_DD_HHMMSS_create_content_images_table.php
      <?php
      
      use Illuminate\Database\Migrations\Migration;
      use Illuminate\Database\Schema\Blueprint;
      use Illuminate\Support\Facades\Schema;
      
      class CreateContentImagesTable extends Migration
      {
          /**
           * Run the migrations.
           *
           * @return void
           */
          public function up()
          {
              Schema::create('content_images', function (Blueprint $table) {
                  $table->id();
                  // 下記を追記する
                  $table->integer('content_id');
                  $table->string('file_path');
                  $table->string('file_name');
                  // 上記までを追記する
                  $table->timestamps();
              });
          }
      
          /**
           * Reverse the migrations.
           *
           * @return void
           */
          public function down()
          {
              Schema::dropIfExists('content_images');
          }
      }
      
    5. laravel8_crudディレクトリで下記コマンドを実行してマイグレーションを行う。

      $ php artisan migrate
      
  4. コントローラファイルの修正

    1. laravel8_crudディレクトリで下記コマンドを実行してコントローラファイルを開く。

      $ vi app/Http/Controllers/ContentController.php
      
    2. 下記のように修正する。

      laravel8_crud/app/Http/Controllers/ContentController.php
      <?php
      
      namespace App\Http\Controllers;
      
      use Illuminate\Http\Request;
      use App\Models\Content;
      // 下記を追記する
      use App\Models\ContentImage;
      use Illuminate\Support\Facades\Storage;
      // 上記までを追記する
      
      class ContentController extends Controller
      {
          public function input()
          {
              return view('contents.input');
          }
      
          public function save(Request $request)
          {
              $input_content = new Content();
              $input_content->content = $request['content'];
              $input_content->save();
      
              // 下記を追記する
              if ($request->file('file')) {
                  $this->validate($request, [
                      'file' => [
                          // 空でないこと
                          'required',
                          // アップロードされたファイルであること
                          'file',
                          // 画像ファイルであること
                          'image',
                          // MIMEタイプを指定
                          'mimes:jpeg,png',
                      ]
                  ]);
      
                  if ($request->file('file')->isValid([])) {
                      $file_name = $request->file('file')->getClientOriginalName();
                      $file_path = Storage::putFile('/images', $request->file('file'), 'public');
      
                      $image_info = new ContentImage();
                      $image_info->content_id = $input_content->id;
                      $image_info->file_name = $file_name;
                      $image_info->file_path = $file_path;
                      $image_info->save();
                  }
              }
              // 上記までを追記する
              return redirect(route('output'));
          }
      
          public function output()
          {
              $contents_get_query = Content::select('*');
              $items = $contents_get_query->get();
      
              // 下記を追記する
              foreach ($items as &$item) {
                  $file_path = ContentImage::select('file_path')
                  ->where('content_id', $item['id'])
                  ->first();
                  if (isset($file_path)) {
                      $item['file_path'] = $file_path['file_path'];
                  }
              }
              // 上記までを追記する
      
              return view('contents.output', [
                  'items' => $items,
              ]);
          }
      
          public function detail($content_id)
          {
              $content_get_query = Content::select('*');
              $item = $content_get_query->find($content_id);
      
              // 下記を追記する
              $file_path = ContentImage::select('file_path')
              ->where('content_id', $item['id'])
              ->first();
              if (isset($file_path)) {
                  $item['file_path'] = $file_path['file_path'];
              }
              // 上記までを追記する
      
              return view('contents.detail', [
                  'item' => $item,
              ]);
          }
      
          public function edit($content_id)
          {
              $content_get_query = Content::select('*');
              $item = $content_get_query->find($content_id);
      
              return view('contents.edit', [
                  'item' => $item,
              ]);
          }
      
          public function update(Request $request)
          {
              $content_get_query = Content::select('*');
              $content_info = $content_get_query->find($request['id']);
              $content_info->content = $request['content'];
              $content_info->save();
              return redirect(route('output'));
          }
      
          public function delete(Request $request)
          {
              $contents_delete_query = Content::select('*');
              $contents_delete_query->find($request['id']);
              $contents_delete_query->delete();
      
              // 下記を追記
              $content_images_delete_query = ContentImage::select('*');
              if ($content_images_delete_query->find($request['id'] !== '[]')) {
                  $content_images_delete_query->delete();
              }
              // 上記までを追記
      
              return redirect(route('output'));
          }
      }
      
  5. ビューファイルの編集

    1. laravel8_crudディレクトリで下記コマンドを実行してビューファイルを開く。

      $ vi resources/views/contents/input.blade.php
      
    2. 下記のように編集する。

      laravel8_crud/resources/views/contents/input.blade.php
      @extends('layouts.app')
      
      @section('content')
      
      <h1>input</h1>
      
      {{-- 下記を修正する --}}
      <form action="{{route('save')}}" method="post" enctype="multipart/form-data">
          @csrf
          <textarea name="content" cols="30" rows="10"></textarea>
          {{-- 下記を追記する --}}
          <br>
          @error('file')
              {{$message}}
              <br>
          @enderror
          <input type="file" name="file">
          {{-- 上記までを追記する --}}
          <input type="submit" value="送信">
      </form>
      
      @endsection
      
    3. laravel8_crudディレクトリで下記コマンドを実行してビューファイルを開く。

      $ vi resources/views/contents/output.blade.php
      
    4. 下記のように編集する。

      laravel8_crud/resources/views/contents/output.blade.php
      @extends('layouts.app')
      
      @section('content')
      
      <h1>output</h1>
      
      @foreach ($items as $item)
      <hr>
      {{-- 下記を追記する --}}
      @if (isset($item['file_path']))
      <img src="{{asset('storage/' . $item['file_path'])}}" alt="{{asset('storage/' . $item['file_path'])}}">
      @endif
      {{-- 上記までを追記する --}}
      <p>{{$item['content']}}</p>
      <a href="{{route('detail', ['content_id' => $item['id']])}}">詳細</a>
      <a href="{{route('edit', ['content_id' => $item['id']])}}">編集</a>
      @endforeach
      
      @endsection
      
    5. laravel8_crudディレクトリで下記コマンドを実行してビューファイルを開く。

      $ vi resources/views/contents/detail.blade.php
      
    6. 下記のように編集する。

      laravel8_crud/resources/views/contents/detail.blade.php
      @extends('layouts.app')
      
      @section('content')
      
      <h1>detail</h1>
      
      {{-- 下記を追記する --}}
      @if (isset($item['file_path']))
      <img src="{{asset('storage/' . $item['file_path'])}}" alt="{{asset('storage/' . $item['file_path'])}}">
      @endif
      {{-- 上記までを追記する --}}
      <p>投稿ID: {{$item['id']}}</p>
      <p>投稿内容: {{$item['content']}}</p>
      <p>投稿時間: {{$item['created_at']}}</p>
      <a href="{{route('edit', ['content_id' => $item['id']])}}">編集</a>
      <form action="{{route('delete')}}" method="post">
          @csrf
          <input type="hidden" name="id" value="{{$item['id']}}">
          <input type="submit" value="削除">
      </form>
      
      @endsection
      
  6. 確認

    1. laravel8_crudディレクトリで下記コマンドを実行してローカルサーバを起動する。

      $ php artisan serve
      
    2. ブラウザで下記にアクセスする。

    3. コメントを入力後に「ファイルを選択」をクリックしてjpegかpngファイルを選択して「送信」をクリックする。

      laravel8_crud-2.png

    4. 下記にアクセスする。

    5. 画像が表示されることを確認する。

    6. 詳細をクリックする。

      laravel8_crud-3.png

    7. 投稿内容の詳細とともに画像が表示されることを確認する。

      laravel8_crud-4.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む