STEP14：Laravel5.7 + Vue2.5 で操作ログを自動記録する

Laravel のミドルウェアを使って操作のログを自動で記録してみます

操作ログはデータベースへ保存して、ログイン済みであればユーザＩＤも自動で記録

記録した操作ログを一覧表示する画面も作成しときます


#ログテーブル作成

まずは操作（アクション）ログを記録するテーブルを作成します

```bash
php artisan make:migration create_actlogs_table --create=actlogs
```

```php:database/migrations/*actlogs*.php

<?php

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

class CreateActlogsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('actlogs', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id') -> unsigned() -> nullable() -> comment("ユーザID");
            $table->string('route') -> nullable() -> comment("route.webで設定した名称");
            $table->string('url') -> nullable() -> comment("要求Path");
            $table->string('method') -> nullable() -> comment("要求メソッド Get Post");
            $table->integer('status') -> unsigned() -> nullable() -> comment("要求結果 200 OK とか 301 move 等");
            $table->text('data') -> nullable() -> comment("要求内容（暗号化して保存）");
            $table->string('remote_addr') -> nullable() -> comment("クライアントIPアドレス");
            $table->string('user_agent') -> nullable() -> comment("ブラウザ名");
            $table->timestamps();

            $table->index(['created_at']);
            $table->index(['user_id']);
        });
    }

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

```

ログに記録するのは以下
　・ユーザID
　・ページ名（routes/web.php の name でつけといた名前）
　・要求パス
　・要求メソッド（GETとかPOSTとか）
　・要求結果 200 OK とか 301 Move とか
　・要求内容（あれば）
　・接続元 IPアドレス
　・ブラウザ名（UserAgent）
　・作成時間（DB記録時間）


定義から実テーブルの作成もしときます

```bash
php artisan migrate:refresh --seed
```





#テーブルに対応するモデルも作成

```php:app/Actlog.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Crypt;

class Actlog extends Model
{
    // 更新日時記録無効化
    const UPDATED_AT = null;

    // Jsonに追加で含める
    protected $appends = ['name'];

    // ユーザの氏名を取得 -- リレーションできなかった場合は空文字を返す
    public function getNameAttribute()
    {
        $user = $this -> user;
        if ($user) return $user -> name;
        else       return '';
    }

    // カラム暗号化 - 要求内容は暗号化して保存する
    public function setDataAttribute($value)
    {
        if ($value) {
            $this->attributes['data'] = Crypt::encrypt( serialize($value) );
        }
    }

    // カラム複合化 - 要求内容を取り出すときに複合化する
    public function getDataAttribute($value)
    {
        if ($value) {
            return unserialize( Crypt::decrypt($value) );
        } else {
            return $value;
        }
    }

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'user_id',
        'route',
        'url',
        'method',
        'status',
        'data',
        'remote_addr',
        'user_agent',
    ];

    // Json に出力する項目
    protected $visible = [
        'user_id',
        'route',
        'url',
        'method',
        'status',
        'data',
        'remote_addr',
        'user_agent',
        'created_at',
        'name',
    ];

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

```

通信内容（ 'data' ）は暗号化して保存
あと、UPDATEはしないので記録しないように設定しときます
一覧で見やすいようにユーザ名（ 'name' ）も返すようにしてあります


#ミドルウェア作成

実際にログを記録するミドルウェアを作成です

```bash
$ php artisan make:middleware ActlogMiddleware
```

```php:app/Http/Middleware/ActlogMiddleware.php
<?php

namespace App\Http\Middleware;

use Closure;
use App\Actlog;
use \Route;

class ActlogMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);
        $this->actlog($request, $response -> getStatusCode());
        return $response;
    }

    public function actlog($request, $status)
    {
        $user = $request -> user();
        $data = [
            'user_id' => $user ? $user->id : null,
            'route' => Route::currentRouteName(),
            'url' => $request -> path(),
            'method' => $request -> method(),
            'status' => $status,
            'data' => count($request->toArray()) != 0 ? json_encode($request->toArray()) : null,
            'remote_addr' => $request -> ip(),
            'user_agent' => $request -> userAgent(),
        ];
        Actlog::create($data);
    }
}

```

ルーティング情報を取得するために Route を利用してます
ログインユーザは$request -> user() から取得
要求内容（ 'data' ）はJson形式で保存しときます



#ミドルウェア登録

```php:app/Http/Kernel.php
    protected $middleware = [
        ～～～～
        
★      \App\Http\Middleware\ActlogMiddleware::class,
    ];

    ～～～～
```

$middleware の中に追加すると グローバルとして必ず呼ばれるミドルウェアとして登録されるみたいです
詳しくはこちら
https://readouble.com/laravel/5.6/ja/middleware.html



# ログアウト対応

このミドルウェアの実行タイミングは各処理が「終わった後」になるので「誰」がログアウトしたのか操作者のIDが記録できません
なのでログアウトの実行前にログを記録するようにしておきます

ログアウトを実際に行うのは 「 vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php 」のようなので
ログアウト操作をこっち「 app/Http/Controllers/Auth/LoginController.php 」で上書きしてやります

```php:app/Http/Controllers/Auth/LoginController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;

class LoginController extends Controller
{
　　～～～～～～～～

    /**
     * Log the user out of the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function logout(Request $request)
    {
        $actlog = new \App\Http\Middleware\ActlogMiddleware;
        $actlog -> actlog($request, 999);

        $this->guard()->logout();

        $request->session()->invalidate();

        return $this->loggedOut($request) ?: redirect('/');
    }
}

```


実際にログアウトを実行する「$this->guard()->logout();」前に、actlogを記録してやります
見分けやすいようにステータスを 「999」で記録しときます





#動作確認

##１）ＤＢに接続してテーブル定義とテーブル内容を確認

```sql
mysql -u lara_user -p lara

mysql> desc actlogs;
+-------------+------------------+------+-----+---------+----------------+
| Field       | Type             | Null | Key | Default | Extra          |
+-------------+------------------+------+-----+---------+----------------+
| id          | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| user_id     | int(10) unsigned | YES  | MUL | NULL    |                |
| route       | varchar(255)     | YES  |     | NULL    |                |
| url         | varchar(255)     | YES  |     | NULL    |                |
| method      | varchar(255)     | YES  |     | NULL    |                |
| status      | int(10) unsigned | YES  |     | NULL    |                |
| data        | text             | YES  |     | NULL    |                |
| remote_addr | varchar(255)     | YES  |     | NULL    |                |
| user_agent  | varchar(255)     | YES  |     | NULL    |                |
| created_at  | timestamp        | YES  | MUL | NULL    |                |
| updated_at  | timestamp        | YES  |     | NULL    |                |
+-------------+------------------+------+-----+---------+----------------+

mysql> SELECT  *  FROM   actlogs;
Empty set (0.00 sec)
```


##２）ログイン画面表示

ブラウザでログイン画面を開きます

```sql
mysql> SELECT  *  FROM   actlogs;
+----+---------+------------+-------+--------+--------+------+-------------+--------------------------------------------------------------------------------------------------------------------+---------------------+------------+
| id | user_id | route      | url   | method | status | data | remote_addr | user_agent                                                                                                         | created_at          | updated_at |
+----+---------+------------+-------+--------+--------+------+-------------+--------------------------------------------------------------------------------------------------------------------+---------------------+------------+
|  1 |    NULL | NULL       | /     | GET    |    302 | NULL | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:42:45 | NULL       |
|  2 |    NULL | login.show | login | GET    |    200 | NULL | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:42:45 | NULL       |
+----+---------+------------+-------+--------+--------+------+-------------+--------------------------------------------------------------------------------------------------------------------+---------------------+------------+
2 rows in set (0.01 sec)
```

最初 / にアクセスして 302で遷移
ログイン画面（login.show）を表示したログが記録されてます



##３）ログイン実行

ブラウザでログイン操作

```sql

+----+---------+------------+-------+--------+--------+----------------------------------------------------------------------+-------------+--------------------------------------------------------------------------------------------------------------------+---------------------+------------+
| id | user_id | route      | url   | method | status | data                                                                 | remote_addr | user_agent                                                                                                         | created_at          | updated_at |
+----+---------+------------+-------+--------+--------+----------------------------------------------------------------------+-------------+--------------------------------------------------------------------------------------------------------------------+---------------------+------------+
|  1 |    NULL | NULL       | /     | GET    |    302 | NULL                                                                 | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:42:45 | NULL       |
|  2 |    NULL | login.show | login | GET    |    200 | NULL                                                                 | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:42:45 | NULL       |
|  3 |       1 | login      | login | POST   |    302 | eyJpdiI6IjVmNE5UZ2ExSUw2QWYrTVh6ZUpqRnc9PSIsInZhbHVlZTRjYzAwZGI0NCJ9 | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:44:28 | NULL       |
|  4 |       1 | NULL       | /     | GET    |    200 | NULL                                                                 | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:44:28 | NULL       |
+----+---------+------------+-------+--------+--------+----------------------------------------------------------------------+-------------+--------------------------------------------------------------------------------------------------------------------+---------------------+------------+
4 rows in set (0.00 sec)
```


id:3 の行でログイン後、 
id:4  / に行って Vue の画面を表示してます（ routes/web.php で指定　）

要求内容もちゃんと暗号化されているようです
ユーザIDも記録されてますね



##４）ページ遷移

別記事で作成済みの「社員一覧」ページを開いてみます

```sql
mysql> SELECT  *  FROM   actlogs;
+----+---------+------------------+----------------+--------+--------+----------------------------------------------------------------------+-------------+--------------------------------------------------------------------------------------------------------------------+---------------------+------------+
| id | user_id | route            | url            | method | status | data                                                                 | remote_addr | user_agent                                                                                                         | created_at          | updated_at |
+----+---------+------------------+----------------+--------+--------+----------------------------------------------------------------------+-------------+--------------------------------------------------------------------------------------------------------------------+---------------------+------------+
|  1 |    NULL | NULL             | /              | GET    |    302 | NULL                                                                 | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:42:45 | NULL       |
|  2 |    NULL | login.show       | login          | GET    |    200 | NULL                                                                 | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:42:45 | NULL       |
|  3 |       1 | login            | login          | POST   |    302 | eyJpdiI6IjVmNE5UZ2UxOWRhMzhjNDdiN2I4NTAxNDcwMDZlODVlZTRjYzAwZGI0NCJ9 | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:44:28 | NULL       |
|  4 |       1 | NULL             | /              | GET    |    200 | NULL                                                                 | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:44:28 | NULL       |
|  5 |       1 | admin.user.index | api/admin/user | POST   |    200 | NULL                                                                 | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:47:39 | NULL       |
+----+---------+------------------+----------------+--------+--------+----------------------------------------------------------------------+-------------+--------------------------------------------------------------------------------------------------------------------+---------------------+------------+

```

id:5 でadmin.user.index ページを開いたログが記録されてます



##５）ログアウト

ログアウト操作をしてみます

```sql
|  5 |       1 | admin.user.index | api/admin/user | POST   |    200 | NULL                                                                 | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:47:39 | NULL       |
|  6 |       1 | logout           | logout         | POST   |    999 | NULL                                                                 | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:50:28 | NULL       |
|  7 |    NULL | logout           | logout         | POST   |    302 | NULL                                                                 | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:50:28 | NULL       |
|  8 |    NULL | NULL             | /              | GET    |    401 | NULL                                                                 | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:50:28 | NULL       |
|  9 |    NULL | NULL             | /              | GET    |    302 | NULL                                                                 | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:50:28 | NULL       |
| 10 |    NULL | login.show       | login          | GET    |    200 | NULL                                                                 | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36 | 2019-02-08 07:50:28 | NULL       |
+----+---------+------------------+----------------+--------+--------+----------------------------------------------------------------------+-------------+--------------------------------------------------------------------------------------------------------------------+---------------------+------------+
10 rows in set (0.00 sec)
```

id:6 で実際のログアウト処理の前にログアウトを記録（999）して
id:7 で実際にログアウト
id:8 で 401（未認証）だから / に行って
id:9 で / から 302 でリダイレクトして
id:10 で ログイン画面を表示

ってことになってますね
ちゃんと操作のログが記録されているようです




# Vue 操作ログの一覧画面

ログ記録の確認が取れたので、画面側も作っておきます

```html:resources/js/components/Admin/ActlogComponent.vue
<template>
  <v-flex>
    <v-card xs12 class="m-3 px-3">

      <v-card-title class="title">
        <v-icon class="pr-2">{{ $route.meta.icon }}</v-icon> {{ $route.meta.name }} {{ /* 操作ログ */ }}
        <v-spacer></v-spacer>
        <v-spacer></v-spacer>
        <v-text-field
          v-model="search"
          prepend-icon="search"
          label="Search"
          single-line
          hide-details
          clearable
        ></v-text-field>
      </v-card-title>

      <v-data-table
        :headers="headers"
        :items="tabledata"
        :pagination.sync="pagination"
        :rows-per-page-items='[10,25,50,{"text":"All","value":-1}]'
        :loading="loading"
        :search="search"
        class="elevation-0 p-1"
      >
        <v-progress-linear slot="progress" color="blue" indeterminate></v-progress-linear>

        <template slot="items" slot-scope="props">
          <tr>
            <td class="text-xs-center" xs1>{{ (props.index + 1) + (pagination.page - 1) * pagination.rowsPerPage }}</td>
            <template v-for="n in (headers.length - 1)">
              <td :class="'text-xs-' + headers[n].align" style="white-space: nowrap;" v-text="props.item[headers[n].value]"></td>
            </template>
          </tr>
        </template>
      </v-data-table>

      <v-spacer></v-spacer>

      <v-card-actions>
        <csv-download url="/api/admin/actlog/download" color="primary" @axios-logout="$emit('axios-logout')"></csv-download>
      </v-card-actions>
    </v-card>
  </v-flex>
</template>

<script>
  import csv_download from './CsvDownload.vue'

  export default {
    name: 'ActlogComponent',

    components: {
      'csv-download': csv_download,
    },

    props: {
    },

    data: () => ({
      loading: false,
      search: '',
      pagination: { sortBy: 'created_at', descending: true, },

      tabledata: [],
      headers: [
        { align: 'center', sortable: false, text: 'No',       },
        { align: 'left',   sortable: true,  text: '日時',     value: 'created_at' },
        { align: 'left',   sortable: true,  text: '氏名',     value: 'name' },
        { align: 'left',   sortable: true,  text: '操作',     value: 'action' },
        { align: 'center', sortable: true,  text: '結果',     value: 'status' },
        { align: 'left',   sortable: true,  text: 'データ',   value: 'data' },
        { align: 'left',   sortable: true,  text: 'ＩＰ',     value: 'remote_addr' },
        { align: 'left',   sortable: true,  text: 'ＵＡ',     value: 'user_agent' },
      ],
    }),

    created() {
      if (process.env.MIX_DEBUG) console.log('Actlog Component created.')
      this.initialize()
    },

    methods: {
      initialize() {
        this.getData()
      },

      getData() {
        if (process.env.MIX_DEBUG) console.log('Actlog Component getData')
        this.loading = true
        axios.post('/api/admin/actlog')

        .then( function (response) {
          this.loading = false
          if (process.env.MIX_DEBUG) console.log(response)
          if (response.data.data) {
            this.tabledata = this.setAction(response.data.data)
          }
        }.bind(this))

        .catch(function (error) {
          this.loading = false
          console.log(error)
          if (error.response && [401, 419].includes(error.response.status)) {
            this.$emit('axios-logout')
          }
        }.bind(this))
      },

      setAction(data) {
        if (process.env.MIX_DEBUG) console.log('Actlog Component setAction')
        var wk = ''
        for (let i=0; i<data.length; i++) {
          switch (data[i].route) {

            // Login - Logout
            case 'show.login' : wk = 'ログイン画面'; break;
            case 'login' :      wk = 'ログイン'; break;
            case 'logout' :     wk = 'ログアウト'; break;

            // USER
            case 'admin.user.index' :    wk = '社員一覧'; break;
            case 'admin.user.store' :    wk = '社員追加'; break;
            case 'admin.user.destroy' :  wk = '社員削除'; break;
            case 'admin.user.download' : wk = '社員CSV_DL'; break;
            case 'admin.user.upload' :   wk = '社員CSV_UP'; break;

            // OTHER
            default: wk = data[i].route

          }
          data[i].action = wk
        }
        return data
      },
    },
  }
</script>

```

一覧を取ってきて、route名をわかりやすい日本語にして表示
ってことをしています

操作履歴はデータ量が多そうなので、取得範囲やら「誰」やらの検索条件指定などもそのうち追加しなくちゃですかね


# Vue ナビゲーション追加

ページを追加したのでナビゲーションも追加しときます

まずは定義ファイル

```javascript:resources/js/router/index.js
		import Vue from 'vue'
		import Router from 'vue-router'
		Vue.use(Router)

		//
		import example_component from '../components/ExampleComponent.vue'
		import admin_component   from '../components/AdminComponent.vue'
		import r_link            from '../components/RouterLink.vue'

		//
		Vue.component('example-component', example_component)
		Vue.component('admin-component', admin_component)
		Vue.component('r-link', r_link)

		//
		import home              from '../components/HomeComponent.vue'
		import admin_user        from '../components/Admin/UserComponent.vue'
		import admin_payslip     from '../components/Admin/PayslipComponent.vue'
★		import admin_actlog      from '../components/Admin/ActlogComponent.vue'

		export default new Router({
		  mode: 'history',
		  routes: [
		    { path: '/admin/user',   name: 'admin_user',    component: admin_user,    meta: {name: '社員管理', icon: 'supervisor_account'}},
		    { path: '/home',         name: 'home',          component: home,          meta: {name: 'ホーム',   icon: 'home'}},
		    { path: '/admin/payslip',name: 'admin_payslip', component: admin_payslip, meta: {name: '給与明細', icon: 'fa-file-invoice-dollar'}},
★		    { path: '/admin/actlog', name: 'admin_actlog',  component: admin_actlog,  meta: {name: '操作履歴', icon: 'list'}},
		    { path: '*',             redirect: '/home' },
		  ],
		})
```

そしてリンク埋め込み

```html:resources/js/components/AdminComponent.vue
<template>
  <v-app id="app">
    <v-navigation-drawer v-model="drawer" clipped fixed app >
      <v-list dense>
        <r-link linkname='home'></r-link>
        <r-link linkname='admin_user'></r-link>
        <r-link linkname='admin_payslip'></r-link>
★      <r-link linkname='admin_actlog'></r-link>
      </v-list>
    </v-navigation-drawer>

    <v-toolbar color="primary" dark fixed app clipped-left>
～～～～

```


# Laravel 操作ログのコントローラ

クライアント（Vue）からの一覧表示要求に答えるコントローラを追加しときます
ついでにCSV ダウンロード機能も

```php:app/Http/Controllers/ActlogController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use App\Actlog;
use App\Facades\Csv;

class ActlogController extends Controller
{
    public function index()
    {
        Log::Debug(__CLASS__.':'.__FUNCTION__);

        $actlog = Actlog::where('user_id', '>', '0')
            -> whereNotNull('route')
            -> orderby('created_at', 'desc')
            -> limit(100)
            -> get();
        return ['data' => $actlog];
    }


    public function download(Request $request)
    {
        Log::Debug(__CLASS__.':'.__FUNCTION__, $request->all());

        // 取得項目設定
        $head = ['created_at', 'route', 'status', 'remote_addr', 'user_agent', 'user_id'];

        // 抽出
        $data = Actlog::select( $head )
            -> where('user_id', '>', '0')
            -> whereNotNull('route')
            -> orderby('created_at', 'desc')
            -> get()
            -> toArray();

        // 自動付与の名前をヘッダーに追加
        $head[] = 'name';

        // CSV DOWNLOAD
        return Csv::download($data, $head, 'test.csv');
    }
}
```

ログイン後の操作履歴（ user_id > 0 ）だけを表示するようにしてます
さらに正規の要求（whereNotNull route）の履歴だけを対象に
ついでに直近１００件（limit 100）のみを表示対象としときます


# Laravel ルーティング設定

コントローラを追加したのでルーティングも設定

```php:routes/web.php

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('home');
})->middleware('auth');

// Authentication Routes...
Route::get('/login',   'Auth\LoginController@showLoginForm') -> name('login.show');
Route::post('/login',  'Auth\LoginController@login')         -> name('login');
Route::post('/logout', 'Auth\LoginController@logout')        -> name('logout');

// Admin
Route::GROUP(['middleware' => ['auth', 'can:admin']], function() {
    Route::prefix('/api/admin/') -> name('admin.') -> group(function() {

        // USER
        Route::name('user.') -> group(function() {
            Route::post('user',          'UserController@index')    -> name('index');
            Route::post('user/store',    'UserController@store')    -> name('store');
            Route::post('user/destroy',  'UserController@destroy')  -> name('destroy');
            Route::post('user/download', 'UserController@download') -> name('download');
            Route::post('user/upload',   'UserController@upload')   -> name('upload');
        });

        // CsvPayslip
        Route::name('csvpayslip.') -> group(function() {
            Route::post('csvpayslip/index',   'CsvPayslipController@index')   -> name('index');
            Route::post('csvpayslip/upload',  'CsvPayslipController@upload')  -> name('upload');
            Route::post('csvpayslip/delete',  'CsvPayslipController@delete')  -> name('delete');
            Route::post('csvpayslip/publish', 'CsvPayslipController@publish') -> name('publish');
        });

        // Payslip
        Route::name('payslip.') -> group(function() {
            Route::post('payslip/index',  'PayslipController@index')  -> name('index');
            Route::post('payslip/delete', 'PayslipController@delete') -> name('delete');
            Route::post('payslip/pdf',    'PayslipController@pdf')    -> name('pdf');
        });

★      // Actlog
        Route::name('actlog.') -> group(function() {
            Route::post('actlog',          'ActlogController@index')    -> name('index');
            Route::post('actlog/download', 'ActlogController@download') -> name('download');
        });
    });
});

// Other
Route::get('/{any}', function () {
  return view('home');
})->middleware('auth')->where('any', '.*');

```


# 一覧画面動作確認

![a3.png](https://qiita-image-store.s3.amazonaws.com/0/153259/0c5725bf-0eff-ac16-074d-f9c45944d66e.png)




---



以上
ミドルウェアとして登録したことで処理系には一切手を入れずにアクションログを自動で記録できるようになりました

Laravel ステキ



今回もソースはこちら
https://github.com/u9m31/u9m31/tree/step14