[Laravel8.16.0]disabledのフォームでバリデーションエラー時に値が消える時

以下のようにLaravel Collectiveでフォームを書いてます。
ユーザに編集して欲しくない値は、’disabled’=>’disabled’属性を付けています。

{!! Form::label('company', '会社名') !!} <span class="badge badge-secondary">必須</span>
{!! Form::text('company', null, ['class' => 'form-control col-md-12', 'disabled'=>'disabled']) !!}

これだと、form送信時にdisabled属性の値は送信されないので、バリデーションエラー時に、空欄になってしまいます。

### 解決法
-> hiddenをつける

<input type="hidden" name="company" value="{{ $client['company'] }}">
{!! Form::label('company', '会社名') !!} <span class="badge badge-secondary">必須</span>
{!! Form::text('company', null, ['class' => 'form-control col-md-12', 'disabled'=>'disabled']) !!}

この事象に関するドキュメントがないから丸一日悩んだけど、あっさり解決した。。

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 Collectiveでのhiddenの扱い

hiddenはinput type=”hidden”で普通に書く

/resources/views/comments/index.blade.php
/resources/views/comments/replies/index.blade.php

Route

Route::group(['middleware'=>'admin'], function(){
	...//
	Route::resource('admin/comments', 'PostCommentsController');
	Route::resource('admin/comments/replies', 'CommentRepliesController');
});

$ php artisan make:model Comment -m
$ php artisan make:model CommentReply -m

comment migration file

Schema::create('comments', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('post_id')->unsigned()->index();;
            $table->integer('is_active')->default(0);
            $table->string('author');
            $table->string('email');
            $table->text('body');
            $table->timestamps();

            $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
        });

comment replies migration file

Schema::create('comment_replies', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('comment_id')->unsigned()->index();
            $table->integer('is_active')->default(0);
            $table->string('author');
            $table->string('email');
            $table->text('body');
            $table->timestamps();

            $table->foreign('comment_id')->references('id')->on('comments')->onDelete('cascade');
        });

Model: Post.php

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

Comment.php

protected $fillable = [
		'post_id',
		'author',
		'email',
		'body',
		'is_active'
	];

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

CommentReply.php

protected $fillable = [
		'comment_id',
		'author',
		'email',
		'body',
		'is_active'
	];

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

$ php artisan make:controller –resource PostCommentController
$ php artisan make:controller –resource CommentRepliesController

admin.blade.php

<li>
                        <a href="#"><i class="fa fa-wrench fa-fw"></i> Posts<span class="fa arrow"></span></a>
                        <ul class="nav nav-second-level">
                            <li>
                                <a href="{{route('admin.posts.index')}}">All Posts</a>
                            </li>

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

                            <li>
                                <a href="{{route('admin.comments')}}">All Comments</a>
                            </li>

                        </ul>
                        <!-- /.nav-second-level -->
                    </li>

PostCommentsController.php

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

route

Route::get('/post/{id}', ['as'=>'home.post','uses'=>'AdminPostsController@post']);

post.blade.php

<h1>{{$post->title}}</h1>

                <!-- Author -->
                <p class="lead">
                    by <a href="#">{{$post->user->name}}</a>
                </p>
                <hr>
               <!-- Date/Time -->
                <p><span class="glyphicon glyphicon-time"></span> Posted {{$post->created_at->diffForhumans()}}</p>
                <hr>
                <!-- Preview Image -->
                <img class="img-responsive" src="{{$post->photo ? '/' . $post->photo->file : ''}}" alt="">
                <hr>
                <!-- Post Content -->
                <p>{{$post->body}}</p>
                <hr>

 <h4>Leave a Comment:</h4>
                    
                    {!! Form::open(['method'=>'POST', 'action'=>'PostCommentsController@store']) !!}
				        {{ csrf_field()}}
				 
				        <div class="form-group">
				            {!! Form::label('body', 'Body') !!}
				            {!! Form::textarea('body', null, ['class'=>'form-control', 'rows'=>3]) !!}
				        </div>				        
				        <div class="form-group">
				            {!! Form::submit('Submit Comment', ['class'=>'btn btn-primary']) !!}
				        </div>
				    {!! Form::close() !!}                   
                </div>

post.blade.php

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

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

@if(Sessin::has('comment_message'))
						{{session('comment_message')}}
                @endif

PostCommentsController.php

use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Auth;
use App\User;
use App\Photo;
use App\Comment;

public function store(Request $request){

    	$user = Auth::user();

    	$data = [
    		'post_id' => $request->post_id,
    		'author' => $user->name,
    		'email' => $user->email,
    		'body' => $request->body
    	];
    	Comment::create($data);
    	$request->session()->flash('comment_message','Your message has been submitted and is waiting moderation' );

    	return redirect()->back();
    }

hiddenはinput type=”hidden”で書くという事は、フォームの確認ページがある場合、そこは全てcollectiveは使わずに書く、ということになりますね。

Laravelでdropzoneの使い方

– htmlべだ書と同様、ヘッダにdropzoneのcss、フッタにjsを読み込ませればいい。
– dropzoneはfileのnameがglobalで’file’なので、controllerで受け取るときは、$request->file(‘file’);と書く。

/resources/views/media/index.blade.php

@if($photos)
		<table class="table table-striped">
			<thead>
			<tr>
			  <th>Id</th>
			  <th>Name</th>
			  <th>Created date</th>
			</tr>
			</thead>
			<tbody>
				@foreach($photos as $photo)
				<tr>
				  <td>{{$photo->id}}</td>
				  <td><img height="50" src="/{{$photo->file}}"></td>
				  <td>{{$photo->created_at ? $photo->created_at : 'no date'}}</td>
				</tr>
				@endforeach			
			</tbody>
		</table>
		@endif

$ php artisan make:controller AdminMediasController

route.php

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

layouts/admin.blade.php

<li>
                                <a href="{{route('admin.medias.index')}}">All Media</a>
                            </li>
s
                            <li>
                                <a href="{{route('admin.medias.create')}}">Upload Media</a>
                            </li>

AdminMediasController.php

 public function index(){
    	$photos = Photo::all();
    	return view('admin.media.index', compact('photos'));
    }
 public function create(){
    	return view('admin.media.create');
    }

https://www.dropzonejs.com/

create.blade.php

@extends('layouts.admin')

@section('styles')
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.5.1/min/dropzone.min.css">
@stop

@section('content')

		<h1>Upload Media</h1>

		{!! Form::open(['method'=>'POST', 'action'=>'AdminMediasController@store','class'=>'dropzone', 'files'=>true]) !!}
        {{ csrf_field()}}
      
        <div class="form-group">
            {!! Form::submit('Create Post', ['class'=>'btn btn-primary']) !!}
        </div>
    {!! Form::close() !!}
@stop

@section('scripts')
	<script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.5.1/min/dropzone.min.js"></script>
@stop

AdminMediasController.php

public function store(Request $request){
    	$file = $request->file('file');
    	$name = time(). $file->getClientOriginalName();
    	$file->move('images',  $name);
    	Photo::create(['file'=>$name]);
    }
public function destroy($id){
    	$photo = Photo::findOrFail($id);
    	unlink(public_path(). '/'. $photo->file);

    	$photo->delete();
    	return redirect('/admin/media');
    }

index.blade.php

<tbody>
				@foreach($photos as $photo)
				<tr>
				  <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>

対象ページのみcssやjsをインクルードさせたい時は、ヘッダとフッタのsectionに追加で書き込めば良い😺

Laravel belongsToのフォームの書き方

formはlaravel collectiveを使用。make:model ${modelName} -mでDBを作って、データを挿入し、select文に対してControllerのcreateメソッドからデータを渡す

View: create.blade.php

{!! Form::open(['method'=>'POST', 'action'=>'AdminPostsController@store', 'files'=>true]) !!}
        {{ csrf_field()}}
 
        <div class="form-group">
            {!! Form::label('title', 'Title') !!}
            {!! Form::text('title', null, ['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::label('category_id', 'Category') !!}
            {!! Form::select('category_id', array(''=>'options'), null, ['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::label('photo_id', 'Photo') !!}
            {!! Form::file('photo_id', null,['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::label('body', 'Description') !!}
            {!! Form::textarea('body', null, ['class'=>'form-control', 'rows'=>3]) !!}
        </div>

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

$ php artisan make:request PostsCreateRequest

Requests/PostCreateRequest.php

public function rules()
    {
        return [
            'category_id' => 'required',
            'photo_id' => 'required',
            'title' => 'required',
            'body' => 'required'
        ];
    }

AdminPostsController.php

use App\Http\Requests\PostsCreateRequest;
use Illuminate\Support\Facades\Auth;
public function store(PostsCreateRequest $request)
    {
        //
        $input = $request->all();
        $user = Auth::user();
        if($file = $request['photo_id']){
            $name = time(). $file->getClientOriginalName();
            $file->move('images', $name);
            $photo = Photo::create(['file'=>$name]);

            $input['photo_id'] = $photo->id;
        }
        $user->posts()->create($input);
        return redirect('/admin/posts');
    }

View: posts/index.blade.php

<tr>
			  <td>{{$post->id}}</td>
			  <td>{{$post->user->name}}</td>
			  <td>{{$post->category_id}}</td>
			  <td><img height="50" src="{{$post->photo ? '/' . $post->photo->file : 'https://placehold.it/50x50'}}"></td>
			  <td>{{$post->title}}</td>
			  <td>{{$post->body}}</td>
			  <td>{{$post->created_at->diffForHumans()}}</td>
			  <td>{{$post->updated_at->diffForHumans()}}</td>
			</tr>

$ php artisan make:model Category -m

Model: Category.php

class Category extends Model
{
    //
    protected $fillable = ['name'];
}

Post.php

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

categories migration files

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

$ php artisan migrate

insert into categories (name) values (‘PHP’);
insert into categories (name) values (‘Laravel’);
insert into categories (name) values (‘JavaScript’);
insert into categories (name) values (‘Photoshop’);

/posts/index.blade.php

<td>{{$post->category ? $post->category->name : 'Uncategorized'}}</td>

AdminPostsController

use App\Category;
public function create()
    {
        //
        $categories = Category::lists('name', 'id')->all();
        return view('admin.posts.create', compact['categories']);
    }

View: posts/create.blade.php

<div class="form-group">
            {!! Form::label('title', 'Title') !!}
            {!! Form::text('title', null, ['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::label('category_id', 'Category') !!}
            {!! Form::select('category_id', array(''=>'Choose Options') + $categories, null, ['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::label('photo_id', 'Photo') !!}
            {!! Form::file('photo_id', null,['class'=>'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::label('body', 'Description') !!}
            {!! Form::textarea('body', null, ['class'=>'form-control', 'rows'=>3]) !!}
        </div>

DBにuncategorizedという値を入れずに、view側で$post->category ? $post->category->name : ‘Uncategorized’と書く方法は面白い、isnt it?

Laravel ファイルアップロードから表示までの書き方

OneToOneの画像用のモデル・migrationファイルを作成し、belongsToでUserからつなげる。ここまでは、他のOneToOneと同じ。
画像は、Controllerで、ファイル名をDBに保存し、画像自体は/images/フォルダに格納。
呼び出しはAccessorを使って、$user->photo->fileでimgタグに埋め込む。

$ php artisan make:migration add_photo_id_to_users –table=users

migration file

public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            //
            $table->string('photo_id');
        });
    }
public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            //
            $table->dropColumn('photo_id');
        });
    }

$ php artisan migrate

AdminUsersController.php

public function store(UsersRequest $request)
    {
        User::create($request->all());

        return redirect('/admin/users');
        // return $request->all();
    }

User.php

protected $fillable = [
        'name', 'email', 'password','role_id', 'is_active',
    ];

$ php artisan make:model Photo -m

$ git add .
$ git commit -m “user able to persits”

photo migration file

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

Model: Photo.php

class Photo extends Model
{
    protected $fillable = ['file'];    
}

Model: User.php

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

$ php artisan migrate

$ php artisan route:list;

View: admin.blade.php
リンクはrouteのname sapaceを使う

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

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

AdminUsersController.php

public function store(UsersRequest $request)
    {
        // User::create($request->all());
        $input = $request->all();

        if($file = $request->file('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::create($input);

        // return redirect('/admin/users');
        // return $request->all();
    }

Model:Photo.php

protected $upload = 'images/';
    protected $fillable = ['file'];

    public function getFileAttribute($photo){
    		return $this->upload . $photo;
    }

View: users/index.blade.php

<tbody>
		@if($users)
			@foreach($users as $user)
			<tr>
			  <td>{{$user->id}}</td>
			  <td><img height="50" src="/images/{{$user->photo ? $user->photo->file : ''}}"></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>
			@endforeach
		@endif
		</tbody>

ここでは画像はpublicのimagesフォルダ配下を想定しているが、S3の場合は、ControllerでS3にmoveと書く必要がある。その場合もViewでS3から呼び出せばよい。画像フォルダの格納ロジックは別途考える必要がある。例えば、/images/${userId}/${imageName}とするかなど。

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 ファイルアップロード時のコントローラーの書き方

普通にHTMLで書いた場合

<form action="/uploadfile" method="post" enctype="multipart/form-data">
	@csrf
	<div class="form-group">
		<input type="file" class="form-control-file" name="fileToUpload" id="exampleInputFile">
	</div>
	<button type="submit" class="btn btn-primary">Submit</button>
</form>

collectiveのForm::openのthird parameterに’files’=>trueを追加する
Form:file(‘file’)とする

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

		<div class="form-group">
			{!! Form::file('file', ['class'=>'form-controll']) !!}
		</div>

		<div class="form-group">
			{!! Form::label('title', 'Title') !!}
			{!! Form::text('title', null, ['class'=>'form-controll']) !!}
		</div>

		<div class="form-group">
			{!! Form::submit('Create Post', ['class'=>'btn btn-primary']) !!}

		</div>
	{!! Form::close() !!}

controller

public function store(CreatePostRequest $request)
    {
        return $request->file('file');
    }

オリジナル名、ファイルサイズ

public function store(CreatePostRequest $request)
    {
        $file = $request->file('file');
        
        echo "<br>";
        echo $file->getClientOriginalName();

        echo "<br>";
        echo $file->getClientSize();
    }

$ php artisan make:migration add_path_column_to_posts –table=posts
migration file

public function up()
    {
        Schema::table('posts', function (Blueprint $table) {
            //
            $table->string('path');
        });
    }

public function down()
    {
        Schema::table('posts', function (Blueprint $table) {
            //
            $talbe->dropColumn('path');
        });
    }

$ php artisan migrate

Post.php

protected $fillable = [
		'user_id',
		'title',
		'content',
		'path'
	];

PostsController.php

public function store(CreatePostRequest $request)
    {

        $input = $request->all();

        if($file = $request->file('file')){
            $name = $file->getClientOriginalName();

            $file->move('./images', $name);

            $input['path'] = $name;

        }

        Post::create($input);
    }

view

<ul>
		@foreach($posts as $post)

		<div class-"image-container">
			<img height="100" src="images/{{$post->path}}">
		</div>
		<li><a href="{{ route('posts.show', $post->id) }}">{{$post->title}}</a></li>
		
		@endforeach
	</ul>

images/{{$post->path}} は、accessorsでimages/を省略する

Model:Post.php

public $directory = "/images/";

public function getPathAttribute($value){

		return $this->directory . $value;
	}

View: index.php

<div class-"image-container">
			<img height="100" src="{{$post->path}}">
		</div>

今回はシンプルな書き方で、ファイルに対する拡張子やファイルサイズのバリデーションをつけていませんが、コントローラーもしくはrequestsでバリデーションを付ければ良いでしょう。

ファイルの格納場所はサーバー内ですが、これをS3にする場合はmove()の箇所を変える必要があります。

laravelのform validation

PostsController

public function store(Request $request)
    {
        $this->validate($request, [
            'title'=> 'required',  
        ]);
        Post::create($request->all());
        return redirect('/posts');
    }

Route
※古い書き方

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

	Route::resource('/posts', 'PostsController');

}]);

view: create
※古い書き方

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

						<li>{{ @error }}</li>
					@endforeach
				</ul>

			</div>
		@endif

$ php artisan make:request CreatePostRequest
./app/Http/Request/CreatePostRequest.php

public function authorize()
    {
        return true;
    }

public function rules()
    {
        return [
            //
            'title' => "required"
        ];
    }

controller

public function store(CreatePostRequest $request)
    {
        $this->validate($request, [
            'title'=> 'required',  
        ]);
        Post::create($request->all());
        return redirect('/posts');
    }

バリデーションは実務で日常的に使うので、比較的馴染みやすいように思います。