フレームワークとは
本教材では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の基礎は大丈夫です。
まだイメージできないなという方は、イメージができるまで何度かこちらの教材を読み直してみてください!