- 投稿日:2020-07-27T23:05:05+09:00
yps並走備忘録 Task2 MySQL(5.7)~nginx(v1.18)~PHP(7.3)~Laravelインストール
yps Task 2 アプリを動かす準備
今回の流れ
1. MySQLインストール
2. Nginxインストール
3. PHPインストール
4. Laravelインストール
5. VS CodeでSSH接続してサーバーにあるファイルを編集-----用意するもの-----
VS Code
折れない心(笑)MySQL5.7のインストール&設定
アプリでデータベース管理を司るMySQLをインストール、設定していきます。
まずはインストール
sudo yum localinstall http://dev.mysql.com/get/mysql57-community-release-el7-7.noarch.rpm
sudo yum install -y mysql-community-server -y
動作確認
mysqld --version
(正常に完了していればMySQLの情報が表示される)
sudo systemctl enable mysqld
(MySQLを自動で起動する設定)
sudo systemctl start mysqld
(MySQLを起動)
sudo systemctl status mysqld
(MySQLの状態を確認、起動できていれば「active(Running)」という緑の文字が出てくるはず)
sudo systemctl stop mysqld
(一旦MySQLを停止)MySQLへログイン・パスワード変更・文字コードを設定
sudo cat /var/log/mysqld.log | grep -i root
(仮で設定されたパスワードを表示、root@localhost: xxxxxxと表示されるはず)
sudo systemctl start mysqld
mysql -u root -p
と打つとPassword:という表示になる、ここで上記の仮パスワードを入力するのですが、Linuxの仕様で入力しても何も表示されないので注意!
ミスタイプがないように打って、Enterを押すと、正しい場合はMySQLに入れ、表示がmysql>
となるはず。パスワード変更
SET PASSWORD = PASSWORD('パスワードをここに入力')
-------------注意事項-------------
- 8文字以上、英字の大文字・小文字・数字・記号を全て混ぜないといけない
- #はエラーに基になるので使用しない変更できたら
exit
と打つとサーバーのターミナルへ戻れる日本語を扱えるように文字コードを変更
sudo vi /etc/my.cnf
最終行に
character-set-server=utf8mb4
を追記
sudo systemctl restart mysqld
(MySQLを再起動して設定を反映)
mysql -u root -p
(MySQLにログイン)
Password:設定したパスワード
mysql> show variables like "chara%";
(文字コードの設定が反映されているか確認)MySQLのインストールここまで
Nginxのインストール&設定
補足:nginxとは? 参考url:
インストール
1. インストール用のyumレポジトリ作成
sudo vi /etc/yum.repos.d/nginx.repo
開いたファイルに以下を記述
[nginx]
name=nginx repo
baseurl=https://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=12. 以下のコマンドを打ってインストール
sudo yum install nginx -y
完了したらバージョンを確認
nginx -v
nginxを自動で起動できるように設定
sudo systemctl enable nginx
3. AWSマネジメントコンソールからインバウンドルールにhttp、httpsポートを追加
http = 80
https = 443nginxを再起動して設定を反映
sudo systemctl nginx
ブラウザにEC2インスタンスのIPを入れてWelcome to Nginxが表示されればインストール完了
4. nginxのrootディレクトリ変更
sudo mkdir /var/www
wwwディレクトリを作成
sudo mkdir /var/www/html
wwwディレクトリの中にhtmlディレクトリを作成
sudo chown -R centos:nginx /var/www
/var/www以下の権限をcentosからnginxに移譲5. nginxの設定変更
sudo cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.org
元々の設定をorgファイルでバックアップ保存
sudo vi /etc/nginx/conf.d/default.conf
設定ファイルを編集--------編集する場所----------
rootのパスを変更
#root /usr/share/nginx/html;
⇒root /var/www/html;
Nginx構文チェック
sudo nginx -t
何も表示されなければOK!Nginxを再起動
sudo systemctl restart nginx
ブラウザで再度EC2のIPアドレスを開いて(or更新して)403エラーになっていればOK
nginxのインストール&設定ここまで
PHP 7.3のインストール&設定
お次はいよいよPHPを動かせるように必要なものをインストールしていきます
1. yum レポジトリのインストール&アップデート
EC2にインストールするためのプログラムが古いのでアップデートします。
sudo yum install epel-release -y
sudo yum install https://rpms.remirepo.net/enterprise/remi-release-7.rpm -y
sudo yum update -y
2. PHPのインストール
sudo yum -y install --enablerepo=epel,remi,remi-php73 php php-devel php-mbstring php-pdo php-gd php-xml php-mcrypt php-fpm php-mysql php-mysqlnd zip unzip
バージョンの確認
php -v
で以下が表示されればOKPHP 7.3.20 (cli) (built: Jul 7 2020 07:53:49) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.20, Copyright (c) 1998-2018 Zend Technologies3. PHPとNginxを連携させます
php-fpmの設定
sudo cp /etc/php-fpm.d/www.conf /etc/php-fpm.d/www.conf.org
恒例、元のファイルバックアップ
sudo vi /etc/php-fpm.d/www.conf
設定ファイル編集変更する場所
user = nginx
group = nginx
listen = /var/run/php-fpm/php-fpm.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0660php-fpmの設定ここまで
Nginxの設定
sudo vi /etc/nginx/conf.d/default.conf
まずは10行目くらいにあるindexのところに追記します
index index.php(←追記) index.html index.htm;
そのすぐ下に下記も追記
try_files $uri $uri/ /index.php?$query_string;
さらにlocation / 以下に下記を追記します
location ~ \.php$ {
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}上記連携が終わったら…
配下のディレクトリもnginxへ所有者変更(使えるようにする)
sudo chown -R centos:nginx /var/www/
/var/www/php-fpmの自動起動
sudo systemctl enable php-fpm
nginx再起動
sudo systemctl restart nginx
php-fpm起動
sudo systemctl start php-fpm
ここまでできればあとちょっと!
テスト表示用のindex.phpファイルを作成
vi /var/www/html/index.phpファイルに以下を記述
<?php echo phpinfo();?>:wqで保存したらnginxとphp-fpmを再起動
sudo systemctl restart nginx
sudo systemctl restart php-fpm
ブラウザを更新してPHPのバージョン情報が見られれば完了です
Laravelのインストール&設定
一時フォルダに移動
cd/tmp
Laravelのインストールに必要なComposerをインストール
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
php -r "unlink('composer-setup.php');"
Composerがどこでも使えるようにします
sudo mv composer.phar /usr/local/bin/composer
sudo chmod +x /usr/local/bin/composer
Laravelを構築します
cd /var/www/html
composer create-project --prefer-dist laravel/laravel yps
※ypsの部分はもちろん任意の名前でOK※phpをインストールした時にzip unzipがちゃんとインストールされていないとここで躓く可能性があります
無事Laravelの構築が終わったら設定をしましょう
cd yps
Laravelのフォルダへ移動
php artisan key:generate
composerでlaravelアプリ用の暗号化キーの生成
cp -p .env.example .env
.envファイルの下記の部分を変更します
APP_URL= EC2のIPアドレス
DB_PASSWORD= MySQLのパスワードnode.jsとnpm(パッケージ管理システム)をインストール
多分後日フロント周りをいじるとき用…かな?
sudo yum install npm node -y
composer install
npm install
下記のコマンドを打てば設定は一段落
sudo chorn-R centos:nginx /var/www/
sudo chmod -R 777 storage/ bootstrap/cache/
laravelのWelcome画面表示
vi /etc/nginx/conf.d/default.conf
/var/www/html;
となっているところを
/var/www/html/yps/public;
に変更して…nginxとphp-fpmを再起動
sudo systemctl restart nginx
sudo systemctl restart php-fpm最後にEC2インスタンスのIPアドレスをブラウザに入力すればLaravelのWelcome画面が見られるはずです。
さらにここからVS Codeで編集できるようにします
- VS Codeを起動して、拡張機能のRemote-SSHをインストール
- 左の「リモートエクスプローラーアイコンをクリック」
- SSH TARGETSと書いてあるところにカーソルを合わせると出てくる歯車アイコンをクリック
- 画面中央上部にSelect SSH configuration file to updateと出るのでC:\users\ユーザー名.ssh\configをクリック
- Host: 表示名、後ほどコマンドプロンプトからSSH接続をするのにも利用できる
- Port: AWSで作成したPort番号
- User: centos
- HostName: EC2のIPアドレス
- IdentifyFile: .ssh/xxxx.pem(秘密鍵ファイル)
- 入力後に右下の緑色の「リモートウインドウを開く」をクリック
- Remote-SSH Connect to Host...をクリック
- 上記で作成した表示名をクリック
- 画面中央上部にLinux, Windowsとか選択肢が3つくらい出てくるのでLinuxを選択
- しばらくするとVS Code Serverが立ち上がる
- 完了すると右下にHost名が出る
- エクスプローラを開いてresources ⇒ views ⇒ welcome.blade.phpを開く
- 84行目にあるウェルカムメッセージを変更して保存
- ブラウザをリロードするとウェルカムメッセージが更新されている(ハズ)
以上でTask2完了です。
- 投稿日:2020-07-27T22:44:10+09:00
phpUnit testはどのように書けばいいですか?
プログラミングの初心者で、laravelの開発練習をしていますが、phpunit test の段階でつまってしまいました、php unit testはどのように書けばいいでしょうか?
- 投稿日:2020-07-27T21:28:35+09:00
一覧表示・新規登録・編集・削除まとめ(Laravel7.x系)
はじめに
いつもググっては結果を書き留めておく習慣がなかったためqiitaに備忘録として残します。
今回はLaravel7.x系で新規登録、編集、削除の実装を行います。
※今回の例だと別に6系でも5系でも書き方変わリません。前提
環境
- Mac OS
- Composer version 1.10.9
- Laravel Framework 7.21.0
- PhpStorm、MAMP(MySQL)使用
実装一覧
誰がどの部署に所属しているのかを一覧にしています。特になんの変哲もないテーブルです。
そのうち拡張してリレーション組みたいですね。
id 名前 年齢 所属部署 1 A子 33 営業部 2 B夫 44 総務部 3 C美 27 技術部 4 B夫 51 経理部 ディレクトリ一覧
app
├── Http
│ ├── Controllers
│ │ └── MeibosController.php
└── Models
└── Meibos.phpresources
└── views
└── meibos
├── index.blade.php
├── register.blade.php
├── remove.blade.php
└── update.blade.phpルーティング一覧
URI HTTPメソッド アクション名 名前 /test/list GET MeibosController@index test.index /test/register/new GET MeibosController@new test.new /test/register/create POST MeibosController@create test.create /test/edit/{id} GET MeibosController@edit test.edit /test/update/{id} POST MeibosController@update test.update /test/remove/{id} POST MeibosController@remove test.destroy 実装
ここから実装に移ります。順番としては新規登録、編集、削除の順。
一覧表示
Model(Meibos)
meibosテーブルの設定を表示しています。更新時に一括で更新を行いたいためfillableで更新項目を設定しています。今回は'name','age','departments'の3項目です。
app/Models/Meibos.phpnamespace App\Models; use Illuminate\Database\Eloquent\Model; class Meibos extends Model { protected $table = 'meibos'; protected $primaryKey = 'id'; protected $fillable = [ 'name','age','departments','updated_at' ]; }Controller(MeibosController)
モデルから全データを引っ張ってくるだけです。値をviewへ渡すためにcomapctを使います。
app/Http/Controllers/MeibosController.php//リスト一覧 public function index(){ $meibos = Meibos::all(); return view('meibos.index',compact('meibos')); }index(View)
次はリスト一覧を表示するためのviewです。テスト用なので簡易的です。
@csrfは予期しないリクエスト等、webアプリケーションの脆弱性から保護してくれる便利機能…と私は解釈しています。
参考: https://readouble.com/laravel/7.x/ja/csrf.htmlformタグのうちどこでもいいので入れておきましょう。
resources/views/meibos/index.blade.php@foreach($meibos as $meibo) <tr> <td class="id"><a href="{{ route('test.edit',$meibo->id) }}">{{ $meibo- >id }}</a></td> <td class="other">{{ $meibo->name }}</td> <td class="other">{{ $meibo->departments }}</td> <td class="other">{{ $meibo->age }}</td> <td class="btn"> <form action="{{ route('test.destroy',$meibo->id) }}" method="POST"> @csrf <input type="submit" value="削除" class="delete_btn2"> </form> </td> </tr> @endforeach新規登録
インスタンスを作ってその中にinputデータを保存する形となります。
リクエストは別途FormRequestで作った方が見栄えはいいんですけどそれは後日拡張しましょう。Controller(MeibosController)
app/Http/Controllers/MeibosController.php//登録(view) public function new(){ return view('meibos.register'); } //登録(実装) public function create(Request $request){ $request->validate([ 'name' => 'required | string | max:255', 'departments' => 'required | string | max:255', 'age' => 'required | integer' ]); $meibos = new Meibos; $meibos->fill($request->all())->save(); return redirect('/test/list'); }register(View)
新規登録画面用のviewです。こちらも記事用のテストデータなので簡易的な作りとしています。
resources/views/meibos/register.blade.php<ul> @foreach($errors->all() as $error) <li class="error">{{ $error }}</li> @endforeach </ul> <form action="{{ route('test.create') }}" method="POST"> @csrf <label for="name">名前</label> <input type="text" name="name" required> <label for="age">年齢</label> <input type="number" name="age" required> <label for="departments">所属部署</label> <input type="text" name="departments" required> <input type="submit" class="submit_btn" value="登録"> </form>編集
find()でDBからデータを取得する。個別ページにする必要があるのでidを指定。$idには主キーが入る仕組み。
Controller(MeibosController)
app/Http/Controllers/MeibosController.php//修正 public function edit($id){ $meibo = Meibos::find($id); return view('meibos.update',compact('meibo')); } public function update(Request $request,$id){ $meibo = Meibos::find($id); $request->validate([ 'name' => 'required | string | max:255', 'departments' => 'required | string | max:255', 'age' => 'required | integer' ]); $meibo->fill($request->all())->save(); return redirect('/test/list'); }update(View)
edit()メソッドでcompactを紐づけたため$meiboを使ってDBデータを引っ張ってこれます。
resources/views/meibos/update.blade.php<ul> @foreach($errors->all() as $error) <li class="error">{{ $error }}</li> @endforeach </ul> <form action="{{ route('test.update',$meibo->id) }}" method="POST"> @csrf <label for="name">名前</label> <input type="text" name="name" required value="{{ $meibo->name }}"> <label for="age">年齢</label> <input type="number" name="age" required value="{{ $meibo->age }}"> <label for="departments">所属部署</label> <input type="text" name="departments" required value="{{ $meibo->departments }}"> <input type="submit" class="submit_btn" value="修正"> </form>削除
全部消したければ別ですが編集する時と同じように個別で指定する必要があるため$id必須。
Controller(MeibosController)
app/Http/Controllers/MeibosController.phppublic function destroy($id){ Meibos::find($id)->delete(); return redirect('/test/list'); }登録情報のような重要なデータだったら確認ページを挟んだ方が閲覧者の誤作動を防げますが、今回はただLaravelの挙動確認するための記事なので割愛します。
index(View)
編集時と同様viewでは遷移先にidのデータを渡す必要があります。そのための$meibo->idです。
resources/views/meibos/index.blade.php<td class="btn"> <form action="{{ route('test.destroy',$meibo->id) }}" method="POST"> @csrf <input type="submit" value="削除" class="delete_btn2"> </form> </td>終わりに
以上で一覧表示、新規登録、編集、削除のまとめを終了します。
認証絡んでなければ今回みたいに気楽にデータ引っ張ってきて面白いんですけどね。
こんな感じでしばらくの間備忘録用にLaravel関係の記事をアップします。お読みいただきましてありがとうございます。
お役に立てれば幸いです。そうでなければ申し訳ございません。
ではまたの機会に。参考URL
- 投稿日:2020-07-27T11:34:03+09:00
Laravelでオリジナルなページネーションを作る
Laravelには協力なページネーションの機能があるので、カスタマイズしなくても大体のことは出来るのですが、
- 見た目を完全にオリジナルにしたい
- ページ数が省略される位置のロジックを特殊なものにしたい、一番端まで行くボタンを追加したい
などの仕様に対応するためには少し工夫が必要です。以下でどうやってやるか解説していきます。
見た目を完全にオリジナルにしたい
php artisan vendor:publish --tag=laravel-pagination
を実行して、Laravelのフレームワーク側が持っているレイアウトを
/resources/views/vendor/pagination/
以下に持ってきて、レイアウトファイルをカスタマイズします。カスタマイズしたページネーターを
/resources/views/vendor/pagination/custom/pagenator.blade.php
とした場合は、{{ $paginator->links('pagination::service/offer/offer-list') }}という形で呼び出すことができます。
ページ数が省略される位置のロジックを特殊なものにしたい、一番端まで行くボタンを追加したい
ページの制御は
LengthAwarePaginator::elements()
にかかれており、その実装はIlluminate\Pagination\UrlWindow
にかかれているので、これら2つをカスタマイズすればよいです。
UrlWindow::getSliderTooCloseToBeginning()
、UrlWindow::getSliderTooCloseToEnding()
、UrlWindow::getUrlSlider()
辺りが、ページネーションの最初の方に何件ページを表示して残りを…にするか、ページネーションの最後の方に何件ページを表示して残りを…にするか、ページネーションの中間のところで何件ページを出して残りを…にするか、というのが設定できるので、そこをカスタマイズすればよいです。おまけ
ページネーションで移動するときはページのトップではなくて、一覧の最初の部分から始まって欲しいのでid指定をしたい、ということもあるでしょう。
そういうときは
fragment()
を使えばよいです。{{ $paginator->fragment('pagination-form')->links('pagination::custom/paginator') }}
- 投稿日:2020-07-27T09:22:57+09:00
【Laravel】データベースを使った画像の取得と表示
画像のアップロードとデータベースへの保存
画像のアップロード
formを作る
新しくviewファイルを作りformを作る。
<form action="{{route('任意のルート名')}}" method="post" enctype="multipart/form-data"> @csrf <input type="file" name="sample_image" > <input type="submit" value="投稿"> </form>
- formはenctype=“multipart/form-data”とする。multipart/form-dataはファイルの送信をするときに指定する必要がある。
- enctypeは送信データのエンコードタイプを指定しており、methodがpostのときにしか使えない。
- 何も指定しなければapplication/x-www-form-urlencodedとなり、これは全ての文字をURLにする。
- enctypeは基本は書かずにファイルの送信をするときだけmultipart/form-dataとすればいい。
- input type=“file”でファイルを選択できる。
Controllerで画像を保存する
public function store(Request $request){ $image = $request->file('sample_image')->store('public/image'); }
- contorollerで$request->file(‘name’)->store(‘任意のディレクトリ);とすることでstorage/app直下の任意のディレクトリにユニークなファイル名で保存される。
- 今回はname="sample_image"、保存するディレクトリはstorage/public/imageになる。
データベースにファイル名を保存する
「Controllerで画像を保存する」で作成したstoreの中身に追加する。
public function store(Request $request){ $image = $request->file('sample_image')->store('public/image'); #この下に追加する $image = str_replace('public/image/', '', $image); $image = new Image; $image->file = $image; $image->save(); }
- str_replace('指定の文字列' , '置換する文字列' , '対象の文字列')で対象の文字列から指定した文字列を置換する文字列に置き換える。
- 今回は$imageに保存されているファイル名からpublic/image/という文字列を''(空文字)に置き換えている。
※今回は画像を表示するのにヘルパ関数のassetを使うことを前提にしているのでファイル名のみをデータベースに保存した。
保存した画像の表示
ファイル名をデータベースから取得する
今回はimages_table(モデルはImage)の中のfileというカラムにファイル名が入っているとする。
public function index(){ $image = Image::all(); return view('image.index', compact('image')); }viewファイルで画像を表示する
※storage/app/public配下(画像を保存した場所)はwebからのアクセスを許可しないので、データベースからファイル名を取得しても画像は表示されない。
artisanコマンドでstorage:linkとすることでstorageのファイルがpublic直下に同期される(publicの中にstorageが追加されている)。
- 画像が保存されている場所:storage/app/public/image
- 画像を取得する場所:public/storage/image
php artisan storage:link
上記のartisanコマンドを通せばpublic/storage配下に画像があるのでにassetを使ってアクセスできる。
<img src="{{asset('storage/image/'.$image->file)}}">バージョン
php:7.3.11
Laravel:7.21.0
- 投稿日:2020-07-27T04:31:47+09:00
LaravelとVueを使ってモダンなWebアプリケーションを構築する
Build a modern web application with Laravel and Vue
- Part 1: Setting up your environment
- Part 2 : Creating our endpoints with REST in mind
- Part 3: Testing your application’s API endpoints
- Part 4: Creating Vue applications in Laravel
- Part 5: Creating a simple Trello clone using Laravel and Vue
上記のリンク通りに進めていきますが、LaravelやVueなどは最新版にします。最終的にはTrelloのクローンアプリを作ります。
※画像は転載元より引用
少しずつ加筆していきます。自分用のメモなので、解説は少なめです。関係ないところは飛ばしていきます。Part 1: Setting up your environment(https://blog.pusher.com/web-application-laravel-vue-part-1/)
記事ではMacでの環境構築が紹介されていますが、Windowsで環境構築を行います。
Prerequisites
このシリーズに沿って進むためには、以下のものが必要です。
- PHPの知識。
- Laravelフレームワークの基本的な知識
- JavaScriptとVueフレームワークの知識
- Node.js **とNPMの基本的な知識
Setting up for PHP development: installing composer
Installing Composer on Windows
Composer-Setup.exeをダウンロードして実行してください。最新のComposerをインストールし、ターミナル内の任意のディレクトリからComposerを呼び出すことができるようにPATHを設定します。
完了したら次のコマンドを実行して確認をします。shell$ composer ______ / ____/___ ____ ___ ____ ____ ________ _____ / / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/ / /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ / \____/\____/_/ /_/ /_/ .___/\____/____/\___/_/ /_/ Composer version 1.10.7 2020-06-03 10:03:56環境構築
Xamppにて環境構築を行います。以下のコマンドを実行します。
shell$ cd C:\xampp\htdocs $ composer create-project laravel/laravel trello-clone --prefer-dist $ cd ./trello-clone詳しい解説が必要な方は、以前に書いたこちらの記事を参考にしてみてください。
Laravelのタイムゾーンと言語設定
config/app.php70行目 'timezone' => 'Asia/Tokyo', 83行目 'locale' => 'ja',データベースの設定
xamppからphpMyAdminを起動してデータベースとユーザーを設定し、設定した情報を「.env」ファイルに書きこみます。
データベース名はフォルダ名と同一にしておくと後から見やすいので、今回はtrello-clone
という名前で作成しました。.env4行目 DB_CONNECTION=mysql 5行目 DB_HOST=127.0.0.1 6行目 DB_PORT=3306 7行目 DB_DATABASE=trello-clone 8行目 DB_USERNAME=ユーザー名 9行目 DB_PASSWORD=パスワードUseful Laravel CLI commands
make コマンドは最もよく使うコマンドです。これを使うと、アプリケーション用のさまざまな種類のコンポーネントを作成することができます。このコマンドを使って、モデル、コントローラ、データベースの移行、その他このシリーズでは取り上げないものを作成することができます。
app/ディレクトリに作成される新しいモデルクラスを作成するには、次のコマンドを実行します。
shell$ php artisan make:model SampleModelapp/Http/Controller/ディレクトリに作成される新しいコントローラクラスを作成するには、次のコマンドを実行します。
shell$ php artisan make:controller SampleControllerデータベース/migrations/ディレクトリに作成される新しいデータベースマイグレーションクラスを作成するには、次のコマンドを実行します。
shell$ php artisan make:migration create_sample_table先に生成したマイグレーションを使用してデータベースに変更をマイグレーションするには、次のコマンドを実行します。
shell$ php artisan migrate本当にシンプルなLaravelアプリケーションでは、これらのコマンドを使用することになるでしょう。これらのコマンドについては、アプリケーションを進めていくうちにさらに詳しく知ることができます。コンソールのドキュメントもチェックしてみてください。
Installing and configuring dependencies (Passport)
これから作るアプリケーションはAPIベースのものになるので、認識されたユーザーだけがアクセスできるようにAPIをセキュアにする方法が必要です。
Installing Laravel Passport
Laravel PassportはAPI認証のためのツールです。Passportは認証を簡単にし、数分でLaravelアプリケーションにOAuth2サーバーの完全な実装を提供します。
Passportをインストールするには、以下のコマンドを実行します。shell$ composer require laravel/passportAPI Auth using Laravel Passport
Laravel Passportを使った認証についての詳細な記事がここにあります。このシリーズの次のパートに進む前に一読しておくと、Laravel Passportに慣れるのに役立ちます。
Conclusion
このパートでは、最新のWebアプリケーションを構築するための開発環境の設定方法を考えました。次のパートでは、Laravelアプリケーションを構築し、構築中に知っておくべき概念を説明します。
アプリケーションの構築を進めていくうちに、VueとLaravelを使って最新のアプリケーションを構築するために、どのようにすべてのツールを組み合わせることができるかがわかるでしょう。
Part 2 : Creating our endpoints with REST in mind
Building the models
Trello-cloneでは3つもモデルを使いますが、後から見やすくなるのでモデルはModelフォルダにまとめます。
Userモデル
まず
app/Model
を作成して、その中にUserモデル:app/User.php
を移動し、app/Model/User.php
になるように変更します。
次に以下のファイルを書き換えます。app/Model/User.php3行目 namespace App; => namespace App\Model;config/app.php71行目 'model' => App\User::class, => 'model' => App\Model\User::class,database/factories/User.php5行目 use App\User; => use App\Model\User;Taskモデル、Categoryモデル
以下のコマンドを実行して、
-mr
オプションを利用してモデルとMigrationファイルを同時に作成します。shell$ php artisan make:model Model/Task -mr $ php artisan make:model Model/Category -mrThe User model
Laravelにはデフォルトのユーザーモデルがバンドルされているので、ユーザーモデルを作成する必要はありません。ユーザーモデルは以下のフィールドを持ちます。
- id - ユーザーのユニークなオートインクリメントID
- name - ユーザーの名前
- email - ユーザーのE-mail
- password - 認証に使用されるパスワード
Userモデルを開き、以下のように編集します。
app/Model/User.php<?php namespace App\Model; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Database\Eloquent\SoftDeletes; class User extends Authenticatable { use SoftDeletes, Notifiable; protected $fillable = ['name', 'email', 'password']; protected $hidden = [ 'password', 'remember_token', ]; public function tasks() { return $this->hasMany(Task::class); } }? A fillable column
代入可能なデータベースカラムのことです。つまり、代入したいデータをモデルのcreate関数に配列を渡すだけで代入できるということです。? SoftDeletes
データベースから実際にデータを削除せずにリソースを削除する方法です。テーブルが作成されると、'deleted_at'というフィールドができ、ユーザーがタスクを削除しようとすると、'deleted_at'フィールドには削除した日付時刻が入力されます。そのため、リソースの取得が行われた場合、'deleted'リソースはレスポンスの一部にはなりません。Task model
タスクモデルは以下のフィールドを持ちます。
- id - タスク自体のID
- name - タスクの名前
- category_id - タスクが属するカテゴリのID
- user_id - タスクが所属するユーザーのID
- order - それぞれのカテゴリ内でのタスクの順番
Taskモデルを以下のように編集します。
app/Model/Task.php<?php namespace App\Model; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class Task extends Model { use SoftDeletes; protected $fillable = ['name', 'category_id', 'user_id', 'order']; public function category() { return $this->hasOne(Category::class); } public function user() { return $this->belongsTo(User::class); } }Category model
カテゴリモデルには以下のフィールドがあります。
- id - カテゴリのID
- name - カテゴリの名前
Categoryモデルを以下のように編集します。
app/Model/Category.php<?php namespace App\Model; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class Category extends Model { use SoftDeletes; protected $fillable = ['name']; public function tasks() { return $this->hasMany(Task::class); } }Writing our migrations
Migrationファイルを以下のように編集します。
Updating our user migration
database/migrations/2014_10_12_000000_create_users_table.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateUsersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('users', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('email')->unique(); $table->string('password'); $table->rememberToken(); $table->timestamps(); $table->softDeletes(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('users'); Schema::table('flights', function (Blueprint $table) { $table->dropSoftDeletes(); }); } }Updating our category migration
database/migrations/XXXX_XX_XX_XXXXXX_create_categories_table.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateCategoriesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('categories', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps(); $table->softDeletes(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('categories'); Schema::table('categories', function (Blueprint $table) { $table->dropSoftDeletes(); }); } }Creating our task migration
外部キーの書き方が変更になっているの(参考リンク)で、チュートリアルとは異なります。
database/migrations/XXXX_XX_XX_XXXXXX_create_tasks_table.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateTasksTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('tasks', function (Blueprint $table) { $table->id(); $table->string('name'); $table->foreignId('user_id')->constrained(); $table->foreignId('category_id')->constrained(); $table->integer('order'); $table->timestamps(); $table->softDeletes(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('tasks'); Schema::table('tasks', function (Blueprint $table) { $table->dropSoftDeletes(); }); } }ファイルの並びを確認する
create_tasks_table.php
はusers
テーブルのIDとcategories
テーブルのIDを外部キーとして使用するため、先にusers
テーブルとcategories
テーブルを作成しないと以下のようなエラーが起こります。SQLSTATE[HY000]: General error: 1005 Can't create table `trello-clone`.`tasks` (errno: 150 "Foreign key constraint is incorrectly formed") (SQL: alter table `tasks` add constraint `tasks_category_id_foreign` foreign key (`category_id`) references `categories` (`id`))エラーを回避するためには
database/migratsions
フォルダのファイルが以下のようにcreate_tasks_table.php
が一番最後にあるかどうか確認します。2014_10_12_000000_create_users_table.php 2019_08_19_000000_create_failed_jobs_table.php XXXX_XX_XX_XXXXXX_create_categories_table.php XXXX_XX_XX_XXXXXX_create_tasks_table.php
create_tasks_table.php
が一番最後にない場合は、一番最後になるようにファイル名を変更します。
次に以下のコマンドを実行してMigrateします。shell$ php artisan migrate? Migrationsはデータベースのバージョン管理のようなものです。これにより、アプリケーションの進化に合わせてデータベースを作成、変更、削除することができ、SQLクエリ(または任意のデータベースが使用するクエリ)を手動で記述する必要がありません。また、あなたとあなたのチームがアプリケーションのデータベーススキーマを簡単に変更して共有することができます。詳細はこちらをご覧ください。
Database seeders
データベースの移行ができたので、アプリケーションをテストするときのためにダミーデータを入れる方法を見てみましょう。Laravelでは、シーダと呼ばれるものがあります。
? シーダーを使うと、ダミーデータを自動的にデータベースに挿入することができます。
Creating our users table seeder
データベースシーダを作成するには、次のコマンドを入力します。
shell$ php artisan make:seeder UsersTableSeederこれにより、
database/seeds/UsersTableSeeder.php
ファイルが作成されます。ファイルを開き、内容を以下のコードに置き換えてください。database/seeds/UsersTableSeeder.php<?php use App\Model\User; use Illuminate\Database\Seeder; class UsersTableSeeder extends Seeder { public function run() { User::create([ 'name' => 'John Doe', 'email' => 'demo@demo.com', 'password' => bcrypt('secret'), ]); } }run関数には、シーダの実行時に実行したいデータベースクエリが含まれています。
? model factoriesを使用して、より良いシードデータを作成することができます。
? 保存する前にパスワードをハッシュするためにbcryptを使用していますが、これはLaravelがパスワードをハッシュするために使用するデフォルトのハッシュアルゴリズムだからです。
Creating our categories table seeder
データベースシーダを作成するには、次のコマンドを入力します。
shell$ php artisan make:seeder CategoriesTableSeederこれにより、
database/seeds/CategoriesTableSeeder.php
ファイルが作成されます。ファイルを開き、内容を以下のコードに置き換えてください。database/seeds/UsersTableSeeder.php<?php use App\Model\Category; use Illuminate\Database\Seeder; class CategoriesTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $categories = ['Ideas', 'On Going', 'Completed']; foreach ($categories as $category) { Category::create(['name' => $category]); } } }Running our database seeders
データベースシーダを実行するには、
database/seeds/DatabaseSeeder.php
ファイルを開き、runメソッドを以下のコードに置き換えます。database/seeds/DatabaseSeeder.phppublic function run() { $this->call([ UsersTableSeeder::class, CategoriesTableSeeder::class, ]); }次に以下のコマンドを実行します。
shell$ php artisan db:seedこれでデータベースがデータで更新されるはずです。ある時点でデータをリフレッシュして再度シードしたい場合は、以下のコマンドを実行してください。
shell$ php artisan migrate:fresh --seedこれはデータベースのテーブルを削除し、それらを元に戻してシーダを実行します。
REST in a nutshell
技術的な用語では、RESTはREpresentational State Transferの略です(他の場所では、単に「リラックスする」という意味です)。この記事をよく理解するために、一口サイズのナゲットに分解する必要がある用語がいくつかあります。
Clients, statelessness, resources – what’s the relationship?
クライアントClients とは、アプリケーションと関わるデバイスのことです。どんなアプリケーションでも、それを操作するクライアントの数は 1 から数十億にもなります。あなたがウェブサイト (https://pusher.com など) にアクセスすると、クライアントはサーバーにリクエストを送信します。サーバーはあなたのリクエストを処理してから、クライアントに応答を送り返し、相互作用を行います。
ステートレスStatelessness とは、最も簡単に言えば、クライアントがすべてのリクエストを完了するために必要なものをすべて持っているような方法でアプリケーションを構築することを意味します。クライアントがそれ以降のリクエストを行うとき、サーバはクライアントに関連するデータを保存したり取得したりしません。アプリケーションがより多くのアクティブな同時使用ユーザを持つようになると、クライアントの状態を管理するサーバに不必要な負担がかかるようになります。ステートレスであることはアプリケーションの設計をシンプルにします。
リソースResources は、コード内の実際のインスタンスを表現したものです。例えば、学生が自分の成績をチェックできるアプリケーションを構築しているとします。これらのリソースは、データベースに保存されるデータとリンクされています。
RESTfulアプリケーションを構築するとき、サーバーはクライアントにリソースへのアクセス権を与えます。クライアントは、リソースを取得、変更、削除するためのリクエストを行うことができます。リソースは通常JSONやXML形式で表現されますが、他にも多くの形式があり、どの形式にするかは実装中に決めることができます。
Creating your first few REST endpoints
エンドポイントの作成を始める前に、REST リソースの命名のベストプラクティスに精通していることを確認してください。
現在、以下の HTTP 動詞を使用しています。
- GET - 通常はリソースを取得するために使用します。
- POST - 新しいリソースを作成するために使用します。
- PUT/PATCH - 既存のリソースを置換/更新するために使用します。
- DELETE - リソースを削除するために使用する
以下は、タスクリソースの REST エンドポイントがどのように見えるかを表形式で表したものです。
METHOD ROUTE FUNCTION POST /api/task Creates a new task GET /api/task Fetches all tasks GET /api/task/{task_id} Fetches a specific task PUT/PATCH /api/task/{task_id} Update a specific task DELETE /api/task/{task_id} Delete a specific task それでは、アプリケーションでルートの作成を始めましょう。routes/api.phpファイルを開き、更新します。
routes/api.phpRoute::resource('/task', 'TaskController'); Route::get('/category/{category}/tasks', 'CategoryController@tasks'); Route::resource('/category', 'CategoryController');上記では、ルートを定義しました。2つのルートがあり、それらを手動で作成しなくても他のすべてのルートを登録してくれます。リソースコントローラとルートについてはこちらをご覧ください。
Formatting responses and handling API errors
ここでは、リクエストが処理されたときのレスポンスの作成方法とフォーマットについて見ていきましょう。
Creating our controllers
ルートができたので、すべてのリクエストを処理するコントローラロジックを追加する必要があります。コントローラを作成するには、ターミナルで以下のコマンドを実行する必要があります。
shell$ php artisan make:controller NameControllerしかし、先ほどの-mrを使った時にリクエストを作成しているので、それを編集してみましょう。
app/Http/Controller/ディレクトリ内のTaskController.phpを開きます。その中で、先ほど作成したルートを処理するための基本的なメソッドを定義します。
ファイル内のストアメソッドを以下のように更新します。app/Http/Controller/TaskController.phppublic function store(Request $request) { $task = Task::create([ 'name' => $request->name, 'category_id' => $request->category_id, 'user_id' => $request->user_id, 'order' => $request->order ]); $data = [ 'data' => $task, 'status' => (bool) $task, 'message' => $task ? 'Task Created!' : 'Error Creating Task', ]; return response()->json($data); }? 16行目で、レスポンスがJSON形式に設定されていることがわかります。データをどのようなレスポンス形式で返したいかを指定することができますが、ここではJSONを使用します。
⚠️ まだアプリケーションの肉付けには力を入れていません。RESTfulエンドポイントを作成する方法を説明しています。後編ではコントローラを完全に作成していきます。
Securing our endpoints with Passport
ルートを作成しましたが、今のままでは誰でもアクセスできてしまうので、制限をかけて安全にしていきます。
Laravelは、デフォルトでWebルートとAPIルートをサポートしています。WebルートはWebブラウザからアクセスした動的に生成されたページのルーティングを処理し、APIルートはJSONまたはXML形式のレスポンスを必要とするクライアントからのリクエストを処理します。
Authentication and authorization (JWT) to secure the APIs
Part 1 では、Laravel Passportを使ったAPI認証について説明しているので、簡単にまとめます。
まず、Laravel Passportをインストールします。
shell$ composer require laravel/passportLaravel Passportには、動作に必要なデータベーステーブル用のMigrateファイルが付属しているので、実行します。
shell$ php artisan migrate次に、アプリケーションを保護するために必要なキーを作成できるように、パスポートのインストールするコマンドを実行します。
shell$ php artisan passport:installこのコマンドは、セキュアなアクセストークンを生成するために必要な暗号化キーに加えて、アクセストークンを生成するために使用される「個人アクセス」と「パスワード付与」クライアントを作成します。
インストール後、Userモデルで
Laravel Passport HasApiToken trait
を使用する必要があります。この特性はモデルにいくつかのヘルパーメソッドを提供し、認証済みユーザーのトークンとスコープを検査できるようにします。app/Model/User.php<?php [...] use Laravel\Passport\HasApiTokens; class User extends Authenticatable { use HasApiTokens, SoftDeletes, Notifiable; [...] }次に、AuthServiceProviderのブートメソッド内のPassport::routesメソッドを呼び出します。このメソッドは、アプリが必要とするトークンを発行するために必要なルートを登録します。
app/Providers/AuthServiceProvider.php<?php [...] use Laravel\Passport\Passport; class AuthServiceProvider extends ServiceProvider { [...] public function boot() { $this->registerPolicies(); Passport::routes(); } [...] }最後に、config/auth.phpの設定ファイルで、API認証ガードのドライバオプションをpassportに設定します。
config/auth.php[...] 'guards' => [ [...] 'api' => [ 'driver' => 'passport', 'provider' => 'users', ], ], [...]Log in and register using the API
Laravel Passportを使ってこのアプリケーションのAPI認証を設定したところで、ログインと登録のエンドポイントを作っていきます。
routes/api.php
ファイルに以下のルートを追加します。routes/api.phpRoute::post('login', 'UserController@login'); Route::post('register', 'UserController@register');また、APIの認証リクエストを処理するためのUserControllerを作成する必要があります。
app/Http/Controllers/UserController.php
というファイルを新規作成し、その中に以下のコードを配置します。app/Http/Controllers/UserController.php<?php namespace App\Http\Controllers; use App\Model\User; use Validator; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Auth; class UserController extends Controller { public function login() { $credentials = [ 'email' => request('email'), 'password' => request('password') ]; if (Auth::attempt($credentials)) { $success['token'] = Auth::user()->createToken('MyApp')->accessToken; return response()->json(['success' => $success]); } return response()->json($response, $status); } public function register(Request $request) { $validator = Validator::make($request->all(), [ 'name' => 'required', 'email' => 'required|email', 'password' => 'required', ]); if ($validator->fails()) { return response()->json(['error' => $validator->errors()], 401); } $input = $request->all(); $input['password'] = bcrypt($input['password']); $user = User::create($input); $success['token'] = $user->createToken('MyApp')->accessToken; $success['name'] = $user->name; return response()->json(['success' => $success]); } public function getDetails() { return response()->json(['success' => Auth::user()]); } }上のコードでは
Login Method: ここでは、ユーザが提供した認証情報を使って Auth::attempt を呼び出しています。認証に成功した場合は、アクセストークンを作成してユーザに返します。このアクセストークンは、ユーザがAPIにアクセスできるようにするために、すべてのAPIコールと一緒に送信するものです。
Register Method: ログインメソッドと同様に、ユーザー情報を検証し、ユーザーのアカウントを作成し、ユーザーのアクセストークンを生成します。
Grouping routes under a common middleware
ルートについては、認証が必要なルートを共通のミドルウェアの下にグループ化することができます。Laravelにはauth:apiミドルウェアが組み込まれているので、それを使っていくつかのルートを保護することができます。
routes/api.php<?php Route::post('login', 'UserController@login'); Route::post('register', 'UserController@register'); Route::group(['middleware' => 'auth:api'], function(){ Route::resource('/task', 'TasksController'); Route::resource('/category', 'CategoryController'); Route::get('/category/{category}/tasks', 'CategoryController@tasks'); });Handling API errors
サーバーがリソースを提供したり操作したりしているときにエラーが発生した場合、クライアントに何か問題が発生したことを伝える方法を実装しなければなりません。そのためには、特定のHTTP ステータスコードでレスポンスを提供しなければなりません。
UserController.php
ファイルを見ると、HTTPステータスコード401を実装しているのがわかります。Conclusion
このシリーズのこの部分では、アプリケーション用の RESTful エンドポイントをどのように作成するかを考えてきました。また、どのようにしてエラーを処理し、正しいHTTPステータスコードをクライアントに提供するかについても検討しました。
次の章では、Postmanを使ってAPIエンドポイントをテストする方法を取り上げます。コマンドラインからのテストに役立つユニットテストをいくつか設定します。