# Laravel 02
## 今日のコンテンツ
- 復習(環境構築、ターミナル操作、MVC、ルーティング、マイグレーション)
- CRUD実装(続き)
- ルート定義からコントローラに処理コードを移行
- バリデーション
- ページネーション
## 今日のゴール
- 1回目よりもMVC設計の流れがわかる
- CRUD処理のコードの書き方になれる
- Laravel Loveになる
---
:::danger
【注意】
- ファイルを編集するときは「自動保存」のチェック or 「Ctrl+S(win) / Command+S(mac)」を忘れずに
- php artisan コマンドは **cms階層**で行う
:::
:::info
Paiza Cloudおすすめ設定
- タブウィンドウモード(箱アイコンのメニューから選択可能)
- ターミナル2つ(1つはLaravelのWebサーバー用、もう一つはコマンド入力用)
- 「phpMyAdmin」と「プレビュー画面(ポート8000)」は、「新しいウィンドウで開く」で、ブラウザのタブに常駐させておく
:::
↓こんな感じ

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

## 【復習1】 環境構築
### ① サーバーの作成
下記設定で、「新規サーバー作成」
:::info
サーバ名:デフォルトでOK
web開発:PHP,Laravel
データベース:MySQL,phpMyAdmin
その他は設定不要
:::
### ② Laravelプロジェクトの作成
1. ターミナル上でLaravelプロジェクト作成コマンドを入力
`composer create-project laravel/laravel cms 6.* --prefer-dist`
2. プロジェクトディレクトリに移動
`cd cms`
3. Composerコマンドを実行
`sudo composer update`
4. BuiltInのLaravelサーバーを起動
`php artisan serve`
5. 動作確認
`Starting Laravel development server: http://127.0.0.1:8000
`
とターミナル上で表示され、ブラウザ(8000番ポート)をクリックしてLaravelのホーム画面が表示されていれば成功!
### ③ データベース作成&接続
1. データベースを作成
* phpMyAdminで作成する場合
①左タブからphpMyAdminのタブ内「Open phpMyAdmin」をクリック
②phpMyAdminの管理画面で `c9` というDBを作成
※照合順は`utf8mb4_general_ci`を選択
* ターミナルでDB作成する場合
ターミナル上で下記の手順で実行
```
mysql -u root -p
root [Enterキー]
create database c9;
exit;
```
2. 隠しファイルを表示
エディタ左側のファイル階層で右クリック→「隠しファイルを表示」
3. .envファイルを編集
cms直下にあるenvファイル内の同じファイルを上書き
```
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=c9
DB_USERNAME=root
DB_PASSWORD=
```
4. Webサーバーを再起動
:::danger
超重要!!!**.envを変更後はWebサーバーを再起動**する
:::
```
# 起動中のwebサーバーを停止
Ctl + C
# cmsディレクトリにいることを確認後、サーバー起動
php artisan serve
```
### ④ Laravelの/app/Providers/AppServiceProvider.php ファイルを修正
```php
// この行を冒頭付近に追加(use Illuminate\Support\ServiceProvider;の下)
use Illuminate\Support\Facades\URL;
public function boot() {
URL::forceScheme('https'); //この行をboot(){...}内に追加
}
```
---
## 【復習2】ログイン認証
1. マイグレーションを実行(cmsディレクトリにいるか確認!)
`php artisan migrate`
2. laravel/ui パッケージをインストール
`composer require laravel/ui:^1.0 --dev`
3. artisan コマンドを実行
`php artisan ui vue --auth`
4. npmパッケージをインストール
`npm install`
5. パッケージをビルド
`npm run dev`
---
## 【復習3】CRUDの登録と読込
### ①ルーティング(コントローラーの役割含む)を作成
1. /routes/web.phpに以下の記述を追加
先頭部
```php
use App\Book;
use Illuminate\Http\Request;
```
中間
```php
// 本のダッシュボード表示(books.blade.php)
Route::get('/', function () {
$books = Book::orderBy('created_at', 'asc')->get();
return view('books', ['books' => $books]);
//return view('books',compact('books')); //も同じ意味
});
// 新「本」を追加
Route::post('/books', function (Request $request) {
// Eloquentモデル(登録処理)
$books = new Book;
$books->item_name = $request->item_name;
$books->item_number = $request->item_number; //ここを変更
$books->item_amount = $request->item_amount; //ここを変更
$books->published = $request->published; //ここを変更
$books->save();
return redirect('/');
});
//「本」の更新画面表示
Route::post('/booksedit/{book}', function (){
//
});
// 本を削除
Route::delete('/book/{book}', function () {
//
});
```
### ②DB-テーブルを作成
1. booksテーブルを作成(マイグレーションファイル作成)
`php artisan make:migration create_books_table --create=books`
2. database/migrationsの直下のbooks_table.phpに以下追記
```php
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('item_name'); //追加
$table->integer('item_number'); //追加
$table->integer('item_amount'); //追加
$table->date('published'); //追加
$table->timestamps();
});
}
```
3. マイグレーションを実行(テーブル作成)
ターミナル上でコマンド実行
```
php artisan migrate
```
4. phpMyAdminでテーブルが作成されているか確認
### ③Modelを作成(テーブルを簡単に扱えるようにする機能)
1. ターミナルでコマンドを実行
```
php artisan make:model Book
```
2. /app/Book.php に作成されたことを確認
### ④Viewを作成(登録&表示のためのページ)
1. /resources/views/books.blade.php を作成
以下コードを貼り付け
```htmlmixed
@extends('layouts.app')
@section('content')
<!-- Bootstrapの定形コード… -->
<div class="card-body">
<div class="card-title">
ブックマーク
</div>
<!-- ↓バリデーションエラーの表示に使用-->
<!-- ↑バリデーションエラーの表示に使用-->
<!-- 本登録フォーム -->
<form action="{{ url('books') }}" method="POST" class="form-horizontal">
@csrf
<!-- 本のタイトル -->
<div class="form-group col-md-6">
<label for="item_name" class="col-sm-3 control-label">タイトル</label>
<input type="text" name="item_name" class="form-control" id="item_name">
</div>
<!-- 冊数 -->
<div class="form-group col-md-6">
<label for="item_number" class="col-sm-3 control-label">冊数</label>
<input type="text" name="item_number" class="form-control" id="item_number">
</div>
<!-- 金額 -->
<div class="form-group col-md-6">
<label for="item_amount" class="col-sm-3 control-label">金額</label>
<input type="text" name="item_amount" class="form-control" id="item_amount">
</div>
<!-- 公開日 -->
<div class="form-group col-md-6">
<label for="published" class="col-sm-3 control-label">公開日</label>
<input type="date" name="published" class="form-control" id="published">
</div>
<!-- 本 登録ボタン -->
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-primary">
Save
</button>
</div>
</div>
</form>
</div>
<!-- Book: 既に登録されてる本のリスト -->
<!-- 現在の本 -->
@if (count($books) > 0)
<div class="card-body">
<table class="table table-striped task-table">
<!-- テーブルヘッダ -->
<thead>
<th>本一覧</th>
<th> </th>
</thead>
<!-- テーブル本体 -->
<tbody>
@foreach ($books as $book)
<tr>
<!-- 本タイトル -->
<td class="table-text">
<div>{{ $book->item_name }}</div>
</td>
<!-- 本: 削除ボタン -->
<td>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
@endsection
```
:::warning
最後に、登録&表示ができているかプレビュー画面で確認しましょう。
前回までの内容は以上です。
:::
---
## ⑦ CRUD機能実装(削除)
:::success
【大まかなステップ】
1. [view]一覧画面から削除ボタンを作成(view)
3. [routing/controller]削除処理をroutesに追加
:::
1. books.blade.phpに削除ボタンを追加
< !-- 本: 削除ボタン -->下の<td></td>要素内に追加
```htmlmixed
<form action="{{ url('book/'.$book->id) }}" method="POST">
@csrf
@method('delete')
<button type="submit" class="btn btn-danger">
削除
</button>
</form>
```
:::info
【解説】
削除ボタンクリック時には,コントローラのdestroy()関数にリクエストが送られる
削除の処理を行うにはDELETEメソッドでリクエストを送る必要があるが、form からは GET または POST でしか送れない
`@method('delete')`を記述することで、DELETEメソッドで送信できる
※`{{ method_field('delete') }}` も同じ役割
引用元:taroosg先生
:::
2. プレビューで削除ボタンが追加されているか確認

3. web.phpに「本を削除」のルート定義と削除処理を追加編集
Viewからformでリクエストを送るところまでできました。
では次に、`/book/{book}` のルート定義のところに削除処理を書きましょう
```php
Route::delete('/book/{book}', function (Book $book) {
$book->delete(); //追加
return redirect('/'); //追加
});
```
:::info
【+α / 暗黙の結合 - `$book->delete()`で自動的に削除できるのはなぜ?】
`{book}`のところで渡ってくるidを使って、Bookモデルがbooksテーブルから対象となるレコード(一つのデータの塊)を自動で取ってきてくれます
より詳しく知りたい方は↓
> https://readouble.com/laravel/master/ja/routing.html#route-model-binding
※授業のコードでは`(App\Book $book)`ではなく`(Book $book)`と書けるのは、web.phpの先頭でBookモデルを読み込んでいる(`use App\Book;`)からです
:::
## ⑧ CRUD機能実装(更新)
この章では更新処理を実装します
:::success
【大まかなステップ】
1. [view]一覧画面から更新画面へのリンクボタンを作成(view)
2. [view]更新画面のviewを新規作成
3. [routing/controller] 更新画面の表示 & 更新処理をroutesに追加
:::
1. books.blade.phpに更新ボタンを追加
削除ボタンの<td></td>タグの下
```htmlmixed
<!-- 本: 更新ボタン -->
<td>
<a href="{{ url('booksedit/'.$book->id) }}">
<button type="submit" class="btn btn-primary">更新</button>
</a>
</td>
```
2. プレビューで更新ボタンが追加されていることを確認

3. booksedit.blade.phpをview配下に新規作成し、以下コードを貼り付け
```htmlmixed
@extends('layouts.app')
@section('content')
<div class="row container">
<div class="col-md-12">
<!-- ↓バリデーションエラーの表示に使用-->
<!-- ↑バリデーションエラーの表示に使用-->
<form action="{{ url('books/update') }}" method="POST">
<!-- item_name -->
<div class="form-group">
<label for="item_name">タイトル</label>
<input type="text" name="item_name" class="form-control" id="item_name" value="{{$book->item_name}}">
</div>
<!--/ item_name -->
<!-- item_number -->
<div class="form-group">
<label for="item_number">冊数</label>
<input type="text" name="item_number" class="form-control" id="item_number" value="{{$book->item_number}}">
</div>
<!--/ item_number -->
<!-- item_amount -->
<div class="form-group">
<label for="item_amount">金額</label>
<input type="text" name="item_amount" class="form-control" id="item_amount" value="{{$book->item_amount}}">
</div>
<!--/ item_amount -->
<!-- published -->
<div class="form-group">
<label for="published">公開日</label>
<input type="date" name="published" class="form-control" id="published" value="{{$book->published}}">
</div>
<!--/ published -->
<!-- Save ボタン/Back ボタン -->
<div class="well well-sm">
<button type="submit" class="btn btn-primary">Save</button>
<a class="btn btn-link pull-right" href="{{ url('/') }}"> Back</a>
</div>
<!--/ Save ボタン/Back ボタン -->
<!-- id 値を送信 -->
<input type="hidden" name="id" value="{{$book->id}}" />
<!--/ id 値を送信 -->
<!-- CSRF -->
@csrf
<!--/ CSRF -->
</form>
</div>
</div>
@endsection
```
3. [routing] web.phpに更新画面表示と更新処理を追加
```php
//「本」の更新画面表示
Route::get('/booksedit/{book}',function(Book $book){
return view('booksedit', ['book' => $book]);
});
//「本」の更新処理
Route::post('books/update',function(Request $request){
// Eloquent モデル
$books = Book::find($request->id);
$books->item_name = $request->item_name;
$books->item_number = $request->item_number;
$books->item_amount = $request->item_amount;
$books->published = $request->published;
$books->save();
return redirect('/');
});
```
:::success
以上で基本的なCRUD処理は終了です :raised_hands:
:::
---
## ルート定義からコントローラに処理コードを移行
今まではルート定義(web.php)にコントローラの役割を持たせていました。
※例:この`function(){}`の部分に、URL`'/'`が呼ばれた時の処理を書いていました
```php
Route::get('/', function () {
$books = Book::orderBy('created_at', 'asc')->get();
return view('books', ['books' => $books]);
});
```
機能が限られているサービスなら、ルート定義にfunction()部分を直接書いても記述量が少なく、一つのファイルで完結するのでわかりやすい面も。
しかし、一般的なサービスでは数十~数百のルート定義があるのはあたりまえ。
そうなるとルート定義のファイルが肥大化して逆にわかりにくいコードになってしまいます。
この章ではルート定義に持たせていた処理の役割をコントローラに移行しましょう。

左が・・・右になります!どちらが見通しがいいですか?

:::success
【本章の大まかなステップ】
1. BooksControllerを作成
2. BooksControllerとModelを関連付け
3. ルート定義の処理の部分をControllerに移行
4. ルート定義に、移行したControllerの場所を記述
:::
### ①BooksControllerを作成
1. BooksControllerを作成
ControllerもMigrationやModelと同じでコマンド一発で作成できます
`php artisan make:controller BooksController --resource`
:::info
【+α / --resourceってなに?】
コントローラで行う処理は大体決まっています。あらかじめよく使う処理のテンプレートを用意し簡潔に書けるようにしてくれるのが**リソースコントローラ**です。
Controllerを作るときに `--resource`のオプションを付けると作成できます
もっと詳しく学びたい方はこちら
> https://readouble.com/laravel/6.x/ja/controllers.html
:::
:::info
【+α / Model/Migration/Controllerを便利に作りたい!】
Modelを作る場合、たいてい対応するテーブルのMigrationとControllerを作ります。Laravelにはそれらをいっぺんに作るコマンドがあります。
もっと学びたい方はこちら
> https://readouble.com/laravel/8.x/ja/eloquent.html
> https://qiita.com/niisan-tokyo/items/9c799989cb535489f201
:::
2. BooksControllerが作成されたか確認

### ②BooksControllerとModelを関連付け
1. BooksControllerとModelを関連付け
6行目に追加
```php=6
use App\Book; //Book モデルを使えるようにする
```
### ③ルート定義の処理の部分をControllerに移行
まずは表示処理をやってみましょう
```php:web.php
// web.php
// 本のダッシュボード表示(books.blade.php)
Route::get('/', function () {
// ↓ここから
$books = Book::orderBy('created_at', 'asc')->get();
return view('books', ['books' => $books]);
// ↑ここまでをBooksControllerの index()に移行
});
```
```php:BooksController.php
// BooksController.php
public function index()
{
// ↓ここから
$books = Book::orderBy('created_at', 'asc')->get();
return view('books', ['books' => $books]);
// ↑ここまでを貼り付け
}
```
### ④ルート定義に、移行したControllerの場所を記述
web.phpの切り取ったルート定義に、移行したコントローラの場所を記述しましょう
```php
Route::get('/','BooksController@index');
```
### ⑤他のルート定義も同様に、③と④を繰り返しましょう
:::info
ルート定義と対応するリソースコントローラのfunctionの名前
| Routing | Controller|
| ------------------------| -------- |
| 登録処理( ダッシュボード表示) | index |
| 登録処理(「本」を追加) | store |
| 更新画面(「本」の更新画面表示) | edit |
| 更新処理(「本」の更新処理) | update |
| 削除処理(「本」を削除) | destroy |
:::
:::danger
※重要!:functionの引数がある場合は、それも移行しましょう
例: `function (Request $request){}` ← 括弧の中もControllerに移行
:::
### ⑥完成したら、CRUD処理が無事できるか確認
View から Controllerに処理の部分を移した後でもしっかり動くか確認しましょう。
### 完成すると下記のようになります
<web.php>
```php
// 本のダッシュボード表示
Route::get('/', 'BooksController@index');
// 登録処理(新「本」を追加)
Route::post('/books','BooksController@store');
// 更新画面(「本」の更新画面表示)
Route::get('/booksedit/{book}','BooksController@edit');
// 更新処理(「本」の更新処理)
Route::post('/books/update','BooksController@update');
// 削除処理(本を削除)
Route::delete('/book/{book}','BooksController@destroy');
```
<BooksController.php>
```php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Book;
class BooksController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$books = Book::orderBy('created_at', 'asc')->get();
return view('books', ['books' => $books]);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store (Request $request)
{
// Eloquentモデル(登録処理)
$books = new Book;
$books->item_name = $request->item_name;
$books->item_number = $request->item_number;
$books->item_amount = $request->item_amount;
$books->published = $request->published;
$books->save();
return redirect('/');
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit(Book $book)
{
return view('booksedit', ['book' => $book]);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request){
// Eloquent モデル
$books = Book::find($request->id);
$books->item_name = $request->item_name;
$books->item_number = $request->item_number;
$books->item_amount = $request->item_amount;
$books->published = $request->published;
$books->save();
return redirect('/');
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy(Book $book) {
$book->delete();
return redirect('/');
}
}
```
---
## バリデーション
**バリデーションとは入力チェック**のことで、ルール通り正しい形式で書かれているか確認することです。この章では今まで作った機能(登録&更新)に、バリデーションを追加しましょう。
今回は`Validatorファサード`というLaravelに備わっている機能を使用し、バリデーションを実装していきましょう
:::success
【大まかなステップ】
1. コントローラでValidatorを使えるようにする
2. コントローラにバリデーションのルールを記述
3. コントローラにバリデーションが失敗した時の処理を記述
4. エラー表示のビューを作成
5. エラー表示のビューを読み込み(@include)
6. エラーの場合、入力した値をviewで再表示
これを登録処理と更新処理の両方で行います
:::
### ① コントローラでValidatorを使えるようにする
<BooksController.php>
```php=
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Book;
use Validator; //ここを追加
```
### ② コントローラにバリデーションのルールを記述
```php
public function store (Request $request)
{
//バリデーション(ここを追加)
$validator = Validator::make($request->all(), [
'item_name' => 'required | min:3 | max:255',
'item_number' => ,
'item_amount' => 'required | max:6',
'published' => 'required',
]);
// Eloquentモデル(登録処理)
// ...
}
```
* item_numberのバリデーションを完成させてみましょう
```
* 必須項目
* 最低1文字
* 最高3文字
```
* 答え
:::spoiler
`'item_number' => 'required | min:1 | max:3',`
:::
--
:::info
【解説 / Validatorのmakeメソッド】
makeメソッドの第1引数は、バリデーションを行うデータが入ります。ここでは`$request->all()`になっているので、formで送られた全データをチェックしています。
第2引数はそのデータに適用するバリデーションルールです。
:::
### ③ コントローラにバリデーションが失敗した時の処理を記述
```php
public function store (Request $request)
{
//バリデーション
$validator = Validator::make($request->all(), [
// 省略
]);
//バリデーション:エラー (ここを追加)
if ($validator->fails()) {
return redirect('/')
->withInput()
->withErrors($validator);
}
// Eloquentモデル(登録処理)
// ...
}
```
### ④ エラー表示のビューを作成
/resources/views/common/errors.blade.php を作成します
1. `/resources/views/`の配下に`common`というフォルダを作り、そこに`errors.blade.php`を作成
2. 下記を貼り付け
```htmlmixed
<!-- resources/views/common/errors.blade.php -->
@if (count($errors) > 0)
<!-- Form Error List -->
<div class="alert alert-danger">
<div><strong>入力した文字を修正してください。</strong></div>
<div>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
</div>
@endif
```
### ⑤ エラー表示のビューを読み込み(@include)
```php
<!-- バリデーションエラーの表示に使用-->
@include('common.errors')
<!-- バリデーションエラーの表示に使用-->
```
ここまでかけたら、バリデーションエラーが表示されるか確認してみましょう。
<books.blade.php>

### ⑥ エラーの場合、入力した値をviewで再表示
inputに下記のoldヘルパーを記述すると、前に入力した値が保持される。
1. 各inputタグに下記を追記
```php
value="{{ old(変数名) }}"
```
例)本のタイトルのinputタグ
```htmlmixed
<!-- 本のタイトル -->
<div class="form-group col-md-6">
<label for="item_name" class="col-sm-3 control-label">タイトル</label>
<input type="text" name="item_name" class="form-control" id="item_name" value="{{old('item_name')}}">
</div>
```
### [更新処理]のバリデーション(Controller + View)
新規登録のバリデーションとエラー表示の機能は実装できました。
もう一つ、更新処理を同様に仕上げましょう。
<BooksController.php> storeメソッドの中に追加
```php
//バリデーション
$validator = Validator::make($request->all(), [
'id' => 'required', // storeに対しての追加分
'item_name' => 'required|min:3|max:255',
'item_number' => 'required|min:1|max:3',
'item_amount' => 'required|max:6',
'published' => 'required',
]);
//バリデーション:エラー
if ($validator->fails()) {
return redirect('/booksedit/'.$request->id)
->withInput()
->withErrors($validator);
}
```
<booksedit.blade.php>
```php
<!-- ↓バリデーションエラーの表示に使用-->
@include('common.errors')
<!-- ↑バリデーションエラーの表示に使用-->
```
:::info
どんなバリデーションルールが用意されているのか検索しながら実装しましょう
参考:https://laravel.com/docs/6.x/validation#available-validation-rules
:::
:::info
【+α / バリデーションの種類は他にも!】
Laravelでのバリデーションの実装方法は数種類あります。興味のある人は`ValidateRequests`や`FormRequest`など、探求してみましょう。
> https://readouble.com/laravel/6.x/ja/validation.html
:::
---
## ページネーション
:::info
> ページネーションでは1ページに表示する制限数を定めることができます。表示制限を設定した場合に「表示できないページ」がでてきますが、「2ページ」「3ページ」とリンクが自動で生成されます。この機能を「ページネーション」と呼んでいます。
>
出典:「はじめてのLaravel6 入門」(著:山崎学校長)
:::
さっそく実装してみましょう。
:::success
【ステップ】
1. コントローラでpaginate()を使ってDBからデータを取得
2. viewにページネーションを埋め込む
:::
1. コントローラでpaginate()を使ってDBからデータを取得
`記述式) モデル名::paginate(1ページあたりの表示数)`
`記述例) $books = Book::paginate(1ページあたりの表示数);`
※`get();` `all()`; の データ取得メソッドに代えて使います 。
BooksControllerのindexの、`get()`の部分を`paginate(数字)`に変更します
```php
public function index() {
$books = Book::orderBy('created_at', 'asc')->paginate(3);
}
```
2. ページネーションをテンプレートに埋め込む
`{{$変数->links()}}` をview(blade.php)に記述する必要があります。
books.blade.phpの`@endif`の下、`@endsection`の上にコードを追加
```htmlmixed
<div class="row">
<div class="col-md-4 offset-md-4">
{{ $books->links() }}
</div>
</div>
```
プレビュで確認するとこのようにページネーションボタンが表示されます

3. App\Http\Middleware\TrustProxiesを変更
変更前
```php=15
protected $proxies;
```
変更後
```php=15
protected $proxies = '*';
```
:::success
Laravel02_授業はここまでです。おつかれ様でした!
【Special Thanks】
:clap: 山崎学校長
:clap: taroosg先生
:clap: 中野先生
:::