Laravel 6.xでS3への保存・呼び出し方法

– hasManyの1M以下のファイルを、storage保存から、S3保存に切り替えたい
### 現状:storage保存

if($file = $request->file('file')){
            $path = $file->getClientOriginalName();
            $file->storeAs('./public/files/tmp/', $path);
            $inputs['path'] = $path;
        } else {
            $inputs['path'] = '';
        }

## 手順
### 1. AWSマネージメントコンソールログイン
https://ap-northeast-1.console.aws.amazon.com/
ユーザ名メニュー 「My Security Credentials」 -> IAMページ表示

### 2. S3用のIAM作成
Users -> Add user
1ページ目
– User name: [ ${project name} ]
– Select AWS access type: check [Programmatic access] ※Enables an access key ID and secret access key for the AWS API, CLI, SDK, and other development tools.

2ページ目
– Set permissions: [Attach existing policies directly]
— [AmazonS3FullAccess]

3ページ目
– Add tags (optional): none

4ページ目
– Review: nothing to do

5ページ目
– User name: ${project name}
– Access key ID : 新規発行
– Secret access key : 新規発行

### 3. S3 bucket作成
https://s3.console.aws.amazon.com/s3/home?region=ap-northeast-1
1ページ目
– Bucket name: [ ${project name} ]
– Region: [Asia Pacific (Tokyo)]

2ページ目
– Properties : 必要に応じて設定

3ページ目
– Block public access : off

4ページ目
– review -> create bucket

### 4. composerインストール
https://readouble.com/laravel/6.x/ja/filesystem.html#driver-prerequisites

// out of memoryとなるのでswapメモリを追加
$ sudo /bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024
$ sudo /sbin/mkswap /var/swap.1
$ sudo /sbin/swapon /var/swap.1
$ free

// s3パッケージインストール
$ php composer.phar require league/flysystem-aws-s3-v3 ~1.0
// キャッシュアダプタインストール(公式ドキュメントに絶対に必要と書いてある。。)
$ php composer.phar require league/flysystem-cached-adapter ~1.0

### 5. envファイルにS3アクセス情報追記

AWS_ACCESS_KEY_ID=${access_key}
AWS_SECRET_ACCESS_KEY=${secrete_access_key}
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=${bucket name}

### 6. controllerから保存
putFileAs(‘S3ディレクトリ’, $file, ‘filename’, ‘public’) で保存。
ファイル名を気にしない場合はputFileでもOK

if($file = $request->file('file')){
            $path = $file->getClientOriginalName();
            Storage::disk('s3')->putFileAs('/', $file, $path,'public');
            $inputs['path'] = $path;
        } else {
            $inputs['path'] = '';
        }

### 7. S3ファイルの表示

Route::get('/read', function(){
	$path = Storage::disk('s3')->url('image.jpeg');
	return "<img src=".$path.">";
});

思ったより簡単でワロタ。ベタがきだと、もう少しコードが必要だった記憶があります。

DBに現在時刻を5分区切りの単位で入れたい時

### フォーム入力
1. フォームから時間を入力する場合はselect文で5分単位で選べるように、5分単位のテーブルを作る
insert into minutes (name) values (’00’);
insert into minutes (name) values (’05’);
insert into minutes (name) values (’10’);
insert into minutes (name) values (’15’);
insert into minutes (name) values (’20’);
insert into minutes (name) values (’25’);
insert into minutes (name) values (’30’);
insert into minutes (name) values (’35’);
insert into minutes (name) values (’40’);
insert into minutes (name) values (’45’);
insert into minutes (name) values (’50’);
insert into minutes (name) values (’55’);

2. select文では、minutesテーブルから値を呼び込む

{!! Form::select('start', $minutes, 1, ['class' => 'form-control']) !!}

### 現在時刻を5分単位でDBに入力

$minute = date('i');

        $minute_margin = 5;
        if($minute % $minute_margin){
            $minute += $minute_margin - ($minute % $minute_margin);
        }

例: 43分の時:43 + (5-3) = 45

上記は端数があった場合繰り上がりですが、$minute -= $minute % $minute_margin とすれば、繰り下がりになる筈です。

あ。。。55〜00分の処理忘れてた。。
### 追加

$hour = date('H');
        $minute = date('i');


        $minute_margin = 5;
        if($minute % $minute_margin){
            if($minute > 55){
                $hour = date('H',strtotime("+1 hour"));
                $minute = 00;
            } else {
                $minute += $minute_margin - ($minute % $minute_margin);
            }          

        }

Laravel 入力フォームが時間・分に別れている時の未来判定バリデーション

– 入力フィールドが時間・分に別れている時に、「終了時間が開始時間より未来」のバリデーションルール作成方法
— どこで時間と分を結合してバリデーションをかけるか? 

*.blade.php

<tr>
                    <th>開始時刻 <span class="badge badge-danger">必須</span></th>
                    <td>
                        <div class="form-inline">
                                {!! Form::select('start_H', $hours, 10, ['class' => 'form-control']) !!}
                                <span class="unit">時</span>
                                {!! Form::select('start_i', $minutes, 1, ['class' => 'form-control']) !!}
                                <span class="unit">分</span>
                        </div>
                    </td>
                </tr>
                <tr>
                    <th>終了時刻 <span class="badge badge-danger">必須</span></th>
                    <td>
                        <div class="form-inline">
                                {!! Form::select('end_H', $hours, 19, ['class' => 'form-control']) !!}
                                <span class="unit">時</span>
                                {!! Form::select('end_i', $minutes, 1, ['class' => 'form-control']) !!}
                                <span class="unit">分</span>
                        </div>
                        @error('end')
                                <span class="error">{{$message}}</span>
                        @enderror
                    </td>
                </tr>

### controller
コントローラーでunix timeを計算し、「終了時間」-「開始時間」 <= 0 の時、withErrorsでエラーメッセージと一緒にredirect backする [php] public function confirm(CustomeRequest $request){ $inputs = $request->all();
$start = $inputs[‘start_H’] . ‘:’ . $inputs[‘remote_start_i’] . ‘:00’;
$end = $inputs[‘end_H’] . ‘:’ . $inputs[‘remote_end_i’] . ‘:00’;
if((strtotime($end) – strtotime($start)) <= 0) { return redirect()->back()->withInput()->withErrors(array(‘end’ => ‘終了時刻は、開始時刻より未来を設定してください’));
}
[/php]

フォームリクエストやカスタムリクエストは、attribute、バリデーション処理、エラーメッセージが対になっているので、コントローラー側で処理をした。

LaravelCollectiveのフォームでvue.jsを使って初期値を表示させたい時

laravelCollectiveでvue.jsを使うと、vue.jsが後から呼び出されるので、エラー時や戻る時にinputフォームの値がリセットされてしまう。

<div id="title">
  {!! Form::text('title', null, ['class'=>'form-control', 'placeholder'=>'タイトルを20文字以内で入力してください', 'maxlength'=>'20', 'v-model.trim'=>'message', 'autocomplete'=>'off']) !!} 
 </div>
                        @error('title')
                                <br><span class="error">{{$message}}</span>
                        @enderror

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
            el: "#title",
            data: { message: ""}
        });
</script>

### {{old(‘title’)}}をvue.jsにセット
vue.jsにセットすれば、エラー時や戻る時にinputフォームの値が表示される。

var titleValues = {!! json_encode(old('title', [])) !!};
        new Vue({
            el: "#title",
            data: { message: titleValues}
        });

Vue.jsの仕組みを理解していれば、直ぐにわかる事なのでしょうが、このトラブルシューティングに数時間かかりました。

vue.jsの{{}}が*.blade.phpでエラーになる時

{{}}がbladeの変数を受け渡す記述と同じなのでエラーになります。'{‘の頭に@を追加します。

### error

<span class="char-length">{{ message.length }}/20</span>

Use of undefined constant message – assumed ‘message’ (this will throw an Error in a future version of PHP)

### 修正

<span class="char-length">@{{ message.length }}/20</span>

これは比較的直ぐに原因がわかる類のエラーです。

Laravel 6.x 親テーブルに対し、複数のbelongsToで呼び込みたい時

親 :usersテーブル(->hasMany)、 子: pictures のリレーションにおいて、
子 picturesの 「user_id」 「review_user_id」 から、belongsToで親テーブルのユーザーを呼び込みたい時のモデルの書き方

### 子テーブルのmigration file

Schema::create('pictures', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->integer('user_id')->index()->unsigned();
            // 省略
            $table->integer('review_user_id')->index()->unsigned()->nullable();
            $table->timestamps();
            $table->softDeletes();
        });

### 子テーブルのモデル
Picture.php
belongsToの第二引数に小テーブルのカラム、第三引数に親テーブルのカラムを記述する。

public function user(){
        return $this->belongsTo('App\User');
    }
public function reviewUser(){
        return $this->belongsTo('App\User', 'review_user_id', 'id');
    }

### 挙動テスト

Route::get('/read', function(){
	// $user = Picture::findOrFail(1)->user;
	// dd($user);
	$user = Picture::findOrFail(2)->reviewUser;
	return $user->name;
});

直ぐに解決するかと思いきや、ドキュメントが少なく意外と苦戦しました。こういうケースでは、Laravel側としては、usersテーブルから2回呼び込むのではなく、users, review_usersという風に親テーブルを分けて設計して欲しいのかもしれません。

Laravel 確認画面でbelongsToの値を表示する

ユーザの権限テーブルを作り、ユーザ登録画面確認画面でデータベースのリレーションを使って権限テーブルから値を取得して表示させたい時

### usersテーブル作成

Schema::create('users', function (Blueprint $table) {
            // ...省略
            $table->integer('role_id')->index()->unsigned()->nullable();
            // 省略...
        });

### rolesテーブルの作成
$ php artisan make:model Role -m
migrationファイル

public function up()
    {
        Schema::create('roles', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->timestamps();
        });
    }

$ php artisan migrate;
insert into roles (name) values (‘権限A’);
insert into roles (name) values (‘権限B’);

User.php

public function role(){
        return $this->belongsTo('App\Role');
    }

mass assignment
// 省略

### 登録画面
create.blade.php
– LaravelCollectiveでradioボタンを使って権限を選択する

<div class="form-check">
	            				{{Form::radio('role_id', 1, true, ['class' => 'form-check-input'])}}
	            				{!! Form::label('role_id', '権限A', ['class'=>'form-check-label']) !!}
            				</div>
            				<div class="form-check">
	            				{{Form::radio('role_id', 2, null, ['class' => 'form-check-input'])}}
	            				{!! Form::label('role_id', '権限B', ['class'=>'form-check-label']) !!}
            				</div>

### Controllerでの読み込み
UsersController.php
– plunkで、rolesテーブルのidとnameを配列で取得して、入力値と一緒にviewに渡す

use App\Role;
public function confirm(Request $request)
    {
        $roles = Role::pluck('name','id')->all();
        $inputs = $request->all();
        return view('admin.account.confirm', compact('inputs','roles'));
    }

### 確認画面での表示
confirm.blade.php
– 入力画面から渡ってきたrolesテーブルのrole_idの値を表示

{{ $roles[$inputs['role_id']] }}