連想配列のデータを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’)) だとうまくいかない。

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

Laravel 6.x bladeのaタグでS3からファイルダウンロード

### index.blade.php
aタグでは、actionでコントローラへリンクします。その際に、S3のファイルを指定するための引数を渡します。

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

### route
ルーティングはgetメソッドで大丈夫です。

Route::get('/download/{id}', 'HogesController@download');

### controller
1. 渡ってきた引数($id)を元に、DB保存してあったS3のファイル名(ファイルパス)を取得します。
2. ファイルのmimeTypeを取得し、ContentTypeに指定します。
3. headerを指定してreturnで返します。

public function download($id){
        $data = Data::where('id', $id)->first();
        $file_path = '/hogehoge/prd/'.$data->attachment_file; 

        $mimeType = Storage::disk('s3')->mimeType($file_path);
        $file_name = $data->attachment_file;

        $headers = [
            'Content-Type' => $mimeType,
            'Content-Disposition' => 'attachment; filename="'. $file_name. '"'
        ];
        return \Response::make(Storage::disk('s3')->get($file_path), 200, $headers);
    }

てっきりDownloadの処理はview側で書くのかと思いましたが、コントローラーに引数を渡してコントローラーで処理する方が一般的のようです。
しかし、S3のディレクトリ構造をどのようにして、ディレクトリ名・ファイル名のどこまでをDBに保存するか悩みます🤔🤔🤔

migration:rollbackで二つ以上前のmigrationに戻したい時

-カラムの修正などで、2つ以上前のmigrationにrollbackしたい時

### –step=n
migrationsのレコードが以下の状態で、aaaaaaa_tableに戻りたい。
mysql> select * from migrations;
+—-+————————————————+——-+
| id | migration | batch |
+—-+————————————————+——-+
| 11 | 2014_10_12_000000_create_users_table | 1 |
| 12 | 2014_10_12_100000_create_password_resets_table | 1 |
| 13 | 2019_08_19_000000_create_failed_jobs_table | 1 |
| 14 | 2019_12_29_222614_create_*****_table | 1 |
| 24 | 2020_01_02_003618_create_*****_table | 2 |
| 25 | 2020_01_02_113019_create_********_table | 2 |
| 26 | 2020_01_02_113040_create_******_table | 2 |
| 27 | 2020_01_02_113114_create_********_table | 2 |
| 28 | 2020_01_03_205110_create_*****_table | 2 |
| 29 | 2020_01_03_205139_create_*******_table | 2 |
| 30 | 2020_01_07_164639_create_aaaaaa_table | 3 |
| 34 | 2020_01_07_170922_create_bbbbb_bbbbbbbb_table | 4 |
| 35 | 2020_01_07_170950_create_ccccccc_table | 4 |
| 36 | 2020_01_10_005229_create_ddddddd_table | 4 |
+—-+————————————————+——-+

–step=nで、バッチを指定するのかと思い、–step=3とすると、3つだけ戻る
$ php artisan migrate:rollback –step=3
Rolling back: 2020_01_10_005229_create_ddddddd_table
Rolled back: 2020_01_10_005229_create_ddddddd_table (0.03 seconds)
Rolling back: 2020_01_07_170950_create_ccccccc_table
Rolled back: 2020_01_07_170950_create_ccccccc_table (0.02 seconds)
Rolling back: 2020_01_07_170922_create_bbbbb_bbbbbbbb
Rolled back: 2020_01_07_170922_create_bbbbb_bbbbbbbb_table (0.02 seconds)

$ php artisan migrate:rollback –step=4
Rolling back: 2020_01_10_005229_create_ddddddd_table
Rolled back: 2020_01_10_005229_create_ddddddd_table (0.02 seconds)
Rolling back: 2020_01_07_170950_create_ccccccc_table
Rolled back: 2020_01_07_170950_create_ccccccc_table (0.02 seconds)
Rolling back: 2020_01_07_170922_create_bbbbb_bbbbbbbb
Rolled back: 2020_01_07_170922_create_bbbbb_bbbbbbbb_table (0.02 seconds)
Rolling back: 2020_01_07_164639_create_aaaaaa_table
Rolled back: 2020_01_07_164639_create_aaaaaa_table (0.02 seconds)

–step=n は、テーブル数の指定なので、4つ戻したい時は4と指定する。

間違ってもクリティカルなエラーにはならないが、手順書などを書く場合は間違うと恥ずかしい😂

request model, requestsテーブルを作成すると

controller

use Illuminate\Http\Request;
// 省略
use App\Request;

view
-> Symfony\Component\Debug\Exception\FatalErrorException
Cannot use App\Request as Request because the name is already in use

laravelではpost methodなどでrequestクラスを使っているので、requestモデル、requestsテーブルを作って使おうとすると、重複エラーになる。

modelやmigrationファイルの作り直しは、関連のコードだけでなく、設計書の修正まで色々手間がかかるので、make:modelの時にエラーを吐いてほしい。
データベース周りの出戻りは面倒なので、やはりDB設計は優秀な人がやらないと大変になる。