blade内でのswitch文の書き方

idなどの判定で分岐が3つ以上になると、if文が冗長に感じてくるのでswitch文を使用したくなる。

@switch($hoge->id)
	@case(1)
		<li></li>
		@break
	@case(2)
	@case(3)
		<li></li>
		@break
	@default
		<li></li>
@endswitch

@case(1, 2) と書くとエラーになるので注意が必要
一般的にインデントは半角スペース4つだが、テーブル内などで分岐処理を書くと、分岐処理をしているところだけインデントが4つ右にずれるが、これはDOMにした時に揃うように左にずらすべきなのだろうか?
綺麗なコードを書いていると見られたいので、DOMのインデントを揃えたい、という欲求が頭を巡るが、レジストリの処理は殆どの変わらないし、コードの保守性は規約に沿ったほうが高まるように思えるので、いつも葛藤になる。

date型レコードの範囲内のYYYYMMの連続した配列を作る

mysql内に、 ‘2019-10-05′,’2020-01-05’,’2020-02-13’などのレコードがあった場合に、その範囲内の連続したYYYYMMの配列(2019-10, 2019-11, 2019-12, 2020-01)を作りたい時
-> 最初と最後のUnixTime範囲内で、1ヵ月づつ増えるFor文を作る。

        $last = Hoge::where('user_id', $id)->orderBy('time','ASC')->first();
        $last_month = substr($last['time'], 0, 7);
        $last_unix = strtotime('first day of ' . $last_month);
        $latest = Hoge::where('user_id', $id)->orderBy('time','DESC')->first();
        $latest_month = substr($latest['time'], 0, 7);
        $latest_unix = strtotime('last day of ' . $latest_month);

        for($time = $last_unix; $time <= $latest_unix; $time = strtotime('+1 month', $time)){
            $Ym[] = date('Ym', $time);
        }

取得したデータをそのままUnixTimeにして 1ヵ月づつ増やすと、 $last_month + 1 month > $latest_month となる場合があるので、$last_monthを月初の日付、$latest_monthを月末の日付に変換してからFor文を回す必要がある。

う〜ん、最初にDBのデータ型考える際に、ロジックの計算処理を考えてデータ型を決めれるようになるな。

laravelでcsv出力

### view
csvダウンロードボタンを作り、postメソッドのhiddenで引数を渡します。

{!! Form::open(['method'=>'POST', 'action'=>['HogeController@download'] ]) !!}
 <input type="hidden" name="period" value="{{ $inputs&#91;'data'&#93; }}">
 {!! Form::submit('CSVダウンロード',['class'=>'btn', 'name'=>'csv']) !!}
{!! Form::close() !!}

### route
csvファイルをreturnするだけなので、設計のコンベンションが無ければディレクトリは然程きにする必要なし。

Route::post('/dir/csv', 'HogeController@download');

### controller

use Symfony\Component\HttpFoundation\StreamedResponse;
public function download(Request $request){
// 省略

// CSV生成
        $response = new StreamedResponse(function() use($data1, $data2){
                $stream = fopen('php://output', 'w');
                stream_filter_prepend($stream, 'convert.iconv.utf-8/cp932//TRANSLIT');
                fputcsv($stream, ['id','raw1','raw2','raw3','raw4','raw5']);
                foreach($data as $value){
                     fputcsv($stream, ['id',$value->raw1,$value->raw2,$value->raw3,$value->raw4,$value->raw5]);
                }
                fclose($stream);
        });
        $response->headers->set('Content-Type', 'application/octet-stream');
        $response->headers->set('Content-Disposition', 'attachment; filename="user.csv"');
        return $response;

特段ストレージやS3などに一時保存しなくて良いので、シンプルだ。
上記はユーザがダウンロードボタンを押した時の処理だが、月次のバッチ処理によるcsv出力なども比較的簡単に実装できそうだ。

連想配列のデータをbuttonのonclick=”location.href=””からactionに渡したい時

やりたい事: controllerからbladeに渡ってきた複数の変数を、csv生成の為に、そのままの値で再度controllerの別のメソッドに渡したい

aタグでidをコントローラーに渡したい時は、controllerと引数を渡すだけ。

<a href="{{ action('HogeController@download', $data->id) }}"></a>

上記を踏まえて、buttonのonclickでコントローラーに連想配列のデータを渡したい時

<button type="button" value="送信" class="btn btn-success mx-auto d-block" onclick="location.href='{{ action('HogeController@download'), $data1 }}'">ダウンロード</button>

このように書くと、onclick=”location.href=””の中にシングルクオテーション(”)が入るためエラーになる

バックスラッシュでエスケープしてもダメ。

<button type="button" value="送信" class="btn btn-success mx-auto d-block" onclick="location.href='{{ action(\'HogeController@download\'), $data1 }}'">ダウンロード</button>

フォームで連想配列を渡そうと思ったが上手くいかない。

{!! Form::open(['method'=>'POST', 'action'=>['HogeController@download', $data1] ]) !!}
    {!! Form::submit('ダウンロード',['class'=>'btn btn-success', 'name'=>'download']) !!}
{!! Form::close() !!}

Facade\Ignition\Exceptions\ViewException
syntax error, unexpected

結局hiddenで引数をcontrollerに渡し、controller側で再度データを成形するしかないのか。

Eloquent query内での条件式(When)の書き方

Eloquent query内で条件判定をする場合は、whenで判定する引数を渡す
– inputsのperiodの値がotherの時は、whereBetweenでdateがinputsの指定した範囲で値を取得する
– inputsのperiodの値がother以外の時は、whereMontheでdateが$dateの値を取得する

$data =  Hoge::where('user_id', $id)->when($inputs, function($query, $inputs) use($date){
                if($inputs['period'] == 'other'){
                    return $query->whereBetween('date', [$inputs['date_start'], $inputs['date_end']]);
                } else {
                    return $query->whereMonth('date', '=', $date);
                }
            })->count('date');

最初、public functionで条件判定する関数を別に書いて呼び出すのかと思ったが、whenを使えば、インラインでいける。
うん、綺麗^^
上記だとコード量は減るんだが、ただこれをforeachで回す場合、foreachの前で条件分岐をした方が、foreachで毎回判定しなくて良いので処理速度は上がる。
コード量が少ない方を優先してしまいがちだが、あくまでユーザの処理スピードを優先して書かないとダメか。
このwhenの書き方は凄く好きなんだけどなー

条件に一致するcount

countの条件処理は、count関数の中ではなく、前で行う

$count =  Hoge::where('user_id', $id)->whereMonth('time', '=', date('m'))->count('apply_id' == 2);

$count =  Hoge::where('user_id', $id)->whereMonth('time', '=', date('m'))->where('apply_id', 2)->count();

公式: https://readouble.com/laravel/5.8/ja/queries.html#aggregates
公式ドキュメントや検索に引っかからない時はデバッグしながらやるしかありません。

Laravel 6.x カラムを結合してCollectiveでselect文を作る書き方

– last_name, first_nameを結合して、bladeのselect文で選択できるようにしたい。
— 以下のようなpluckだと、引数は2つだけしか取得できないので、上手く行かない。

$names = User::pluck('last_name', 'first_name', 'id')->all();

## solution
concatでカラムの値を繋げてあげる。
controller

use DB;
// 省略
$users = User::select(DB::Raw('concat(first_name, last_name) as name'), 'id')->pluck('name','id');
dd($users);

blade

{!! Form::select('name', $users, null, ['class' => 'form-control','placeholder'=>'選択してください']) !!}

OK🤩

laravel 6.x paginationで2ページ目以降も検索条件を引き継ぐ書き方

検索フォームから検索結果に遷移した後、2ページ目以降も検索条件を引継ぎたい

### bladeのページネーション

{{$data->render()}}

{{$data->appends(request()->input())->render()}}

こう書いたんだが、2ページ目以降、一向反映されない。
何故だああああああ????
(※今日はbiz confortってコワーキングに行こうと思って、横浜元町まで来たら祝日でやってないじゃないか。。。何故だあああああ)

フラ〜とアクエリアス飲んで考えてたら、気が付いた。appendsは、パラメータを配列で渡すメソッドなので、検索フォームはGETメソッドでないと上手く動作しない

### 検索フォーム

{!! Form::open(['method'=>'POST', 'action'=>'HogeController@index']) !!}
// 省略
{!! Form::close() !!}

{!! Form::open(['method'=>'GET', 'action'=>'HogeController@index']) !!}
 <input type="hidden" name="search" value="{{ rand() }}">
// 省略
{!! Form::close() !!}

Postメソッド時のControllerでの検索時の処理は、isMethod(‘post’)で判定していたが、Getメソッドの場合は、hiddenでname=”search”をcontrollerに送って、それで判定する事にした。

### Controller

if($request->isMethod('post')){
$inputs = $request->all();
$query = Data::query();
// 検索処理 省略 
}

if($request->has('search')){
  $inputs = $request->all();
  $query = Data::query();
// 検索処理 省略 
}

FORMはセキュリティ上、POSTメソッドしか使わないと考えてたが、ユーザ検索のページネーションの処理では、POSTメソッドではなくGETメソッドの方が都合が良い、ということを理解しました。

### 1冊やっておいても損はない本

belongsToのテーブルを検索するときのwhereHas

– 複数の入力フォームから、ユーザの入力値に応じて、belongsToのテーブルも検索したい

### blade

{!! Form::open(['method'=>'POST', 'action'=>'SearchController@index']) !!}
 // 省略
{!! Form::close() !!}

### model

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

### route

Route::match(['get','post'],'/search', 'SearchController@index');

### controller
– Data::query()->whereHas(‘user’, function($q){})で検索する
– 検索時にユーザ入力の変数で検索したい場合は、whereHas(‘user’, function($q) use($inputs){} として、渡す

public function index(Request $request){
 if($request->isMethod('post')){
 $inputs = $request->all();
 $query = Data::query();

 if(!empty($inputs['start'])){
    $query->where('start_at','>=', date('Y-m-d', strtotime($inputs['start'])) );
                }
// 省略

// 名前検索
            if(!empty($inputs['name'])){
                $works = $query->whereHas('user', function($q) use($inputs){
                    $q->where('last_name','like', '%'.$inputs['name'].'%')->orWhere('first_name','like', '%'.$inputs['name'].'%');
                })->orderBy('start','ASC')->paginate(10);
            } else {
                $data = $query->orderBy('start','ASC')->paginate(10);
            }

} else {
            $data = Data::whereMonth('start', '=', date('m'))->orderBy('start','ASC')->paginate(10);
        }
// 省略

whereHas(‘user’, function($q, $inputs){} で上手く行きそうな気がしたのですがエラーに成ります。
入力フォームの設置順に検索するのではなく、DBのリレーション順に検索をかけるので、注意が必要です。

DBから今月のデータを取得したい時

– DBから今月のデータを取得して、一覧で表示したい時
— 月単位で探す場合はwhereMonthを使う

### whereMonth

$data = Hoge::whereMonth('start_at', '=', date('m'))->get();
dd($data);

whereMonth以外にも、whereDay、whereYearなどがある。
whereDate(‘start_at’, ‘=’, date(‘Y-m’)) だとうまくいかない。

改めてカラムのデータ型の重要性を認識。