フレームワークとは
本教材ではPHPのフレームワークであるLaravelについて学習しますが、Laravelを学習する前にフレームワークがどのようなものかについて説明します。
フレームワークを一言で説明すると、「アプリケーションやシステムを開発するために必要な機能が詰まっているパッケージ」となります。
例えば新しいサービスを開発する場合、いかに早く、適切なサービスを開発することができるかが重要になります。
なぜなら、リリース日が遅れてしまったりサービスに不備があった場合、競合他社に市場シェアを先取りされてしまったり、市場からの信用を失ってしまうことになりかねないからです。
そのため、多くの開発現場では素早く、安全にアプリケーションを開発するためにフレームワークを使用して開発することが一般的です。
また、フレームワークにはたくさんの種類があり、使用するプログラミング言語やできることが異なります。
もちろんPHPのフレームワークもたくさん存在していて、LaravelやCakePHP、Symfony、CodeIgniter、Phalcon、FuelPHPなどがあります。
一方でフレームワークを使用するデメリットも存在します。
それは、プログラミング言語の知識以外にフレームワーク自体の理解も必要となることです。
しかし、多くのフレームワークはとても分かりやすく、使用していて楽しいものです。
これまでたくさんの課題を乗り越えてきた皆さんなら、ある程度集中して学習すればフレームワークを使用した開発にも慣れていただけると思います。
ぜひ安心してフレームワークの学習に取り組んでください。
★検索ワード
・Webフレームワーク
・フレームワーク メリット デメリット
・PHP フレームワーク
Laravelとは
Laravel(ララベル)は2011年にリリースされた比較的新しいフレームワークですが、PHPの中で1番人気のあるフレームワークです。
主にWebアプリケーションを作成する際に用いられるフレームワークですが、スマートフォン用アプリのバックエンド側やシステム開発など幅広いジャンルでの活用が可能となっています。
また、Laravelはとてもわかりやすい設計のため、学習コストが低く、世界的に人気であり、拡張性も高いことから、初めて学習するフレームワークとして最適です。
Laravelのバージョン
Laravelは比較的後発のフレームワークであり、2011年6月にバージョン1がリリースされました。
その後バージョン2、3と早いペースでリリースされた後に、バージョン4ではほぼゼロベースで再構築され、抜本的な変更が行われました。
その後、5、6、7、8、9と徐々に新しいバージョンがリリースされ、現在はバージョン10が最新となっています。
今後は毎年1回メジャーバージョンアップがされる予定になっているので、2024年にはバージョン11のリリースが予定されています。
このように毎年アップデートされているLaravelですが、基本的な部分はすでに確立されており、多少アップデートが入っても大きな変更はあまりありません。
そのため、今回は最新バージョンのLaravel10を使用して学習を進めていきますが、6以降のバージョンではそれほど変化はないので、まずはバージョンを気にせず、最新バージョンの10を学んでいきましょう!
Laravelのドキュメントについて
Laravelが世界中の開発者に選ばれている理由の1つとして、公式マニュアルがとても充実していることが挙げられます。
また、非公式ではありますが有志の方が作成している日本語訳のドキュメントもあります。
公式マニュアルは学習を開始して間もないころだと読みにくいものかもしれませんが、「こういうものがあるんだな。」程度に知っておくと後々の開発で役立ちますのでブックマークをしておきましょう。
★検索ワード
・Laravel できること
・Laravel document
・Laravel ドキュメント
Laravelで作成するもの
Laravelを効率よく学習していただくために、まずは簡単なブログシステムを開発していきます。
ネイティブPHPでもブログシステムを開発したと思いますが、Laravelで改めてブログシステムを作成することで、フレームワークを使うことのメリットを感じていただければと思います。
CRUD(クラッド)とは
実はブログシステムを開発することで、アプリケーションの基本となるCRUDを学ぶことができます。
CRUDとは、Create、Read、Update、Deleteの頭文字で、ほとんどのアプリケーションが持っている4つの基本機能のことです。
これら4つの基本機能は、下記の通りです。
-
Create:生成・登録機能→新しいデータの登録ができる機能 -
Read:読取・表示機能→登録されているデータの表示ができる機能 -
Update:更新機能→登録されているデータの更新ができる機能 -
Delete:削除機能→登録されているデータの削除ができる機能
例としてTwitterを考えてみましょう。
Twitterでは、「ツイートを投稿する」、「ツイートを表示する」、「ユーザー登録情報を更新する」、「ツイートを削除する」など様々なことができます。
このように、基本的なアプリケーションはCRUD操作をすることができます。
そのため、まずはブログシステムの開発を通してLaravelで記事の投稿・表示・更新・削除機能を作成することでCRUD操作について学習していきましょう。
★検索ワード
・CRUDとは
環境構築
PHPの学習前に環境構築としてMAMPやXAMPPをインストールしたように、Laravelを学ぶ前にも環境構築が必要になります。
Laravelの開発環境を構築する方法はいくつかありますが、今回は公式ドキュメントでもおすすめされているLaravel Sailを用いた環境構築を行っていきます。
Laravel Sailを簡単に説明すると、Laravelの開発環境をDockerで簡単に構築できる機能となります。
ここでDockerという新しいサービスが登場しました。
Laravel Sailは、Dockerの経験がない人でも簡単にLaravelの環境構築ができる機能なので、Dockerについての知識は必要ありませんが、簡単に説明しておきます。
Dockerとは
Dockerとは開発に必要な環境をOS(Windows OSやmacOSなど)レベルでまとめてくれるサービスのことです。
これまで開発環境を構築するためには、各OSやパソコンごとに必要な手順が異なっていました。
しかし、Dockerが登場したことでOSに関係なくコマンドを複数回入力するだけで開発環境を構築することができるようになりました。
つまり、Dockerは環境構築を簡単にしてくれる便利機能だと思っていただければ大丈夫です!
Laravel Sailでの環境構築
それでは、さっそくLaravel Sailでの環境構築を行っていきましょう!
ちなみに、Laravel Sailでの環境構築方法はLaravelの日本語訳ドキュメントにも記載がありますので、1度目を通してみてもいいでしょう。
・Laravelの日本語訳ドキュメント:Laravelの環境構築方法
Laravel Sailでの開発環境構築方法はMacとWindowsで違う部分がありますので、それぞれ分けて解説していきます。
Macでの環境構築
それではMacでの開発環境構築方法を説明していきます。
※Windowsの方はこちらのレクチャーを飛ばして下さい。
MacでLaravel Sailを使用するためには、MAMPのストップとMac本体の設定変更とDocker Desktopが必要になります。
MAMPのストップ
DockerとMAMPを同時に起動するとエラーが起こる可能性があるので、必ずMAMPはSTOPしておいてください。
設定変更
まずはMac本体の設定から変更していきます。
OSのバージョン差異によって設定方法が下記説明通り行かない場合がございます。
その場合はmac フルディスクアクセス ターミナルで検索してみてください。
画面左上のアップルマークをクリックして、システム環境設定をクリックしてください。

システム環境設定が開くので、セキュリティとプライバシーをクリックしてください

プライバシーを選択しフルディスクアクセスをクリック、左下の鍵マークをクリックするとMac本体のパスワード入力が求められるので、パスワードを入力してください。
そして、ターミナルのチェックボックスにチェックを入れてください。

するとターミナルを再起動するか聞かれるので、終了して再度開くをクリックしてください。
※自動的に再起動がされない場合は、手動でターミナルを再起動してください。
ターミナルが再起動されればMacの設定変更は完了です。
Docker Desktopのインストール
次にDocker Desktopをインストールしましょう。
下記URLからDocker Desktopをインストールして下さい。
https://www.docker.com/products/docker-desktop/
インストール手順は頻繁に変更されるので、下記説明通りいかない場合がございます。
その場合はmac docker desktop インストールで検索をしてみてください。
URLにアクセスすると下記のような画面が出てきます。
2022年4月現在、MacにはIntel製のチップとApple製のチップの2種類が存在しています。
ご自身のチップに合う方のボタン(赤枠で囲われている)をクリックして下さい。

なお、ご自身のMacがどちらのチップかわからない場合は画面左上のアップルマークをクリックして、このMacについてをクリックして下さい。

概要をクリックしてチップを確認して下さい。
下記画像のようにApple M1などとなっている場合はApple製のチップです。

下記画像のようにIntel Core i7などとなっている場合はIntel製のチップです。

どちらかのボタンをクリックすると、チップに合ったDocker.dmgファイルがダウンロードされるので、ダウンロードしたDocker.dmgをダブルクリックして下さい。
下記画像のようなウィンドウが出てくるので、そのウィンドウ内でDockerアイコンをApplicationsフォルダにドラッグします。

Docker.appを起動して下さい。

警告が表示されますが、開くをクリックして下さい。

アクセス権限の可否を聞かれた場合はOKボタンをクリックして、Mac本体のパスワードを入力して下さい。

これでDocker Desktopのインストールは完了です。
Laravelプロジェクト作成
次にLaravel Sailを使用して、Laravelの開発環境を構築していきます。
今回はデスクトップにLaravelフォルダを作成し、その中にさらにfirst-appフォルダを作成してから、first-appフォルダ内にLaravelのファイル一式を格納します。
ターミナルを起動して、下記3つのコマンドを実行して下さい。
$ cd ~/Desktop
$ mkdir Laravel
$ cd Laravel
上記コマンドを入力すると、デスクトップにLaravelフォルダが作成され、ターミナルでLaravelフォルダが選択状態になります。
次に下記コマンドを入力して下さい。
$ curl -s "https://laravel.build/first-app" | bash
これでLaravelファイル一式のダウンロードが始まります。
コマンド内にfirst-appとありますが、ここがフォルダ名になります。
例えば、first-appをmy-appなどに変更してコマンドを実行すると、Laravelフォルダ下にフォルダ名my-appが作成されます。
Docker is not running.などのエラーでダウンロードが始まらない場合は、Dockerのアプリケーションが開いているか確認しましょう。
なお、途中で下記画像のようにMacのパスワード入力が求められるので入力して下さい。
※文字を入力してもターミナルには何も反映されませんが、そのまま入力して下さい。

下記画像のようになればLaravelファイル一式のダウンロードが完了し、デスクトップ上のLaravelフォルダ内にfirst-appというフォルダのLaravelプロジェクトが作成されています。

下記コマンドを入力してLaravel Sailを起動します。
$ cd first-app
$ ./vendor/bin/sail up -d
終了まで時間がかかるので、気長に待ちましょう。
無事に実行が完了すると以下のような画面になります。

次はhttp://localhost/へアクセスしてください。
下記画像のような画面が出てくればOKです。
※画像はMacの設定をダークモードにしているため、全体的に黒色のサイトになっています。

次に、下記コマンドを実行してみましょう。
$ ./vendor/bin/sail stop
無事に実行が完了すると以下のような画面になります。

このコマンドはシャットダウンと同じで開発環境をオフにすることができます。
もちろん開発環境をオフにしたので、http://localhost/にアクセスしてもLaravelの画面は表示できません。
Macをシャットダウンする時や開発を中断する時は、このコマンドを実行してください。
開発を再開する場合は、ターミナルで下記コマンドを~/Desktop/Laravel/first-app配下で実行しましょう。
$ ./vendor/bin/sail up -d
Windowsでの環境構築
次にWindowsでの環境構築方法を説明していきます。
※Macの方はこちらのレクチャーを飛ばして下さい。
WindowsでLaravel Sailを使用するには、XAMPPのストップとWSL2、Docker Desktopが必要になります。
XAMPPのストップ
DockerとXAMPPを同時に起動するとエラーが起こる可能性があるので、必ずXAMPPはSTOPしておいてください。
WSL2のインストール
WSLはWindows Subsystem for Linuxの略で、Windows上でLinux環境を実行することができます。
Macの場合、ターミナル(terminal)というアプリケーションでLinuxコマンドを実行することができますが、WindowsはデフォルトでLinuxコマンドを実行できないので、WSL2をインストールします。
画面左下の検索窓(Windowsマークの隣)でPowerShellと検索し、Windows PowerShellを選択して、管理者として実行するをクリックしてください。

管理者で実行できたら、下記画像の通りwsl --installと入力し、エンターを押してください。
ちなみに、テキストを入力してエンターを押すことをコマンドを実行するなどと言います。

下記画像のようになれば、WSL2のインストールは完了です。

WSL2を有効にするためには再起動が必要なので、パソコンを再起動してください。
万が一、下記画像のようなエラーが発生した場合はWindowsのアップデートが必要です。

Windowsのアップデート方法は、画面左下のWindowsマークをクリックして、設定をクリック、Windows Updateをクリックして、更新プログラムの確認をクリックすると最新のWindowsにアップデートすることができます。
参考URL:Windows の更新
アップデートが完了次第、PowerShellにて管理者としてwsl --installを実行しましょう。
WSL2の設定
再起動後、自動でUbuntuというアプリケーションが起動されます。
※自動でUbuntuが起動しない場合は、画面左下の検索窓(Windowsマークの隣)でUbuntuと検索し、開くをクリックしてください。
Ubuntuが起動したら、画面に従ってユーザー名とパスワードを設定してください。
ここで設定したユーザー名とパスワードは忘れないようにしましょう。
※パスワード入力時は何も表示されませんが、気にせず入力してください。

ユーザー名とパスワードが設定できると下記のような画面になります。

これでWSL2の準備は完了です。
なお、以降Ubuntuアプリのことをターミナルと表記致します。
「ターミナルでコマンドを実行してください。」という指示があった場合は、Ubuntuアプリでコマンドを実行してください。
また、万が一下記画像のようなエラーが出た場合は、BIOSの設定変更が必要になります。

BIOSの起動方法は、基本パソコン起動直後のメーカーロゴ表示中にDeleteキーかF2キーを押すことが一般的ですが、パソコンメーカーによって異なります。
ご自身のパソコンメーカー名でBIOSの起動方法を調べてください。
例えば、「Lenovo」の場合は、Lenovo BIOS 起動方法などで検索してください。
自作PCを使用している方は、マザーボードのメーカー名で検索してください。
BIOSを起動できた方はVirtualization TechnologyをEnableにしてください。
Virtualization Technologyの場所もパソコンメーカーで異なるので、パソコンメーカー名 Virtualization Technology Enableなどで検索してください。
無事にVirtualization TechnologyをEnableにできたら、保存してBIOSを終了してください。
その後、画面左下の検索窓(Windowsマークの隣)でUbuntuと検索し、開くをクリックしてください。
そして、画面に従ってユーザ名とパスワードを設定してください。
※パスワード入力時は何も表示されませんが、気にせず入力してください。
Docker Desktopのインストール
下記URLからDocker Desktopをインストールして下さい。
https://www.docker.com/products/docker-desktop/
インストール手順は頻繁に変更されるので、下記説明通りいかない場合がございます。
その場合はwindows docker desktop インストールで検索をしてみてください。

Also available for Windows and Linuxと書いてあるので、Windowsをクリックしてください。
クリックすると、Docker Desktop Installer.exeファイルがダウンロードされるので、そのファイルを開いてください。
ファイルを開くとインストール画面が出てくるので、画面に従ってインストールしてください。
インストールが完了すると、利用規約などに同意するか聞かれる場合があるので、聞かれた場合は同意(accept)してください。
初回起動時はチュートリアルなどがあるかもしれませんが、全てスキップしてしまって大丈夫です。
下記のような画面が出てくればインストールは無事に完了しています。

ここまでくればWindowsでLaravel Sailが使えるようになりました。
Laravelプロジェクト作成
次にLaravel Sailを使用して、Laravelの開発環境を構築していきます。
今回はホームディレクトリにLaravelフォルダを作成して、その中にさらにfirst-appフォルダを作成してから、first-appフォルダ内にLaravelのファイル一式を格納します。
画面左下の検索窓(Windowsマークの隣)でUbuntuと検索し、開くをクリックしてUbuntuアプリ(ターミナル)を起動してください。
起動が完了したら、下記コマンドを入力してください。
$ cd ~
$ mkdir Laravel
$ cd Laravel
上記コマンドを実行すると、ホームディレクトリにLaravelフォルダが作成され、ターミナルでLaravelフォルダが選択状態になります。
次に下記コマンドを実行してください。
$ curl -s "https://laravel.build/first-app" | bash
途中で下記画像のようにUbuntuのパスワード入力が求められるので入力して下さい。
※パスワード入力時は何も表示されませんが、気にせず入力してください。

下記画像のようになれば、Laravelプロジェクトの作成が完了しました。

先ほど実行したコマンド内にfirst-appとありますが、ここがフォルダ名になります。
例えば、first-appをmy-appなどに変更してコマンドを実行すると、フォルダ名がmy-appとなります。
下記コマンドを入力してLaravel Sailを起動します。
$ cd first-app
$ ./vendor/bin/sail up -d
終了まで時間がかかるので、気長に待ちましょう。
無事に実行が完了すると以下のような画面になります。

次はhttp://localhost/へアクセスしてください。
下記画像のような画面が出てくればOKです。

次に、下記コマンドを実行してみましょう。
$ ./vendor/bin/sail stop
無事に実行が完了すると以下のような画面になります。

このコマンドはシャットダウンと同じで開発環境をオフにすることができます。
もちろん開発環境をオフにしたので、http://localhost/にアクセスしてもLaravelの画面は表示できません。
Windowsをシャットダウンする時や開発を中断する時は、このコマンドを実行してください。
開発を再開する場合は、ターミナルで下記コマンドを~/Laravel/first-app配下で実行しましょう。
$ ./vendor/bin/sail up -d
VSCodeでfirst-appディレクトリを開く
VSCodeでfirst-appディレクトリを開くためには、ターミナルで下記コマンドを実行してください。
$ cd ~/Laravel/first-app
$ code .
万が一下記のようなエラーが出た場合は対応が必要です。

ターミナルで下記コマンドを実行してください。
$ cd ~
$ vim .bashrc
vim .bashrcを実行すると、.bashrcというファイルがvimというエディタを使用してターミナル上で開かれます。
vimはVSCodeのようなエディタなので、vim ファイル名とすることでUbuntu上でファイルを開いて中身を確認したり、変更することができます。
それでは、キーボードのIを押してください。
すると下記画像のように画面下に-- INSERT --という文字が出現します。

これで.bashrcファイルを変更することができるようになりました。
さっそくエンターキーを押してみてください。
1番上の行が改行されるはずです。
改行してできた新しい行に下記の1行を追加してください。
export PATH=$PATH:'/mnt/c/Users/[ユーザー名]/AppData/Local/Programs/Microsoft VS Code/bin'
※[ユーザー名]となっている部分はご自身のUbuntuのユーザー名を入力してください。
追加できたらキーボードのESCキー(通常キーボードの左上)を押してください。
すると下記画像のように、先ほどまであった-- INSERT --がなくなります。

この状態で:wqと入力してください。
すると下記画像のようになります。

この状態でエンターキーを押すとvimを終了させることができます。
万が一間違えたキーを押してしまってもESCキーを押してから再度:、W、Qからエンターキーでvimから抜けることができます。
vimから抜けたらターミナルを閉じて、再度開いてから下記コマンドを実行してください。
$ cd ~/Laravel/first-app
$ code .
それでもVSCodeでfirst-appディレクトリが開かなければ、パソコンの再起動をしてみてください。
・参考URL
codeコマンドでエラー:https://snowsystem.net/other/windows/vscode-wsl-run-error/
vimの使い方:https://qiita.com/pe-ta/items/0510bee10bcfd88afeee
★検索ワード
・Mac Laravel sail 環境構築
・Windows Laravel sail 環境構築
・Mac Docker Desktop インストール方法
・Windows10 WSL2 インストール方法
・Windows11 WSL2 インストール方法
・Windows Docker Desktop インストール方法
phpMyAdminのインストール
次に、データベースを操作するためのphpMyAdminをインストールしていきましょう!
まず、Laravel Sailで作成したfirst-appフォルダをVSCodeで開いてみましょう。
その中にdocker-compose.ymlファイルがあるのでそちらを編集していきます。
ちなみに、このdocker-compose.ymlファイルはLaravel Sailで開発環境を構築した場合に作成されるファイルで、このファイルにDockerの設定が記述されています。
それでは、docker-compose.ymlファイルを下記のように編集してください。
# For more information: https://laravel.com/docs/sail
version: '3'
services:
# --- ここから追加 ---
phpmyadmin:
image: phpmyadmin/phpmyadmin
links:
- mysql:mysql
ports:
- 8888:80
environment:
PMA_HOST: mysql
networks:
- sail
# --- ここまで追加 ---
laravel.test:
# --- 以下省略 ---
追加部分のみを抜粋すると下記の通りです。
phpmyadmin:
image: phpmyadmin/phpmyadmin
links:
- mysql:mysql
ports:
- 8888:80
environment:
PMA_HOST: mysql
networks:
- sail
ポイントは、追加部分のコードをservices:の下に記述することです。
また、インデントをservices:より1つ下げてください。
docker-compose.ymlでは、インデントがしっかりしていないとエラーになるので気を付けてください。
無事にコードの追加が終わった方は、ターミナルで下記コマンドをfirst-appディレクトリ上で実行してください。
$ ./vendor/bin/sail up -d
実行が完了したら、http://localhost:8888/へアクセスしてください。
下記のような画面が出てくれば、phpMyAdminのインストールは完了です。

★検索ワード
・Laravel sail phpMyAdmin 導入
各URL&コマンド一覧
これで全ての環境構築が完了しました。
開発を始める前に、各URLとコマンドを改めて確認しましょう。
Laravel画面URL
phpMyAdmin画面URL
first-appディレクトリへ移動
sail upコマンドやsail stopコマンドなど、これから出てくるsailコマンドは必ずfirst-app配下で行ってください。
なぜなら、sailコマンドを実行するためのファイルはfirst-appディレクトリに存在するためです。
他のディレクトリ配下でsailコマンドを実行しても、no such file or directory: ./vendor/bin/sailなどのエラーが出てしまいます。
そうならないためにもターミナルやubuntuではfirst-appディレクトリに移動する必要があります。
そのためのコマンドは下記の通りです。
Macの場合
ターミナルを開いて下記コマンドの実行
$ cd ~/Desktop/Laravel/first-app
Windowsの場合
ubuntuを開いて下記コマンドの実行
$ cd ~/Laravel/first-app
Sail upコマンド
開発を始める場合は、ターミナルで下記コマンドを実行してください。
$ .vendor/bin/sail up -d
Sail stopコマンド
開発を終了する場合は、ターミナルで下記コマンドを実行してください。
$ .vendor/bin/sail stop
Laravelのディレクトリ構成
Laravelでの開発環境構築が無事に終了したので、さっそくLaravelについて学習していきましょう。
Laravelをダウンロードして最初に驚くことの1つとして、ダウンロードされたフォルダとファイルの多さです。
これほどまでに多くのフォルダとファイルがダウンロードされた理由は、それら全てがLaravelでアプリケーションを開発するために必要な機能を担っているからです。
それらのフォルダやファイルがどのような役割なのかをざっくり把握してもらうために、まずはLaravelのディレクトリ構成から説明してきます。
教材で説明するディレクトリ構成と実際にダウンロードしたfirst-appを見比べるために、first-appをVSCodeで表示しましょう。
Laravelをインストールすると下記のようなディレクトリ構成になっています。
firstapp
├── app
├── bootstrap
├── config
├── database
├── lang
├── public
├── resources
├── routes
├── storage
├── tests
├── vendor
└── その他ファイル(.envやwebpack.mix.jsなど)
それでは、各ディレクトリを簡単に説明していきます。
app
アプリケーションのコアとなるディレクトリです。
Laravelを使用した開発では、主にappディレクトリ内のファイルを操作することでアプリケーションを構築していきます。
bootstrap
フレームワークの初期処理を担っているディレクトリです。
基本的にこちらのbootstrapディレクトリ内のファイルを操作することはありません。
config
アプリケーションの設定ファイルを置くディレクトリです。
アプリケーションの設定を追加、変更、削除をする場合に操作します。
database
データベースの内容変更やテストデータを作成するような機能が格納されているディレクトリです。
lang
言語ごとの設定をすることができるディレクトリです。
現状はあまり操作することはありません。
public
CSSやJavaScript、画像ファイルなどが入ってるディレクトリです。
resources
CSSやJavaScript、BladeというHTMLのような特有のテンプレートファイルを格納するディレクトリです。
routes
アプリケーションのURL設定などを行うディレクトリです。
strage
キャッシュファイルやログファイルなどが格納されるディレクトリです。
tests
PHPUnitという自動テストファイルなどを格納するディレクトリです。
vendor
Composerの依存内容が格納されているディレクトリです。
以上がLaravelのディレクトリ構成となっています。
現段階でどのディレクトリがどのような働きをしているのかをわざわざ暗記する必要は全くありません。
これからLaravelの学習を行うにあたって、徐々に何がどこにあるかを把握していきましょう!
★検索ワード
・Laravel ディレクトリ構成
データベース接続
以前のレクチャーでphpMyAdminをインストールしているので、さっそくデータベースへ接続してみましょう。
http://localhost:8888/へアクセスすると、下記の通りphpMyAdminのログイン画面になります。

Laravelではデータベースへの接続情報を.envというファイルで管理しています。
まずはfirst-app/.envファイルをVSCodeで開きましょう。
すると下記のようにたくさんの設定が記述されています。
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:IRagpojO8u1OxSEJJKG5LYdpOwUMwi1sHn+CukKEuS0=
APP_DEBUG=true
APP_URL=http://first-app.test
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=first_app
DB_USERNAME=sail
DB_PASSWORD=password
BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DISK=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
MEMCACHED_HOST=memcached
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://meilisearch:7700
この.envファイルには、データベースへの接続情報だけではなくLaravelの設定内容が色々記述されています。
その中でデータベースの情報は下記の部分です。
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=first_app
DB_USERNAME=sail
DB_PASSWORD=password
DB_******となっている部分がデータベースの設定情報ですが、DBはデータベースの略です。
とても覚えやすいですね!
上記コードの中にDB_USERNAMEがsail、DB_PASSWORDがpasswordと記述されているので、phpMyAdminのユーザー名にsail、パスワードにpasswordとそれぞれ入力してログインをクリックしてください。

また、DB_DATABASEがfirst_appと記述されているので、今回使用するデータベース名はfirst_appということになります。
Laravel Sailで環境構築をした場合、すでにデータベースは下記画像のように作成されているので、データベースへの接続確認は以上で終了になります。

★検索ワード
・Laravel phpMyAdmin 使い方
Laravelの初期設定
Laravelプロジェクトを作成した直後にやっておいたほうが良い初期設定を行っていきます。
初期設定で行うことは以下の2つです。
- アプリケーション名の変更
- タイムゾーンと言語設定
アプリケーション名の変更
はじめにアプリケーション名を任意の名前に変更しておきましょう。
Laravelのアプリケーション名は以前データベースの設定を確認したfirst-app/.envファイルで設定します。
.envファイルを下記のように変更してください。
# APP_NAME=Laravelから下記に変更
APP_NAME="ブログシステム"
APP_ENV=local
# --- 以下省略 ---
Laravelをインストールした直後の名前はLaravelになっています。
それを、今回開発するブログシステムという名前に変更しましょう。
ちなみに、日本語にする場合や半角スペースを入れたい場合は"(ダブルクオーテーション)で囲みます。
タイムゾーンと言語設定
次に、日本時間の設定と言語設定を日本語に変更します。
これらの設定はfirst-app/config/app.phpで設定されているので、そちらのファイルを開いてください。
app.phpを下記の通り変更してください。
// 'timezone' => 'UTC'から下記に変更(72行目付近)
'timezone' => 'Asia/Tokyo',
// 'locale' => 'en'から下記に変更(85行目付近)
'locale' => 'ja',
// 'faker_locale' => 'en_US'から下記に変更(111行目付近)
'faker_locale' => 'ja_JP',
app.phpはかなりコード量が多いですが、各変更前のコードをVSCode上で文字列検索すると簡単に見つけることができます。
VScodeでの文字列検索方法は下記の通りです。
まずVScodeで対象ファイルを開いてください。
開いたらご自身のOSに合ったキーボードのキーを押してください。
・Mac:Command + f
・Windows:Ctrl + f
変更したコードはそのままの意味ですが、意味は下記の通りです。
'timezone' => 'Asia/Tokyo',としましたが、日本時間に設定しています。
'locale' => 'ja',としましたが、言語設定を英語から日本語に設定しています。
'faker_locale' => 'ja_JP'としましたが、テストデータを作る際に日本語で作成してくれる設定をしています。
これでファイルの変更は終わりです。
最後にターミナルで下記2つのコマンドをfirst-appディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan cache:clear
$ ./vendor/bin/sail php artisan config:clear
実行後は下記画像のようになります。

これらのコマンドを実行することで、先ほど変更した内容がアプリケーションへ確実に反映されるようになります。
.envファイルやapp.phpなどを変更した場合は、上記コマンドを実行してください。
以上で初期設定は終わりです。
★検索ワード
・Laravel 初期設定
・Laravel アプリケーション名変更
・Laravel 日本時間設定
・Laravel 日本語設定
マイグレーション(migration)
Laravelには、データベースのテーブル作成や編集などを管理する方法としてマイグレーションという機能が用意されています。
今回はそのマイグレーション機能を使用して、テーブル作成やテーブル設定を行っていきます。
マイグレーションでテーブルを作成するための手順は、下記3段階に分けることができます。
- マイグレーションファイルの作成
- マイグレーションファイルにテーブル定義を記述
- マイグレーションの実行
マイグレーションファイルはどんなテーブルを作成するのかを定義する設計書のようなものです。
また、設計書を作成した後に設計書に基づいて作成作業をしなければテーブルは完成しません。
この作成作業に当たる部分がマイグレーションの実行というわけです。
マイグレーションファイルの作成
まず、1つのテーブルを作成するのに必要なマイグレーションファイルは1つだということを覚えておいてください。
マイグレーションファイルはターミナルからコマンド1つで作成することができます。
コマンドは以下の通りです。
$ php artisan make:migration create_テーブル名(名詞の複数形)_table
今回はLaravel Sailで開発環境を構築しているので、下記のようなコマンドになります。
$ ./vendor/bin/sail php artisan make:migration create_テーブル名(名詞の複数形)_table
本来Laravelが用意しているコマンドはphp artisanから始まります。
しかし、Laravel Sailで開発環境を構築した場合は./vendor/bin/sail php artisanとなるので注意してください。
また、テーブル名のところは作成するテーブル名を複数形で入力してください。
今回は記事テーブルを作成するのでarticlesテーブルとして作成します。
それでは、ターミナルで下記コマンドをfirst-appディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:migration create_articles_table
Created Migration: 2022_06_03_182626_create_articles_tableなどと出力されればマイグレーションファイルの作成は完了です。
ちなみに、2022_06_03_182626の部分は作成日時になるので、皆さんが作成したファイルでは異なる時間となっているはずです。
マイグレーションファイルの構成
作成したarticlesテーブルのマイグレーションファイルは、first-app/database/migrationsの中に存在し、先ほど作成した2022_06_03_182626_create_articles_table.phpもfirstapp/database/migrationsの中に入っています。
また、Laravelのバージョンによっても異なりますが、デフォルトで下記のマイグレーションファイルが存在します。
- 2014_10_12_000000_create_users_table.php
- 2014_10_12_100000_create_password_resets_table.php
- 2019_08_19_000000_create_failed_jobs_table.php
- 2019_12_14_000001_create_personal_access_tokens_table.php
これらデフォルトで存在するマイグレーションファイルのほとんどは、ログイン機能を実装する場合などに使用します。
今回の学習では使用しないので4つとも削除してしまってもOKです。
それでは、実際に作成した2022_06_03_182626_create_articles_table.phpをVScodeで開いて中身を確認してみましょう。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('articles');
}
};
まず、マイグレーションファイルはMigrationクラスを継承したクラスです。
このMigrationクラスを継承していることで、Laravelの機能であるマイグレーションを使用することができます。
また、マイグレーションファイルの中にはupメソッドとdownメソッドがあります。
upメソッドが実行(作成)するメソッドなので、このメソッドの中にテーブル定義を記述していきます。
一方でdownメソッドは基本使用しませんが、元に戻す(削除)処理を記述することができます。
また、public function up(){}やpublic function down(){}となっていますが、これらは関数のことです。
Laravelでは関数のことをメソッドと呼ぶのでそちらも合わせて覚えておいてください。
upメソッドの中を見てみると、Schema::create()と記述しています。
このSchema::create()の()内でテーブル名やカラムの定義をしていくことになります
まずSchema::create('articles')と記述されている部分ですが、この第一引数であるarticlesがテーブル名となります。
また、第二引数でfunction (Blueprint $table) {}と記述されていますが、LaravelではBlueprintという機能を使用してカラムを作成していきます。
{}内を確認すると、すでに$table->id();と$table->timestamps();が記述されています。
これら2つのコードは、テーブルがほぼ必ず持っているといっても過言ではない3つのカラムを生成することができます。
$table->id();でidカラム、$table->timestamps();でcreated_atカラムとupdated_atカラムという作成日時と更新日時のカラム3つを生成することができます。
これらのデフォルトのコードに、作成するカラムの記述をプラスしてテーブル定義を定義していきます。
マイグレーションファイルの記述
マイグレーションファイルを使用してカラムを追加する場合、基本$table->カラムタイプ('カラム名')という形でカラム定義を記述します。
ちなみに使用できるカラムタイプはドキュメントに全て載っています。
どのようなカラムがあるのか気になる方は、こちらのドキュメントの利用可能なカラムタイプを参照してください。
下記画像はドキュメントの利用可能なカラムタイプをスクショしたものですが、そちらからもわかるように先ほど確認したidもtimestampsもありますね!

ドキュメントに利用可能なカラムは全て載っているので、暗記せずに使いながら覚えていきましょう!
そもそもバージョンの違い等で利用可能なカラムは変わる場合もあるので、全てを暗記している人はいません!
では、実際にarticlesテーブルの定義を記述していきましょう。
2021_12_15_140034_create_articles_table.phpを下記の通り記述してください。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->string('title', 100); // ここを追加
$table->text('content'); // ここを追加
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('articles');
}
};
追加したコードは下記の2行です。
$table->string('title', 100);
$table->text('content');
$table->string('title', 100);とすることでtitleというカラム名の100文字という文字数制限があるカラムを生成します。
$table->text('content');とすることでcontentというカラム名の文字数制限がないカラムを生成します。
これでブログシステムに必要な記事のタイトル(titleカラム)と内容(contentカラム)を保存できるarticlesテーブルを作成することができます。
マイグレーションの実行
マイグレーションファイルの記述が終わったので、実際にコマンドでマイグレーションの実行をしていきましょう。
ターミナルで下記コマンドをfirst-appディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan migrate
下記のような実行結果が返ってくれば成功です。
Migration table created successfully.
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated: 2019_12_14_000001_create_personal_access_tokens_table (81.27ms)
Migrating: 2022_05_30_144855_create_articles_table
Migrated: 2022_05_30_144855_create_articles_table (42.16ms)
では、テーブルが作成されているのかをphpMyAdminにて確認してみましょう。
http://localhost:8888/へアクセスして、データベースfirst_appの中に、articlesテーブルとmigrationsテーブル、personal_access_tokensテーブルの3つが作成されていればOKです。
※デフォルトのマイグレーションファイルを削除していない方はusersテーブルやpassword_resetsテーブルなども一緒に作成されますが、そのままで大丈夫です。
ちなみにmigrationsテーブルはマイグレーションをした履歴が保存されるテーブルです。
また、先ほどデフォルトのマイグレーションファイルは全て削除しましたが、personal_access_tokensテーブルは自動的に作成されてしまいます。
これらのテーブルは自動的に作成されるものなので、そのままで大丈夫です。
★検索ワード
・Laravel マイグレーション
・Laravel マイグレーションファイル作成
テストデータの作成
Laravelには、データベースにテストデータを作成するためのシーダー(seeder)という機能が用意されています。
今回はそのシーダーを使用してテストデータの準備をしていきます。
シーダーでテストデータを作成するための手順は、下記4段階に分けることができます。
- シーダーファイルの作成
- シーダーファイルにデータ内容を記述
- シーダーファイルの登録
- シーダーの実行
それでは上記手順に沿って、テストデータを作成してみましょう。
シーダーファイルの作成
シーダーファイルもマイグレーションファイル同様、1コマンドで作成することができます。
コマンドは下記の通りです。
$ ./vendor/bin/sail php artisan make:seeder テーブル名TableSeeder
それでは、ターミナルで下記コマンドをfirst-appディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:seeder ArticlesTableSeeder
今回はarticlesテーブルのテストデータを作成するので、ArticlesTableSeederとなります。
※ArticlesTableSeederとテーブル名の1文字目がAと大文字になっていることに注意してください。
Seeder created successfully.と出力されればシーダーファイルの作成は完了です。
first-app/database/seeders配下にArticlesTableSeeder.phpが作成されていればOKです。
シーダーファイルの記述
それでは作成したArticlesTableSeeder.phpをVSCodeで開いて中身を確認してみましょう。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class ArticlesTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
//
}
}
まず、シーダーファイルはSeederクラスを継承したクラスです。
このSeederクラスを継承していることで、Laravelの機能であるシーダーを使用することができます。
また、シーダーファイルはデフォルトでrunメソッドを含んでいます。
このrunメソッド内に、データベースへ入れるデータの内容を記述することでテストデータを作成することができます。
それでは、さっそくテストデータ作成のための編集を行っていきましょう。
下記のようにfirst-app/database/seeders/ArticlesTableSeeder.phpを編集してください。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB; // ここを追加
class ArticlesTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// --- ここから追加 ---
DB::table('articles')->insert([
'title' => '1番目の投稿',
'content' => 'これは1番目の投稿のテストデータです',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
DB::table('articles')->insert([
'title' => '2番目の投稿',
'content' => 'これは2番目の投稿のテストデータです',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
DB::table('articles')->insert([
'title' => '3番目の投稿',
'content' => 'これは3番目の投稿のテストデータです',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
// --- ここまで追加 ---
}
}
では、記述したコードの説明をしていきます。
まず、use Illuminate\Support\Facades\DB;を記述することで、DBファサードという機能を使えるようにしています。
ファサードという機能はとても難しい概念なので、まずはDBファサードを使用することでデータベースに関連した作業ができるというイメージだけを持っていただければ大丈夫です。
次に、DB::table('テーブル名')->insert([追加するデータ群]);と記述することで記述したテーブル名のテーブルへデータを追加することができるようになります。
ちなみに追加するデータは'カラム名' => '追加するデータ'と記述します。
articlesテーブルにはtitleとcontentという2つのカラムが存在しているので、それぞれに追加するデータを入れ、created_atとupdated_atにdate('Y-m-d H:i:s')と記述することで、現在の日時を入れる処理をしています。
idカラムには作成した順に自動的に番号が入るので指定しなくてOKです。
これでシーダーファイルの編集は終わりです。
シーダーファイルの登録
作成したシーダーファイルは、実行するためにDatabaseSeeder.phpへ登録することが必要です。
登録の仕方は下記の通りです。
database/seeders/DatabaseSeeder.phpを下記の通り編集してください。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
$this->call(ArticlesTableSeeder::class); // ここを追加
// \App\Models\User::factory(10)->create();
// \App\Models\User::factory()->create([
// 'name' => 'Test User',
// 'email' => 'test@example.com',
// ]);
}
}
作成したシーダーファイルをDatabaseSeeder.phpへ登録するときは、$this->call(シーダークラス::class)と記述するだけです。
今回作成したシーダーファイルはArticlesTableSeeder.phpでクラス名はArticlesTableSeederクラスとなっているので、$this->call(ArticlesTableSeeder::class);となります。
シーダーファイルの実行
最後にシーダーファイルを実行していきましょう。
ターミナルで下記コマンドをfirst-appディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan db:seed
下記のような実行結果が返ってくれば成功です。
Seeding: Database\Seeders\ArticlesTableSeeder
Seeded: Database\Seeders\ArticlesTableSeeder (52.20ms)
Database seeding completed successfully.
では、実際にデータが作成されているのかをhttp://localhost:8888/へアクセスして、phpMyAdminにて確認してみましょう。
下記画像のように、データベースfirst_appのarticlesテーブルに3つのレコードが登録されていればOKです。

これでテストデータの作成は完了です。
システムの実装より先にテストデータをに作成しておくことで、実装時に動作確認などがしやすくなるので、シーダーでのテストデータ作成は覚えておきましょう。
★検索ワード
・Laravel シーダーファイル作成
・Laravel テストデータ作成
MVCモデル
これからの学習をよりわかりやすくするためにMVCモデルというアプリケーションを作成する際のデザインパターンについて説明していきます。
MVCモデルとはアプリケーション開発で採用されるコードの構成手法です。
ユーザーに対する入出力処理とシステムの内部処理を分離することを目的としており、システムの機能を3つの役割に分けて開発を行っていきます。
モデル(Model)
1つ目はモデルです。
主にアプリケーションのデータに対する操作を担います。
データベースへデータ登録する処理やデータ取得の処理を記述することが理想です。
ビュー(View)
2つ目はビューです。
主にユーザーに対する入出力処理を担います。
実際にユーザーが見る画面がこのViewです。
コントローラー(Controller)
そして最後はコントローラーです。
主にModelとViewの橋渡しを担います。
ビューを表示させる処理やデータをモデルからビューへ渡す処理など幅広い処理を記述することができます。
3つの関連性
これら3つの頭文字をとりMVCモデルと呼ばれています。
LaravelではMVCモデルが採用されており「どのディレクトリにM・V・Cを格納するのか」がざっくり決まっています。
また、LaraveだけではなくRubyのフレームワークであるRuby on Railsなどでも採用されており、その他にもたくさんのフレームワークがMVCモデルを採用しています。
そんな汎用性の高いMVCモデルですが、それぞれ3つの関連性をわかりやすくするために図を使って説明していきます。

上の図を見てください。
MVCモデルの場合、クライアントからアクセスが来るとアクセスの処理をControllerへ割り振ります。
データの取得や変更などを行う場合は、ControllerからModelに移動してデータの処理を行います。
Modelではデータベースとの接続ができるので、受け取ったデータを処理した後にControllerへデータを渡します。
その後ControllerからViewへデータを受け渡し、Viewでサイトの表示を行うという流れになっています。
流れを確認しただけですべてを理解することはできないので、それぞれのModel、View、Controllerについて学習しながら、少しずつ理解していきましょう!
今の段階では、「Laravelはサイトを表示するための役割を3つに分けて実装していくのだな」というイメージを持っておいていただければOKです。
★検索ワード
・フレームワーク MVCとは
・Laravel MVC
モデルについて
それではさっそくMVCの中のモデル(Model)について学習していきます。
先ほども説明した通り、モデルはデータベースへのアクセスやデータ操作などの役割を担います。
Laravelでは、モデルを簡単に実装するための仕組みが用意されているのでさっそく使ってみましょう。
モデルファイルの作成
モデルファイルもマイグレーションファイルやシーダーファイルのように、Artisanコマンドを使用して作成することができます。
※Artisanコマンドとは、php artisan ~~~~~~のコマンドのことです。
コマンドは以下の通りです。
$ ./vendor/bin/sail php artisan make:model クラス名
基本クラス名はモデルを使用して操作したいテーブル名の単数形を使用します。
そのため、articlesテーブルのデータを取得したりするモデルファイルを作成する場合のモデル名はArticleとなります。
それではarticlesテーブルのモデルファイルを作成するので、ターミナルで下記コマンドをfirst-appディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:model Article
Model created successfully.と出力されればモデルファイルの作成は完了です。
first-app/app/Models配下にArticle.phpが作成されていればOKです。
Laravelのモデルは、Eloquentモデルとも言うことができます。
EloquentとはLaravelで提供されているデータ操作のための機能であり、モデルとデータベースを紐付ける役割をしています。
それでは作成したArticle.phpを確認してみましょう。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
use HasFactory;
}
ファイルの中にはModelクラスを継承したArticleクラスが作成されています。
ModelクラスはLaravelが初期機能として持っているクラスであり、このクラスを継承しているため、Articleクラスはモデルとしての機能を発揮することができます。
また、作成したArticleクラスとLaravelに接続しているデータベースのarticlesテーブルが、Laravelによって自動的に紐づけられます。
よって、Articleクラスを操作することで簡単にテーブルの操作を行うことができます。
Article.phpの中のArticleクラスと、データベースのarticlesテーブルで名前が単数形と複数形で異なることに注意してください。
Eloquentでは、モデルのクラス名を複数形にしてアッパーキャメルケースだったものをスネークケースにしたものが、紐づけ先のテーブルとして自動的に認識されます。
少し難しい言葉が並んでいますが、例は下記の通りです。
※アッパーキャメルケース例:OpenHoursのように先頭の文字と単語の区切り文字を大文字している名前
※スネークケース例:open_hoursのようにアンダースコア(アンダーバー)で区切られている名前
例えば「admin_statuses」というテーブルに紐づけたい場合のクラス名は、下記のようになります。
①単数形にする(admin_statuses->admin_status)
②アッパーキャメルケースにする(admin_status->AdminStatus)
つまり、php artisan make:model AdminStatusというコマンドになります。
今回は、articlesテーブルに紐づけたいので、Articleというファイル名にしてあります。
※モデルのクラス名に拡張子が付いたものがモデルのファイル名になります。
モデルの記述
モデルファイルはほとんど中身を変更せずに使用することができますが、追加しておいた方がいいこともあります。
まずは、下記の通りfirst-app/app/Models/Article.phpを編集してください。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
use HasFactory;
// テーブル名
protected $table = 'articles';
// 可変項目
protected $fillable = [
'title',
'content',
];
}
まず追加したのは下記のコードです。
protected $table = 'articles';
先ほど説明した通り、モデルファイルはテーブル名の単数形であり、アッパーキャメルケースであれば、自動的にテーブルとモデルが紐付けられますが、protected $tableを使用することで任意のテーブル名を指定することもできます。
もしモデルクラス名とテーブル名が全く違う場合などはこのように記述してください。
今回のコードであれば、記述しなくても自動的にモデルファイルとテーブルは結びついていますが、解説のためにこちらも記述しております。
次に追加したのが下記のコードです。
// 可変項目
protected $fillable = [
'title',
'content',
];
こちらのコードはコントローラーなどでデータベースに登録する際に使用するcreateメソッドを使うために記述します。
createメソッドを使用しない場合は必要ありませんが、将来的に使用するので初めに記述しておきましょう。
まずはprotected $fillable = []と記述し、[]の中にはテーブルの可変項目のカラム名を記述していきます。
今回はarticlesテーブルの2つのカラムtitleとcontentは、各レコードによって入れたい値が可変になるので[]に記述しています。
基本$fillableにはidカラムとcreated_atカラム、updated_atカラム以外のカラムを入れておけばOKです。
状況に応じてidカラムやcreated_atカラム、updated_atカラムなども入れておきましょう。
これでモデルファイルの作成は終わりです。
モデルを学習しただけだと全体像が掴めないと思うので、次に進んでみましょう。
★検索ワード
・Laravel モデル作成
・Laravel Eloquent
・Laravel fillable guarded
・Laravel モデル テーブル名指定
ルート(Route)について
LaravelにはMVCの他にルート(Route)という機能が存在しています。
今回はそんなルートについて学習していきましょう。
ルートとは
ルートとはLaravelでルーティングを行う機能です。
ルーティングとはブラウザからアクセスがあった際に、どのコントローラーのどの処理を動かすのかを定義することを言います。
もう少しわかりやすく言うと、「あるURLへリクエストがあったとき、コントローラーのどのメソッドを実行するのかを命令する機能」です。
MVCの図にルートを追加してもう一度確認してみましょう。

このようにブラウザからあるURLへアクセスがあると、始めにルートにたどり着き、どのコントローラーのどのメソッドに処理を振り分けるか決定します。
そこから振り分けられたコントローラーが指定されたメソッドを実行するというイメージです。
では、なぜルートという機能が必要なのでしょうか?
それは、コントローラーはアプリケーション内に複数あることが普通であり、URLごとにどのコントローラーのどのメソッドに振り分けるのかを決める必要があるからです。
つまり、ルートがないとURLにアクセスがあった時に、どのコントローラーのどのメソッドを実行すればいいかLaravelが認識できないので、ページを表示することができなくなってしまいます。
このように、ルートはページを表示させるためには無くてはならない機能なのです。
web.phpについて
Laravelではルーティングをfirst-app/routes/web.phpファイルで行います。
web.phpはすでに下記のような記述がされています。
<?php
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
※余計なコメントなどは消しています。
Route::get()というコードが記述されていますが、第一引数に「指定したいパス」、第二引数には「第一引数のパスへアクセスがあった場合に実行する処理」を記述します。
この場合、パスは/だけなのでhttp://localhostが開かれたら、return view(‘welcome’);を実行する、という意味になります。
このルーティングこそが最初に見た下記画面を表示しているルートです。

なお通常、第二引数にはコントローラーとメソッド名を記述しますが、デフォルトの記述のように無名関数を使用してweb.phpにそのまま処理を記述することもできます。
return view('welcome')は、ヘルパーメソッドのViewを使用して、first-app/resources/views/welcome.blade.phpというBladeファイルを表示するという意味になります。
※ヘルパーメソッドは、Laravelの中に最初から組み込まれている関数のことです。
また、今回始めてBladeファイルというものが出てきました。
詳しくはビューのレクチャーで説明でしますが、このBladeファイルがLaravelで見た目を表示するためのファイルです。
少しwelcome.blade.phpを見てみましょう。
VSCodeでfirst-app/resources/views/welcome.blade.phpを開いてください。
<!-- ここから上は省略 -->
<div class="p-6">
<div class="flex items-center">
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-8 h-8 text-gray-500"><path d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"></path></svg>
<div class="ml-4 text-lg leading-7 font-semibold"><a href="https://laravel.com/docs" class="underline text-gray-900 dark:text-white">Documentation</a></div>
</div>
<div class="ml-12">
<div class="mt-2 text-gray-600 dark:text-gray-400 text-sm">
Laravel has wonderful, thorough documentation covering every aspect of the framework. Whether you are new to the framework or have previous experience with Laravel, we recommend reading all of the documentation from beginning to end.
</div>
</div>
</div>
<!-- ここから下は省略 -->
今回はwelcome.blade.phpのすべてを表示するととても長くなってしまうので、下記画像の赤枠部分のみを抜粋しています。

ファイルの中身を見ると、DocumentationというテキストやLaravel has wonderful...という文章が記述されているのがわかると思います。
例えばDocumentationをドキュメントと変更して、ファイルを保存すると下記画像のようになります。

Bladeファイルについては後ほど学習するので、現時点で気にする必要はありません。
ルートの記述
では、このweb.phpへ新たに1つのルートを追加してみましょう。
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ArticleController; // ここを追加
Route::get('/', function () {
return view('welcome');
});
// 投稿一覧を表示
Route::get('/articles', [ArticleController::class, 'showArticles'])->name('showArticles'); // ここを追加
まず、後ほど作成するArticleコントローラーを使えるように下記の1文を追加しています。
use App\Http\Controllers\ArticleController;
※ArticleControllerはまだ作成していません。
このuse文を使用することで、後ほど作成するコントローラーをweb.phpで使用することができるようになります。
web.phpで他のコントローラーを使用する場合はuse App\Http\Controllers\コントローラーのクラス名を記載しましょう。
初めてのルーティングとして、投稿一覧を表示するためのルートを記述したいので、Route::get('/articles', [ArticleController::class, 'showArticles'])->name('showArticles');というコードを追加しました。
ルートファイルでは、Routeファサードという便利機能を使用することができます。
Route::と記述したあとにHTTPリクエストを記述してください。
今回は記事一覧をただ表示するだけなので、HTTPリクエストはgetを指定していますが、他にもpostなどが使用できます。
第一引数では先ほど学んだ通り'/articles'というパスを設定しています。
そのため、http://localhost/articlesというURLにアクセスすると、ここへルーティングされるということです。
第二引数内に記述しているArticleCotroller::classは、後ほど作成するArticleコントローラーを指定しています。
また、showArticlesはArticleコントローラー内に定義されているメソッド名です。
つまり[ArticleController::class, 'showArticles']と記述することで、http://localhost/articlesへアクセスがあった場合にArticleコントローラーのshowArticlesメソッドが実行されるということです。
また、Laravelのヘルパーメソッドであるnameメソッドを使用することによって、ルートごとに名前を付けることができます。
今回の場合だと、記事一覧を表示させる画面のルートにshowArticlesと名前を付けたことになります。
この段階では何が便利かわかりにくいですが、ルートに名前を指定しておけばルートを指定する時にとても便利なので、なるべくわかりやす名前を付けておきましょう!
まとめると、Route::get('/articles', [ArticleController::class, 'showArticles'])->name('showArticles');と記述することで、http://localhost/articlesにアクセスがあった場合、ArticleコントローラーのshowArticlesメソッドが実行されるということです。
今のArticleコントローラーを作成する前の状態でhttp://localhost/articlesにアクセスしても、下記画像のようなエラーが発生します。

ArticleCotrollerがないよ!としっかりエラーが出ていますね!
Laravelはエラー画面がとてもわかりやすいのも特徴です!
★検索ワード
・Laravel ルーティング
・Laravel ルート 記述方法
コントローラー(Controller)について
ルートの説明でArticleコントローラーが登場したので、次はMVCのCであるコントローラーについて説明します。
コントローラーでは、ビューを表示させる処理やモデルからビューへデータを渡す処理など幅広い処理を記述することができます。
Laravelでは、コントローラーを簡単に実装するための仕組みが用意されているのでさっそく使ってみましょう。
コントローラーファイルの作成
コントローラーファイルもArtisanコマンドを使用して作成することができます。
コマンドは以下の通りです。
$ ./vendor/bin/sail php artisan make:controller クラス名
基本クラス名はテーブル名の単数形にControllerを連結させたものです。
そのため、articlesテーブルのコントローラーを作成する場合のクラス名はArticleControllerとなります。
それではコントローラーファイルを作成するので、ターミナルで下記コマンドをfirst-appディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:controller ArticleController
Controller created successfully.と出力されればコントローラーファイルの作成は完了です。
first-app/app/Http/Controllers配下にArticleController.phpが作成されていればOKです。
それでは作成したArticleController.phpを確認してみましょう。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ArticleController extends Controller
{
//
}
ファイルの中にはControllerクラスを継承したArticleControllerクラスが作成されています。
ControllerクラスはLaravelが初期機能として持っているクラスであり、このクラスを継承しているため、ArticleControllerクラスはコントローラーとしての機能を発揮することができます。
もう一度web.phpに追加したコードを確認してみましょう。
Route::get('/articles', [ArticleController::class, 'showArticles'])->name('showArticles');
上記のコードをweb.phpへ追加しましたが、簡単に言うとhttp://localhost/articlesへアクセスがあった場合、ArticleコントローラーのshowArticlesメソッドを実行するという意味でした。
コントローラーの記述
ここまででArticleコントローラーは作成できたので、showArticlesメソッドを作成していきましょう。
first-app/app/Http/Controllers/ArticleController.phpを下記のように編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ArticleController extends Controller
{
// --- ここから追加 ---
/**
* 記事一覧を表示する
*/
public function showArticles()
{
return view('articles.index');
}
// --- ここまで追加 ---
}
まず、ルーティングで指定したshowArticlesメソッドを作成します。
web.phpで指定したメソッドをコントローラーファイル内で定義する場合は、public function メソッド名(){}と記述してください。
そして{}内にメソッドの処理を記述していきます。
今回はreturn view('articles.index')がshowArticlesメソッドの処理として記述されています。
このreturn view()は以前ルーティングのところでも説明しましたが、ヘルパーメソッドのViewメソッドを使用して、first-app/resources/views配下のBladeファイルを表示するという意味です。
また、()内に'articles.index'と記述されているので、first-app/resources/views/articles/配下のindex.blade.phpというBladeファイルを表示するという処理になっています。
さらに下記のコメントのようなものが、メソッドの上に追加されています。
/**
* 記事一覧を表示する
*/
これはPHPDocと呼ばれていて、クラスやメソッドの上に記述することでどのようなクラスやメソッドなのかを説明することができる機能です。
現段階でPHPDocについて意識する必要は全くないので、コメント程度に思っておいていただければOKです!
ビューを表示する
コントローラーの作成から記述までが終わったので、さっそくhttp://localhost/articlesへアクセスしてみましょう。
アクセスすると下記のようなエラーが出ると思います。

エラーの内容はfirst-app/resources/views/articles配下にindex.blade.phpというファイルが存在しないという内容です。
showArticlesメソッドでreturn view('articles.index')と指定しましたが、まだBladeファイルを作成していないので当然のエラーですね。
では、さっそくBladeファイルを作成してみましょう。
コマンドでも画面上でそのまま作成してもどちらでも大丈夫です。
まずはfirst-app/resources/views配下にarticlesディレクトリを作成してください。
コマンドで作成する場合は、ターミナルで下記コマンドをfirst-appディレクトリ上で実行してください。
$ mkdir resources/views/articles
次に作成したarticlesディレクトリにindex.blade.phpを作成してください。
コマンドで作成する場合は、ターミナルで下記コマンドをfirst-appディレクトリ上で実行してください。
$ touch resources/views/articles/index.blade.php
作成したindex.blade.phpを開いて、下記の通り編集してください。
Laravelで初めての画面出力
編集して保存したらhttp://localhost/articlesへアクセスしてください。
ブラウザにLaravelで初めての画面出力と表示されていればOKです。
詳しいBladeファイルの記述方法はビューの解説で説明しますので、まずはコントローラーでBladeファイルを表示する順序だけわかっていただければOKです。
画面表示の流れの確認
一度Bladeファイルを表示する流れをおさらいしておきましょう。
まず、URL(http://localhost/articles)にアクセスがされると、ルーティングの処理(Route::get('/articles', [ArticleController::class, 'showArticles'])->name('showArticles');)が実行されます。
ルーティングでは、ArticleコントローラーのshowArticlesメソッドを実行するように処理が記述されています。
そのため、first-app/app/Http/Controllers/ArticleController.phpの中のArticleControllerクラスのshowArticlesメソッドを実行します。
showArticlesメソッドにはreturn view('articles.index')と記述されており、first-app/resources/views/articles/index.blade.phpを表示する処理になっています。
index.blade.phpを確認するとLaravelで初めての画面出力とテキストが記述されているので、URL(http://localhost/articles)にアクセスがされると、ブラウザにLaravelで初めての画面出力というテキストが表示されることになります。
今回はテキストのみの表示だけだったので、データベースへの接続がない分モデルは登場しませんでした。
なので、下記画像の②データ処理と③DB接続は行っていません。

後ほど記事一覧を表示させる時に②と③の方法については説明します。
★検索ワード
・Laravel コントローラー作成
・PHPDocとは
ビュー(View)について
コントローラーの説明で作成したfirst-app/resources/views/articles/index.blade.phpですが、このBladeファイルこそがLaravelのビュー(View)にあたります。
Bladeテンプレートとは
そもそもBladeファイルはBladeテンプレートというテンプレートエンジンで作成されたファイルのことです。
テンプレートエンジンとは、テンプレート部分とデータを合成してドキュメントを出力するソフトウェアのことを指します。
いまいちイメージが湧かないかもしれませんが、HTMLとPHPが直接記述できる便利機能だと思っていただければ大丈夫です。
また、Bladeテンプレートではテンプレートの継承やデータの受け渡しを簡単に行うことができるようになっており、Laravelを扱う上で必須の知識となるので、さっそく学習していきましょう。
ちなみにテンプレートエンジンはBladeの他にもSmartyやTwigなどがありますが、LaravelではBladeを採用しています。
Bladeテンプレートの使い方
Bladeテンプレートには使用できる構文が存在しており、わかりやすいように主なものを表にまとめておきます。
| 構文 | 説明 |
|---|---|
| @extends('Bladeファイル') | ()内のBladeファイルを継承することができる |
| @yield('セクション名') | ()内のセクション名が同じ@section~@endsectionまでを読み込むことができる |
| @section('セクション名') ~ @endsection | @sectionと@endsectionで囲まれた部分を@yield部分に追加する |
| @include('Bladeファイル') | ()内のファイルを読み込むことができる |
これらの構文を使用することでHTMLファイルやPHPファイルをそのまま使用するよりも遥かに管理しやすい状態で見た目(View)を作成することができるようになります。
使い方は見た目を実装しながら、順番に説明していきます。
Bladeの記述
先ほども少し触れた通り、BladeファイルにはHTMLをそのまま記述することができます。
なので、ざっくりとした見た目を最初に作成してしまいましょう。
それでは、以前作成したfirst-app/resources/views/articles/index.blade.phpを下記のように編集してください。
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ブログ</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/css/bootstrap.min.css" integrity="sha384-r4NyP46KrjDleawBgD5tp8Y7UzmLA05oM1iAEQ17CSuDqnUK2+k9luXQOfXJCJ4I" crossorigin="anonymous">
</head>
<body>
<!-- ヘッダー -->
<header>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container-fluid">
<a class="navbar-brand" href="#">ブログ</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-item nav-link active" href="#">記事一覧 <span class="sr-only"></span></a>
<a class="nav-item nav-link" href="#">記事投稿</a>
</div>
</div>
</div>
</nav>
</header>
<!-- コンテンツ -->
<div class="container mt-4">
<div class="row">
<h2>記事一覧</h2>
<table class="table table-bordered table-hover ">
<thead class="bg-info text-light">
<tr>
<th scope="col">#</th>
<th scope="col">タイトル</th>
<th scope="col">内容</th>
<th scope="col">日付</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>テスト1</td>
<td>テスト1です。</td>
<td>2022/01/01</td>
</tr>
<tr>
<td>2</td>
<td>テスト2</td>
<td>テスト2です。</td>
<td>2022/01/02</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- フッター -->
<footer class="footer bg-primary fixed-bottom">
<div class="container text-center">
<span class="text-light">©︎Laravel教材</span>
</div>
</footer>
<!-- JavaScript -->
<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/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
</body>
</html>
編集が終わったら保存してhttp://localhost/articlesへアクセスしてみましょう。
下記画像のように画面が表示されればOKです。

まず、記事一覧に表示されている記事自体はHTMLで直接記述しているものになります。
後ほどデータベースから記事を取得して表示させますが、まずはHTMLで直接記述することで記事の表示をしておきます。
また、今回は見た目部分を整えるためにBootstrapというフレームワークを使用しています。
Bootstrapは複雑なCSSやJavaScriptを記述しなくてもHTMLにclassやidを指定することで、簡単に見た目を整えることができるので重宝されています。
そんな便利なBootstrapですが、CDNでお手軽に使用することができます。
Bootstrapを使用する際のCDNは下記のようになっています。
<!-- CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/css/bootstrap.min.css" integrity="sha384-r4NyP46KrjDleawBgD5tp8Y7UzmLA05oM1iAEQ17CSuDqnUK2+k9luXQOfXJCJ4I" crossorigin="anonymous">
<!-- JavaScript -->
<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/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
本教材はLaravelを学習するための教材なのでBootstrapについての解説は省きますが、興味のある方は公式ドキュメントがあるので見てみてください。
※Bootstrapの基本的な使い方は、ドキュメントに記述しているコードをコピペして使用していきます。
Bladeの共通化
それでは、index.blade.phpに全て記述していた見た目部分をBladeの力を使用してヘッダーやコンテンツ、フッターと共通化させていきます。
共通化の流れは下記の通りです。
- 共通のテンプレートである
layout.blade.phpを作成する - 共通のヘッダーを作成する
- 共通のフッターを作成する
-
index.blade.phpに共通のテンプレートであるlayout.blade.phpを継承させる
共通化や継承と言われてもなかなかイメージがしにくいと思うので、まずは実装していきましょう。
layout.blade.phpの作成
それでは共通のテンプレートにするためのlayout.blade.phpを作成しましょう。
first-app/resources/viewsディレクトリにlayout.blade.phpをコマンドかVSCode上で作成してください。
コマンドで作成する場合は、ターミナルで下記コマンドをfirst-appディレクトリ上で実行してください。
$ touch resources/views/layout.blade.php
作成したlayout.blade.phpにindex.blade.phpの内容をそのまま貼り付けてください。
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ブログ</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/css/bootstrap.min.css" integrity="sha384-r4NyP46KrjDleawBgD5tp8Y7UzmLA05oM1iAEQ17CSuDqnUK2+k9luXQOfXJCJ4I" crossorigin="anonymous">
</head>
<body>
<!-- ヘッダー -->
<header>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container-fluid">
<a class="navbar-brand" href="#">ブログ</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-item nav-link active" href="#">記事一覧 <span class="sr-only"></span></a>
<a class="nav-item nav-link" href="#">記事投稿</a>
</div>
</div>
</div>
</nav>
</header>
<!-- コンテンツ -->
<div class="container mt-4">
<div class="row">
<h2>記事一覧</h2>
<table class="table table-bordered table-hover ">
<thead class="bg-info text-light">
<tr>
<th scope="col">#</th>
<th scope="col">タイトル</th>
<th scope="col">内容</th>
<th scope="col">日付</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>テスト1</td>
<td>テスト1です。</td>
<td>2022/01/01</td>
</tr>
<tr>
<td>2</td>
<td>テスト2</td>
<td>テスト2です。</td>
<td>2022/01/02</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- フッター -->
<footer class="footer bg-primary fixed-bottom">
<div class="container text-center">
<span class="text-light">©︎Laravel教材</span>
</div>
</footer>
<!-- JavaScript -->
<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/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
</body>
</html>
これで今回作成したfirst-app/resources/views/layout.blade.phpとfirst-app/resources/views/articles/index.blade.phpのコードは全く同じ状態です。
共通ヘッダーの作成
次にどのページにも共通するヘッダーであるheader.blade.phpの作成を行っていきます。
first-app/resources/viewsディレクトリにheader.blade.phpをコマンドかVSCode上で作成してください。
コマンドで作成する場合は、ターミナルで下記コマンドをfirst-appディレクトリ上で実行してください。
$ touch resources/views/header.blade.php
作成したheader.blade.phpにlayout.blade.phpのヘッダー部分をコピーして、下記の通り貼り付けてください。
<header>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container-fluid">
<a class="navbar-brand" href="#">ブログ</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-item nav-link active" href="#">記事一覧 <span class="sr-only"></span></a>
<a class="nav-item nav-link" href="#">記事投稿</a>
</div>
</div>
</div>
</nav>
</header>
また、layout.blade.phpのヘッダー部分を下記の通り編集してください。
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ブログ</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/css/bootstrap.min.css" integrity="sha384-r4NyP46KrjDleawBgD5tp8Y7UzmLA05oM1iAEQ17CSuDqnUK2+k9luXQOfXJCJ4I" crossorigin="anonymous">
</head>
<body>
<!-- ヘッダー -->
@include('header')
<!-- コンテンツ -->
<div class="container mt-4">
<div class="row">
<h2>記事一覧</h2>
<table class="table table-bordered table-hover ">
<thead class="bg-info text-light">
<tr>
<th scope="col">#</th>
<th scope="col">タイトル</th>
<th scope="col">内容</th>
<th scope="col">日付</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>テスト1</td>
<td>テスト1です。</td>
<td>2022/01/01</td>
</tr>
<tr>
<td>2</td>
<td>テスト2</td>
<td>テスト2です。</td>
<td>2022/01/02</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- フッター -->
<footer class="footer bg-primary fixed-bottom">
<div class="container text-center">
<span class="text-light">©︎Laravel教材</span>
</div>
</footer>
<!-- JavaScript -->
<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/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
</body>
</html>
このように@include('header')と記述することでfirst-app/resources/views/header.blade.phpを読み込むことができます。
つまり、本来であればヘッダー部分をlayout.blade.phpに直接記述していましたが、ヘッダー部分だけをheader.blade.phpで管理することによって、ヘッダーを編集したい場合はheader.blade.phpのみを編集すればいいことになります。
このようにヘッダー部分を別ファイルで管理することによってメンテナンス性の向上や可読性に繋がります。
共通フッターの作成
それではヘッダーと同じように、どのページにも共通するフッターfooter.blade.phpの作成を行っていきます。
first-app/resources/viewsディレクトリにfooter.blade.phpをコマンドかVSCode上で作成してください。
コマンドで作成する場合は、ターミナルで下記コマンドをfirst-appディレクトリ上で実行してください。
$ touch resources/views/footer.blade.php
作成したfooter.blade.phpにlayout.blade.phpのフッター部分をコピーして、下記の通り貼り付けてください。
<footer class="footer bg-primary fixed-bottom">
<div class="container text-center">
<span class="text-light">©︎Laravel教材</span>
</div>
</footer>
また、layout.blade.phpもフッター部分を下記の通り編集してください。
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ブログ</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/css/bootstrap.min.css" integrity="sha384-r4NyP46KrjDleawBgD5tp8Y7UzmLA05oM1iAEQ17CSuDqnUK2+k9luXQOfXJCJ4I" crossorigin="anonymous">
</head>
<body>
<!-- ヘッダー -->
@include('header')
<!-- コンテンツ -->
<div class="container mt-4">
<div class="row">
<h2>記事一覧</h2>
<table class="table table-bordered table-hover ">
<thead class="bg-info text-light">
<tr>
<th scope="col">#</th>
<th scope="col">タイトル</th>
<th scope="col">内容</th>
<th scope="col">日付</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>テスト1</td>
<td>テスト1です。</td>
<td>2022/01/01</td>
</tr>
<tr>
<td>2</td>
<td>テスト2</td>
<td>テスト2です。</td>
<td>2022/01/02</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- フッター -->
@include('footer')
<!-- JavaScript -->
<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/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
</body>
</html>
ヘッダーと同じく@include('footer')と記述することでfirst-app/resources/views/footer.blade.phpを読み込むことができます。
これでフッター部分はfooter.blade.phpで管理することができるようになりました。
index.blade.phpにlayout.blade.phpを継承させる
それでは、残っているコンテンツ部分を@yieldと@section~@endsectionで読み込ませるようにしてから、index.blade.phpにlayout.blade.phpを継承させていきます。
コンテンツの表示
まずはコンテンツ部分を@yieldと@section~@endsectionを使用して読み込ませる記述をしていきます。
layout.blade.phpを下記の通り編集してください。
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ブログ</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/css/bootstrap.min.css" integrity="sha384-r4NyP46KrjDleawBgD5tp8Y7UzmLA05oM1iAEQ17CSuDqnUK2+k9luXQOfXJCJ4I" crossorigin="anonymous">
</head>
<body>
<!-- ヘッダー -->
@include('header')
<!-- コンテンツ -->
<div class="container mt-4">
@yield('content')
</div>
<!-- フッター -->
@include('footer')
<!-- JavaScript -->
<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/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
</body>
</html>
まず共通のレイアウトとなるlayout.blade.phpのコンテンツ部分にある<div class="container mt-4"></div>以外のコードを全て削除してから、代わりに@yield('content')と記述します。
このように@yield('content')と記述することで、あるファイルでlayout.blade.phpが継承された時に@section('content')と@endsectionで囲まれた部分を@yield('content')部分に読み込ませることができます。
それではindex.blade.phpでlayout.blade.phpを継承して、@section('content')と@endsectionで囲んだ部分にコンテンツを記述してみましょう。
index.blade.phpを下記の通り編集してください。
@extends('layout')
@section('content')
<div class="row">
<h2>記事一覧</h2>
<table class="table table-bordered table-hover ">
<thead class="bg-info text-light">
<tr>
<th scope="col">#</th>
<th scope="col">タイトル</th>
<th scope="col">内容</th>
<th scope="col">日付</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>テスト1</td>
<td>テスト1です。</td>
<td>2022/01/01</td>
</tr>
<tr>
<td>2</td>
<td>テスト2</td>
<td>テスト2です。</td>
<td>2022/01/02</td>
</tr>
</tbody>
</table>
</div>
@endsection
まず継承するためには@extends()を使用します。
@extends()を使用し()内に継承したいBladeファイルを記述することで、index.blade.phpに()内で指定したBladeファイルを継承することができます。
今回はindex.blade.phpにlayout.blade.phpを継承したいので、@extends('layout')となります。
無事にlayout.blade.phpを継承することができたので、layout.blade.phpの中に記述されている全てのコードがindex.blade.phpに記述されているのと同じ状況を作り出すことができました。
そして、最後にindex.balde.phpの先ほどlayout.blade.phpで削除したコンテンツ部分を@section('content')と@endsectionで囲まれた部分に記述すればOKです。
さっそくhttp://localhost/articlesへアクセスしてみましょう。
下記画像のように画面が表示されればOKです。

これで無事にコンテンツ部分を読み込ませることに成功しましたが、実はあと1つ@yield()と@section()~@endsectionで記述した方が良い箇所があります。
それはlayout.blade.phpのtitle要素です。
タイトル部分はページ毎に変更したいので、ここも@yield()を使用してページ毎に変更可能にさせましょう。
それでは、layout.blade.phpを下記の通り編集してください。
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>@yield('title', 'ブログ')</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/css/bootstrap.min.css" integrity="sha384-r4NyP46KrjDleawBgD5tp8Y7UzmLA05oM1iAEQ17CSuDqnUK2+k9luXQOfXJCJ4I" crossorigin="anonymous">
</head>
<body>
<!-- ヘッダー -->
@include('header')
<!-- コンテンツ -->
<div class="container mt-4">
@yield('content')
</div>
<!-- フッター -->
@include('footer')
<!-- JavaScript -->
<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/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
</body>
</html>
変更した箇所は、title要素の中身です。
変更前はブログというテキストのみを記述していましたが、このままだと全てのページでtitle要素がブログになってしまいます。
代わりに@yield('title', 'ブログ')と記述することで、layout.blade.phpを継承したファイルで@section('title')~@endsectionを使用することで、ページ固有のタイトルを決めることができるようになります。
また、万が一継承したファイルに@section('title')~@endsectionがない場合も@yield()の第二引数であるブログがページのタイトルになるので、設定し忘れたとしてもしっかりとタイトルブログが表示されます。
では、次にindex.blade.phpを下記の通り編集してください。
@extends('layout')
@section('title')
記事一覧
@endsection
@section('content')
<div class="row">
<h2>記事一覧</h2>
<table class="table table-bordered table-hover ">
<thead class="bg-info text-light">
<tr>
<th scope="col">#</th>
<th scope="col">タイトル</th>
<th scope="col">内容</th>
<th scope="col">日付</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>テスト1</td>
<td>テスト1です。</td>
<td>2022/01/01</td>
</tr>
<tr>
<td>2</td>
<td>テスト2</td>
<td>テスト2です。</td>
<td>2022/01/02</td>
</tr>
</tbody>
</table>
</div>
@endsection
追加したコードは以下の通りです。
@section('title')
記事一覧
@endsection
layout.blade.phpで@yield('title')と記述したので、index.blade.phpでは@section('title')~@endsectionと記述します。
また、index.blade.phpは記事一覧を表示させるためのページなので、記事一覧というページタイトルにしています。
それでは改めてhttp://localhost/articlesへアクセスしてみましょう。
下記画像のように画面が表示されていて、Chromeのタブ部分であるページタイトルが記事一覧になっていればOKです。

このようにBladeで共通化してもユーザーが見る見た目はindex.blade.phpへ全てのHTMLを記述したときとまったく変わっていません。
その代わりindex.blade.phpの内容はlayout.blade.phpを継承したことで大きく変わっています。
これからBladeファイルを作成するときはlayout.blade.phpを継承し、titleとcontent部分だけを記述すればページが完成します。
まだ記事一覧ページしか作成していないのでBladeの恩恵を感じにくいかもしれませんが、これから記事詳細画面や記事作成画面、記事編集画面を作成していく中でより恩恵を感じられると思います。
ひとまず記事一覧画面の見た目の作成はこれで完了です。
★検索ワード
・Laravel view 作成
・Laravel view 共通化
・Laravel @extends
・Laravel @yield
・Laravel @section @endsection
・Laravel @include
・Bootstrap 使い方
全ての記事を取得して一覧で表示
ここまでで記事一覧画面をHTMLのみで作成しましたが、これからは記事のデータをデータベースから取得して、取得したデータを表示するようにしていきます。
今回初めてLaravelを使用してデータベースからデータを取得するので、どのようにデータを取得して表示させるのかの流れを確認していきます。
流れは下記のようになっています。
- モデルを使用してデータベースから記事データを取得する
- コントローラーでモデルを呼び出してビューにデータを渡す
- ビュー(Bladeファイル)で記事データを全て表示する
それでは流れに沿って一つひとつ解説していきます。
なお、コードは後ほど書き換えるので下記の説明時には変更しなくて大丈夫です。
記事データを取得する方法
Laravelのモデルを使用してデータを取得する方法はいくつかありますが、全てのデータを取得したい場合にはallメソッドを使用することができます。
allメソッドの使い方は下記の通りです。
$変数名 = モデルクラス名::all();
データを格納するための変数を用意し、モデルクラス名に:all()と記述するだけで、モデルに紐付いたテーブルのデータが全て変数に格納することができます。
今回作成しているArticleモデルを使用する場合は下記のようになります。
$articles = Article::all();
データを格納するための変数名は、データが複数あることが考えられるのでarticlesと複数形にしています。
また、モデルクラス名はfirst-app/app/Models/Article.phpファイルのクラス名になるので、Articleになります。
それに::all()を記述するだけです。
とても簡単ですね!
コントローラーでモデル呼び出してデータを渡す方法
それでは、先ほど学んだデータの取得方法のallメソッドをコントローラーのメソッド内で実行していきます。
まずfirst-app/app/Http/Controllers/ArticleController.phpを確認してみましょう。
その中にshowArticlesというメソッドがあります。
このメソッドこそが記事一覧を表示するためのメソッドです。
そのため、このメソッド内でallメソッドを使用してデータを取得し、ビュー(Bladeファイル)へデータを渡すことになります。
ビューへデータを渡す方法はいくつかありますが、まずは配列で渡す場合を説明します。
$articles = Article::all();
return view('articles.index', [
'articles' => $articles,
]);
viewメソッドの第二引数として配列を記述し、その中に'Bladeで受け取る時の名前' => 渡したいデータの変数名とすることでBladeファイルへデータを渡すことができます。
今回の例だとindex.blade.phpのBladeファイルで変数articlesのデータを受け取る時は、$articlesと記述することで受け取れるようになります。
次に配列ではなくcompact関数を使用した場合も説明します。
$articles = Article::all();
return view('articles.index', compact('articles'));
viewメソッドの使い方は同じですが、第二引数に配列ではなくcompact関数を使用しています。
compact('渡したいデータの変数名')を記述することでBladeファイルへデータを渡すことができます。
配列よりもコードがコンパクトになるので、こちらをおすすめします。
また、今回の例もindex.blade.phpのBladeファイルで変数articlesのデータを受け取る時は、$articlesと記述することで受け取れるようになります。
Bladeでデータを表示する方法
BladeファイルにはPHPを直接記述することができます。
その機能とBladeの構文を使用することで簡単にデータを表示することができます。
今回Bladeファイルで受け取るデータはarticlesテーブルの全データなので、複数あることが予想されます。
そのためforeachを使用したループ処理を使用して、全ての記事データを表示していきます。
Bladeでは@foreach~@endforeachというループ処理用の構文が用意されているので、そちらを使用してデータを表示させることができます。
今回の場合は下記のようになります。
@foreach($articles as $article)
{{ $article->id }}
@endforeach
@foreach($articles as $article)とすることで、変数articlesの要素を変数articleとして取り出すことができます。
Bladeファイル内でPHPを記述する場合は{{}}で囲んでください。
{{}}で囲うことでブラウザに値を出力することができます。
$article->idについては実装時に説明します。
実装
実際に手を動かしながら実装していきましょう。
まずはfirst-app/app/Http/Controllers/ArticleController.phpを下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article; // ここを追加
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
dd($articles);
}
}
まず追加したのは下記コードです。
use App\Models\Article;
このようにuse文を使用することで、Articleモデルを他ファイルで使用することができるようになります。
use文の使い方はuseと記述してから、使用したいファイルのnamespace部分とクラス名を記述します。
例えば、今回の場合はfirst-app/app/Models/Article.phpのnamespaceを確認します。
するとnamespace App\Models;と記述されており、クラス名はArticleとなっています。
つまり、Articleモデルを他ファイルで使用したい場合はuse App\Models\Articleと記述することになります。
これでArticleコントローラー内でArticleモデルを使用できるようになったので、Article::all()と記述することで、Articleモデルを通してarticlesテーブルから全てのデータを取得することができます。
取得したデータを変数articlesへ格納するために$articles = Article::all();と記述しています。
これでデータベースに保存されている全ての記事データを変数articlesに格納することができました。
実はLaravelには変数内にどのような値が入っているのかを確認するためのヘルパーメソッドが用意されています。
それがddメソッドです。
このddメソッドを使用すると、第一引数に記述した変数の内容をブラウザで視覚的に確認することができます。
また、ddメソッド以降の処理は実行されないので、ある時点での変数の中身を確認したい時に重宝します。
今回は全ての記事データが入っている変数articlesを確認するためにdd('articles')と記述してみました。
それではhttp://localhost/articlesへアクセスしてみましょう。
ddメソッドを使用すると、下記画像のような画面が出てきます。

#items: array:3[▶]となっている▶をクリックしてみてください。
そうすると下記画像のように中身が展開されます。

現在articlesテーブルには、テストデータとして作成した3つのデータが入っています。
その3つのデータが配列で入っていることがこれで確認できました。
次に0 => App\Mo…\Article{#319 ▶}の▶をクリックしてみましょう。
※ #319という番号は人によって違う場合がございます。

するとさらに展開され、その中に#attributes: array5 [▶]があると思います。
その▶をクリックしてみてください。

attributesが展開され、中に入っているデータを確認することができます。

これはシーダーを使用して追加した1番目のテストデータですね。
このように、モデルで取得したデータの内容はattributesに格納されるので、覚えておきましょう。
2番目や3番目のデータも同じように▶をクリックして展開していくことでデータ内容を確認することができます。
このようにddメソッドを使用すると、変数の内容をブラウザで簡単に確認することができます。
デバッグや実装中にどのような値が入っているか一旦確認したい場合に使用してみましょう。
次はコントローラーからBladeファイルに値を渡すための処理を記述しましょう。
first-app/app/Http/Controllers/ArticleController.phpを下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
}
先ほど説明した通り、コントローラーからBladeファイルへ値を渡すためにcompact関数を使っています。
return view('articles.index', compact('articles'));と記述することでindex.blade.phpに全ての記事データが入っている変数articlesを渡すことができます。
それでは渡した変数をindex.blade.phpで表示するようにしましょう。
first-app/resources/views/articles/index.blade.phpを下記の通り編集してください。
@extends('layout')
@section('title')
記事一覧
@endsection
@section('content')
<div class="row">
<h2>記事一覧</h2>
<table class="table table-bordered table-hover ">
<thead class="bg-info text-light">
<tr>
<th scope="col">#</th>
<th scope="col">タイトル</th>
<th scope="col">内容</th>
<th scope="col">日付</th>
</tr>
</thead>
<tbody>
@foreach ($articles as $article)
<tr>
<td>{{ $article->id }}</td>
<td>{{ $article->title }}</td>
<td>{{ $article->content }}</td>
<td>{{ $article->updated_at }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endsection
ArticleコントローラーのshowArticlesメソッドから全ての記事データ入った変数articlesが渡されてきたので、Bladeファイルではループ処理を使用して1つずつ表示させていきます。
Laravelでループ処理を実装したい場合は@foreach()~@endforeachを使用することで、簡単にループ処理が実装できます。
下記の通りddメソッドを使用して、変数articlesの一つひとつのデータを確認すると、テーブルのカラム名がそのままキーとして設定されていることがわかります。

そのため、記事のIDを取得したい場合は$article->id、タイトルを取得したい場合は$article->title、内容を取得したい場合は$article->content、更新日を取得したい場合は$article->updated_atと記述すればいいことになります。
また、今回は使用していませんが、作成日を取得したい場合は$article->created_atとなります。
Bladeでは、それらを{{}}で囲むことで安全にブラウザに表示することができます。
それではhttp://localhost/articlesへアクセスしてみてください。
下記のようにデータベースから取得した3つのデータが表示できていればOKです!

データ取得から表示までの流れの確認
最後にデータベースからデータを取得した後、データを表示させるまでの流れをMVCの画像を見ながら確認していきましょう。
まず、下記の画像がMVCにルートを追加した画像です。

http://localhost/articlesへアクセスがあった時に最初に読まれるファイルはfirst-app/routes/web.phpです。
これが①のアクセスです。
その中のRoute::get('/articles', [ArticleController::class, 'showArticles'])->name('showArticles');が実行されることで、ArticleコントローラーのshowArticlesメソッドが実行されます。
そのため次は、first-app/app/Http/Controllers/ArticleController.phpのshowArticlesメソッドの中身の処理を確認しましょう。
showArticlesメソッドには$articles = Article::all();という処理が記述されています。
これはArticleモデルが継承しているModelクラスが持つallメソッドを実行しています。
allメソッドを実行するとモデルがデータベースへ接続しにいき、取得したarticlesテーブルの全データをコントローラーの変数articlesに格納します。
つまり②のデータ処理がここで行われており、③のDB接続も同時に行っています。
そして、return view('articles.index', compact('articles'));と記述することで、コントローラーからビューであるfirst-app/resources/views/articles/index.blade.phpというBladeファイルへ変数articlesを渡しています。
これが④のデータ譲渡です。
そして最後にfirst-app/resources/views/articles/index.blade.phpで@foreachを使用して全てのデータを一つひとつ表示しているという流れです。
これが⑤のサイト表示です。
Laravelでは、このように①→②→③→②→④→⑤のような流れでデータを取得し、表示することができます。
これがMVCの基本的な流れになるので、ぜひ覚えておきましょう!
★検索ワード
・Laravel 全てのデータを取得
・Laravel Eloquent all
・Laravel ddメソッド
・Laravel compact
・Laravel View データを渡す
記事詳細を取得して表示
ここまでで全ての記事を一覧で表示できるようになりましたが、今回は一つひとつの記事の詳細画面を作成していきます。
記事詳細画面を作成する流れは下記のようになります。
- 詳細を表示する記事のIDでリンクを作成する
- ルートでIDを受け取りコントローラーへ渡す
- 受け取ったIDのデータをモデルを使用して取り出す
- ビューで表示する
それでは流れに沿って一つひとつ実装していきましょう。
なお、コードは後ほど書き換えるので下記の説明時には変更しなくて大丈夫です。
詳細表示する記事のIDでリンクを作成する
特定の記事を取得するためにはIDを使用します。
IDを使用する理由は、IDとは記事一つひとつに与えられた固有の値であり、同一テーブル内でIDがかぶることがないからです。
例えばIDが1の記事を取得する場合は下記のようなURLにします。
http://localhost/article/1
こうすることでURL内の1とテーブル内のIDが1のデータが紐付けられるので、IDが1のデータを取得することができます。
ルートでIDを受け取りコントローラーへ渡す
では、http://localhost/article/1というURLへアクセスされると、ルート(web.php)へ処理が移動します。
なので、ルートでIDを受け取り、その後コントローラーにもIDを渡すことが必要になります。
そのためのルートの記述は下記のようになります。
Route::get('/article/{id}', [ArticleController::class, 'showArticle']);
このような記述をすることで、http://localhost/article/1へアクセスされるとArticleコントローラーのshowArticleメソッドが実行されるようになります。
ちなみに、showArticleメソッドは後ほど作成します。
また、'/article/{id}'の{id}についてですが、ルートパラメーターと呼ばれていて、{}で囲むことで定義することができます。
今回の例だと$idという変数で値をコントローラーへ渡すことができます。
つまり、http://localhost/article/1へアクセスがあった場合、1を引数idとしてshowArticleメソッドに渡すことができるということです。
受け取ったIDのデータを取り出す
showArticleメソッドでIDを受け取った後は、そのIDを使用してarticlesテーブルからIDが1のデータを取得します。
1つのデータを取得するときはallメソッドではなく、findメソッドを使用します。
具体的な使い方は下記の通りです。
$article = Article::find(1);
このようにArticle::find(1)と記述することでIDが1の記事を取得することができます。
つまり、allメソッドでは全てのデータを取得していましたが、findメソッドを使用することで特定のデータを取得することができます。
また、別の方法としてSQLのWhere句のような記述方法もあります。
$article = Article::where('id' => '1')->first();
whereメソッドとfirstメソッドを使用することで、記事IDが1のデータを取得することができます。
whereメソッドの第一引数には検索するカラム名を取得し、第二引数には検索するための値を指定します。
今回の記事IDが1のデータを取得するためには、where('id' => '1')となります。
また、whereメソッドで検索条件を決めた後にfirstメソッドを->で繋げることで、検索条件に合うデータを1つ取得することができます。
ビューで表示する
あとはコントローラーで取得したデータをビューに渡して表示するだけです。
詳細表示用のBladeファイルも後ほどlayout.blade.phpを継承して作成していきます。
実装
それでは記事詳細画面を作成する流れを説明したので、実際に手を動かしながら実装していきましょう。
まずはfirst-app/routes/web.phpを下記の通り編集してください。
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ArticleController;
Route::get('/', function () {
return view('welcome');
});
// 記事一覧を表示
Route::get('/articles', [ArticleController::class, 'showArticles'])->name('showArticles');
// 記事詳細を表示
Route::get('/article/{id}', [ArticleController::class, 'showArticle'])->name('showArticle'); // ここを追加
これで記事詳細画面を表示するルートを記述することができました。
先ほど説明した通り、ルートパラメーターを使用して記事IDをルートで受け取り、コントローラーへ渡せるようにしています。
次にfirst-app/resources/views/articles/index.blade.phpを下記の通り編集してください。
@extends('layout')
@section('title')
記事一覧
@endsection
@section('content')
<div class="row">
<h2>記事一覧</h2>
<table class="table table-bordered table-hover ">
<thead class="bg-info text-light">
<tr>
<th scope="col">#</th>
<th scope="col">タイトル</th>
<th scope="col">内容</th>
<th scope="col">日付</th>
</tr>
</thead>
<tbody>
@foreach ($articles as $article)
<tr>
<td>{{ $article->id }}</td>
<td>
<a href="{{ route('showArticle', $article->id) }}">
{{ $article->title }}
</a>
</td>
<td>{{ $article->content }}</td>
<td>{{ $article->updated_at }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endsection
変更したコードは下記の通りです。
<td>
<a href="{{ route('showArticle', $article->id) }}">
{{ $article->title }}
</a>
</td>
上記のコードを見ていただくとわかるように、記事タイトルをaタグで囲ってリンクにしています。
今回は記事タイトルをリンクにし、クリックされた時に記事詳細画面を表示するようにしていきます。
先ほど、記事詳細画面を作成する流れでも説明した通り、まずは記事の詳細を表示するための記事IDに結びついたリンクを作成しています。
aタグのリンク先を決めるためにはhref属性を使用しますが、そのhref属性にLaravelのヘルパーメソッドであるrouteメソッドを使用します。
routeメソッドの第一引数を見てみるとshowArticleとなっています。
このshowArticleはルーティングでshowArticleメソッドを実行するルートのルート名です。
なんとなくルーティング時に指定していたルート名ですが、ここでようやく使用するときがやってきました。
それではweb.phpのshowArticleメソッドを実行するためのルートを見てください。
Route::get('/article/{id}', [ArticleController::class, 'showArticle'])->name('showArticle');
ルート名を指定していたのはname('showArticle');です。
このルート名をrouteメソッドの第一引数に指定することで、指定したルートへのリンクが作成できるようになります
次に第二引数を見てみると$article->idとなっています。
これは記事IDを第二引数に指定しているということです。
このように第二引数に紐付けたい値を記述することでルートに記事ID渡すことができます。
では、実際にhttp://localhost/articlesへアクセスしてから1番目の投稿をクリックしてください。
下記のようなエラー画面が表示されますが、URLを確認してみましょう。
http://localhost/articles/1になっているはずです。

今回は記事IDが1である記事タイトル1番目の投稿をクリックしたので、URLには1が表示されています。
ちなみに、エラー内容はArticleコントローラーにshowArticleメソッドがありませんというエラーです。
なので、次はshowArticleメソッドを作成していきましょう!
受け取ったIDのデータを取り出す
まずはfirst-app/app/Http/Controllers/ArticleController.phpを下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
dd($article);
}
}
追加したコードは下記の通りです。
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
dd($article);
}
まず、ルートから渡されてきた記事IDはshowArticleメソッドの第一引数として取得することができます。
そのため、public function showArticle($id){}としましょう。
次に、受け取った記事IDの記事データを取得したいので下記コードのようにfindメソッドを使用します。
Article::find($id)
そしてddメソッドを使用して、取得したデータを一度ブラウザで確認しておきます。
http://localhost/article/1へアクセスしてみてください。

上記のようなデータが表示されるので、先ほどと同じようにattributesを展開するとデータの中身を確認することができます。
無事に記事IDが1のデータが取得できていますね!
データが確認できたので、改めてfirst-app/app/Http/Controllers/ArticleController.phpを下記の通り編集してビューに値を渡しましょう。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
}
ここは記事一覧を表示させた時と同じように、viewメソッドを使用してdetail.blade.phpへ値を渡しています。
もちろんhttp://localhost/article/1へアクセスしても、detail.blade.phpが存在しないので、View[articles.detail] not foundというdetail.blade.phpが見つからないよ!というエラーが発生します。
ビューで表示する
最後にコントローラーから渡されてきた記事データを表示するための画面を作成しましょう。
articlesディレクトリにdetail.blade.phpを作成してください。
コマンドで作成する場合は、ターミナルで下記コマンドをfirst-appディレクトリ上で実行してください。
$ touch resources/views/articles/detail.blade.php
作成したdetail.blade.phpを開いて、下記の通り編集してください。
@extends('layout')
@section('title')
記事詳細
@endsection
@section('content')
<h2>{{ $article->title }}</h2>
<div class="d-flex">
<span class="mr-2">作成日:{{ $article->created_at }}</span>
<span>更新日:{{ $article->updated_at }}</span>
</div>
<p class="mt-4">{{ $article->content }}</p>
<a href="{{ route('showArticles') }}" class="mt-3 btn btn-secondary">戻る</a>
@endsection
index.blade.phpと同じようにlayout.blade.phpを継承することで簡単にページを作成できました。
変えたのは@sectionの中身だけで、titleには記事一覧として、contentには記事のデータが表示できるようにしています。
コントローラーから記事IDが1の記事を変数articleで渡されているので$article->titleと記述すると記事のタイトルが取得できます。
その他の作成日や更新日、内容なども変数articleを使用すれば、簡単に表示させることができます。
また、今回は受け取った記事は記事IDが1の記事1つだけなのでループ処理はいりません。
そして、最後に戻るボタンを実装しています。
先ほどは記事IDに紐付いたリンクを生成しましたが、戻るボタンの実装では記事一覧画面へ飛べるリンクを生成するだけなので、特に第二引数は必要ありません。
記事一覧画面を表示させるルート名showArticlesをrouteメソッドの第一引数に記述するだけで、記事一覧画面に遷移することができます。
動作確認
それでは念の為、正しく実装できているか動作確認をしてみましょう。
まずはhttp://localhost/articlesへアクセスして、1番目の投稿をクリックしてください。
そうすると下記のように記事IDが1の記事詳細画面が表示されます。

次に戻るボタンをクリックしてみましょう。
そうすると記事一覧画面に戻ります。
最後に3番目の投稿をクリックしてみましょう。
下記のように記事IDが3の記事詳細画面が表示されればOKです。

★検索ワード
・Laravel 詳細ページ
・Laravel Eloquent find
・Laravel Eloquent where
・Laravel Eloquent first
記事投稿
ここまで記事一覧画面と記事詳細画面を作ってきましたが、ここからは記事を投稿する機能の作成をしていきます。
記事投稿の流れは下記のようになっています。
- ビューのフォームからデータを飛ばす
- ルートで受け取りコントローラーへ渡す
- フォームリクエストを使用してバリデーションをする
- モデルでデータを登録する
- エラー処理を記述
ビューのフォームからデータを飛ばす
まずは記事投稿フォームに記事のタイトルと内容が入力され、登録ボタンがクリックされた時にデータを飛ばしていきます。
ルートで受け取りコントローラーへ渡す
ルートでリクエストを受け取り、データを登録するためのメソッドにデータを渡します。
フォームリクエストを使用してバリデーションをする
Laravelにはバリデーションを簡単に実装するためのフォームリクエストという機能が最初から組み込まれています。
フォームリクエストの作成方法を説明する前に、そもそもバリデーションとは何なのかということについても学習していきましょう。
バリデーションとは
入力されたデータが正しい値かどうかをチェックする機能をバリデーションと言います。
例えば、マイグレーションでarticlesテーブルを作成した時に、テーブル定義でタイトルと内容は必須で登録しなければいけないことにしています。
しかし、記事をフォームで登録する時にタイトルと内容の2つが入力されていなかった場合は、ない値をデータベースに登録することはできないので、エラーになってしまいます。
バリデーションではこのような事態を避けるために、必須チェックというものを実装することができます。
簡単に説明すると、フォームに値が入っているか入っていないかのチェックができるということです。
また、バリデーションは必須チェックだけではなく、データの型チェックや文字数チェックなど様々なチェックをすることができます。
つまり、開発者の意図しないデータがフォームに入力されることを防ぐことができるので、セキュリティ強化の面からバリデーションはほぼ必須となります。
フォームリクエストの作成方法
それではバリデーションがどのようなものか説明したので、フォームリクエストの作成方法について説明します。
フォームリクエストも下記コマンド1つで作成することができます。
$ ./vendor/bin/sail php artisan make:request クラス名
このコマンドを使用すると、1度目の実行時にfirst-app/app/Http/ディレクトリにRequestsというディレクトリが生成され、その中にクラス名で指定した名前のPHPファイルが作成されます。
2回目以降はfirst-app/app/Http/Requestsディレクトリがすでに存在しているので、その中に新しいフォームリクエストファイルが作成されます。
使用方法などは実装する時に説明していきます。
モデルでデータを登録
すでにモデルの機能であるallメソッドとfindメソッドについて学習しましたが、モデルでデータを登録する時にはcreateメソッドというものを使用します。
使い方は下記の通りです。
Article::create([
'title' => 'タイトル',
'content' => 'コンテンツ',
]);
このようにArticle::create()と記述し、第一引数に配列で登録するデータを入れるだけです。
また、Laravelにはもう1つデータを登録するためのfillメソッド + saveメソッドが提供されています。
使い方は下記の通りです。
$article = new Article;
$article->fill([
'title' => 'タイトル',
'content' => 'コンテンツ',
]);
$article->save();
他にも登録方法はありますが、セキュリティの観点からそれらは非推奨となっているので、上記2つの方法のどちらかでデータ登録機能を実装しましょう。
おすすめは記述するコードが少ないcreateメソッドです。
また、これら2つの登録方法を使用した場合、変更できるカラムはfirst-app/app/Models/Article.phpのprotected $fillableかprotected $guardedで指定したものだけです。
そのため、first-app/app/Models/Article.phpでprotected $fillableでtitleとcontentを指定しています。
エラー処理を記述
最後にデータベースへ登録できなかった場合のエラー処理をしておきます。
これをすることによって万が一エラーが発生した時に、ユーザーが視覚的に記事を登録できないことを把握することができます。
実装
それでは記事登録の流れを説明したので、さっそく実装していきましょう。
記事投稿用のルート設定
まずは記事を登録するフォームを表示するためのルートを設定していきましょう。
first-app/routes/web.phpを下記の通り編集してください。
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ArticleController;
Route::get('/', function () {
return view('welcome');
});
// 投稿一覧を表示
Route::get('/articles', [ArticleController::class, 'showArticles'])->name('showArticles');
// 記事投稿フォームを表示
Route::get('/article/create', [ArticleController::class, 'createArticle'])->name('createArticle'); // ここを追加
// 投稿詳細を表示
Route::get('/article/{id}', [ArticleController::class, 'showArticle'])->name('showArticle');
いつも通りRoute::getでパスとコントローラー、メソッド名を指定しています。
今回は記事を作成するという意味のcreateArticleメソッドとしました。
createArticleメソッドの作成
次に、Articleコントローラーにルートで指定したcreateArticleメソッドを記述していきましょう。
first-app/app/Http/Controllers/ArticleController.phpを下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
}
追加したコードは下記の通りです。
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
これで記事投稿フォームを表示するためのcreateArticleメソッドを定義することができました。
投稿フォームに表示させるデータは何もないので、create.blade.phpへ渡す処理をviewメソッドを使用して記述するだけでOKです。
記事投稿用フォームの作成
次にfirst-app/resources/views/articles配下にcreate.blade.phpをコマンドかVSCodeで作成してください。
コマンドで作成する場合は、ターミナルにて下記コマンドをfirst-appディレクトリ上で実行してください。
$ touch resources/views/articles/create.blade.php
作成したfirst-app/resources/views/articles/create.blade.phpを下記の通り編集してください。
@extends('layout')
@section('title')
記事作成
@endsection
@section('content')
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h2>記事投稿フォーム</h2>
<form method="POST" action="">
<div class="form-group">
<label for="title">
タイトル
</label>
<input id="title" name="title" class="form-control" value="{{ old('title') }}" type="text">
@if ($errors->has('title'))
<div class="text-danger">
{{ $errors->first('title') }}
</div>
@endif
</div>
<div class="form-group mt-3">
<label for="content">
内容
</label>
<textarea id="content" name="content" class="form-control" rows="4">{{ old('content') }}</textarea>
@if ($errors->has('content'))
<div class="text-danger">
{{ $errors->first('content') }}
</div>
@endif
</div>
<div class="mt-3">
<a class="btn btn-secondary mr-2" href="{{ route('showArticles') }}">
キャンセル
</a>
<button type="submit" class="btn btn-primary">
投稿
</button>
</div>
</form>
</div>
</div>
@endsection
それではhttp://localhost/article/createへアクセスしてください。
下記画像のような画面が出てくればOKです。

フォーム部分のコードを説明していきます。
まず、form要素のmethod属性はPOSTにしています。
action属性は本来登録処理のルートを設定しなければなりませんが、今は何も指定していません。
というのも、存在しないルートをビューで指定してしまうと下記のようなエラーが出てしまうためです。

上記のエラーは、まだ存在していないルート名storeArticleをrouteメソッドで指定した時のエラーです。
次にタイトル入力欄について確認してみましょう。
<input id="title" name="title" class="form-control" value="{{ old('title') }}" type="text">
まずタイトル入力欄にはinputタグを使用しています。
name属性にはtitleを設定しており、登録処理の際にname属性で入力された値を指定することでデータを受け取ることができます。
また、value属性にold('title')と記述していますが、こちらはLaravelのヘルパーメソッドであるoldメソッドを使用しています。
()内にname属性と同じ値を指定することで、バリデーションのエラー時にフォームの値を保持することができます。
ここはバリデーション実装時に改めて確認しましょう。
また、下記のコードも追加しています。
@if ($errors->has('title'))
<div class="text-danger">
{{ $errors->first('title') }}
</div>
@endif
Bladeテンプレートでは条件分岐を@if(条件式)~@endifと記述することができます。
もちろん複数の条件分岐をしたい場合は@if(条件式A)~@elseif(条件式B)~@else~@endifのように記述することもできます。
今回のコードでは条件式に$error->has('title')と記述されています。
このコードの意味は、タイトルをバリデーションで検証した結果、エラーがあればtrue、なければfalseを出力するといったものです。
そして、エラーがあった場合はtrueとなり条件分岐内の処理が実装され、$errors->first('title')と記述されているので、1つ目のエラーメッセージが出力されます。
こちらもバリデーションを実装した時に動きを確認してみましょう。
次に内容入力欄について確認してみましょう。
ほとんどタイトル入力欄と同じですが、inputタグではなくtextareaタグを使用しています。
textarea要素にはvalue属性がないので、old('content')はtextareaの開始タグとtextareaの終了タグで囲みましょう。
バリデーションエラーを出すためのコードはタイトル部分と全く同じです。
最後にボタン部分について確認してみましょう。
まず、キャンセルボタンに関してはaタグを使用して、クリックされた時にroute('showArticles')に遷移する処理を実装しています。
こうすることで、キャンセルボタンをクリックされた後に処理一覧画面に戻すことができます。
次に投稿ボタンに関してはbuttonタグを使用して、type属性にsubmitを指定することで、ボタンがクリックされた時にフォームを送信するようにしています。
以上でフォーム部分の説明は終わりです。
記事投稿処理用のルート設定
次に投稿処理用のルートを設定していきましょう。
first-app/routes/web.phpを下記の通り編集してください。
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ArticleController;
Route::get('/', function () {
return view('welcome');
});
// 投稿一覧を表示
Route::get('/articles', [ArticleController::class, 'showArticles'])->name('showArticles');
// 記事投稿フォームを表示
Route::get('/article/create', [ArticleController::class, 'createArticle'])->name('createArticle');
// 記事登録処理
Route::post('/article/store', [ArticleController::class,'storeArticle'])->name('storeArticle'); // ここを追加
// 投稿詳細を表示
Route::get('/article/{id}', [ArticleController::class, 'showArticle'])->name('showArticle');
今回はフォームからPOSTでデータが送られてくるので、Route::getではなくRoute::postとしています。
あとはいつも通りパスとコントローラー、メソッド名を指定しています。
メソッド名は保存するという意味のstoreArticleメソッドとしました。
フォームにaction属性を追加
ルートを設定したので、create.blade.phpにaction属性を追加します。
first-app/resources/views/articles/create.blade.phpのform開始タグを下記のように変更してください。
<form method="POST" action="{{ route('storeArticle') }}">
action属性に登録処理用のルート名であるstoreArticleを指定すると、投稿ボタンがクリックされるとArticleコントローラーのstoreArticleメソッドが実行されます。
storeArticleメソッドの作成
では、コントローラーにstoreArticleメソッドを記述していきましょう。
first-app/app/Http/Controllers/ArticleController.phpを下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
/**
* 記事投稿処理
*/
public function storeArticle(Request $request)
{
dd($request);
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
}
追加したコードは下記の通りです。
/**
* 記事投稿処理
*/
public function storeArticle(Request $request)
{
dd($request);
}
フォームから送信された値はstoreArticle(Request $request)と記述することで、変数requestとして受け取ることができます。
これはArticleコントローラーの5行目あたりを見てもらうとuse Illuminate\Http\Request;と記述されています。
この機能を使用することで、Laravelではフォームから送信された値を変数requestとして受け取ることができるということです。
まずはフォームから何か値が送られてくるときはRequest $requestとなることを覚えておきましょう。
フォームから送られてきた値が本当に取得できているのかを確認するためにdd($request)と記述しておきます。
それでは、変更が完了したらhttp://localhost/article/createへアクセスし、タイトルと内容に適当なテキストを入力して投稿ボタンをクリックしてください。
すると419 PAGE EXPIREDエラー画面が表示されます。
CSRF保護
実は、Laravelでフォームを使用する場合はCSRF保護というものをしなければなりません。
そもそもCSRF(シーサーフ)とは、クロスサイトリクエストフォージェリの略で、Webアプリケーションに存在する脆弱性、もしくはその脆弱性を利用した攻撃方法のことです。
具体的には、アプリケーションに対して攻撃しようとしている攻撃者が作成した「不正なリンク」を攻撃対象者にクリックさせることで、あたかも攻撃対象者が操作したかのようにみせかけて攻撃者がアプリケーションに対して不正なリクエストを送信することです。
このような攻撃からアプリケーションを守るために、LaravelではCSRF保護というものが提供されています。
やり方は至って簡単で、layout.blade.phpに<meta name="csrf-token" content="{{ csrf_token() }}">を追加することとform開始タグ直後に@csrfというコードを追加するだけです。
こうすることでフォーム送信時にトークンが発行され、そのトークンを使用してリクエストがなりすましかどうかを判断することができます。
CSRFの概念は難しいので理解をする必要はありません。
どのようにCSRFの対策ができるのかを理解しておきましょう。
それではさっそくfirst-app/resources/views/layout.blade.phpを下記の通り編集してください。
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="csrf-token" content="{{ csrf_token() }}">
<!-- titleは可変 -->
<title>@yield('title', 'ブログ')</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/css/bootstrap.min.css" integrity="sha384-r4NyP46KrjDleawBgD5tp8Y7UzmLA05oM1iAEQ17CSuDqnUK2+k9luXQOfXJCJ4I" crossorigin="anonymous">
</head>
<body>
<!-- ヘッダー -->
@include('header')
<!-- コンテンツ -->
<div class="container mt-4">
@yield('content')
</div>
<!-- フッター -->
@include('footer')
<!-- JavaScript -->
<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/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
</body>
</html>
head要素の中に<meta name="csrf-token" content="{{ csrf_token() }}">という1コードを追加しています。
次にfirst-app/resources/views/articles/create.blade.phpのform開始タグの直後に下記コードを追加してください。
<form method="POST" action="{{ route('storeArticle') }}">
@csrf
再度http://localhost/article/createへアクセスして、タイトルと内容に適当なテキストを入れて投稿ボタンをクリックしてください。
下記画像のようにddメソッドを使用した変数requestの結果が表示されていればOKです。

その中のrequestを展開してみましょう。
requestの中にはparametersがあり、それを展開すると_token、title、contentが入っています。
_tokenはCSRF保護で作成されたトークンのことであり、titleとcontentは入力した値が入っていればフォームで入力された値が取得できていればOKです。
これで変数requestを使用すると、簡単にフォームから入力された値が取得できることが確認できました。
ddメソッドはとても便利ですね!
登録処理を実装
それでは、Articleコントローラーにフォームから受け取った値を登録する処理を記述していきましょう。
first-app/app/Http/Controllers/ArticleController.phpを下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
/**
* 記事投稿処理
*/
public function storeArticle(Request $request)
{
// 記事登録処理
Article::create([
'title' => $request->title,
'content' => $request->content,
]);
return redirect()->route('showArticles');
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
}
変更したコードは下記の通りです。
/**
* 記事投稿処理
*/
public function storeArticle(Request $request)
{
// 記事登録処理
Article::create([
'title' => $request->title,
'content' => $request->content,
]);
return redirect()->route('showArticles');
}
先ほど説明した通りcreateメソッドを使用しています。
また、return redirect()->route('showArticles');と記述しています。
以前作成した他のメソッドとは少し違いviewメソッドではなくredirectメソッドを使用して画面の遷移を実装しています。
viewメソッドもredirectメソッドも画面を遷移させる処理という点は同じですが、ルートを記述した時にRoute::getとした場合はviewメソッドを使用して、反対にRoute::postとした場合はredirectメソッドを使用して画面遷移を実装してください。
わかりやすいように表にまとめておきます。
| ルート | メソッド |
|---|---|
| Route::get | view() |
| Route::post | redirect() |
それではhttp://localhost/article/createへアクセスして、タイトルと内容に適当なテキストを入れて投稿ボタンをクリックしてください。
登録ボタンをクリックすると記事一覧画面へリダイレクトされ、入力した内容が4つ目の記事として表示されていれば記事投稿処理は無事に実装できています。
フォームリクエストを使用してバリデーションをする
投稿処理を実装することはできましたが、記事投稿画面でタイトルと内容が空の状態で投稿ボタンをクリックされると下記画像のようなエラーが発生してしまいます。

これはテーブルの設定でtitleやcontentがNULL(値がないこと)を許可していないからです。
なので、titleとcontentには何かしら文字列型の値が入っている必要があります。
その制御であるバリデーションをLaravelではフォームリクエストという機能を使用して実装します。
それでは、先ほど説明した下記コマンドをターミナルにてfirst-app配下で実行してください。
$ ./vendor/bin/sail php artisan make:request ArticleStoreRequest
今回は記事登録のバリデーションを作成したいので、クラス名はArticleStoreRequestとします。
指定したクラス名+phpの拡張子のファイルであるArticleStoreRequest.phpがfirst-app/app/Http/Requestsに作成されていればOKです。
first-app/app/Http/Requests/ArticleStoreRequest.phpを下記の通り編集してください。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ArticleStoreRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true; // falseからtrueへ変更
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'title' => 'required|string|max:100',
'content' => 'required|string',
];
}
}
まず、ArticleStoreRequestクラスではFormRequestクラスを継承していることから、フォームリクエストというLaravelの機能が使用できます。
次にauthorizeメソッドのreturn falseをreturn trueに変更してください。
こちらのauthorizeメソッドは、フォームリクエストの利用が許可されているかどうかを示すメソッドで、falseの場合はArticleStoreRequestクラスが使用できなくなるので、必ずtrueにしてください。
次にrulesメソッドのreturn [];にバリデーションをしたいカラム名を入力して=> ''と記述し、''内に、どのようなバリデーションをするかを記述します。
''内に記述できるバリデーションは多岐に渡り、ドキュメントの使用可能なバリデーションルールに全て記載されています。

これらのバリデーションルールを暗記する必要は全くありません!
使用したいバリデーションルールを実装する時にドキュメントを見ながら設定できればOKです!
今回、タイトルと内容に適応させたバリデーションルールは下記の通りです。
- title(タイトル):
required(必須チェック)、string(文字列型チェック)、max:100(文字数制限100文字まで) - content(内容):
required(必須チェック)、string(文字列型チェック)
タイトルのmax:100についてですが、テーブル設定の際に最大文字数を100文字に指定しているので、PHP側でも100文字までしかタイトルを入れられないようにしています。
これでArticleStoreRequest.phpの編集は終わりなので、Articleコントローラーへ反映させましょう。
独自に作成したフォームリクエストをコントローラーに反映させるためにはuse文を使用して、作成したフォームリクエストクラスを読み込まなければなりません。
なので、今回作成したArticleStoreRequestを読み込むためには、下記のコードがArticleコントローラーに必要となります。
use App\Http\Requests\ArticleStoreRequest;
次に記事投稿処理であるstoreArticleメソッドの引数にRequest $requestと記述していますが、Request部分をArticleStoreRequestとします。
こうすることでフォームから送信された値がArticleStoreRequest.phpで指定したバリデーションを通過した後に、Articleコントローラーの変数requestで受け取れるようになります。
それではfirst-app/app/Http/Controllers/ArticleController.phpを下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
use App\Http\Requests\ArticleStoreRequest;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
/**
* 記事投稿処理
*/
public function storeArticle(ArticleStoreRequest $request)
{
// 記事登録処理
Article::create([
'title' => $request->title,
'content' => $request->content,
]);
return redirect()->route('showArticles');
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
}
これでバリデーションの実装は完了したので、さっそく動作確認をしてみましょう。
http://localhost/article/createへアクセスして、タイトルと内容に何も入力しないで投稿ボタンをクリックしてください。
下記画像の通り、エラーメッセージが表示されればOKです。

エラーメッセージが英語でわかりにくいですが、titleとcontentは入力必須です。というエラーが起こっています。
日本語化
初期設定で日本語の設定はしたはずですが、なぜかバリデーションエラーが英語のままです。
それは、初期設定の設定だけだと設定を変更しただけで日本語のファイルがLaravelにないために起こります。
Laravelでは日本語のファイルをダウンロードする方法がドキュメントに記載されているので、そちらのコマンドを実行していきましょう。
ドキュメントには./vendor/bin/sailが付いていませんが、今回はDocker環境なので付けて実行しましょう。
それでは、ターミナルで下記3つのコマンドをfirst-appディレクトリ上で実行してください。
$ ./vendor/bin/sail php -r "copy('https://readouble.com/laravel/8.x/ja/install-ja-lang-files.php', 'install-ja-lang.php');"
$ ./vendor/bin/sail php -f install-ja-lang.php
$ ./vendor/bin/sail php -r "unlink('install-ja-lang.php');"
この3つのコマンドを実行しても、特にコマンド実行成功などのテキストはターミナルには表示されませんがfirst-app/resources/lang/jaフォルダが追加されており、さらにその中には4つのファイルが追加されます。
これら4つのファイルが日本語用のファイルになります。
改めてhttp://localhost/article/createへアクセスして、タイトルと内容に何も入力しないで投稿ボタンをクリックしてください。
下記画像の通り、エラーメッセージが表示されればOKです。

まだtitleとcontentが英語なので、これも修正していきましょう。
これを修正するためにはArticleStoreRequest.phpを編集する必要があります。
first-app/app/Http/Requests/ArticleStoreRequest.phpを下記の通り編集してください。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ArticleStoreRequest 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<string, mixed>
*/
public function rules()
{
return [
'title' => 'required|string|max:100',
'content' => 'required|string',
];
}
public function attributes()
{
return [
'title' => 'タイトル',
'content' => '内容',
];
}
}
このようにattributesメソッドを追加して、name属性値 => '翻訳内容'と記述して翻訳していきましょう。
それではhttp://localhost/article/createへアクセスして、タイトルと内容に何も入力しないで投稿ボタンをクリックしてください。
下記画像の通り、エラーメッセージが表示されればOKです。

これでバリデーションのエラーメッセージの日本語化は完了です。
ちなみに、タイトルに100文字以上入力し、内容にこれは内容です。というテキストを入力してから投稿ボタンをクリックすると、バリデーションエラーが以下画像のようなメッセージに変わります。

まず、タイトルに入力した内容がバリデーションエラーが出ても消えていないことに注目してください。
例えば、バリデーションエラーが起こった際に入力した内容が全て消えてしまっていたら、ユーザーはどのように入力してエラーになったのかがわかりません。
それだけではなく、入力した内容の一部分だけ修正したいと思っても入力内容が全てクリアされていたら、また1から書き直しということになってしまいます。
このような状態を改善するために、create.blade.phpではoldメソッドを使用しています。
oldメソッドはold()の()内にname属性値を指定することで、バリデーションエラー時にフォームの値を保持することができます。
今回タイトルに100文字以上の値を入力しているので、文字数オーバーのバリデーションエラーが出ていますが、oldメソッドをvalue属性で使用しているため、フォームの内容が保持されており、入力内容がクリアされることなくバリデーションエラー後も出力されています。
このようにLaravelでフォームを作成する際は基本oldメソッドを使用しましょう。
次に、内容部分に注目してください。
こちらはバリデーションエラーがないため、エラーメッセージは表示されません。
それは以前create.blade.phpで実装した下記の条件分岐の記述があったためです。
@if ($errors->has('content'))
<div class="text-danger">
{{ $errors->first('content') }}
</div>
@endif
$errors->has('content')がfalseとなるので、エラーが表示されないということです。
一方でタイトル部分は100文字以上入力しているので、$errors->has('title')がtrueとなり、エラーメッセージが表示されています。
エラー処理を記述
無事に記事登録処理を実装することができましたが、データベースにデータを登録する際や編集・削除する際にはエラー処理を記述することが一般的です。
というのも、もし処理がうまく行かずデータが登録できなかった場合やデータベースに繋げなかった場合などにエラーが発生してしまいます。
そのような予期せぬエラーが発生してしまった場合に備えて、エラー時にデータを登録できないようにする処理とログにエラー内容を出力させる処理、エラー画面へ遷移させるための処理の3つを実装しておきます。
まずはfirst-app/app/Http/Controllers/ArticleController.phpを下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
use App\Http\Requests\ArticleStoreRequest;
use Illuminate\Support\Facades\DB; // ここを追加
use Illuminate\Support\Facades\Log; // ここを追加
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
/**
* 記事投稿処理
*/
public function storeArticle(ArticleStoreRequest $request)
{
// トランザクション開始
DB::beginTransaction();
try {
// 記事登録処理
Article::create([
'title' => $request->title,
'content' => $request->content,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::critical($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticles');
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
}
それでは追加したコードについて説明します。
まず、トランザクションという処理を実装するためにuse Illuminate\Support\Facades\DB;を追加し、ログを出力させるためにuse Illuminate\Support\Facades\Log;を追加しています。
これらのuse文を暗記する必要はありません。
例えば、Laravel トランザクションと検索するだけでトランザクションを実装するための記事がたくさん出てくるからです。
もちろん、その記事の中でuse文を追加することが書いてあります。
ログ出力の場合もそうです。
Laravel ログ出力などで検索すると、トランザクションの時と同じように実装方法を説明してくれている記事がたくさん出てきます。
それらの記事を見ながら実装できれば完璧です。
次に、新たに記事投稿処理のstoreArticleメソッドにtry-catch文とトランザクション処理、ログ出力処理、エラー画面への遷移処理の4つを追加しています。
追加した4つについて詳しく見ていきましょう。
try-catch文
try-catch文を使用するとtry{}で囲まれた処理の中でエラーが発生した場合に処理を止めて、catch(){}内の処理を実行することができます。
例えば、今回でいうと登録処理に失敗した場合は、catch文の中にデータベースにデータを登録させないようにトランザクション処理を記述していたり、エラー内容を受け取ってログを出力させる処理、エラー画面へ遷移させる処理の3つを記述しています。
このようにエラー処理をすることで、エラーが発生しているにも関わらず不正にデータが登録されることを防ぎ、開発者にどこでどのようなエラーが起こっているのかがわかるようにログを出力し、ユーザーには何かしらのエラーが起こったことを画面を通して伝えることができます。
これらの処理を実装するための記述がtry-catch文です。
基本の使い方は下記の通りです。
try {
// 一連の処理
} catch(\Exception $e) {
// エラー時に行う処理
}
上記のように記述するだけでtry-catch文を使用することができます。
また、catch(\Exception $e){}と記述していますが、引数eにはエラー内容が渡されているので、エラー内容をログに残す際などに使用することができます。
トランザクション処理
トランザクションという言葉か何回か出てきましたが、そもそもトランザクションとは一連の作業を1つの処理として管理するために用いられるものです。
このトランザクションを使用することで、一連の処理が全て成功したかどこかで失敗したの2パターンで処理を分けることができます。
今回でいうと、登録処理が無事に成功すればデータベースに値を登録し、登録処理が失敗したらデータを登録しないという処理を実装することができます。
Laravelでのトランザクションの使い方は下記の通りです。
use Illuminate\Support\Facades\DB; // use文
DB::beginTransaction();
try {
// データベースへの保存や編集、削除処理など
DB::commit();
} catch() {
DB::rollback();
// エラー時の処理
}
トランザクションはtry-catch文とセットで使うことを覚えておきましょう。
成功したらcommitとして、失敗した場合にはrollbackとすることで、処理が成功した時のみデータベースへデータが登録されることになります。
ログ出力
JavaScriptではconsole.log()を使用して、コンソールにログを出力していましたが、Laravelでも簡単にログを出力することができます。
Laravelではddメソッドが使用できるので、基本的なデバッグ時にログを使用することは少ないですが、ログを出力することはエラー時にエラー内容を確認したり、後々ddメソッドが使えない場合などのデバッグをする際にもとても役立につので、ぜひ覚えておきましょう。
Laravelでのログ出力方法は下記の通りです。
use Illuminate\Support\Facades\Log;
Log::emergency('エラーメッセージ');
Log::alert('エラーメッセージ');
Log::critical('エラーメッセージ');
Log::error('エラーメッセージ');
Log::warning('エラーメッセージ');
Log::notice('エラーメッセージ');
Log::info('エラーメッセージ');
Log::debug('エラーメッセージ');
()内にはもちろん変数も指定できるので、今回のコードのようにLog::critical($e)とすることで、エラー内容をログに出力することができます。
※変数eはtry-catct文を使用した際にエラーが入っている変数のこと
また、ログにはログレベルというものがあり、どのログを使用してもOKですが、エラー内容や緊急度によってログレベルを設定することができます。
emergencyが緊急度MAXで、下に行くほど緊急度が下がりdebugまでになると、開発者がデバッグするために出力するためのログになります。
基本使用するのは今回使用したエラー用のcriticalとデバッグしたい時に使用するdebug、何かの情報をログでお知らせをしたい場合(〇〇メソッドが実行された)などに使用するinfoなどが有名です。
基本はその3つをおさえておけばOKです!
またログはデフォルトでfirst-app/storage/logs/laravel.logに出力されるので、後ほど確認してみましょう。
エラー画面への遷移
エラー画面はLaravelにもともと備わっており、エラーが発生すると表示されます。
PHPの処理が失敗した時に出力されるエラーはステータスコードが500の500エラーなので、Laravelのヘルパーメソッドであるabortメソッドを使用して、abort(500)と記述してあげることで500エラーページへ遷移させるようにしています。
今回説明したtry-catch文やトランザクション、ログ出力、エラー画面への遷移の実装は必須ではありませんが、プログラマーとして仕事をするのであれば実装しておきましょう。
最後に登録時にわざとエラーを起こして、ログが出力されているかを確認してみましょう。
first-app/app/Http/Controllers/ArticleController.phpのstoreArticleメソッドを下記の通り変更してください。
/**
* 記事投稿処理
*/
public function storeArticle(ArticleStoreRequest $request)
{
// トランザクション開始
DB::beginTransaction();
try {
// 記事登録処理
Article::create([
'title' => $request->title,
// 'content' => $request->content,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::critical($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticles');
}
登録処理のcontent部分をコメントアウトしたことにより、必須項目がデータベースへ登録されないのでエラーが発生します。
実際にhttp://localhost/article/createへアクセスして、タイトルと内容に適当なテキストを入れて投稿ボタンをクリックしてください。
下記画像のように500 | SERVER ERRORの画面が表示されればOKです。

もちろんこれがabortメソッドで500を指定していたので、サーバーエラーの画面に遷移しました。
また、first-app/storage/logs/laravel.logを開いてください。
Laravelはデフォルトでここにログが残るようになっています。
今回Log::critical($e)としているので、下記のようなログが出力されます。
[2022-06-17 20:26:28] local.CRITICAL: PDOException: SQLSTATE[HY000]: General error: 1364 Field 'content' doesn't have a default value in /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connection.php:527
エラーが起こった日時とlocal.CRITICALとなっています。
また、エラー内容も出力されており、Field 'content' doesn't have a default valueとなっています。
つまり、「contentカラムはデフォルトの値がテーブル設定でされていないので何かしらの値が必要です」というエラーです。
ちなみに、Log::debug($e)と記述していた場合はlocal.DEBUGとなります。
このようにログを出力することで、ブラウザでは500 | SERVER ERRORとなっていてエラー内容が確認できませんが、ログを確認することでどのようなエラーが起こっているのかを確認できます。
ブラウザでエラーが確認できない場合は積極的にログを確認しましょう。
それでは、first-app/app/Http/Controllers/ArticleController.phpのstoreArticleメソッドを下記の通り元に戻しておきましょう。
/**
* 記事投稿処理
*/
public function storeArticle(ArticleStoreRequest $request)
{
// トランザクション開始
DB::beginTransaction();
try {
// 記事登録処理
Article::create([
'title' => $request->title,
'content' => $request->content,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticles');
}
これで登録処理が全て完了しました。
ヘッダーの遷移先を編集
最後にヘッダーの記事投稿ボタンから記事投稿画面へ遷移できるようにheader.blade.phpを変更しましょう。
first-app/resources/views/header.blade.phpを下記の通り編集してください。
<header>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container-fluid">
<a class="navbar-brand" href="{{ route('showArticles') }}">ブログ</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-item nav-link active" href="{{ route('showArticles') }}">記事一覧 <span class="sr-only"></span></a>
<a class="nav-item nav-link" href="{{ route('createArticle') }}">記事投稿</a>
</div>
</div>
</div>
</nav>
</header>
変更した内容は下記の通りです。
まず、記事投稿ボタンのhref属性に記事投稿画面を表示させるルート名をrouteメソッドを使用して指定しています。
これで記事投稿ボタンがクリックされると、投稿フォームを表示することができるようになりました。
また、ブログと記事一覧ボタンにも記事一覧画面を表示させるルート名をrouteメソッドを使用して指定しています。
これでブログと記事一覧ボタンがクリックされると、記事一覧画面を表示することができるようになりました。
いい感じにブログシステムが構築できてきましたね!
これで記事投稿機能の実装は完了です。
★検索ワード
・Laravel 登録機能実装
・Laravel create fill save
・Laravel バリデーション実装
・Laravel CSRF 実装
・Laravel リダイレクト
・Laravel フォームリクエスト
・Laravel 日本語化
・PHP try-catch 使い方
・Laravel トランザクション
・Laravel ログ出力
・Laravel abortメソッド
記事編集
記事詳細画面表示と記事投稿ができるようになったので、次は投稿した記事の編集をする機能の作成をしていきます。
記事編集機能は記事詳細画面表示と記事投稿を合わせたような機能になっているので、それらの実装方法を思い出しながら実装してみましょう!
記事編集の流れは下記のようになっています。
- 記事詳細からIDに紐付けたリンクを作成して編集フォームへ遷移
- 編集フォームからPOSTで編集内容を送る
- コントローラーとモデルを使用してデータを編集する
- フォームリクエストを使用してバリデーション
- エラー処理
データの更新メソッド
リンクの作成方法やバリデーション、エラー処理などについてはこれまでの機能実装時に説明したので、まだ説明していないデータ更新のメソッドについて説明します。
実は記事投稿用メソッドの際に2つの方法を説明したと思いますが、データの更新にも2つの方法があります。
それがfillメソッド + saveメソッドとupdateメソッドです。
まずはfillメソッド + saveメソッドの使い方から説明します。
実は記事投稿用メソッドを紹介した時にfillメソッド + saveメソッドの使い方も紹介しています。
その時の内容とほとんど一緒ですが、今回は記事編集なので編集するための記事の取得が必要になります。
使い方は下記の通りです。
$article = Article::find(1);
$article->fill([
'title' => 'タイトル',
'content' => 'コンテンツ',
]);
$article->save();
まず、編集したい記事の情報をfindメソッドを使用して取得します。
今回は例として記事IDが1の記事を取得しています。
次にfillメソッドを使用して、編集したカラム名と編集内容を=>で結びつけます。
今回は例としてタイトル、コンテンツとテキストをそのまま入力していますが、実際はフォームで入力された値をこちらに設定します。
そして最後にsaveメソッドを使用して保存します。
updateメソッドの使い方はfillメソッド + saveメソッドとほとんど同じです。
使い方は下記の通りです。
$article = Article::find(1);
$article->update([
'title' => 'タイトル',
'content' => 'コンテンツ'
]);
イメージとしては、fillメソッド + saveメソッドを一気に行えるのがupdateメソッドです。
fillメソッド + saveメソッドとupdateメソッドの違いは、fillメソッド + saveメソッドが差分更新なのに対して、updateメソッドはメソッドが実行されると差分があるかないか関係なく毎回更新します。
データベースへ負荷をなるべくかけないためにも、データベースに登録されている値と登録しようとしている値に差分があったときのみ更新するfillメソッド + saveメソッドを使用するのが一般的です。
実装
fillメソッド + saveメソッド以外の内容は記事詳細画面表示機能と記事投稿機能とほぼ同じなので、さっそく実装してみましょう。
記事詳細からIDに紐付けたリンクを作成して編集フォームへ遷移
まずは編集画面表示と編集処理用のルートを作成していきます。
first-app/routes/web.phpを下記の通り編集してください。
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ArticleController;
Route::get('/', function () {
return view('welcome');
});
// 投稿一覧を表示
Route::get('/articles', [ArticleController::class, 'showArticles'])->name('showArticles');
// 記事投稿フォームを表示
Route::get('/article/create', [ArticleController::class, 'createArticle'])->name('createArticle');
// 記事登録処理
Route::post('/article/store', [ArticleController::class,'storeArticle'])->name('storeArticle');
// 記事編集フォームを表示
Route::get('/article/edit/{id}', [ArticleController::class, 'editArticle'])->name('editArticle'); // ここを追加
// 記事編集処理
Route::post('/article/update/{id}', [ArticleController::class,'updateArticle'])->name('updateArticle'); // ここを追加
// 投稿詳細を表示
Route::get('/article/{id}', [ArticleController::class, 'showArticle'])->name('showArticle');
追加したのは下記の2行です。
// 記事編集フォームを表示
Route::get('/article/edit/{id}', [ArticleController::class, 'editArticle'])->name('editArticle'); // ここを追加
// 記事編集処理
Route::post('/article/update/{id}', [ArticleController::class,'updateArticle'])->name('updateArticle'); // ここを追加
記事編集フォームを表示させるためには記事IDが必要なので、/article/edit/{id}としています。
また、記事編集処理ではフォームを使用してPOSTで内容を送信しているのでRoute::postとしています。
次にルートで作成した記事編集画面表示のためのeditArticleメソッドを作成します。
first-app/app/Http/Controllers/ArticleController.phpを下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
use App\Http\Requests\ArticleStoreRequest;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
/**
* 記事投稿処理
*/
public function storeArticle(ArticleStoreRequest $request)
{
// トランザクション開始
DB::beginTransaction();
try {
// 記事登録処理
Article::create([
'title' => $request->title,
'content' => $request->content,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticles');
}
/**
* 記事編集画面を表示する
*/
public function editArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.edit', compact('article'));
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
}
追加したコードは下記の通りです。
/**
* 記事編集画面を表示する
*/
public function editArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.edit', compact('article'));
}
記事詳細画面を表示させる時とほぼ同じコードですね!
違うのは遷移させるのがedit.blade.phpになっているだけです。
次に、記事詳細画面から編集画面に遷移できるようにボタンを作成していきましょう。
first-app/resources/views/articles/detail.blade.phpを下記の通り編集してください。
@extends('layout')
@section('title')
記事詳細
@endsection
@section('content')
<h2>{{ $article->title }}</h2>
<div class="d-flex">
<span class="mr-2">作成日:{{ $article->created_at }}</span>
<span>更新日:{{ $article->updated_at }}</span>
</div>
<p class="mt-4">{{ $article->content }}</p>
<a href="{{ route('showArticles') }}" class="mt-3 btn btn-secondary">戻る</a>
<a href="{{ route('editArticle', $article->id) }}" class="mt-3 btn btn-primary">編集</a>
@endsection
追加したのは下記コードです。
<a href="{{ route('editArticle', $article->id) }}" class="mt-3 btn btn-primary">編集</a>
これで編集ボタンをクリックすると記事編集画面へ遷移し、ArticleコントローラーのeditArticleメソッドへ引数として記事IDを渡すことができます。
編集フォームからPOSTで編集内容を送る
次に編集フォームからPOSTで編集内容を送信するためにedit.blade.phpを作成していきます。
first-app/resources/views/articlesディレクトリにedit.blade.phpをコマンドかVSCodeで作成してください。
コマンドで作成する場合は、ターミナルで下記コマンドをfirst-appディレクトリ上で実行してください。
$ touch resources/views/articles/edit.blade.php
作成したedit.blade.phpを下記の通り編集してください。
@extends('layout')
@section('title')
記事編集
@endsection
@section('content')
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h2>記事編集フォーム</h2>
<form method="POST" action="{{ route('updateArticle', $article->id) }}">
@csrf
<div class="form-group">
<label for="title">
タイトル
</label>
<input id="title" name="title" class="form-control" value="{{ old('title', $article->title) }}" type="text">
@if ($errors->has('title'))
<div class="text-danger">
{{ $errors->first('title') }}
</div>
@endif
</div>
<div class="form-group mt-3">
<label for="content">
内容
</label>
<textarea id="content" name="content" class="form-control" rows="4">{{ old('content', $article->content) }}</textarea>
@if ($errors->has('content'))
<div class="text-danger">
{{ $errors->first('content') }}
</div>
@endif
</div>
<div class="mt-3">
<a class="btn btn-secondary mr-2" href="{{ route('showArticle', $article->id) }}">
キャンセル
</a>
<button type="submit" class="btn btn-primary">
更新
</button>
</div>
</form>
</div>
</div>
@endsection
http://localhost/article/edit/1へアクセスして、下記画像のように表示されればOKです!

今回は例として記事IDが1の記事の編集画面を表示しています。
編集フォームの内容はほとんど登録フォームと同じですが、変更した部分について解説します。
まず、ページタイトルを記事編集、ページの見出しを記事編集フォームとしています。
次に、フォームで送信する先をaction属性の属性値として、編集処理のメソッドであるupdateArticleメソッドに指定しました。
また、記事IDを渡すためにrouteメソッドの第二引数に$article->idと指定しています。
次にinput要素のvalueとtextarea要素の内容をそれぞれ編集しています。
編集した理由は、データベースに保存されている記事タイトルと記事内容のデータをそれぞれのフォームに反映させるためです。
oldメソッドの第二引数に値を設定することで、編集フォームが表示された直後はデフォルト値としてデータベースに保存されたデータをフォームに表示してくれます。
つまり、記事IDが1の編集画面を表示すると、下記画像のようにデータベースに保存されている記事ID1の記事タイトルと記事内容がフォームに表示されるということです。

一方でバリデーションに引っかかった場合はデフォルトの値ではなく、バリデーションで引っかかった時に入力された内容がフォームに反映されて、さらにバリデーションのエラーメッセージがその下に赤文字で出力されるようになります。
次に、キャンセルボタンの遷移先を記事詳細画面にするためにhref属性の属性値を記事詳細画面を表示するルート名showArticleに変更しています。
もちろん記事詳細画面への遷移には、どの記事の詳細画面なのかを指定しなければならないので、routeメソッドの第二引数に記事IDを渡しています。
最後に投稿ボタンを編集用に更新ボタンにして記事編集フォームの実装は終わりです。
コントローラーとモデルを使用してデータを編集する
編集フォームからPOSTで送信されたデータをデータベースに保存するための処理を実装していきましょう。
すでにルートには編集処理用のルーティングをしているので、そのルーティングに従ってArticleコントローラーにupdateArticleメソッドを作成していきましょう。
first-app/app/Http/Controllers/ArticleController.phpを下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
use App\Http\Requests\ArticleStoreRequest;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
/**
* 記事投稿処理
*/
public function storeArticle(ArticleStoreRequest $request)
{
// トランザクション開始
DB::beginTransaction();
try {
// 記事登録処理
Article::create([
'title' => $request->title,
'content' => $request->content,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticles');
}
/**
* 記事編集画面を表示する
*/
public function editArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.edit', compact('article'));
}
/**
* 記事編集処理
*/
public function updateArticle(Request $request, $id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
// 編集する内容をfillメソッドを使用して記述
$article->fill([
'title' => $request->title,
'content' => $request->content,
]);
// 保存処理
$article->save();
return redirect()->route('showArticle', $article->id);
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
}
追加したコードは下記の通りです。
/**
* 記事編集処理
*/
public function updateArticle(Request $request, $id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
// 編集する内容をfillメソッドを使用して記述
$article->fill([
'title' => $request->title,
'content' => $request->content,
]);
// 保存処理
$article->save();
return redirect()->route('showArticle', $article->id);
}
記事登録時の時と同じ様に、POSTで送信された値はupdateArticleメソッドの引数でRequest $requestと記述することで受け取る事ができます。
また、今回は編集する記事IDもフォームから送られてきているので、$idと記述して引数を受け取ります。
これでPOSTで送信されてきた値と記事IDをupdateArticleメソッド内で使用できるようになりました。
次は渡されてきた記事IDを使用して記事データを取得していきます。
取得するにはいつも通りfindメソッドを使用するだけです。
先ほど説明したfillメソッド + saveメソッドを使用して編集処理をしています。
$request->titleと記述することでフォームで入力したタイトルを取得することができます。
また、$request->contentと記述することでフォームで入力した内容を取得することができます。
これも記事登録処理と同じですね。
最後にリダイレクト先を記事詳細画面に指定すれば終わりです。
記事詳細画面を表示するためには記事IDが必要になるので、routeメソッドの第二引数に$article->idとして渡しておきましょう。
これで投稿を編集することができました。
しっかり記事が編集できるかの動作確認は各自行ってください!
フォームリクエストを使用してバリデーション
次にフォームリクエストを使用してバリデーションを実装しましょう。
今回は記事編集用のバリデーションなのでArticleUpdateRequest.phpを作成しましょう。
それでは、ターミナルで下記コマンドをfirst-appディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:request ArticleUpdateRequest
first-app/app/Http/Requests配下にArticleUpdateRequest.php`が作成されていればOKです。
それではfirst-app/app/Http/Requests/ArticleUpdateRequest.phpを下記の通り編集してください。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ArticleUpdateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true; // trueへ変更
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'title' => 'required|string|max:100',
'content' => 'required|string',
];
}
public function attributes()
{
return [
'title' => 'タイトル',
'content' => '内容',
];
}
}
内容はArticleStoreRequest.phpと全く同じなので、説明は省略します。
次にArticleコントローラーのupdateArticleメソッドにArticleUpdateRequestのバリデーションを反映させましょう。
first-app/app/Http/Controllers/ArticleController.phpを下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
use App\Http\Requests\ArticleStoreRequest;
use App\Http\Requests\ArticleUpdateRequest; // ここを追加
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
/**
* 記事投稿処理
*/
public function storeArticle(ArticleStoreRequest $request)
{
// トランザクション開始
DB::beginTransaction();
try {
// 記事登録処理
Article::create([
'title' => $request->title,
'content' => $request->content,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticles');
}
/**
* 記事編集画面を表示する
*/
public function editArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.edit', compact('article'));
}
/**
* 記事編集処理
*/
public function updateArticle(ArticleUpdateRequest $request, $id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
// 編集する内容をfillメソッドを使用して記述
$article->fill([
'title' => $request->title,
'content' => $request->content,
]);
// 保存処理
$article->save();
return redirect()->route('showArticle', $article->id);
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
}
まずはArticleUpdateRequestをArticleコントローラー内で使用できるように、use文を使用して下記のように記述しましょう。
use App\Http\Requests\ArticleUpdateRequest;
次にupdateArticleメソッドの第一引数をRequest $requestからArticleUpdateRequest $requestに変更します。
これで記事編集時にArticleUpdateRequest.phpで設定したバリデーションが実行されるので、記事編集フォームで何も入力しないで更新ボタンをクリックしたり、タイトルを100文字以上にすると、エラーメッセージが出力されるようになりました。
エラー処理
最後にエラー処理を実装しておきましょう。
実装する内容は下記の通りです。
- try-catch文
- トランザクション
- ログ出力
- エラー画面への遷移
これも登録処理とほとんど変わりません。
first-app/app/Http/Controllers/ArticleController.phpを下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
use App\Http\Requests\ArticleStoreRequest;
use App\Http\Requests\ArticleUpdateRequest;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
/**
* 記事投稿処理
*/
public function storeArticle(ArticleStoreRequest $request)
{
// トランザクション開始
DB::beginTransaction();
try {
// 記事登録処理
Article::create([
'title' => $request->title,
'content' => $request->content,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticles');
}
/**
* 記事編集画面を表示する
*/
public function editArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.edit', compact('article'));
}
/**
* 記事編集処理
*/
public function updateArticle(ArticleUpdateRequest $request, $id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
// トランザクション開始
DB::beginTransaction();
try {
// 編集する内容をfillメソッドを使用して記述
$article->fill([
'title' => $request->title,
'content' => $request->content,
]);
// 保存処理
$article->save();
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticle', $article->id);
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
}
変更したコードは下記の通りです。
/**
* 記事編集処理
*/
public function updateArticle(ArticleUpdateRequest $request, $id)
{
// トランザクション開始
DB::beginTransaction();
try {
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
// 編集する内容をfillメソッドを使用して記述
$article->fill([
'title' => $request->title,
'content' => $request->content,
]);
// 保存処理
$article->save();
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticle', $article->id);
}
実装内容自体は記事登録時と同じですね!
もしわからない箇所がある場合は、記事登録部分を読み直してください!
★検索ワード
・Laravel 編集機能実装
・Laravel fill save update
記事削除
ここまで記事投稿・編集ができるようになったので、最後に記事の削除をできるようにしましょう。
記事削除機能に関しても記事編集機能同様、以前に実装したことの応用なので過去の実装を思い出しながら実装していきましょう。
記事削除の流れは下記のようになっています。
- 記事詳細からIDに紐付けたリンクを作成してルートへ渡す
- コントローラーとモデルを使用してデータを削除する
- エラー処理
データの削除メソッド
まずはデータを削除するためのメソッドの解説です。
登録や編集と同じように、データの削除を実現させるためのメソッドも2つあります。
それはdeleteメソッドとdestroyメソッドです。
まずはdeleteメソッドの使い方から説明します。
使い方は下記の通りです。
$article = Article::find(1);
$article->delete();
まず削除したい記事を取得して、その記事にdeleteメソッドを使用するというイメージです。
とても簡単ですね!
次にdestroyメソッドの使い方を説明します。
使い方は下記の通りです。
Article::destroy(1);
destroyメソッドは記事ID(プライマリーキー)を引数として指定することで、指定したIDのデータを削除することができます。
destroyメソッドはプライマリーキーしか指定できないので、取得したデータを削除できるdeleteメソッドを使用すればOKです。
本教材でもdeleteメソッドを利用します。
実装
deleteメソッド以外の内容は過去の実装とほぼ同じなので、さっそく実装してみましょう。
記事詳細からIDに紐付けたリンクを作成してルートへ渡す
まずは削除処理用のルートを作成していきます。
first-app/routes/web.phpを下記の通り編集してください。
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ArticleController;
Route::get('/', function () {
return view('welcome');
});
// 投稿一覧を表示
Route::get('/articles', [ArticleController::class, 'showArticles'])->name('showArticles');
// 記事投稿フォームを表示
Route::get('/article/create', [ArticleController::class, 'createArticle'])->name('createArticle');
// 記事登録処理
Route::post('/article/store', [ArticleController::class,'storeArticle'])->name('storeArticle');
// 記事編集フォームを表示
Route::get('/article/edit/{id}', [ArticleController::class, 'editArticle'])->name('editArticle');
// 記事編集処理
Route::post('/article/update/{id}', [ArticleController::class,'updateArticle'])->name('updateArticle');
// 投稿詳細を表示
Route::get('/article/{id}', [ArticleController::class, 'showArticle'])->name('showArticle');
// 記事削除処理
Route::post('/article/delete/{id}', [ArticleController::class, 'deleteArticle'])->name('deleteArticle'); // ここを追加
追加したのは下記の1行です。
Route::post('/article/delete/{id}', [ArticleController::class, 'deleteArticle'])->name('deleteArticle');
記事の削除処理には削除する記事のIDが必要なので、/article/delete/{id}としています。
また、 削除処理にはRoute::postを使用します。
次にルートで作成した記事削除処理のためのボタンを作成します。
記事詳細画面に削除ボタンを設置して、ボタンをクリックされた時に削除させたいので、first-app/resources/views/articles/detail.blade.phpを下記の通り編集してください。
@extends('layout')
@section('title')
記事詳細
@endsection
@section('content')
<h2>{{ $article->title }}</h2>
<div class="d-flex">
<span class="mr-2">作成日:{{ $article->created_at }}</span>
<span>更新日:{{ $article->updated_at }}</span>
</div>
<p class="mt-4">{{ $article->content }}</p>
<div class="d-flex">
<a href="{{ route('showArticles') }}" class="mt-3 mr-2 btn btn-secondary">戻る</a>
<a href="{{ route('editArticle', $article->id) }}" class="mt-3 mr-2 btn btn-primary">編集</a>
<form method="POST" action="{{ route('deleteArticle', $article->id) }}">
@csrf
<button type="submit" class="mt-3 mr-2 btn btn-danger">削除</button>
</form>
</div>
@endsection
http://localhost/article/1にアクセスして、下記画像のように削除ボタンが追加されていればOKです。

削除ボタンはフォームを使用して作成します。
そのためルートを記述した時にRoute::postとしました。
また、CSRF保護の@csrfも忘れないようにしてください。
もちろんaction属性にはdeleteArticleメソッドを指定し、第二引数には削除する記事のIDを$article-idとして指定しています。
コントローラーとモデルを使用してデータを削除する
それではArticleコントローラーにdeleteArticleメソッドを実装していきましょう。
first-app/app/Http/Controllers/ArticleController.phpを下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
use App\Http\Requests\ArticleStoreRequest;
use App\Http\Requests\ArticleUpdateRequest;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
/**
* 記事投稿処理
*/
public function storeArticle(ArticleStoreRequest $request)
{
// トランザクション開始
DB::beginTransaction();
try {
// 記事登録処理
Article::create([
'title' => $request->title,
'content' => $request->content,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticles');
}
/**
* 記事編集画面を表示する
*/
public function editArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.edit', compact('article'));
}
/**
* 記事編集処理
*/
public function updateArticle(ArticleUpdateRequest $request, $id)
{
// トランザクション開始
DB::beginTransaction();
try {
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
// 編集する内容をfillメソッドを使用して記述
$article->fill([
'title' => $request->title,
'content' => $request->content,
]);
// 保存処理
$article->save();
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticle', $article->id);
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
/**
* 記事削除処理
*/
public function deleteArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
// 記事削除処理
$article->delete();
return redirect()->route('showArticles');
}
}
追加したコードは下記の通りです。
/**
* 記事削除処理
*/
public function deleteArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
// 記事削除処理
$article->delete();
return redirect()->route('showArticles');
}
今回は削除する記事IDが送られてきているので、$idと記述し引数として受け取ります。
これで削除する記事IDをdeleteArticleメソッドで使用できるようになりました。
取得したIDを使用して、先ほど説明したdeleteメソッドで削除処理を実装しています。
最後にリダイレクト先をshowArticlesに指定し、記事一覧表示画面遷移させる処理を実装して終わりです。
これで記事詳細画面から削除ボタンをクリックすることで記事を削除できるようになりました。
エラー処理
最後にエラー処理を実装しておきましょう。
実装する内容はいつもと同じで下記の通りです。
- try-catch文
- トランザクション
- ログ出力
- エラー画面への遷移
first-app/app/Http/Controllers/ArticleController.phpを下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Article;
use App\Http\Requests\ArticleStoreRequest;
use App\Http\Requests\ArticleUpdateRequest;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class ArticleController extends Controller
{
/**
* 記事一覧を表示する
*/
public function showArticles()
{
// 全ての記事データを取得
$articles = Article::all();
// compact関数で渡す場合
return view('articles.index', compact('articles'));
}
/**
* 記事投稿フォームを表示
*/
public function createArticle()
{
return view('articles.create');
}
/**
* 記事投稿処理
*/
public function storeArticle(ArticleStoreRequest $request)
{
// トランザクション開始
DB::beginTransaction();
try {
// 記事登録処理
Article::create([
'title' => $request->title,
'content' => $request->content,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticles');
}
/**
* 記事編集画面を表示する
*/
public function editArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.edit', compact('article'));
}
/**
* 記事編集処理
*/
public function updateArticle(ArticleUpdateRequest $request, $id)
{
// トランザクション開始
DB::beginTransaction();
try {
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
// 編集する内容をfillメソッドを使用して記述
$article->fill([
'title' => $request->title,
'content' => $request->content,
]);
// 保存処理
$article->save();
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticle', $article->id);
}
/**
* 記事詳細を表示する
*/
public function showArticle($id)
{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
return view('articles.detail', compact('article'));
}
/**
* 記事削除処理
*/
public function deleteArticle($id)
{
// トランザクション開始
DB::beginTransaction();
try{
// 渡されてきた記事IDのデータを取得
$article = Article::find($id);
// 記事削除処理
$article->delete();
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('showArticles');
}
}
これも編集時と同じで実装内容自体は記事登録時と同じですね!
もしわからない箇所がある場合は、記事登録部分を読み直してください!
★検索ワード
・Laravel 削除機能実装
・Laravel delete destroy
おめでとうございます!
Laravelの学習お疲れさまでした。
教材を1度読んだだけではなかなかLaravelを理解することは難しいと思いますが、PHPで実装するよりもLaravelで実装した方が色々簡単になりそうなことはわかりましたか?
まずはここまで理解できていればLaravelの基礎は大丈夫です。
まだイメージできないなという方は、イメージができるまで何度かこちらの教材を読み直してみてください!