Blogブログ

laravel

2022.02.14

laravelでログインとadminのCRUDをちょろっとな。

前回の続き

今回はログインとadminのCRUDをやっていきます。

まずはログインの実装方法を解説

Route::group(['prefix' => 'admin', 'namespace' => 'Admin', 'as' => 'admin.'], function($router) {
    $router->get('login', 'Auth\LoginController@showLoginForm')->name('login');
    $router->post('login', 'Auth\LoginController@login')->name('dologin');
    $router->get('/auth/logout', 'Auth\LoginController@logout')->name('logout');

});

admin/XXXX

というadminがつくときのroutingの書き方も載せておきます。

prefixにadminがつくと、
その{}の中のURLにloginが書いてある場合に、自動的にURLはadmin/loginのようになります。

namespaceにAdminと書くと、
{}に囲まれた中で、コントローラーをAuth\LoginController@showLoginFormと書くと、
自動的にAdmin\LoginController@showLoginForm
ということになります。

‘as’ => ‘admin.
に関しては、viewでroute()で呼び出すときに、自動的にadmin.がつくことになるため、
例えば
$router->post(‘login’, ‘Auth\LoginController@login’)->name(‘dologin’);
なら、route(admin.dologin)で呼べることになります。

というわけで、ログインのコントローラーを作っていきます。

<?php

namespace App\Http\Controllers\Admin\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use App\Models\Admin;

class LoginController extends Controller
{
    use AuthenticatesUsers;

    protected $guard = 'admin';

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/admin/dashboard';

    public function showLoginForm()
    {
        if ($this->guard()->check()) {
            return redirect()->route('admin.dashboard');
        }
        $data['login'] = 'login';
        return view('admin.auth.login', $data);
    }

    public function login(Request $request)
    {
        $this->validateLogin($request);
        if ($this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);
            return $this->sendLockoutResponse($request);
        }
        $user = Admin::where('email', $request->email)->first();
        if (empty($user)) {
            $request->flash();
            return back()->withErrors(['email' => trans('auth.error')]);
        }

        if ($this->attemptLogin($request)) {
            return $this->sendLoginResponse($request);
        }

        $this->incrementLoginAttempts($request);

        $request->flash();
        return back()->withErrors(['password' => trans('auth.failed')]);
    }

    public function logout()
    {
        $this->guard()->logout();
        return redirect()->route('admin.login');
    }

    protected function authenticated(Request $request, $user)
    {
        if ($request->session()->has('__redirect_url')) {
            $redirectUrl = $request->session()->get('__redirect_url');
            $request->session()->forget('__redirect_url');
            return redirect($redirectUrl);
        }

        return redirect()->route('admin.dashboard');
    }

    public function username()
    {
        return 'email';
    }

    protected function guard()
    {
        return Auth::guard($this->guard);
    }
}

このコントローラーを解説する前に、laravel/config/auth.phpを開いてみましょう。

その中の、ガードというところに注目します。

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],
    ],

ここにログインに必要な単位というものがあると思ってください。
apiはまた今度解説するのでおいておきます。
webのほうにproviderとかいてありusersとありますね。
これがusersの色々設定するための共通マークだと思ってください。
共通マークだと初心者臭がだいぶするのでプロバイダーと呼びましょう。
では、userプロバイダーを意識しながら、
すぐその下を見ると、

  'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],
                                 

このように書いてあります。
ユーザプロバイダーのときはApp\Userをインスタンス化するよって書いてあります。
ここでのdriverはあまり気にしなくてもいいです。
laravelはORMでeloquentを使うのですがその指定が書いてあるだけなので一旦無視します。

その下のコードをみてもuserプロバイダーの設定と意識してみると..

 App\User::class,
    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],

パスワードのリセットのときに使うテーブル名(password_resets)やその有効期間なんかの指定がされていることがわかると思います。

では、ガードにadminを追加していきます。

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],
    ],

ガードはこうなりますよね。

プロバイダーadminsに関しては..

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
        'admins' => [
            'driver' => 'eloquent',
            'model' => App\Models\Admin::class,
        ],

こうなりますよね。
このとき、AdminはApp/Models/Admin.phpになるので、
そのpathを意識して書きました。

そしてパスワードリセットのところは、


    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
        'admins' => [
            'provider' => 'admins',
            'table' => 'admin_password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],

こんな感じでしょうか。
tableとしてadmin_password_resetsを作ったので、
マイグレーションで作成していきます。
artisanがあるところで、
php artisan make:migration create_admin_password_resets

で、だいたいユーザ側のパスワードリセットがすでにあるので、
それを編集してadminのパスワードリセットも作りましょう。
こんな感じになると思います。

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateAdminPasswordResets extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('admin_password_resets', function (Blueprint $table) {
            $table->string('email')->index();
            $table->string('token');
            $table->timestamp('created_at')->nullable();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('admin_password_resets');
    }
}  

ファイル名とクラス名を一致させることを忘れずに。
(3箇所くらいadmin_つけただけです。)

そして、
いつもの
docker exec -it XXXX /bin/bash
でコンテナの中に入り
php artisan migrateを行うと
テーブルができたことを確認できると思います。

はい。できていますね。

これで、コントローラーを改めてみてみましょう。

さっき、routeのweb.phpで、
$router->get(‘login’, ‘Auth\LoginController@showLoginForm’)->name(‘login’);
とかいたので、
/loginとアクセスすると、
Auth\LoginControllerのshowLoginFormアクションを見にいきます。
この時のviewは、
return view(‘admin.auth.login’, $data);なので、
admin/auth/login.blade.phpを作りましょう。

本物っぽいviewをお見せしましょう(layoutなしで。)

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title></title>
  <link href="{{ asset('assets/admin/css/style.css') }}" rel="stylesheet" media="screen">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="header">
  <div id="header_left">
    <a href="#please_use_something">
      <h1>解説用HTML</h1>
    </a>
  </div>
</div>
<div id="login_box">
  <form autocomplete="off" action="{{route('admin.dologin')}}" method="post">
    @csrf
    <h1>Login</h1>
    <div>
      <label for="email">メールアドレス</label>
      <input type="text" title="email" name="email" placeholder="xxxx@gmail.com" value="{{ old('email') }}" />
      @if ($errors->has('email'))
        <div style="color: red; margin-top: 10px;">{{$errors->first('email')}}</div>
      @endif
    </div>
    <div>
      <label for="passwordinput">パスワード</label>
      <input type="password" title="password" name="password" placeholder="xxxxxxxx(6文字以上)" value="{{ old('password') }}" />
      @if ($errors->has('password'))
        <div style="color: red; margin-top: 10px;">{{$errors->first('password')}}</div>
      @endif
    </div>
    <button type="submit" name="singlebutton" class="login_btn">ログイン</button>
  </form>
</div>
@include('admin.common.footer')
</body>
</html>

まずひとつだけassetがでてきたので説明します。

 <link href="{{ asset('assets/admin/css/style.css') }}" rel="stylesheet" media="screen">

このassetでpublic/assets/admin/css/style.cssを見に行きます。

これをsubmitすると,admin/dologinを見に行くのはわかると思います。

ログイン処理のところの解説

解説①

これは検索部分の解説の時にいろいろなパターンを書こうと思うので
いったんはさらっといきます。
laravelはデータを取得する時にuseで読み込んだmodelのクラスをstatic関数で呼び、where句等の条件を追加していくという書き方が多いです。

Admin::where(‘カラム’,$値)->first();

このfirst()は詳細ページの情報のような一件だけもってくるときに使う関数です。
ログインは普通は1名のユーザなのでfirst()を使います。

ちなみに一覧のような複数の取得の時はget()を使います。
例を見るとわかりやすいので記事取得のときに実際に書いたコードで
雰囲気を掴んでください。
Article::where(‘special’,1)->where(‘draft’,2)->orderBy(‘updated_at’,’desc’)->get();

解説②
back()という関数は直前の画面に戻すという関数です。
back()->withErrors(‘email’,trans(‘auth.error’));
という書き方で、直前のログイン画面にemailというキーにメッセージをもって戻すことができます。
transは configの中のapp.phpの言語ファイルがenなら /laravel/resources/lang の中の、enを、 configの中のapp.phpの言語ファイルがjaなら /laravel/resources/lang の中の、jaを、 見るようになっていて、 例えばenならそのフォルダに行くと…今回の場合はauth.phpの中に 配列でerrorというキーに対してメッセージが設定されているという感じです。
今はerrorというキーはないので、追加してみてください。

解説③
attemptlogin()はその名の通り、
ログインを試みます。

もし成功したらsendLoginResponse($request)が実行されていますが、
このとき、クラスの上部に記載したredirectToというインスタンス変数のパスをみてそこにリダイレクトしています。

Modelsも直す必要があります。

extends Authenticatable .. .

アクセスしたらエラーがでた…

このときは入れ忘れてたものがあるので、
composer require laravel/ui
を実行してください。
(laravel7以降はログイン系のコードがlaravel/uiに移動しているので、それをインストールしないとでした)

root@0617f6b463f0:/var/www/laravel# composer require laravel/ui
Warning from https://repo.packagist.org: Support for Composer 1 is deprecated and some packages will not be available. You should upgrade to Composer 2. See https://blog.packagist.com/deprecating-composer-1-support/
Using version ^2.5 for laravel/ui
./composer.json has been updated
Loading composer repositories with package information
Warning from https://repo.packagist.org: Support for Composer 1 is deprecated and some packages will not be available. You should upgrade to Composer 2. See https://blog.packagist.com/deprecating-composer-1-support/
Updating dependencies (including require-dev)

Fatal error: Allowed memory size of 1610612736 bytes exhausted (tried to allocate 75497472 bytes) in phar:///usr/bin/composer/src/Composer/DependencyResolver/RuleSet.php on line 90

Check https://getcomposer.org/doc/articles/troubleshooting.md#memory-limit-errors for more info on how to handle out of memory errors.root@0617f6b463f0:/var/www/laravel# 

多くの方が実行可能だと思いますが、
もしこんな感じで、メモリーが足りないと言われたら
COMPOSER_MEMORY_LIMIT=-1  composer require laravel/ui  
のように先頭にメモリ気にせずにという記述を書いてください。
macではデフォルトの設定でdockerのメモリの上限が決まっていたりするのでときどきこのエラーが出る人がいるかと思います。

今のHTMLだとfooterがないので、
/resources/views/admin/common/footer.blade.phpを作りそこに
何かしらの記述を書いてアクセスすると…

できました。

ふむふむ、バリデーションも動いてることがわかります。

本物のAdmin作成画面作りますね。

routeに
$router->resource(‘accounts’, ‘AccountsController’);
を追加してください

今はおそらく、

Route::group(['prefix' => 'admin', 'namespace' => 'Admin', 'as' => 'admin.'], function($router) {
    $router->get('login', 'Auth\LoginController@showLoginForm')->name('login');
    $router->post('login', 'Auth\LoginController@login')->name('dologin');
    $router->get('/auth/logout', 'Auth\LoginController@logout')->name('logout');
    //これを追加ね。
    $router->resource('accounts', 'AccountsController');
});   

このresourceという関数を使ってroutingを作ると、
このように書くだけで、

accountsに対して、
詳細、一覧、作成(getとpost)、更新(getとpost)、削除を作ってくれます。
7つのルーティングを作成してくれます。
URLは
一覧は/accounts
詳細が/accounts/$id
作成がaccounts/create
作成のpost先が/accounts/store
編集は/accounts/edit/$id
更新が/accounts/update/$id
削除は/accounts/destrory
を作ってくれます。

結構便利ですね。
でも例えば、accounts/index というアクションと,accounts/showというものだけを作りたいんだけどというときは、
$router->resource(‘accounts’, ‘AccountsController’)->only([‘index’, ‘show’]);

とやると実現できます。

これで使い方はわかりましたね。

では、コントローラーのAdminフォルダの中にAccountsController.phpを作りますが、その前にbaseコントローラーを継承したいので、
/app/Http/Controllers/Admin/AdminBaseController.phpを作ります。


<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;

class AdminBaseController extends Controller
{
    protected $guard = 'admin';

    protected $activeMenu = '';

    protected function handleView($view)
    {
        view()->share('__activeMenu', $this->activeMenu);
    }
}

view->share()は継承される全てのviewに共通の文字列を渡したいときに使います。

ではAccountsController.phpを作りましょう。

<?php
namespace App\Http\Controllers\Admin;

use App\Http\Request\Admin\Account\CreateEditRequest;
use App\Models\Admin;
use Illuminate\Http\Request;

class AccountsController extends AdminBaseController
{
    protected $viewPathPrefix = 'admin.accounts';

    public function index()
    {
        $list = Admin::getList();
        return view('admin.accounts.index', compact('list'));
    }

    public function create()
    {
        $data = new Admin();
        $type = 'create';
        return view('admin.accounts.create', compact('type', 'data'));
    }

    public function update($id)
    {
        $data = Admin::findOrFail($id);
        $type = 'update';
        return view('admin.accounts.create_update', compact('type', 'data'));
    }

    public function detail($id)
    {
        $data = Admin::findOrFail($id);
        return view('admin.accounts.detail', compact('data'));
    }

    public function store(CreateEditRequest $request)
    {
        $params = $request->all();
        $admin = new Admin();
        // パスワード暗号化
        $admin->password = bcrypt($params['password']);
        $admin->fill($params);
        if ($admin->save()) {
            return redirect(route('admin.accounts.index'));
        }
        return back()->withInput();
    }
}

create()アクションが作成画面です。
Adminモデルをインスタンス化して、オブジェクトをviewに渡してることがわかります。

back()->withInput()とかくと、バリデーションエラー時に値を保持して戻してくれます。

バリデーションも考えてRequestを作ってみます。

app/Http/Request/Admin/AccountにCreateEditRequest.phpを作ってください。

<?php

namespace App\Http\Request\Admin\Account;

use App\Rules\Admin\RequireRule;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;

class CreateEditRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        // dd($request()->all())
        return [
            'name' => 'required',
            'email' => 'required|email',
            'password' => 'required|confirmed',
        ];
    }

    public function attributes()
    {
        return [
            'name' => 'アカウント名r',
            'email' => 'メールアドレス',
            'password' => 'パスワード'
        ];
    }

    protected function failedValidation(Validator $validator)
    {
        throw (new HttpResponseException(response()->json([
            'code' => 422,
            'status' => 'FAIL',
            'message' => $validator->errors(),
        ], 200)));
    }
}

ここで、
roules()の中の
‘password’ => ‘required|confirmed’,
をみてください。

このconfirmedを入れると、
view側のname属性で、「password_confirmation」が使えるようになり、
よくある「確認用パスワード」の確認をしてくれるようになります。

ではviewを作ってみましょう。

先に
resources/views/admin/layoutsをつくって
そこにlayoutsをつくり、default.blade.phpをつくりましょう。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>Admin</title>
  <meta name="description" content="" />
  <meta name="keywords" content="" />
  <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no" />
  <meta name="format-detection" content="telephone=no,address=no,email=no" />
  <meta property="og:type" content="website" />
  <meta name="SKYPE_TOOLBAR" content="SKYPE_TOOLBAR_PARSER_COMPATIBLE" />
  <meta name="csrf-token" content="{{ csrf_token() }}" />
  @stack('meta')
  <link rel="shortcut icon" type="image/vnd.microsoft.icon" href="{{ asset('favicon.ico') }}" />
  <link href="{{ asset('assets/admin/css/style.css') }}" rel="stylesheet" media="screen">
  <script type="text/javascript" src="{{ asset('assets/admin/js/jquery-2.2.4.min.js') }}"></script>
  <script type="text/javascript" src="{{ asset('assets/admin/js/admin.js?v=2020090901') }}"></script>
  <script type="text/javascript" src="{{ asset('assets/admin/js/sidebar.js') }}"></script>
  <script src="https://cdn.jsdelivr.net/npm/sweetalert2@8"></script>
  @stack('css')


</head>
<body>
  <div id="body">
    @include('admin/common/header')

    @yield('content')

    @include('admin/common/footer')

    @stack('script')
    <script type="text/javascript">
      $(function(){
        $('.from').focus(function(){
          var to = $(this).next().val();
          if (to.length > 0) {
            $(this).attr('max', to);
          }
        });
        $('.to').focus(function(){
          var from = $(this).prev().val();
          if (from.length > 0) {
            $(this).attr('min', from);
          }
        });
      });
    </script>
  </div>
</body>
</html>

@stack(‘script’)
というのが気になったと思いますが、
レイアウトにこのように書いておくことで、
継承先のviewで、ある特定のviewだけ呼び出したいjs等があるときに
____________
@push(‘script’)
この間にコードを書くことで差し込むことができるという機能です。
@endpush
_____________

headerとfooterをcommonから呼び出したいので、
作っていきます。
はずは、
admin/common/にheader.blade.phpをつくり

<div id="header">
  <div id="header_left">
    <div id="sp_menu">
      <div class="drawer">
        <label class="drawer__icon">
          <input class="drawer__toggler" type="checkbox">
          <span class="drawer__icon_parts"></span>
          <div class="overlay"></div>
          <div class="nav__menu">
            <ul class="nav__list">
              <div id="sidebar">
                <ul>
                  <li>
                    <a href="#">
                      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="18px" height="18px">
                        <path d="M0 0h24v24H0V0z" fill="none"/>
                        <path d="M12 5.69l5 4.5V18h-2v-6H9v6H7v-7.81l5-4.5M12 3L2 12h3v8h6v-6h2v6h6v-8h3L12 3z"/>
                      </svg>
                      <p>ダッシュボード</p>
                    </a>
                  </li>
                  <li>
                    <a href="{{ route('admin.accounts.index') }}">
                      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="18px" height="18px">
                        <path d="M0 0h24v24H0V0z" fill="none"/>
                        <path d="M12 5.69l5 4.5V18h-2v-6H9v6H7v-7.81l5-4.5M12 3L2 12h3v8h6v-6h2v6h6v-8h3L12 3z"/>
                      </svg>
                      <p>アカウント</p>
                    </a>
                  </li>
                </ul>
              </div>
            </ul>
          </div>
        </label>
      </div>
    </div>
  </div>
  <div id="header_right">
    <a href="#" id="profile">
      <p></p>
    </a>
  </div>
</div>

footerも同様に、つくりましょう。
コードはこんな感じ、

<div id="footer">
  <p>
    © copyright ~ 2022 All Rights Reserved
  </p>
</div>

つぎに
このlayoutsを継承するかたちで、
resources/views/admin/accountsにcreate.blade.phpを作りましょう。

@extends('admin.layouts.default')

@section('content')
  <div id="container">
    <div id="main">
      <h1 id="page_title">Adminを作成する </h1>
      <div id="main_contents">
        <div id="main">
          <form class="form-horizontal" action="{{route('admin.accounts.store')}}" id="form_data" method="post">
            @csrf
            <fieldset>
              <div class="form-group">
                <label class="col-md-4 control-label">アカウント名 <span>必須</span></label>
                <div class="col-md-4">
                  <input name="name" value="{{ old('name', $data->name) }}" type="text" placeholder="" class="form-control input-md"  />
                  <div hidden id="url_error" name="error_div" style="color: red; margin-top: 10px;"></div>
                </div>
              </div>
              <div class="form-group">
                <label class="col-md-4 control-label">メールアドレス <span>必須</span></label>
                <div class="col-md-4">
                  <input name="email" value="{{ old('email', $data->email) }}" type="email" placeholder="" class="form-control input-md"  />
                  <div hidden id="url_error" name="error_div" style="color: red; margin-top: 10px;"></div>
                </div>
              </div>
              <div class="form-group">
                <label class="col-md-4 control-label">パスワード <span>必須</span></label>
                <div class="col-md-4">
                  <input name="password" value="" type="password" placeholder="" class="form-control input-md"  />
                  <div hidden id="url_error" name="error_div" style="color: red; margin-top: 10px;"></div>
                </div>
              </div>
              <div class="form-group">
                <label class="col-md-4 control-label">確認用パスワード <span>必須</span></label>
                <div class="col-md-4">
                  <input name="password_confirmation" value="" type="password" placeholder="" class="form-control input-md"  />
                  <div hidden id="url_error" name="error_div" style="color: red; margin-top: 10px;"></div>
                </div>
              </div>
              <div class="form-group">
                <div class="col-md-4">
                  <button type="button" onclick="window.location.href='{{ route('admin.accounts.index') }}'" class="back_btn">戻る</button>
                  <button type="submit"  class="register_btn">@if($type == 'create')登録@else更新@endif</button>
                </div>
              </div>
            </fieldset>
          </form>
        </div>
      </div>
    </div>
  </div>
@endsection

@push('css')

@endpush

@push('script')

@endpush

ルーティングでresouceを使ったので、 index.blade.phpと edit.blade.phpも作らないといけないので、 何も書かなくても良いのでファイルだけ作っておきましょう。

index.blade.phpは一応先に書いておきます。

@extends('admin.layouts.default')

@section('content')
  <div id="container">
    <div id="main">
      <h1 id="page_title">Amin一覧</h1>
      <div id="btn_block">
        <button onclick="location.href='{{ route('admin.accounts.create') }}'" class="create_btn">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black" width="18px" height="18px">
            <path d="M0 0h24v24H0V0z" fill="none"/>
            <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
          新規作成
        </button>
      </div>
      <table class="flex-table">
        <thead>
        <tr>
          <th class="table_5">
            <input type="checkbox" id="select_all" onchange="select_all();" class="checkbox" />
          </th>
          <th class="table_40">id</th>
          <th class="table_40">アカウント名</th>
          <th class="table_15">操作</th>
        </tr>
        </thead>
        <tbody>
        @foreach($list as $item)
          <tr>
            <td data-label="チェック" class="table_5">
              <input type="checkbox" name="checked_id[]" value="{{ $item->id }}" class="checkbox" />
            </td>
            <td data-label="id" class="table_40">{{ $item->id }}</td>
            <td data-label="アカウント名" class="table_40">{{ $item->name }}</td>
            <td data-label="操作" class="table_15">
              <button onclick="location.href='{{ route('admin.accounts.show', $item->id) }}'" class="detail_btn">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black" width="18px" height="18px">
                  <path d="M0 0h24v24H0V0z" fill="none"/>
                  <path d="M12 6.5c3.79 0 7.17 2.13 8.82 5.5-1.65 3.37-5.02 5.5-8.82 5.5S4.83 15.37 3.18 12C4.83 8.63 8.21 6.5 12 6.5m0-2C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zm0 5c1.38 0 2.5 1.12 2.5 2.5s-1.12 2.5-2.5 2.5-2.5-1.12-2.5-2.5 1.12-2.5 2.5-2.5m0-2c-2.48 0-4.5 2.02-4.5 4.5s2.02 4.5 4.5 4.5 4.5-2.02 4.5-4.5-2.02-4.5-4.5-4.5z"/>
                </svg>
              </button>
              <button onclick="location.href='{{ route('admin.accounts.edit', $item->id) }}'" class="edit_btn">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black" width="18px" height="18px">
                  <path d="M0 0h24v24H0V0z" fill="none"/>
                  <path d="M14.06 9.02l.92.92L5.92 19H5v-.92l9.06-9.06M17.66 3c-.25 0-.51.1-.7.29l-1.83 1.83 3.75 3.75 1.83-1.83c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.2-.2-.45-.29-.71-.29zm-3.6 3.19L3 17.25V21h3.75L17.81 9.94l-3.75-3.75z"/>
                </svg>
              </button>
              <form action="{{ route('admin.accounts.destroy', $item->id) }}" method="post">
                @csrf
                @method("delete")
                <button class="delete_btn" type="submit">
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black" width="18px" height="18px">
                    <path d="M0 0h24v24H0V0z" fill="none"/>
                    <path d="M16 9v10H8V9h8m-1.5-6h-5l-1 1H5v2h14V4h-3.5l-1-1zM18 7H6v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7z"/>
                  </svg>
                </button>
              </form>
            </td>
          </tr>
        @endforeach
        </tbody>
      </table>
      {{ $list->links('admin.pagination.default') }}
    </div>
  </div>
@endsection

@push('css')

@endpush

@push('script')

@endpush

paginationのviewも作っておきます。

admin/pagination/default.blade.phpを作ります。

@if ($paginator->hasPages())
  <nav class="pg_navi">
    <div class="pagination">
      @if ($paginator->onFirstPage())
        <a class="pg_number prev" href="javascript:return false;">prev</a>
      @else
        <a class="pg_number prev" href="{{ $paginator->previousPageUrl() }}">prev</a>
      @endif

      @foreach($elements as $element)
        @if(is_string($element))
          <span aria-current="page" class="pg_number current">{{ $element }}</span>
        @endif

        @if(is_array($element))
          @foreach ($element as $page => $url)
            @if ($page == $paginator->currentPage())
              <span aria-current="page" class="pg_number current">{{ $page }}</span>
            @else
              <a class="pg_number" href="{{ $url }}">{{ $page }}</a>
            @endif
          @endforeach
        @endif
      @endforeach

      @if ($paginator->hasMorePages())
        <a class="pg_number next" href="{{ $paginator->nextPageUrl() }}">next</a>
      @endif
    </div>
  </nav>
@endif

adminのmodelに一つ関数追加します。

    public static function getList()
    {
        return Admin::orderBy('id', 'desc')->paginate(10);
    }

これで動くかな。
http://127.0.0.1/admin/accounts/
これで一覧が表示
http://127.0.0.1/admin/accounts/createで作成ページが表示されるはず。

表示されていますね。

では次はeditと削除をやって、cssもそれっぽくしていきましょう。

resources/views/admin/accounts/edit.blade.php

@extends('admin.layouts.default')

@section('content')
  <div id="container">
    <div id="main">
      <h1 id="page_title">Adminを作成する </h1>
      <div id="main_contents">
        <div id="main">
          <form class="form-horizontal" action="{{route('admin.accounts.update',$data->id)}}" id="form_data" method="post">
            @csrf
            @method('put')
            <fieldset>
              <div class="form-group">
                <label class="col-md-4 control-label">アカウント名 <span>必須</span></label>
                <div class="col-md-4">
                  <input name="name" value="{{ old('name', $data->name) }}" type="text" placeholder="" class="form-control input-md"  />
                  <div hidden id="url_error" name="error_div" style="color: red; margin-top: 10px;"></div>
                </div>
              </div>
              <div class="form-group">
                <label class="col-md-4 control-label">メールアドレス <span>必須</span></label>
                <div class="col-md-4">
                  <input name="email" value="{{ old('email', $data->email) }}" type="email" placeholder="" class="form-control input-md"  />
                  <div hidden id="url_error" name="error_div" style="color: red; margin-top: 10px;"></div>
                </div>
              </div>
              <div class="form-group">
                <label class="col-md-4 control-label">パスワード <span>必須</span></label>
                <div class="col-md-4">
                  <input name="password" value="" type="password" placeholder="" class="form-control input-md"  />
                  <div hidden id="url_error" name="error_div" style="color: red; margin-top: 10px;"></div>
                </div>
              </div>
              <div class="form-group">
                <label class="col-md-4 control-label">確認用パスワード <span>必須</span></label>
                <div class="col-md-4">
                  <input name="password_confirmation" value="" type="password" placeholder="" class="form-control input-md"  />
                  <div hidden id="url_error" name="error_div" style="color: red; margin-top: 10px;"></div>
                </div>
              </div>
              <div class="form-group">
                <div class="col-md-4">
                  <button type="button" onclick="window.location.href='{{ route('admin.accounts.index') }}'" class="back_btn">戻る</button>
                  <button type="submit"  class="register_btn">更新</button>
                </div>
              </div>
            </fieldset>
          </form>
        </div>
      </div>
    </div>
  </div>
@endsection

@push('css')

@endpush

@push('script')

@endpush

ポイントは、@csrfの下の@putですね。
laravelはmethod=”post”なんですけど、
更新の時は@method(‘put’)つけないとエラーになります。

{{ old(‘email’, $data->email)
は、もともとDBに登録されていた値を表示します、
第一引数が優先されるので、第一引数のoldはバリデーションエラーで帰ってきたときにここに入ってくるものです。

では、AccountsControllerコントローラーにeditとupdateも追記しましょう。


    public function edit($id)
    {
        //findはidで検索する時使うないなら500エラー返ってくるよ。
        //$data = Admin::find($id);
        //findOrFailはidで検索する時使うないなら404返ってくるよ。
        //$data = Admin::findOrFail($id);
        //firstでとるならnullが返ってくるよ。
        $data = Admin::where('id',$id)->first();
        if(empty($data))
        {
            return redirect(route('admin.accounts.index'));
        }
        //$data = Admin::where('id',$id)->first();
        return view('admin.accounts.edit', compact('data'));
    }
    

    public function update(CreateEditRequest $request, $id)
    {

        $admin = Admin::findOrFail($id);
        $admin->fill($request->all());
        $admin->password = bcrypt($request->get('password'));
        if ($admin->save()) {
            return redirect(route('admin.accounts.index'));
        }
        return back()->withInput();        
    }

次に詳細ページを作っていきましょう

コントローラーには

    public function show($id)
    {
        //findはidで検索する時使うないなら500エラー返ってくるよ。
        //$data = Admin::find($id);
        //findOrFailはidで検索する時使うないなら404返ってくるよ。
        //$data = Admin::findOrFail($id);
        //firstでとるならnullが返ってくるよ。
        $data = Admin::where('id',$id)->first();
        if(empty($data))
        {
            return redirect(route('admin.accounts.index'));
        }
        return view('admin.accounts.show', compact('data'));
    }

これでOKです。

view側にはshow.blade.phpを追加し、中身は

@extends('admin.layouts.default')

@section('content')
  <div id="container">
    <div id="main">
      <h1 id="page_title">Admin詳細</h1>
      <div id="main_contents">
        <div id="main">
            <fieldset>
              <div class="form-group">
                <label class="col-md-4 control-label" for="name">ID</label>
                <div class="col-md-4">
                  {{ $data->id }}
                </div>
              </div>
              <div class="form-group">
                <label class="col-md-4 control-label" for="name">アカウント名</label>
                <div class="col-md-4">
                  {{ $data->name }}
                </div>
              </div>
              <div class="form-group">
                <label class="col-md-4 control-label" for="name">メールアドレス</label>
                <div class="col-md-4">
                  {{ $data->email }}
                </div>
              </div>
                <div class="form-group">
                  <div class="col-md-4">
                    <button type="button" onclick="window.location.href='{{ route('admin.accounts.index') }}'" class="back_btn">戻る</button>
                  </div>
                </div>
              </form>
            </fieldset>
        </div>
      </div>
    </div>
  </div>
@endsection

@push('css')

@endpush

@push('script')
@endpush

にしてください。

これで、詳細ページも作れました。

最後に削除機能をつけてみます。

コントローラーにこれを追加しましょう。

    public function destroy(Request $request, $id)
    {
        $admin = Admin::findOrFail($id);
        $admin->delete();
        return redirect(route('admin.accounts.index'));
        
    }

ここでは、第一引数をCreateEditRequestではなくてRequestだけします。

CreateEditRequestはRequestの中でパスワード等の必須条件が走ってしまうので、削除のときこれいらないので、普通のRequestにしました。

indexのviewを見て欲しいのですが、

<form action="{{ route('admin.accounts.destroy', $item->id) }}" method="post">
                @csrf
                @method("delete")
                <button class="delete_btn" type="submit">
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black" width="18px" height="18px">
                    <path d="M0 0h24v24H0V0z" fill="none"/>
                    <path d="M16 9v10H8V9h8m-1.5-6h-5l-1 1H5v2h14V4h-3.5l-1-1zM18 7H6v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7z"/>
                  </svg>
                </button>
              </form>

削除のところをformで囲み、
@csrfの下に@method(“delete”)を入れてます。
これはupdateのときと同じで、削除の時はlaravelのときはこのように入れると動きます。

これでadminのCURDができたと思います。

確認をするまえに見た目がちょっとダサいのでcssも書きましょう。

publicの中にassetsというフォルダを作り
そこに….cssやjsやckeditorなんかをいれます。

ちょっとこれをひとつひとつ書くのは心が折れたので割愛します。

では、
見た目もきれいになったところで、

アクセスしてみると…

できてますね。

以上です。