extendsの条件分岐

非同期などの機能を実装する際に、同じルーティングで、blade内のインクルードファイルを切り分けたい時
-> if elseだと両方のextendsが読み込まれる為、 三項演算子(?:;)で切り分ける

## 前

@if($id == 1)
	@extends('layouts.hoge')
@else
	@extends('layouts.foo')
@endif

## 後

@extends(($id == 1) ? 'layouts.hoge':'layouts.foo')

ルーティングは要検討か。

Collectiveでのcheckboxの書き方

– view側でnameをブランケットで記述する。
– controllerのwhereInでチェックした値を呼び出せる

### blade

<div class="item">
	{!! Form::checkbox('category[]', 1, 1, ['class'=>'form-check-input']) !!}
	{!! Form::label('category[]', 'シティホテル', ['class'=>'form-check-label']) !!} 
</div>
<div class="item">
	{!! Form::checkbox('category[]', 2, 1, ['class'=>'form-check-input']) !!}
	{!! Form::label('category[]', 'ビジネスホテル', ['class'=>'form-check-label']) !!} 
</div>

### controller

if($request->has('category')){
            $Hotels = Message::where('id', $id)->whereIn('category_id', $request->input('category'))->orderBy('created_at','ASC')->paginate(2)->onEachSide(1);
        } else

input::all()の場合は、配列で渡ってくるので、そのままDBに流し込めば良い。
controller側で配列を作ろうとしたら、checkboxの最後の値しか渡ってこずに失敗しました。

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のインデントを揃えたい、という欲求が頭を巡るが、レジストリの処理は殆どの変わらないし、コードの保守性は規約に沿ったほうが高まるように思えるので、いつも葛藤になる。

連想配列のデータを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側で再度データを成形するしかないのか。

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 JavasScriptへデータを渡す書き方

HTML同様、scriptタグ内でも{{$variable}}と書くとデータが渡る

$ php artisan make:controller AdminController

web.php

Route::get('/', 'HomeController@index');
Route::get('/admin', 'AdminController@index');

AdminController.php

class AdminController extends Controller
{
    //
    public function index(){
    	return view('admin/index');
    }
}

AdminController.php

public function index(){
    	$postsCount = Post::count();
    	$categoriesCount = Category::count();
    	$commentsCount = Comment::count();

    	return view('admin/index',compact('postsCount','categoriesCount','commentsCount'));
    }

index.blade.php

<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>
<script>
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: ['Posts', 'Categories', 'Comments'],
        datasets: [{
            label: 'Data of CMS',
            data: [{{$postsCount}},{{$categoriesCount}},{{$commentsCount}}],
            backgroundColor: [
                'rgba(255, 99, 132, 0.2)',
                'rgba(54, 162, 235, 0.2)',
                'rgba(255, 206, 86, 0.2)',
                'rgba(75, 192, 192, 0.2)',
                'rgba(153, 102, 255, 0.2)',
                'rgba(255, 159, 64, 0.2)'
            ],
            borderColor: [
                'rgba(255, 99, 132, 1)',
                'rgba(54, 162, 235, 1)',
                'rgba(255, 206, 86, 1)',
                'rgba(75, 192, 192, 1)',
                'rgba(153, 102, 255, 1)',
                'rgba(255, 159, 64, 1)'
            ],
            borderWidth: 1
        }]
    },
    options: {
        scales: {
            yAxes: [{
                ticks: {
                    beginAtZero: true
                }
            }]
        }
    }
});
</script>

controllerのdataの取得は、count()以外にも、JSで表現したい内容に合わせてControllerで取得・整形する
JS側ではなくController側で整えるのが一般的か

Laravel CollectiveのPATCHとupdateメソッド

Laravel Collectiveで、編集する際は、Controllerから送られてきた$idに対し、Form::Model($user, [‘method’=>’PATCH’, …と書くだけ。

AdminUsersController.php

public function edit($id)
    {
        $user = User::FindOrFail($id);
        $roles = Role::lists('name', 'id')->all();
        return view('admin.users.edit', compact(['user','roles']));
    }

View: index.blade.php
{{route(‘admin.users.edit’, $user->id)}}で、Editページへのリンク

<tr>
			  <td>{{$user->id}}</td>
			  <td><img height="50" src="/{{$user->photo ? $user->photo->file : ''}}"></td>
			  <td><a href="{{route('admin.users.edit', $user->id)}}">{{$user->name}}</a></td>
			  <td>{{$user->email}}</td>
			  <td>{{$user->role->name}}</td>
			  <td>{{$user->is_active == 1 ? 'Active' : 'No Active'}}</td>
			  <td>{{$user->created_at->diffForHumans()}}</td>
			  <td>{{$user->updated_at->diffForHumans()}}</td>
			</tr>

edit.blade.php
->create.blade.phpをコピー
collectiveは、Form::Model($user, [‘method’=>’PATCH’, ‘action’=>[‘AdminUsersController@update’,$user->id], ‘files’=>true])と書く

{!! Form::Model($user, ['method'=>'PATCH', 'action'=>['AdminUsersController@update',$user->id], 'files'=>true]) !!}
        {{ csrf_field()}}
 
        <div class="form-group">
            {!! Form::label('name', 'Name') !!}
            {!! Form::text('name', null, ['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::label('email', 'Email') !!}
            {!! Form::email('email', null, ['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::label('role_id', 'Roles') !!}
            {!! Form::select('role_id',$roles, null, ['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::label('is_active', 'Status') !!}
            {!! Form::select('is_active',array(1 => 'Active', 0 =>'Not Active'), null, ['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::label('password', 'password') !!}
            {!! Form::password('password', null, ['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::label('photo_id', 'Photos') !!}
            {!! Form::file('photo_id', null, ['class'=>'form-control']) !!}
        </div>
        
        <div class="form-group">
            {!! Form::submit('Create Post', ['class'=>'btn btn-primary']) !!}
        </div>
    {!! Form::close() !!}

### 画像の表示
画像がないときは、placehold.itの画像を表示する🤩

<div class="col-sm-3">
        <img src="{{$user->photo ? '/'.$user->photo->file : 'https://placehold.it/400x400'}}" alt="" class="img-responsive img-rounded">
    </div>

AdminUsersController.php

public function update(UsersRequest $request, $id)
    {
        //
        $user = User::findOrFail($id);
        $input = $request->all();

        if($file = $request('photo_id')){
            $name = time(). $file->getClientOriginalName();
            $file->move('images', $name);

            $photo = Photo::create(['file'=>$name]);

            $input['photo_id'] = $photo->id;
        }

        $input['password'] = bcrypt($request->password);
        $user->update($input);
        return redirect('/admin/users');
    }

$ php artisan make:request UsersEditRequest

Request/UsersEditRequest.php

public function authorize()
    {
        return true;
    }
    public function rules()
    {
        return [
            'name' => 'required',
            'email' => 'required',
            'role_id' => 'required',
        ];
    }

AdminUsersController

use App\Http\Requests\UsersEditRequest;
public function update(UsersEditRequest $request, $id)
    {
        //
        $user = User::findOrFail($id);
        if($request->password == '' ){
            $input = $request->except('password');
        } else {
            $input = $request->all();
            $input['password'] = bcrypt($request->password);
        }

        if($file = $request->file('photo_id')){
            $name = time(). $file->getClientOriginalName();
            $file->move('images', $name);

            $photo = Photo::create(['file'=>$name]);

            $input['photo_id'] = $photo->id;
        }

        $user->update($input);
        return redirect('/admin/users');
    }

img src=”{{$user->photo ? ‘/’.$user->photo->file : ‘https://placehold.it/400×400’}}” alt=”” class=”img-responsive img-roundedの書き方は面白い。placeholdは画像化してimgフォルダに格納して呼び出していましたが、CDNのような使い方ができるのであれば、その方が楽。

Laravel Collectiveを使用して入力フォームを作成するまで

Laravel Collectiveを使用して入力フォーム・バリデーションを作成するまでの一連の流れです。フォームを作成してから、requestsを作ります。

php composer.phar require laravelcollective/html

.config/app.php

Collective\Html\HtmlServiceProvider::class,

'Form' => Collective\Html\FormFacade::class,
'Html' => Collective\Html\HtmlFacade::class,

view: resources/view/admin/users/
create.blade.php

<h1>Create Users</h1>
	{!! Form::open(['method'=>'POST', 'action'=>'AdminUsersController@store', 'files'=>true]) !!}
        {{ csrf_field()}}
 
        <div class="form-group">
            {!! Form::label('name', 'Name') !!}
            {!! Form::text('name', null, ['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::submit('Create Post', ['class'=>'btn btn-primary']) !!}
        </div>
    {!! Form::close() !!}

AdminUsersController.php
テーブルからリストを呼び出す

public function create()
    {
        //
        $roles = Role::lists('name','id')->all();
        return view('admin.users.create', compact('roles'));
    }

create.blade.php
array(”=>’Choose Options’) + $rolesとして、rolesテーブルから自動的にselect文を生成する

{!! Form::open(['method'=>'POST', 'action'=>'AdminUsersController@store', 'files'=>true]) !!}
        {{ csrf_field()}}
 
        <div class="form-group">
            {!! Form::label('name', 'Name') !!}
            {!! Form::text('name', null, ['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::label('email', 'Email') !!}
            {!! Form::email('email', null, ['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::label('role_id', 'Roles') !!}
            {!! Form::select('role_id',array(''=>'Choose Options') + $roles, null, ['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::label('status', 'Status') !!}
            {!! Form::select('status',array(1 => 'Active', 0 =>'Not Active'), null, ['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::label('password', 'Password') !!}
            {!! Form::password('password', null, ['class'=>'form-control']) !!}
        </div>
        
        <div class="form-group">
            {!! Form::submit('Create Post', ['class'=>'btn btn-primary']) !!}
        </div>
    {!! Form::close() !!}

$ php artisan make:request UsersRequest

Request/UsersRequest.php
※authorizeをfalseのままだとforbiddenに遷移します

public function authorize()
    {
        return true;
    }
public function rules()
    {
        return [
            
            'name' => 'required',
            'email' => 'required',
            'role_id' => 'required',
            'password' => 'required'
        ];
    }

Controller: AdminUsersRequest.php

use App\Http\Requests\UsersRequest;
public function store(UsersRequest $request)
    {
        return $request->all();
    }

create.blade.php

@if(count($errors) > 0)
		
		<div class="alert alert-danger">
			<ul>
				@foreach($errors->all() as $error)
					<li>{{$error}}</li>
				@endforeach
			</ul>

		</div>

    @endif

### エラーはテンプレート化する

@include('includes.form_error')

LaravelCollectiveも、初めて使ったときはメリットが分かりませんでしたが、少しずつ実用性を体感していきます。

Laravel id 1 = aaa, 2 = bbb, 3 = cccのviewの書き方

ユーザがradioボタンで選択した値をDBから呼び込んでViewで表示させたい時
DB側には id 1, 2, 3というように、値にidを入れる

View側ではif statementで値を書くのではなく、値のtableを作って、modelでbelongsToで呼び込めば、{{$user->role->name}}というようにシンプルに書ける

<tr>
			  <td>{{$user->id}}</td>
			  <td>{{$user->name}}</td>
			  <td>{{$user->email}}</td>
			  <td>{{$user->role->name}}</td>
			  <td>{{$user->is_active == 1 ? 'Active' : 'No Active'}}</td>
			  <td>{{$user->created_at->diffForHumans()}}</td>
			  <td>{{$user->updated_at->diffForHumans()}}</td>
			</tr>

青天の霹靂🤮🤮🤮
ということは、table設計・ER図が大分変ってくる

作りながら学ぶ方が一見早道に見えたが、基礎を一通り学んでから作り始めた方が出戻りが少なく、結果的に効率的になりそう
危ないところだった。。