Vue.jsでLaravelのリレーション(belongsTo, hasMany)の値を表示

## Messageモデル
– usersテーブルに対し、messagesテーブルがbelongsToのリレーション関係

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

## controller

$messages =Message::where(function($q) use ($foo, $hoge){
// 省略
})->orderBy('***','ASC')->get();

### 通常時のblade
– $message->user->name で名前を呼び出す

@foreach($messages as $message)
// 省略
{{ $message->user ? $message->user->name : '削除済ユーザ' }}
// 省略
@endforeach

# Vue.js使用時
## controller
– with(‘user’)でユーザテーブルの情報も取得する

return Message::with('user')->where(function($q) use ($foo, $hoge){
// 省略
})->orderBy('***','ASC')->get();

## blade
– vueで表示する際は、m.user.name と書く

<div v-for="m in messages">
  // 省略
  <span v-text="m.user.name"></span>
  // 省略
</div>

belongsToもVueで表示できるのか!? 表示できないなら、Vueは断念しようかと思ってた。

Laravel + Ajaxでbladeの一部の値(ログインユーザ順のデータ)を自動更新させたい

– bladeの一部の値(ログインユーザ順のデータ)を自動更新させたい

## 初期
controller
-> 自分以外のユーザをログイン順に取得している。

$users = User::where('id', '!=', $user_id)->orderBy('last_login','DESC')->get();

– controllerでjson形式で返せるらしい
-> ルーティングでgetとpostに対応してajaxのメソッドに繋げます
## Route

Route::get('/hoge/new', 'HogesController@create');
Route::match(['get', 'post'], '/hoge/getuser/', 'HogesController@getUser');

## Controller

public function getUser(){
        $user_id = 1;
        $users = User::where('id', '!=', $user_id)->orderBy('last_login','DESC')->get(['id','name','hoge']);
        $json = ["userData" => $userData];
        return response()->json($json);
    }

## view
-setTimeoutで5秒ごとにgetメソッドでcontrollerからデータを取得する
-while( table.rows[ 0 ] ) table.deleteRow( 0 ); でtableのrowsを全て削除し、jsonデータを入れたtd, trをappendする

<table class="table" id="userList">
</table>
// 省略

<script>
$(function(){
			getData()
		})

		function getData(){
			$.ajaxSetup({
				headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')}
			});
			$.get({
				url: '/hoge/getuser',
				method: 'GET',
				dataType: 'json',
			})
			.done(function(data){
				
				var table = document.getElementById('userList');
				while( table.rows[ 0 ] ) table.deleteRow( 0 );
				var rows = "";
				for(var i = 0; i < data.userData.length; i++){
					rows += "<tr>";
					rows += "<td>";
					rows += "hoge";
					rows += "</td>";
					rows += "</tr>";
				}
				$("#userList").append(rows);
			})
			.fail(function(){

			})
			setTimeout("getData()", 5000);
		}
</script>

これ思ってたより複雑だな。jsonで持ってくる場合、hasOneやbelongsToのデータはController側で整形しないとダメか。

ajaxのjsonデータは、getで取得するURLを叩くと、jsonを見ることが可能で、デバッグには良いが、商用環境で->get() とするとセキュリティ上問題があるので、viewで使用するカラムのみ ->get([‘columName’]) で取得するようにする。

laravel whereで複数の一致条件 ~ a =A, b = B もしくは a = B, b = A

Controllerでa =A, b = B もしくは a = B, b = A を探したい時。

複数のwhereの場合は、whereを繋げれば良いのですが、複数のwhereのどちらかの場合は、where whereをorWhereで繋げれば良い。

$messages = Hotels::where(function($q) use ($id, $user_id){
            $q->where('user_id', $id)->where('second_id', $user_id);
        })->orWhere(function($q) use ($id, $user_id){
            $q->where('user_id', $user_id)->where('second_id', $id);
        })->get();

割と簡単に書けます。

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の書き方は凄く好きなんだけどなー

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

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

### whereMonth

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

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

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

Laravel return redirect()->back();

Controllerでは、viewの指定なしにredirect()->back();と書ける。

PostCommentsController.php

 public function index(){
    	$comments = Comment::all();
    	return view('admin.comments.index', compact('comments'));
    }

admin/comments/index.blade.php

@if($comments)
	<h1>Comments</h1>
	<table class="table table-striped">
		<thead>
		<tr>
		  <th>Id</th>
		  <th>Author</th>
		  <th>Email</th>
		  <th>Body</th>
		  <th>Post</th>
		</tr>
		</thead>
		<tbody>
		
			@foreach($comments as $comment)
			<tr>
			  <td>{{$comment->id}}</td>
			  <td>{{$comment->author}}</td>
			  <td>{{$comment->email}}</td>
			  <td>{{$comment->body}}</td>
			  <td><a href="{{route('home.post',$comment->post_id)}}">View Post</td>
			  <td>
			  	@if($comment->is_active == 1)
			  			{!! Form::open(['method'=>'PATCH', 'action'=>['PostCommentsController@update', $comment->id]]) !!}
					        {{ csrf_field()}}
					        <input type="hidden" name="is_active" value="0">

					        <div class="form-group">
					            {!! Form::submit('Un-approve', ['class'=>'btn btn-info']) !!}
					        </div>
        				{!! Form::close() !!}
						
			  	@else
						{!! Form::open(['method'=>'PATCH', 'action'=>['PostCommentsController@update', $comment->id]]) !!}
					        {{ csrf_field()}}
					        <input type="hidden" name="is_active" value="1">

					        <div class="form-group">
					            {!! Form::submit('Approve', ['class'=>'btn btn-info']) !!}
					        </div>
        				{!! Form::close() !!}

			  	@endif
			  </td>
			  <td>
			  	{!! Form::open(['method'=>'DELETE', 'action'=>['PostCommentsController@destroy', $comment->id]]) !!}
			        {{ csrf_field()}}

			        <div class="form-group">
			            {!! Form::submit('Delete', ['class'=>'btn btn-danger']) !!}
			        </div>
				{!! Form::close() !!}

			  </td>
			</tr>
			@endforeach
		
		</tbody>
	</table>
	@else
	<h1 class="text-center">No Comments</h1>
		
	@endif

PostCommentController.php

public function update(Request $request, $id){
    	Comment::findOrFail($id)->update($request->all());
    	return redirect('/admin/comments');
    }

    public function destroy($id){
    	Comment::findOrFail($id)->delete();
    	return redirect()->back();
    }

Post/index.blade.php

<td><a href="{{route('home.post', $post->id)}}">View Post</td>
			  <td><a href="{{route('admin.comments.show', $post->id)}}">View Comments</td>

Post.php

public function comments(){
        return $this->hasMany('App\Comment');
    }

Route

Route::group(['middleware'=>'auth'], function(){

		Route::post('comment/reply', 'CommentRepliesController@createReply');
});

post.blade.php

@if($comments)
                    @foreach($comments as $comment)
                <!-- Comment -->
                <div class="media">
                    <a class="pull-left" href="#">
                        <img class="media-object" src="http://placehold.it/64x64" alt="">
                    </a>
                    <div class="media-body">
                        <h4 class="media-heading">{{$comment->author}}
                            <small>{{$comment->created_at->diffForhumans()}}</small>
                        </h4>
                        {{$comment->body}}

                        @if($comment->replies)
                            @foreach($comment->replies as $reply)
                        <div class="nested-comment media">
                            <a class="pull-left" href="#">
                                <img class="media-object" src="http://placehold.it/64x64" alt="">
                            </a>
                            <div class="media-body">
                                <h4 class="media-heading">{{$reply->author}}
                                    <small>{{$reply->created_at->diffForhumans()}}</small>
                                </h4>
                                {{$reply->body}}
                            </div>

                            {!! Form::open(['method'=>'POST', 'action'=>'CommentRepliesController@store']) !!}
                                {{ csrf_field()}}

                                <input type="hidden" name="comment_id" value="{{$comment->id}}">
                         
                                <div class="form-group">
                                    {!! Form::label('body', 'Body') !!}
                                    {!! Form::textarea('body', null, ['class'=>'form-control', 'rows'=>1]) !!}
                                </div>                                
                                <div class="form-group">
                                    {!! Form::submit('Submit', ['class'=>'btn btn-primary']) !!}
                                </div>
                            {!! Form::close() !!}

                        </div>
                            @endforeach
                        @endif

post.blade.php

<script
  src="https://code.jquery.com/jquery-2.2.4.min.js"
  integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
  crossorigin="anonymous"></script>
    <script>
        $(".comment-reply-container .toggle-reply").click(function(){
            $(this).next().slideToggle("slow");
        });
    </script>

replies/show.blade.php

@section('content')
	
	@if($replies)
	<h1>replies</h1>
	<table class="table table-striped">
		<thead>
		<tr>
		  <th>Id</th>
		  <th>Author</th>
		  <th>Email</th>
		  <th>Body</th>
		  <th>Post</th>
		</tr>
		</thead>
		<tbody>
		
			@foreach($replies as $reply)
			<tr>
			  <td>{{$reply->id}}</td>
			  <td>{{$reply->author}}</td>
			  <td>{{$reply->email}}</td>
			  <td>{{$reply->body}}</td>
			  <td><a href="{{route('home.post',$reply->comment->post->id)}}">View Post</td>
			  <td>
			  	@if($reply->is_active == 1)
			  			{!! Form::open(['method'=>'PATCH', 'action'=>['CommentRepliesController@update', $reply->id]]) !!}
					        {{ csrf_field()}}
					        <input type="hidden" name="is_active" value="0">

					        <div class="form-group">
					            {!! Form::submit('Un-approve', ['class'=>'btn btn-success']) !!}
					        </div>
        				{!! Form::close() !!}
						
			  	@else
						{!! Form::open(['method'=>'PATCH', 'action'=>['CommentRepliesController@update', $reply->id]]) !!}
					        {{ csrf_field()}}
					        <input type="hidden" name="is_active" value="1">

					        <div class="form-group">
					            {!! Form::submit('Approve', ['class'=>'btn btn-info']) !!}
					        </div>
        				{!! Form::close() !!}

			  	@endif
			  </td>
			  <td>
			  	{!! Form::open(['method'=>'DELETE', 'action'=>['CommentRepliesController@destroy', $reply->id]]) !!}
			        {{ csrf_field()}}

			        <div class="form-group">
			            {!! Form::submit('Delete', ['class'=>'btn btn-danger']) !!}
			        </div>
				{!! Form::close() !!}

			  </td>
			</tr>
			@endforeach
		</tbody>
	</table>
	
	@else
	
		<h1 class="text-center">No replies</h1>
	@endif
	
@stop

comments/index.blade.php

<td><a href="{{route('admin.comments.replies.show', $comment->id)}}">View Replies</a></td>

CommentRepliesController.php

use App\Comment;
public function show($id)
    {
        //
        $comment = Comment::findOrFail($id);
        $replies = $comment->replies;
        return view('admin.comments.replies.show', compact('replies'));
    }

public function update(Request $request, $id)
    {
        //
        CommentReply::findOrFail($id)->update($request->all());

        return redirect()->back();
    }
public function destroy($id)
    {
        //
        CommentReply::findOrFail($id)->delete();

        return redirect()->back();
    }

Laravel index, create, update, delete

index, create, update, deleteの順番に作ります。
最初にindexで表示項目のデータをDBに入れておく。

$ php artisan make:controller –resource AdminCategoriesController

Route::group(['middleware'=>'admin'], function(){
	Route::resource('admin/users', 'AdminUsersController');
	Route::resource('admin/posts', 'AdminPostsController');	
	Route::resource('admin/categories', 'AdminCategoriesController');	
});

AdminCategoriesController.php

public function index()
    {
        $categories = Category::all();
        return view('admin.categories.index', compact('categories'));
    }

layouts/admin.blade.php

<li>
                                <a href="{{route('admin.categories.index')}}">All Categories</a>
                            </li>

                            <li>
                                <a href="{{route('admin.categories.create')}}">Create Category</a>
                            </li>

categories/index.blade.php

<h1>Categories</h1>	

	<div class="col-sm-6">
		{!! Form::open(['method'=>'POST', 'action'=>'AdminCategoriesController@store']) !!}
        {{ 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 Category', ['class'=>'btn btn-primary']) !!}
	        </div>
	    {!! Form::close() !!}

	</div>

	<div class="col-sm-6">
		
		@if($categories)
		<table class="table table-striped">
			<thead>
			<tr>
			  <th>Id</th>
			  <th>Category</th>
			  <th>Created date</th>
			</tr>
			</thead>
			<tbody>
				@foreach($categories as $category)
				<tr>
				  <td>{{$category->id}}</td>
				  <td>{{$category->name}}</td>
				  <td>{{$category->created_at ? $category->created_at->diffForhumans() : 'no date'}}</td>
				</tr>
				@endforeach			
			</tbody>
		</table>
		@endif
	</div>

AdminCategoriesController.php

public function store(Request $request)
    {
        //
            Category::create($request->all());

            return redirect('/admin/categories');
    }

public function edit($id)
    {
        //
        $category = Category::findOrFail($id);
        return view('admin.categories.edit', compact('category'));
    }

view: index.blade.php

<tr>
				  <td>{{$category->id}}</td>
				  <td><a href="{{route('admin.categories.edit', $category->id)}}">{{$category->name}}</a></td>
				  <td>{{$category->created_at ? $category->created_at->diffForhumans() : 'no date'}}</td>
				</tr>

AdminCategoriesController.php

public function destroy($id)
    {
        Category::findOrFail($id)->delete();
        return view('/admin/categories');
    }

Keep going!
気分転換に馬喰町のロボットコーヒーでも行くかな☕

Laravel ユーザ削除の書き方

一覧や編集ページから削除ボタンを押すと、$idを渡したcontroller destroyメソッドでdelete()で削除する。画像の場合は、unlink。
削除完了メッセージは、Session::flashでsetして、遷移後のページで、@if(Session::has())で、メッセージを表示する方法がある。

edit.blade.php

{!! Form::open(['method'=>'DELETE', 'action'=>['AdminUsersController@destroy', $user->id]]) !!}
        {{ csrf_field()}}
        
        <div class="form-group">
            {!! Form::submit('Delete User', ['class'=>'btn btn-danger']) !!}
        </div>
    {!! Form::close() !!}

AdminUsersController.php

public function destroy($id)
    {
        //
        User::findOrFail($id)->delete();
        return redirect('/admin/users');
    }

### flushの利用

use Illuminate\Support\Facades\Session;
public function destroy($id)
    {
        //
        User::findOrFail($id)->delete();

        Session::flash('deleted_user', 'The user has benn deleted');
        return redirect('/admin/users');
    }

index.blade.php

@if(Session::has('deleted_user'))
		<p class="bg-danger">{{session('deleted_user')}}</p>
	@endif

deleteする際には、belongsToでコネクトしている画像も削除する

public function destroy($id)
    {
        //
        $user = User::findOrFail($id);

        unlink(public_path() ."/". $user->photo->file);
        $user->delete();

        Session::flash('deleted_user', 'The user has benn deleted');
        return redirect('/admin/users');
    }

User.php

public function isAdmin(){
        if($this->role->name == "administrator" && $this->is_active == 1){
            return true;
        }
        return false;
    }

### ログイン後のページ設定
app/Http/Auth/AuthController.php

protected $redirectTo = '/admin';

ユーザ削除の仕様は、要件定義によって変わってくると思います。その場合は、controllerで制御する。

プロジェクト作成からmake:controller –resourceまでの一連の流れ

make:authとした後、先にViewファイル, migration, modelを作ってから、Controllerを作成して挙動を確認する

$ php artisan migrate
$ php artisan make:auth

resourcesにファイルを作成
./resources/views/admin/index.blade.php
./resources/views/admin/users/index.blade.php
./resources/views/admin/users/create.blade.php
./resources/views/admin/users/edit.blade.php
./resources/views/admin/posts/index.blade.php
./resources/views/admin/posts/create.blade.php
./resources/views/admin/posts/edit.blade.php
./resources/views/admin/categories/index.blade.php
./resources/views/admin/categories/edit.blade.php

migration file:user

Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('role_id')->index()->unsigned()->nullable();
            $table->integer('is_active')->default(0);
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });

$ php artisan make:model Role -m

migration file: role

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

Model: Role.php

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

$ php artisan migrate:refresh

insert into roles (name) values (‘administrator’);
insert into roles (name) values (‘author’);
insert into roles (name) values (‘subscriber’);

update users set role_id=1 where id=1;

$ php artisan tinker
Psy Shell v0.7.2 (PHP 7.1.7 — cli) by Justin Hileman
>>> $user = App\User::find(1);
=> App\User {#647
id: 1,
role_id: 1,
is_active: 0,
name: “peter”,
email: “peter@gmail.com”,
created_at: “2019-12-13 13:52:49”,
updated_at: “2019-12-13 13:52:49”,
}
>>> $user->role
=> App\Role {#637
id: 1,
name: “administrator”,
created_at: null,
updated_at: null,
}

Route

Route::resource('admin/users', 'AdminUsersController');

$ php artisan make:controller –resource Adm inUsersController

Controller: AdminUsersController.php

public function index()
    {
        //
        return view('admin.users.index');
    }

フロントのテンプレートファイルを流し込んで、controllerを作り、ログイン機能は後から実装するのかと思っていましたが、そうではなくmake:authは一番最初に実行し、ユーザモデルを先に作ってしまうんですね👻

php tinker

$ php artisan tinker
>>> $post = App\Post::create([‘title’=>’php post from tinker’, ‘content’=>’content from tinker’]);

mysql> select * from posts;
+—-+———————-+———————+———————+———————+———-+————+
| id | title | content | created_at | updated_at | is_admin | deleted_at |
+—-+———————-+———————+———————+———————+———-+————+
| 1 | php post from tinker | content from tinker | 2019-12-08 03:09:23 | 2019-12-08 03:09:23 | 0 | NULL |
+—-+———————-+———————+———————+———————+———-+————+
1 row in set (0.00 sec)

異なる書き方
>>> $post = new App\Post;
=> App\Post {#627}
>>> $post->title = ‘new title’;
=> “new title”
>>> $post->content = ‘yea maybe conding’;
=> “yea maybe conding”
>>> $post->save();
=> true

mysql> select * from posts;
+—-+———————-+———————+———————+———————+———-+————+
| id | title | content | created_at | updated_at | is_admin | deleted_at |
+—-+———————-+———————+———————+———————+———-+————+
| 1 | php post from tinker | content from tinker | 2019-12-08 03:09:23 | 2019-12-08 03:09:23 | 0 | NULL |
| 2 | new title | yea maybe conding | 2019-12-08 03:13:19 | 2019-12-08 03:13:19 | 0 | NULL |
+—-+———————-+———————+———————+———————+———-+————+
2 rows in set (0.00 sec)

>>> $post = App\Post::find(2);
=> App\Post {#663
id: 2,
title: “new title”,
content: “yea maybe conding”,
created_at: “2019-12-08 03:13:19”,
updated_at: “2019-12-08 03:13:19”,
is_admin: 0,
deleted_at: null,
}

>>> $post = App\Post::find(2);
=> App\Post {#659
id: 2,
title: “new title”,
content: “yea maybe conding”,
created_at: “2019-12-08 03:13:19”,
updated_at: “2019-12-08 03:13:19”,
is_admin: 0,
deleted_at: null,
}
>>> $post->title = “update record with this”
=> “update record with this”
>>> $post->content = “also update record with 2”
=> “also update record with 2”
>>> $post->save();

>>> $post->delete();
=> true
>>> $post = App\Post::onlyTrashed()
=> Illuminate\Database\Eloquent\Builder {#666}
>>> $post->forceDelete();
=> 1
>>>

tinkerは挙動の簡単なテストなどに使える