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']] }}

laravel 6.x 確認画面を挟んだstorage画像の保存処理

Laravelでは画像はstorageフォルダに格納する
Webからのアクセスを許すには、public/storageからstorage/app/publicへシンボリックリンクを張る必要がある
https://readouble.com/laravel/6.x/ja/filesystem.html

### 駄目な例
moveコマンドで、public配下に格納

UsersController.php

if($file = $request->file('file')){
            $name = $file->getClientOriginalName();
            $file->move('./images/tmp/', $name);
            $inputs['path'] = $name;

### 格納先・読み込み元をstorageに修正
$ php artisan storage:link
// ./public/storageが./storage/app/publicへのリンクとなる

UsersController.php

if($file = $request->file('file')){
            $name = $file->getClientOriginalName();
            // $file->move('./images/tmp/', $name);
            $file->storeAs('./public/images/tmp/', $name);
            $inputs['path'] = $name;

confirm.blade.php

<img src="{{ $inputs&#91;'path'&#93; ? asset('/storage/images/tmp/' . $inputs&#91;'path'&#93;) : 'https://placehold.jp/100x100.png' }}" class="img-icon">

確認画面で戻るボタンが押された場合は、Storage::deleteで削除する。確認画面で登録完了ボタンが押された場合は、画像の前部にCarbonでtimestampを付けて、prdフォルダに移動させる。画像のpathはDBに格納する。

UsersController.php

use Illuminate\Support\Facades\Storage;
use Carbon\Carbon;
public function store(Request $request)
    {
        $action = $request->get('action');
        $inputs = $request->except('action');

        if($action == '戻る'){
            Storage::delete('public/images/tmp/'.$inputs['profile_img']);
            return redirect()->action('UsersController@create')->withInput($inputs);
        }
        $timestamp = Carbon::now()->timestamp;
        $path = $timestamp.'_'.$inputs['profile_img'];
        Storage::move('public/images/tmp/'.$inputs['profile_img'], 'public/images/prd/'.$path);
        return 'done';
    }

ドキュメントを読む限り、storageに格納した方がファイルシステムの使う上で都合が良いように見えます。

laravel 6.x nullableなフォームのカスタムバリデーションの作り方

– 電話番号のフォームに入力があった場合、電話番号のバリデーションをかけたい

/^(([0-9]{2,4}-[0-9]{2,4}-[0-9]{3,4})|([0-9]{8,11})$/

– 未入力の場合はバリデーションをパスする

カスタムバリデーション公式ドキュメント: https://readouble.com/laravel/6.x/ja/validation.html#custom-validation-rules

$ php artisan make:rule PhoneRule

./app/Rules/PhoneRule.php
入力値がなかった場合は、tureを返す

 public function passes($attribute, $value)
    {
        if($value){
            return preg_match('/^(([0-9]{2,4}-[0-9]{2,4}-[0-9]{3,4})|([0-9]{8,11}))$/', $value);
        } else {
            return true;
        }       
    }
    public function message()
    {
        return trans('validation.phone');
    }

./app/Http/Requests/CreateUserRequest.php

use App\Rules\PhoneRule;
public function rules()
    {
        return [
            'phone' => [new PhoneRule],
        ];
    }

./resources/lang/ja/validation.php

'phone' => ':attributeはハイフン有りか無しで半角英数字8~11個の数字で入力してください。',
'attributes' => [
        'phone'=>'電話番号',
    ],

コントローラーやRequestsにクロージャーでも書けるそうですが、コードの保守性を鑑みても、make:ruleとしてカスタムバリデーションを作った方が良さそうです。

画像バリデーションにおけるbmp、SVG、webpの扱い

### 画像バリデーションの拡張子
一昔前は、画像の拡張子といえば、jpeg,jpg,gif,pngのみが多かった印象がありますが(自分だけ?)、Laravel6.xのimageのバリデーションでは「jpg、png、bmp、gif、svg、webp」となっております。そのまま、jpeg,jpg,gif,pngに加えて、bmp,svg, webpも採用するかについて考えたいと思います。

### SVGとは?
SVGとは、Scalable Vector Graphicsの略です。
名称からどのような画像かほぼ想像がつきますが、念の為コードを書きながら特徴を見てみましょう。

<svg width="160" height="160">
		<rect x="0" y="0" width="160" height="160" fill="skyblue"/>
		<rect x="40" y="40" width="80" height="80" fill="tomato"/>
	</svg>

### view box

<svg width="160" height="160">
		<rect x="0" y="0" width="160" height="160" fill="skyblue"/>
		<rect x="40" y="40" width="80" height="80" fill="tomato"/>
	</svg>
	<svg width="160" height="160" viewBox="0 0 320 320">
		<rect x="0" y="0" width="160" height="160" fill="skyblue"/>
		<rect x="40" y="40" width="80" height="80" fill="tomato"/>
	</svg>

<svg width="160" height="160">
		<rect x="0" y="0" width="160" height="160" fill="skyblue"/>
		<rect x="20" y="20" width="30" height="30" fill="#08c"/>
		<rect x="65" y="20" width="30" height="30" fill="rgb(255, 0, 0, 4)"/>
		<rect x="110" y="20" width="30" height="30" fill="hsla(120, 40%, 40%, .4)"/>
	</svg>

<svg width="160" height="160">
		<rect x="0" y="0" width="160" height="160" fill="skyblue"/>
		<rect x="20" y="20" width="30" height="30" fill="#08c"/>
		<rect x="65" y="20" width="30" height="30" fill="rgb(255, 0, 0, 4)"/>
		<rect x="110" y="20" width="30" height="30" fill="hsla(120, 40%, 40%, .4)"/>
		<rect x="20" y="65" width="30" height="30" fill="none" stroke="olive" stroke-width="2"/>
		<rect x="65" y="65" width="30" height="30" fill="none" stroke="olive" stroke-dasharray="10"/>
		<rect x="110" y="65" width="30" height="30" fill="none" stroke="olive" stroke-width="2" stroke-dasharray="10, 2"/>
	</svg>

<svg width="160" height="160">
		<rect x="0" y="0" width="160" height="160" fill="skyblue"/>
		<rect x="20" y="20" width="30" height="30" style="fill:#08c; stroke:#eee; stroke-width:3;"/>
	</svg>

<svg width="160" height="160">
		<defs>
			<style>
				.my-box {
					fill: pink;
				}
			</style>
		</defs>
		<rect x="0" y="0" width="160" height="160" fill="skyblue"/>
		<rect x="20" y="20" width="30" height="30" style="fill:#08c; stroke:#eee; stroke-width:3;"/>
		<rect x="60" y="20" width="30" height="30" class="my-box"/>
	</svg>

<svg width="160" height="160">
		<rect x="0" y="0" width="160" height="160" fill="skyblue"/>
		<circle cx="80" cy="80" r="40" fill="gold" />
	</svg>	

### Photoshop、IllustratorのSVG、bmpの扱い
photoshopではbmpはありますが、SVGはありません。

一方、ベクターなのでIllustratorはSVGの取り扱いがあります。

### 結論
卒業写真など、オフィシャルなものであれば写真オンリーでしょうが、compassなどを見るとjpegの写真は殆どの見ないくらい写真以外をプロフィール画像にすることは浸透しているため、bmp,svg, webpもバリデーションに加えて良いでしょう。

プログラミングの義務教育が始まるので、近い未来、卒業写真にベクター画像を載せる学校が出てくるかもしれませんね。

jpeg/gif/bmp/svg

jpeg: Joint Photographic Experts Group, 24bit 1670万色, 1/10〜1/50圧縮率
gif: Graphic Interchange Format, 8bit 256色、高圧縮可能、ファイルサイズが小さい、ロゴ・ボタン・アイコンなど
png: Portable Network Graphic, 48bit, 280兆色、可逆圧縮、画質劣化がない、透明・不透明・半透明対応
bmp: Microsoft Windows Bitmap Image, 無圧縮、データが大きい
svg: Scalable Vector Graphics, XMLベースの2次元ベクターイメージ

jpg

gif

png

bmp
-> not permitted in wordpress

svgは割と最近使われていると言われているので、基礎は勉強する必要がありそうです。