Laravel学習用備忘録 その2

雑多にLaravel ver.7.xでの作成に関し、綴る。
本記事の目的は私コーボー自身の備忘録である。記述が不正確な部分も多くあると思う。
正しい記述に関してはhttps://readouble.com/laravel/7.x/ja/ を参照すると、ハッピーである。
また、項目ごとに参考にした記事を載せるのでそちらを適宜参照すること。

### databaseの文字コード選択
新規に作る場合は、とりあえず、utf8mb4_general_ciを選べば良さそう。
日本語に対応するにはutf8を指定するようにとするサイトが多いが、調べるとutf8mb4にしておくと漢字にもしっかり対応するとのこと（自信なし。）。
いまのところ不具合はなさそう。

### ユーザー認証とログインしての記事投稿
参照：https://readouble.com/laravel/7.x/ja/authentication.html

#### ユーザー認証機能の追加
laravel7にはかんたんにユーザー認証機能を追加してくれるようになっているので、これを使う。以下を参考に進める。
[Laravel7でユーザー認証_1】基本のき](https://qiita.com/crosawassant/items/bf3982666b6333bcb8c8)
[日本語化についてはこちら。](https://qiita.com/crosawassant/items/d1f4c5c93f30afa9eb65)

### 日本語化
#### 参考
[Laravelでバリデーションエラーを日本語化対応する](https://qiita.com/hiro_K1992/items/12d02300c76c6ff55161)
[Laravel Document（日本語） | 多言語化](https://readouble.com/laravel/7.x/ja/localization.html?header=%25E7%25BF%25BB%25E8%25A8%25B3%25E6%2596%2587%25E5%25AD%2597%25E5%2588%2597%25E3%2581%25AE%25E3%2582%25AD%25E3%2583%25BC%25E4%25BD%25BF%25E7%2594%25A8)
[Laravel Document（日本語） | validation.php言語ファイル
](https://readouble.com/laravel/7.x/ja/validation-php.html?header=%25E8%25AA%25AC%25E6%2598%258E)

#### 全体

```php:config/app.php
//    'timezone' => 'UTC',
    'timezone' => 'Asia/Tokyo',

//    'locale' => 'en',
    'locale' => 'ja',

//ダミーデータを作るときに日本語で作ってくれる
    'faker_locale' => 'ja_JP',

//fallback_localeは編集しなくていい
    'fallback_locale' => 'en',
```

コンフィグをいじったので、下のコマンドを実施。
`$ php artisan config:cache`

#### バリデーション

#### 日本語ファイルを作成
[Laravel Document（日本語） | validation.php言語ファイル
](https://readouble.com/laravel/7.x/ja/validation-php.html?header=%25E8%25AA%25AC%25E6%2598%258E)を参考にすると、以下の３つのコマンドを打てば作られるみたい。
私の環境では、エラーが出たがちゃんとできたみたい。

```
php -r "copy('https://readouble.com/laravel/7.x/ja/install-ja-lang-files.php', 'install-ja-lang.php');"
php -f install-ja-lang.php
php -r "unlink('install-ja-lang.php');"
```

~~Laravel-lang~~
~~[https://github.com/caouecs/Laravel-lang/tree/master/src/ja]~~
~~からありがたくいただき`resources/lang/ja`にコピペする。~~

#### ja.jsonの作成
参考：[Qiita - 【Laravel7でユーザー認証_2】ユーザー認証を日本語化](https://qiita.com/crosawassant/items/d1f4c5c93f30afa9eb65)

`resources/lang/ja.json`を作る。そこにjson形式で記していく。
keyは__{hoge}で指定される"hoge"を、valueを翻訳したい文字列”ほげ”とする。

```php:ja.json
{
    "Register": "ユーザー登録",
    "Name": "氏名",
    "E-Mail Address": "メールアドレス",
    "Password": "パスワード",
    "Confirm Password": "パスワード (確認用)",
    "Login": "ログイン",
    "Logout": "ログアウト",
    "Remember Me": "ログインしたままにする",
    "Forgot Your Password?": "パスワードを忘れた方はこちら",
    "Reset Password": "パスワード再設定",
    "Send Password Reset Link": "パスワード再設定用のリンクを送る",
    "Verify Your Email Address": "メールアドレス認証を行ってください",
    "A fresh verification link has been sent to your email address.": "ユーザー登録の確認メールを送信しました。",
    "Before proceeding, please check your email for a verification link.": "メールに記載されているリンクをクリックして、登録手続きを完了してください。",
    "If you did not receive the email,": "メールが届かない場合、",
    "click here to request another.": "こちらをクリックして再送信してください。",
    "Please confirm your password before continuing.": "続行するにはパスワードを入力してください。"
}

```

### 画像のアップロード
わかりやすい右の記事を参考。[「Laravelで画像ファイルをアップロードする」](https://qiita.com/10mi8o/items/ce9c875e736c7b1d2498)
[Laravel公式Doc「ファイルストレージ」](https://readouble.com/laravel/7.x/ja/filesystem.html)も参考。

大きな流れとしては、DBにはパス名、そのパス名からサーバー上のLaravelプロジェクト内のディレクトリに保管したファイル名を参照することにする。

以下やること。

#### マイグレーションファイル
マイグレーションファイルで```$table->string('logo_path')->nullable();```などと画像パスを保管するカラムを作っておく。


#### フォームのビューテンプレート
フォーム画面のビューテンプレートに以下のような感じで記述。

```
<form action="{{route('football_teams.store')}}" method="post" enctype='multipart/form-data'>
<div class="form-group">
        <label for="logo_path">ロゴ：</label>
        <input type="file" id="logo_path" name="logo_path" class="form-control-file"
            value="{{ $team->logo_path ?? old('logo_path') }}">
</div>
```

ポイントは、```enctype='multipart/form-data'```をフォームタグに入れること。これがないと、他の形式と合わせてファイルをアップロードすると、エラーとなるので注意。

#### コントローラ
コントローラのDB保管するメソッド名のところに以下を追記する。

```
$logo_path = $request->file('logo_path')->store('public/logos'); //logo_pathはフルパス。
$team->logo_path = basename($logo_path); //DBへの保存はbasenameメソッドで画像名のみにするのがポイント。
```

fileメソッド内はinputタグのname値を記述する。storeで保管するディレクトリを指定。上のようにすると、storage/app/public/logosに保管される。
basenameでファイル名のみの保管にしないと、次のビューの行程でうまく行かないので、こうしておく。

#### シンボリックリンクを貼る
```php artisan storage:link```を実行。
コントローラで指定した、public/app/public/logosの保管では外部からアクセスできないとのこと。上のコマンドをうっておくと、public/storageからstorage/app/publicへシンボリックリンクを貼ることができ、public/storageにリンクが貼られる。

#### イメージの表示
ビューテンプレート内で以下のように記述すると、イメージが表示できる。

```
<img src="{{ asset('storage/logos/' . $team->logo_path) }}" alt="{{$team->name}}の画像">
```

シンボリックリンクを貼っているので、```asset()```でアクセスするようにする。

### マイグレーション
難しい。カラム名を変えるだけでも難しかった。
[「laravel migration カラム名変更」](https://qiita.com/kamotetu/items/b28a684ed4eecdb96a31)を参考にした。

#### マイグレーションの操作
`migrate:reset`　　マイグレーションの全ロールバック。
`migrate:refresh`　マイグレーションを全ロールバック後に、マイグレートを実施。
`migrate:fresh`　　DBの全テーブル削除後に、マイグレート。

#### マイグレーションのリセット
わかんなくなったら、一度リセットするほうがいいかも。
方法は、`php artisan migrate:reset`を実行。そのあと、migrationファイルを削除する。（ただし、最初からある上の２つは残しておく。）そして、データベースを削除し、同じ名前で作り直す。データは消えてしまうけどね。。。

### Modelでの$fillable
モデルの中に、入力可能なカラムのみ指定しておく。

```php:Task.php
class Task extends Model
{
    protected $fillable = [
        'name',
        'done',
    ];
}
```

\$task->fillでリクエストパラメータから値を取得してくれるようになる。
メリットは、\$fillableで設定しておくと、セキュリティの向上と、コントローラでの記述を少なくできる。

### CSSの適用
- プロジェクト直下のpublicにCSSフォルダを作成し、style.cssファイルを作成
- viewファイルのhead内に`<link rel="stylesheet" href="{{ asset('css/style.css') }}">`を記述し、読み込ませる。

### Bladeテンプレート
`{{ }}`で囲うことで、変数展開可能。そしてphpで記述できるらしい。例えば、`{{ isset($sample) ? $sample : "" }}`のように三項演算子も記述できる。

### DB：外部キー制約
- teams（多）　対 　members（一）
- 例えば、teamsテーブルのidをmembersのテーブルに連携させたいときは、membersマイグレーションファイルに`$table->foreignId('team_id')->constrained();`と記述すればオッケー（team_idと単数形になっていることに注意）。上記の書き方は`$table->foreignId('team_id')->constrained('teams')`と書いても良いが、規約に基づいて書かれている場合に、`constrained()`と省略しても勝手に連携してくれる。ただし、constrained()を使った書き方はLaravel 7以降の機能のようなので注意！
- 更に、連携先を削除したときのために、`$table->foreignId('team_id')->constrained()->onDelete('cascade');`とonDelete('cascade')をつけておけばよいみたい。

### リレーション
連携させたら、リレーションをモデルに定義すると良い。
それぞれのモデルは作成済みとする。
#### 多対1
Teamモデルのクラス表記内に
 
```php:Team.php
public function members()
    {
        return $this->hasMany('App\Member');
    }
```
とする。function名を複数形で書いている。
その結果、`$members = App\Team::find(1)->members`と()なし表記をすると、動的プロパティとして取得できる。
これをforeachで回して取得するとよい。

あとは、members()とすることでメソッドとして働く。`$member = App\Team::find(1)->members()->where('position', 'FW');`などとして、どのmemberを取得するのかを制約することもできる。

#### 1対多
Memberモデルのクラス表記内に

```php:Member.php
public function team()
    {
        return $this->belogsTo('App\Team');
    }
```
今度は単数形で書いていることに注意。
そして、以下のようにして表現できるようになる。

```
$member = App\Member::find(1);
echo $member->team->name;
```

### ルーティング
コントローラーを作成済みとする。
そのコントローラで一括設定。

```php:web.php
Route::resource('teams', 'TeamsController');
```

を追記。
ルーティングの確認は、`php artisan route:list`
~~なおルーティングの命名について注意しないと、ビューなどでのrouteメソッドでうまく値の受け渡しがうまくいかなくなったので注意しよう。~~
ビューテンプレート内で使うときには、

```
{{ route('football_teams.edit', ['football_team'=>1]) }}
```

みたいな感じにすると、第一引数のnameで設定されている`football_teams/{football_team}/edit`が,`football_teams/1/edit`として展開される。

```
{{ route('football_teams.edit', 1) }}
```

と省略しても上のURLとして展開される。