Laravel upgrade to 5.6

### laravel 5.5
$ npm install

"require": {
        "php": ">=5.5.9",
        "laravel/framework": "5.5.*",
        "laravelcollective/html": "5.5.*",
        "cviebrock/eloquent-sluggable": "^4.3",
        "unisharp/laravel-filemanager": "^1.9",
        "intervention/image": "^2.5"
    },
    "require-dev": {
        "fzaninotto/faker": "~1.4",
        "mockery/mockery": "0.9.*",
        "phpunit/phpunit": "~6.0",
        "symfony/css-selector": "2.8.*|3.0.*",
        "symfony/dom-crawler": "2.8.*|3.0.*"
    },

### laravel 5.6
composer.json

"scripts": {
        "post-root-package-install": [
            "php -r \"copy('.env.example', '.env');\""
        ],
        "post-create-project-cmd": [
            "php artisan key:generate"
        ],
        "post-install-cmd": [
            "Illuminate\\Foundation\\ComposerScripts::postInstall",
        ],
        "post-update-cmd": [
            "Illuminate\\Foundation\\ComposerScripts::postUpdate",
        ]
    },

$ php composer.phar update
$ php artisan –version

Pythonでも何だかんだ言われながらpytho2.7から3に変わっていきましたし、常に最新バージョンで開発する癖をつけた方が良さそうです。

Laravel Seeder & Factoryの使い方

DatabaseSeederの初期
./database/seeds/DatabaseSeeder.php

public function run()
    {
        $this->call(UsersTableSeeder::class);
    }

### make:seeder
$ php artisan make:seeder UsersTableSeeder
./database/seeds/UsersTableSeeder.php

public function run()
    {
        //
        DB::table('users')->insert([
        	'name'=>str_random(10),
        	'role_id'=>1,
        	'is_active'=>1,
        	'email'=>str_random(10).'@gmail.com',
        	'password'=>bcrypt('secret')
        ]);
    }

$ php artisan db:seed
> select * from users;
1件追加される

### factory
./database/factories/ModelFactory.php

$factory->define(App\User::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->safeEmail,
        'password' => bcrypt(str_random(10)),
        'remember_token' => str_random(10),
    ];
});

./database/seeds/DatabaseSeeder.php

public function run()
    {
        // $this->call(UsersTableSeeder::class);
        factory(App\User::class, 10)->create();
    }
public function run()
    {
        // $this->call(UsersTableSeeder::class);
        factory(App\User::class, 10)->create()->each(function($user){
        	$user->posts()->save(factory(App\Post::class)->make());
        });
    }

### users以外でfactoryの利用
ModelFactory.php

$factory->define(App\Post::class, function (Faker\Generator $faker) {
    return [
        'category_id' => $faker->numberBetween(1,0),
        'photo_id' => 1,
        'title' => $faker->sentence(7, 11),
        'body' => $faker->paragraphs(rand(10,15), true),
    ];
});

$ php artisan db:seed

1件だけ追加するならrow sql queryのinsert intoで十分ですが、ページネーションのテストなどでデータ数百件入れたいときなどはFactoryが使えます。

あれ、これ、$fakerって英語だけ?
laravel 5.5以上だと、config/app.phpで’faker_locale’ => ‘ja_JP’,とすると、日本語化されるとのことです。

larave 5.3 -> 5.4 & Webpack

– 5.3 -> 6.0など一気に上げず、一つ一つバージョンを上げていく
– 5.4からcompileはgulpではなくwebpackになっており、基本的にwebpackを使う

"require": {
        "php": ">=5.5.9",
        "laravel/framework": "5.4.*",
        "laravelcollective/html": "5.4.*",
        "cviebrock/eloquent-sluggable": "^4.0",
        "unisharp/laravel-filemanager": "^1.9",
        "intervention/image": "^2.5"
    },
    "require-dev": {
        "fzaninotto/faker": "~1.4",
        "mockery/mockery": "0.9.*",
        "phpunit/phpunit": "~5.7",
        "symfony/css-selector": "2.8.*|3.0.*",
        "symfony/dom-crawler": "2.8.*|3.0.*"
    },

webpack.mix.js

const { mix } = require('laravel-mix');

mix.js('resources/assets/js/app.js', 'public/js')
    .sass('resources/assets/sass/app.scss', 'public/css');

mix.styles([
    'resources/assets/css/libs/blog-post.css',
    'resources/assets/css/libs/bootstrap.css',
    'resources/assets/css/libs/font-awesome.css',
    'resources/assets/css/libs/metisMenu.css',
    'resources/assets/css/libs/sb-admin-2.css'

], 'public/css/libs.css');

mix.scripts([
    'resources/assets/js/libs/jquery.js',
    'resources/assets/js/libs/bootstrap.js',
    'resources/assets/js/libs/metisMenu.js',
    'resources/assets/js/sb-admin-2.js',
    'resources/assets/js/libs/jquery.js',
    'resources/assets/js/libs/scripts.js'

], 'public/js/libs.js');

package.json

{
  "private": true,
  "scripts": {
    "dev": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch-poll": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --watch-poll --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "hot": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
    "production": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
  },
  "devDependencies": {
    "axios": "^0.15.3",
    "bootstrap-sass": "^3.3.7",
    "jquery": "^3.1.1",
    "laravel-mix": "^0.8.1",
    "lodash": "^4.17.4",
    "vue": "^2.2.2"
  }
}

$ npm install
$ npm run dev
$ npm run watch

webpackはちょっと取っつきにくいイメージがありましたが、build inもありますし、そんなことありませんね。

Laravel checkbox複数一括処理の書き方

view側でinput type=”checkbox” class=”checkBoxes” name=”checkBoxArray[]” value=”{{$photo->id}}” と書いて、配列でcontrollerに送り、Photo::findOrFail($request->checkBoxArray)で受け取る。

Route

Route::delete('/delete/media', 'AdminMediasController@deleteMedia');
@if($photos)

		<form action="/delete/media" method="post" class="form-inline">
{{csrf-field()}}
			{{method_field('delete')}}
			<div class="form-group">
				<select name="checkBoxArray" id="" class="form-control">
					<option value="delete">Delete</option>
				</select>
			</div>
			<div class="form-group">
				<input type="submit" class="btn btn-primary">
			</div>

		<table class="table table-striped">
			<thead>
			<tr>
			  <th><input type="checkbox" id="options"></th>
			  <th>Id</th>
			  <th>Name</th>
			  <th>Created date</th>
			</tr>
			</thead>
			<tbody>
				@foreach($photos as $photo)
				<tr>
				  <th><input type="checkbox" class="checkBoxes" name="checkBoxArray&#91;&#93;" value="{{$photo->id}}"></th>
				  <td>{{$photo->id}}</td>
				  <td><img height="50" src="/{{$photo->file}}"></td>
				  <td>{{$photo->created_at ? $photo->created_at : 'no date'}}</td>
				  <td>
				  	{!! Form::open(['method'=>'DELETE', 'action'=>['AdminMediasController@destroy', $photo->id]]) !!}
			        {{ csrf_field()}}
			        
			        <div class="form-group">
			            {!! Form::submit('Delete', ['class'=>'btn btn-danger']) !!}
			        </div>
			        {!! Form::close() !!}

				  </td>
				</tr>
				@endforeach			
			</tbody>
		</table>

		</form>
		@endif
public function deleteMedia(Request $request){

        if(isset($request->delete_single)){
            $this->destroy($request->photo);
            return redirect()->back();
        }

        if(isset($request->delete_all) && !empty($request->checkBoxArray)){
            $photos = Photo::findOrFail($request->checkBoxArray);
            foreach($photos as $photo){
                $photo->delete();
            }
            return redirect()->back();
        } else {
            return redirect()->back();
        }

        
    }
	$(document).ready(function(){

	$('#options').click(function(){
		if(this.checked){
			$('.checkBoxes').each(function(){
				this.checked = true;
			});
		} else {
			$('.checkBoxes').each(function(){
				this.checked = false;
			});
		}
	});
});

一括削除はUX上、あまり宜しくないと思ったが、よく考えたら複数一括処理はselectで一括ステータス変更などにも使えるので、書き方自体は覚えておきたい。

LaravelでtinyMCE & filemanagerの使い方

tinyMCEだと、textareaで文字の装飾はもちろん画像の挿入ができる

### viewの作成
views/includes/tinyeditor.blade.php

<script src="https://cdn.tiny.cloud/1/no-api-key/tinymce/5/tinymce.min.js" referrerpolicy="origin"></script>
<script>tinymce.init({selector:'textarea'});</script>

posts/create.blade.php

@extends('layouts.admin')

@section('content')

    @include('includes.tinyeditor')
...

### filemanager install
https://unisharp.github.io/laravel-filemanager/installation

$ php composer.phar require unisharp/laravel-filemanager
$ php composer.phar require intervention/image

Unisharp\Laravelfilemanager\LaravelFilemanagerServiceProvider::class,
        Intervention\Image\ImageServiceProvider::class,

'Image' => Intervention\Image\Facades\Image::class,

$ php artisan vendor:publish –tag=lfm_config
$ php artisan vendor:publish –tag=lfm_public

### tinyeditor.blade.php

<script src="//cdn.tinymce.com/4/tinymce.min.js"></script>
<script>
  var editor_config = {
    path_absolute : "/",
    selector: "textarea",
    plugins: [
      "advlist autolink lists link image charmap print preview hr anchor pagebreak",
      "searchreplace wordcount visualblocks visualchars code fullscreen",
      "insertdatetime media nonbreaking save table contextmenu directionality",
      "emoticons template paste textcolor colorpicker textpattern"
    ],
    toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image media",
    relative_urls: false,
    file_browser_callback : function(field_name, url, type, win) {
      var x = window.innerWidth || document.documentElement.clientWidth || document.getElementsByTagName('body')[0].clientWidth;
      var y = window.innerHeight|| document.documentElement.clientHeight|| document.getElementsByTagName('body')[0].clientHeight;

      var cmsURL = editor_config.path_absolute + 'laravel-filemanager?field_name=' + field_name;
      if (type == 'image') {
        cmsURL = cmsURL + "&type=Images";
      } else {
        cmsURL = cmsURL + "&type=Files";
      }

      tinyMCE.activeEditor.windowManager.open({
        file : cmsURL,
        title : 'Filemanager',
        width : x * 0.8,
        height : y * 0.8,
        resizable : "yes",
        close_previous : "no"
      });
    }
  };

  tinymce.init(editor_config);
</script>

### config
lfm.php

'base_directory' => 'public',
'images_folder_name' => 'images',

### view
post.blade.php

<p>{!! $post->body !!}</p>

{{$post->body}}だと画像が表示されないので注意が必要
殆ど公式に書いてあるので、公式との闘いになりそう

Laravel upgradeの基本的な進め方

– まずlogin, authから確認
– upgrade後に基本動作から試していく
– packageもlaravelのヴァージョンに依存関係がある為、composer.jsonも必ず確認する(laravel collective)

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

$ php artisan route:list;

https://github.com/laravel-shift/laravel-5.3/blob/master/app/Http/Controllers/Auth/LoginController.php
Authフォルダの下にcontroller fileを追加
LoginController.php
ResetPasswordController.php
RegisterController.php
ForgotPasswordController.php

web.php

Route::auth();
Route::get('/logout', 'Auth\LoginController@logout');

LoginController.php

protected $redirectTo = '/';

web.php

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

	Route::get('/admin', function(){
		return view('admin.index');
	});

	Route::resource('admin/users', 'AdminUsersController',['names'=>[
		'index'=>'admin.users.index',
		'create'=>'admin.users.create',
		'store'=>'admin.users.store',
		'edit'=>'admin.users.edit',
	]]);
	Route::resource('admin/posts', 'AdminPostsController', ['names'=>[
		'index'=>'admin.posts.index',
		'create'=>'admin.posts.create',
		'store'=>'admin.posts.store',
		'edit'=>'admin.posts.edit',
	]]);	
	Route::resource('admin/categories', 'AdminCategoriesController', ['names'=>[
		'index'=>'admin.categories.index',
		'create'=>'admin.categories.create',
		'store'=>'admin.categories.store',
		'edit'=>'admin.categories.edit',
	]]);	
	Route::resource('admin/media', 'AdminMediasController', ['names'=>[
		'index'=>'admin.media.index',
		'create'=>'admin.media.create',
		'store'=>'admin.media.store',
		'edit'=>'admin.media.edit',
	]]);

	Route::resource('admin/comments', 'PostCommentsController', ['names'=>[
		'index'=>'admin.comments.index',
		'create'=>'admin.comments.create',
		'store'=>'admin.comments.store',
		'edit'=>'admin.comments.edit',
	]]);
	Route::resource('admin/comments/replies', 'CommentRepliesController', ['names'=>[
		'index'=>'admin.replies.index',
		'create'=>'admin.replies.create',
		'store'=>'admin.replies.store',
		'edit'=>'admin.replies.edit',
	]]);
});

AdminUsersController
lists->pluckに変更

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

composer.json

"require": {
        "php": ">=5.5.9",
        "laravel/framework": "5.3.*",
        "laravelcollective/html": "5.3.*"
    },

Middleware: kernel.php

protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'admin' => \App\Http\Middleware\Admin::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ];

$ php composer.phar require cviebrock/eloquent-sluggable:^4.0

$ php artisan vendor:publish –provider=”Cviebrock\EloquentSluggable\ServiceProvider”

フレームワークのアップデートは、アップデートに伴う変更点を一つ一つ潰していくのかと思っていましたが、そうではなく、まずupgradeして、挙動を一つ一つ確認しながらエラーを潰していく。発想が真逆でした。道理で、凄い大変そうにみえたわけだ。。

ただし、マイクロアプリケーションなら比較的すぐ終わりそうですが、大規模アプリケーションだと、テスト工数が結構かかりそうです。

Laravel Upgrade

– upgrade時には公式のドキュメントを読むと同時に、参考記事を探し、主な変更点を確認する
– 新しいversionをインストールして、folder構成の違いを確認する
– composer updateでエラーが出た場合は、コードを修正する

$ php artisan –version

### composer.jsonでupdate

"require": {
        "php": ">=5.5.9",
        "laravel/framework": "5.3.*",
        "laravelcollective/html": "^5.2.0"
    },

### composer update
$ php composer.phar update

app/Providers/EventServiceProvider.php

public function boot()
    {
        parent::boot();

        //
    }

app/Providers/RouteServiceProvider.php

use Illuminate\Support\Facades\Route;
public function boot()
    {
        //

        parent::boot();
    }
public function map()
    {
        $this->mapWebRoutes();

        //
    }
protected function mapWebRoutes()
    {
        Route::group([
            'namespace' => $this->namespace, 'middleware' => 'web',
        ], function ($router) {
            require base_path('routes/web.php');
        });
    }

routes/web.php

Route::get('/', function () {
    return view('welcome');
});

routes/console.php

Artisan::command('inspire', function () {
    $this->comment(Inspiring::quote());
})->describe('Display an inspiring quote');

routes/api.php

Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:api');

$ php composer.phar update
$ php artisan –version
$ php artisan route:list;

技術負債は後々問題になりやすいので、「じゃー新しいの作る?」となってしまわないよう、設計時によく考えておかないといけない。

Gravatarとは?

https://ja.gravatar.com/support/what-is-gravatar/

Model User.php

public function getGravatarAttribute(){
        $hash = md5(strtolower(trim($this->attribute['email'])));
        return "http://www.gravatar.com/avatar/$hash";
    }

Viewのimg srcでAuth::user()->gravatar()と書くと、gravatarに登録した画像が表示される

公式ドキュメント
https://ja.gravatar.com/site/implement/images/
https://www.gravatar.com/avatar/${hashedUrl}

あんまり使ってるの見たことないけど、アバターのマイナンバーみたいな発想は面白い

Laravel ページングの書き方

グローバルのrender()を使います。

AdminPostsController.php

public function index()
    {
        // $posts = Post::all();
        $posts = Post::paginate(4);
        return view('admin.posts.index', compact('posts'));
    }

posts/index.blade.php

<div class="row">
		<div class="col-sm-6 col-sm-offset-5">
			{{$posts->render()}}
		</div>
    </div>

URLに自動的に?page=${n} が付与されます。

super amazing! あれほどページングべた書きに苦労したのは一体何だったんだ

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();
    }