# Laravel 03
## 今日のコンテンツ
- 認証機能①(ルート関連)
- 認証機能②(処理関連)
- ファイルアップロード
## 今日のゴール
- MVCの流れをある程度意識しながら開発できる
- 機能を追加・修正するときにMVCのどこを操作すればいいのかなんとなく想像できる
- Laravel Loveになる
---
:::danger
【注意】
- 「自動保存」のチェック or 「Ctrl+S(win) / Command+S(mac)」を忘れずに
- php artisan コマンドは **cms**階層で行う
:::
:::info
Paiza Cloudおすすめ設定
- タブウィンドウモード(箱アイコンのメニューから選択可能)
- ターミナル2つ(1つはLaravelのWebサーバー用、もう一つはコマンド入力用)
- 「phpMyAdmin」と「プレビュー画面(ポート8000)」は、「新しいウィンドウで開く」で、ブラウザのタブに常駐させておく
:::
↓こんな感じ

↓phpMyAdminとプレビュー画面をブラウザのタブには表示するには、このアイコンをクリック

---
## 認証機能①(ルート関連)
01の授業で、組み込み済みのLaravelの認証機能のベースを簡単に実装しました。この章ではもう少し具体的かつ実践的な認証機能を追加していきましょう。
:::success
【本章で学べること】
1. ルートの保護:ログインしていないとルート定義で「案内」しないようにする
2. 認証成功後のパスのカスタマイズ:認証成功後のリダイレクト先を変更
:::
### ルートの保護
:::success
【ステップ】
1. ルートグループ(Route::group)で認証後に実行するルート定義を囲う
以上!!
:::
1. ルートグループ(Route::group)で認証後に実行するルート定義を囲う
```php
Route::group(['middleware' => 'auth'], function () {
// この中に認証済み後のルート定義を入れる
});
```
:::warning
【課題】
1. ダッシュボード('/')を認証しないと見れないようにする
2. 「ダッシュボード・登録処理」は認証なしでOK、
「更新画面・更新処理・削除処理」は認証あり
:::
:::spoiler 回答例(ここをクリック)
1.
```php
Route::group(['middleware' => 'auth'], function(){
// 本のダッシュボード表示(books.blade.php)
Route::get('/','BooksController@index');
});
//「本」の更新画面表示
Route::get('/booksedit/{book}', 'BooksController@edit');
//「本」の更新処理
Route::post('books/update', 'BooksController@update');
// 新「本」を追加
Route::post('/books', 'BooksController@store');
// 本を削除
Route::delete('/book/{book}', 'BooksController@destroy');
```
※ ↑これだと例えば更新画面などはURLを直接入力してアクセスできちゃいます
2.
```php
// 本のダッシュボード表示(books.blade.php)
Route::get('/','BooksController@index');
Route::group(['middleware' => 'auth'], function(){
//「本」の更新画面表示
Route::get('/booksedit/{book}', 'BooksController@edit');
//「本」の更新処理
Route::post('books/update', 'BooksController@update');
// 新「本」を追加
Route::post('/books', 'BooksController@store');
// 本を削除
Route::delete('/book/{book}', 'BooksController@destroy');
});
```
:::
:::info
【解説 / middlewareとは】 ['middleware' => 'auth']ってなにしてる?
簡単に言えば、入国審査(や出国審査)のような、門番の役割
コントローラーの処理を実行する前(やした後)に何かをチェックしてくれる機能
例えば「ある特定のユーザーにしかできない機能(role)」「特定のIPアドレスからだけ許可」「スマホ専用ページへリダイレクト」など、リクエストを送ったユーザーの状況に応じて門番が振り分けてくれる役割などがある
> 参考:https://readouble.com/laravel/6.x/ja/middleware.html
:::
:::info
【解説 / Route::groupメソッド】
ルートグループは多くのルートで共通なもの(ミドルウェアや名前空間のようなルート属性)一括して適用してくれる便利なルート定義の方法
例えば、管理者と利用者のサイトが分かれていて、ルート定義が多くなってきたときに
```php
Route::prefix('admin')->group(function(){
// admin用のルート定義がここに入る
}
```
と書くことで、`https://xxxxx.com/admin/yyyy`のように、`admin/`以下のルート定義をgroup内に書くことができるようになる
> 参考:(ルートグループの項)
https://readouble.com/laravel/6.x/ja/routing.html
:::
### 認証成功後のパスのカスタマイズ
デフォルトでは、ユーザーが認証に成功すると、`/home`のURIへリダイレクトします
認証後のリダイレクトパスをカスタマイズするには、`RouteServiceProvider`の中で`HOME定数`を変更します
<app/Providers/RouteServicePrivicer.php>
```php=24
public const HOME = '/home';
```
これを下記に変更
```php=24
public const HOME = '/';
```
---
## 認証機能②(処理関連)
前章では認証済みユーザーかどうかでルートを保護する方法を学びました
しかし、まだ認証済みユーザー全員が同じブックマークを共有する、「全ユーザー1サービス」 でした
そこで本章では、認証済みユーザーの情報を使って、ユーザごとに表示・登録などCRUDできるデータを切り替える方法を学びましょう
これを学ぶことでユーザーに紐づいた情報を安全に管理でき、「1ユーザー1サービス」を実装することができるようになります
早速、ログインユーザーが登録した本のみ表示する機能を実装していきましょう
:::success
【主なステップ】
1. [migration]booksテーブルにuser_idのカラム(列)を追加
2. [controller]BooksControllerで「認証済みユーザのid」を使い、CRUD処理を修正
画面の流れ

出典:はじめてのLaravel6 入門(山崎学校長)
:::
### ① booksテーブルにuser_idのカラム(列)
「ある一冊の本のデータを誰が登録したか」を判別するためには、「登録したユーザーのid」を情報として含める必要があります。ここではテーブルにuser_idのカラムを新たに追加しましょう。
1. books_tableのマイグレーションファイルを編集し、user_idを追加する
< database/migrations/ [作成日時]_create_books_table.php>
```php
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('user_id'); //ここを追加
$table->string('item_name');
$table->integer('item_number');
$table->integer('item_amount');
$table->date('published');
$table->timestamps();
});
}
```
※ `id`が`bigIncrements`ではなく`increments`の場合は、`user_id`も`integer`にして整合性を保ちます
2. マイグレーションを実行(refresh)
ターミナルで下記を実行(cms階層)
```
php artisan migrate:refresh
```
3. booksテーブルが更新されているか、phpMyAdminで確認

:::info
【+α / テーブルのカラムを後から編集したいときは】
今回はわかりやすさを考慮し、すでにあるマイグレーションファイルを編集→refresh(初期化→再マイグレーション)することでカラムを追加しました
他には、カラム追加用の新たなmigrationファイルを作りmigrateを実行する、という方法が取られます
データベース設計が初めからしっかりできているのがもちろん一番いいですが、後からでも修正が可能なので安心して開発しましょう
:::
### ② 認証済みユーザーの取得
ここでは、「認証済みユーザーの取得方法」と、「それを利用しCRUD処理を行う方法」を学びましょう
1. 認証済みユーザーを取得する
まずは認証済みユーザーの情報を取得する方法を見てみましょう
< BooksController >
```php
use Auth; // 冒頭付近に追加
// 試しにindexの中で認証ユーザーの情報を取得してみましょう
public function index(Request $request){
// 現在認証されているユーザーの取得
$user = Auth::user(); // $request->user(); でもOK
// 現在認証されているユーザーのID取得
$id = Auth::id(); // Auth::user()->id(); でもOK
}
```
`$user` と `$id`をそれぞれ`dd()`もしくは`ddd()`で確認してみましょう
```php
dd($user);
```
:::danger
【開発のポイント】
初めのうちは1つ1つのステップで細かく確認作業をすると、逆にスムーズに開発が進むようになることがわかるでしょう。
Laravelでは`dd()` または `ddd()` という便利な確認ツールがあります。利用しない手はないですね!
:::
:::info
【+α / 認証済みか確認】
`Auth::check()` をすると、認証済みであれば`true`が返ります
より詳しくは
> https://readouble.com/laravel/6.x/ja/authentication.html
:::
2. 登録処理(store)を変更
本を登録するときに認証済みユーザーのuser_idを保存するようにしましょう
```php
// Eloquentモデル(登録処理)
$books = new Book;
$books->user_id = Auth::user()->id; //追加
$books->item_name = $request->item_name;
//...
```
3. 表示処理(index)を変更
```php
// 変更前
$books = Book::orderBy('created_at', 'asc')->paginate(3);
// 変更後
$books = Book::where('user_id', Auth::id())->orderBy('created_at', 'asc')->paginate(3);
```
アカウントを2つ作って、実際にユーザーに紐づくアカウントだけ表示されているか確認してみましょう!

4. 更新画面表示(edit)を編集
これではまだbookseditにどの認証済みユーザーもアクセスできてしまいます。
そこで自分の「本」にだけアクセスできるようにする条件を付け加えましょう
```php
public function edit(Book $book){
if($book->user_id === Auth::id()){
return view('booksedit', ['book' => $book]);
} else {
return "アクセス権がありません";
}
}
```
5. 更新処理(update)を編集
```php
// 変更前
$books = Book::find($request->id);
// 変更後
$books = Book::where('user_id', Auth::id())->find($request->id);
```
---
## ファイルアップロード
この章では画像ファイルをアップロードして、表示する機能を実装しましょう。
:::success
【主なステップ】
1. [migration]booksテーブルに画像用のカラム(列)を追加
2. [view]画像用にformタグを編集 / inputタグを追加
3. [controller]BooksController@storeを編集
4. [view]Viewに画像を表示する`<img>`タグを追加
:::
### ① [migration] booksテーブルに画像用のカラム(列)を追加
1. マイグレーションファイルを更新
</database/migrations/******create_books_table.php>
```php
$table->string('item_img')->nullable(); // 追加
```
:::info
【解説 / nullable()】
NullがOKのカラム(列)には`->nullable()` をつけましょう
該当のinputの値が空でrequestしても、登録・更新処理ができるようになります
:::
2. マイグレーションを実行
```
php artisan migrate:refresh
```
3. phpMyAdminでbooksテーブルを確認
### ② [view] 画像用にformタグを編集 / inputタグを追加
1. formタグを編集
books.blade.phpを編集
```html
<form enctype="multipart/form-data" action="{{ url('books') }} "method="POST" class="form-horizontal">
```
2. inputタグを追加
<公開日>の下に追加
```html
<!-- 画像 -->
<div class="form-group col-sm-6">
<label for="item_img" class="col-sm-3 controll-label">画像</label>
<input type="file" name="item_img" id="item_img">
</div>
```
### ③ [controller] BooksController@storeを編集
バリデーション処理の後に追加
```php
// ファイルアップロード
$file = $request->file('item_img'); //file取得
if( !empty($file) ){ //fileが空かチェック
$filename = $file->getClientOriginalName(); //ファイル名を取得
$move = $file->move('./upload/', $filename); //ファイルを移動
}else{
$filename = "";
}
```
`$books->published = $request->published;`の後に追加
```php
//登録処理に1行追加
$books->item_img = $filename;
```
### ④ [view] Viewに画像を表示する`<img>`タグを追加
最後に、books.blade.phpに画像を表示するimgタグを追加して終わりです。
```html
<!-- 本タイトル -->
<td class="table-text d-flex flex-row">
@if($book->item_img !== "")
<img src="upload/{{$book->item_img}}" width="100">
@endif
<div>{{ $book->item_name }}</div>
</td>
```
:::info
【コード解説】
今回item_imgはnullable(つまり空欄でもOK)にしています。
item_imgのファイル&パス(参照先)がある場合に画像を表示するよう、条件分岐しています。
:::
:::success
Laravel03_授業はここまでです。おつかれ様でした!
【Special Thanks】
:clap: 山崎学校長
:clap: taroosg先生
:clap: 中野先生
:::