
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、ここ数回Laravel
の話題をお届けしましたので、この流れでもう1つLaravel
の記事をお届けしたいと思います。
今回はおそらく訪問ユーザー向けと言うよりは、運営者側がほしがるような内容かもしれませんが、その内容はというと、
サイトの集計データをチャートで表示する
というものです。
サイトの集計データといえば、Google Analytics
が有名ですがあれはブラウザでのアクセスに限られたデータです。
そのため、例えば「注文を受けた回数の集計データ」などより複雑なデータ集計をしようと思うと、自前で実装するしかありません。
そこで!
今回は、ログインした日時を集計して「ログインするのが多い時間帯はいつ?」が分かるコンテンツをLaravel
で作ってみたいと思います。
ぜひ皆さんのお役に立てると嬉しいです
(最後に今回開発したソースコード一式をダウンロードすることができます)
開発環境: Laravel 6.x
目次 [非表示]
やりたいこと
今回は「各時間ごとのログイン回数を月単位で」集計することにします。
つまり、以下のようなことができるようになれば成功です。
- 「先月は14時台が一番ログインが多かったのか
」
- 「今月は19時台のログインが少ないぞ
」
- 「意外と深夜1時でもログイン回数は少なくないなー
」
では、実際にやっていきましょう!
前提として
今回もログイン機能がLaravel
にインストールされていることが前提となっています。もしインストールがまだの方は以下を参考してください。
- Laravel 6.x 以上 ・・・ Laravel6.0でログイン機能を使う方法
- それ未満 ・・・ 【Laravel5.6】インストール直後にやること3点
モデルとマイグレーションをつくる
まずは、以下のコマンドでログイン集計に必要なモデル&マイグレーションを作成します。
php artisan make:model Login -m
コマンドを実行したらdatabase/migrations/****_**_**_******_create_logins_table.php
というファイルが作成されますので、それらを開いて中身を次のようにします。
public function up()
{
Schema::create('logins', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id')->comment('ユーザーID');
$table->unsignedInteger('year')->comment('ログイン・年');
$table->unsignedInteger('month')->comment('ログイン・月');
$table->unsignedInteger('day')->comment('ログイン・日');
$table->unsignedInteger('hour')->comment('ログイン・時');
$table->unsignedInteger('minute')->comment('ログイン・分');
$table->unsignedInteger('second')->comment('ログイン・秒');
$table->foreign('user_id')->references('id')->on('users');
$table->index(['year', 'month']);
});
}
なお、集計が高速になるように通常のcreated_at
とupdated_at
は使わず各「年・月・日・時・分・秒」をデータで保持するようにしています。また、同じ理由からインデックスをyear + month
でつけています。
そのため、Login
モデルがタイムスタンプを使わないようにapp/Login.php
に以下のコードを追加しておいてください。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Login extends Model
{
public $timestamps = false; //created_at,updated_at は使わない
}
では、マイグレーションを実行してテーブルを作成しましょう。
php artisan migrate
すると、テーブルはこのようになります。
ログイン日時を保存するようにする
続いて、ユーザーがログインをしたら自動的に先ほど作成したlogins
テーブルにデータが書き込まれるようにしておきましょう。
app/Http/Controllers/Auth/LoginController.php
の中に以下のコードを追加してください。
// 省略
class LoginController extends Controller
{
// 省略
protected function authenticated(Request $request, $user)
{
$dt = now();
$login = new \App\Login();
$login->user_id = $user->id;
$login->year = $dt->year;
$login->month = $dt->month;
$login->day = $dt->day;
$login->hour = $dt->hour;
$login->minute = $dt->minute;
$login->second = $dt->second;
$login->save();
}
}
これで、ログインをするたびに以下のようなデータがlogins
テーブルに追加されることになります。
Seederでテストデータを用意する
このまま集計部分をつくってもいいのですが、何回もログインをしてデータを追加するのは時間がかかりますので、Seeder
でテストデータをつくることにします。
以下のコマンドを実行してください。
php artisan make:seed LoginsTableSeeder
すると、database/seeds/LoginsTableSeeder.php
というファイルが作成されるのでrun()
内を以下のように変更してください。
<?php
use Illuminate\Database\Seeder;
class LoginsTableSeeder extends Seeder
{
public function run()
{
$today = today();
for($i = 0 ; $i < 500 ; $i++) {
$random_days = rand(1, 100);
$random_hours = rand(1, 23);
$dt = $today->copy()
->subDays(rand(1, 100)) // -1〜100日前
->addHours(rand(1, 23)) // +1〜23時間
->addMinutes(rand(1, 59)) // +1〜23分
->addSeconds(rand(1, 59)); // +1〜23秒
$login = new \App\Login();
$login->user_id = 1; // テストなのでユーザーIDは "1" で固定
$login->year = $dt->year;
$login->month = $dt->month;
$login->day = $dt->day;
$login->hour = $dt->hour;
$login->minute = $dt->minute;
$login->second = $dt->second;
$login->save();
}
}
}
ファイルを変更したら、database/seeds/DatabaseSeeder.php
に以下のように登録します。
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
// 省略
$this->call(LoginsTableSeeder::class);
}
}
では、artisan
コマンドを使ってSeeder
を実行してみましょう。
php artisan db:seed
うまくいくとlogins
テーブルはこのようになります。
では、このデータを使って実際に集計をしていきましょう。
集計した棒グラフをつくる
では、ここからが実際に集計データを表示していく作業になります。
クリック1つで「年・月」を変更できるようにしたいので、データはAjax
を通して取得することにします。
ルートをつくる
routes/web.php
に以下を追加してください。
Route::get('login_count', 'LoginCountController@index');
Route::get('ajax/login_count', 'LoginCountController@ajax_index');
上がブラウザで、下がAjax
でアクセスするページになります。
コントローラーをつくる
続いてコントローラーです。
以下のコマンドを実行してください。
php artisan make:controller LoginCountController
app/Http/Controllers/LoginCountController.php
というファイルが作成されるので、中身を次のようにします。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class LoginCountController extends Controller
{
public function index() {
return view('login_count');
}
public function ajax_index(Request $request) {
$year = date('Y');
$month = date('m');
if($request->filled('year', 'month')) {
$year = $request->year;
$month = $request->month;
}
$logins = \App\Login::select('hour', \DB::raw('COUNT(id) AS login_count'))
->where('year', $year)
->where('month', $month)
->groupBy('hour')
->pluck('login_count', 'hour');
$counts = [];
for($i = 0 ; $i < 24 ; $i++) {
$counts[$i] = $logins->get($i, 0); // 存在しない時間は "0" 回
}
return $counts;
}
}
この中では、「年・月」で絞り込んだデータをグループ化して時間(hour)ごとにデータ数をカウントしています。
ただし、例えば12時台にデータが存在していない場合は、集計データが存在しないことになってしまいますので、存在しない時間は0
にするコードを追加してデータを修正しています。
ビューをつくる
では、実際ブラウザで目にする棒グラフの部分をつくっていきましょう。resources/views/login_count.blade.php
というファイルを作成して以下のようにしてください。
<html>
<body>
<div id="app">
<div>
集計:
<span v-text="year"></span>年
<span v-text="month"></span>月
<button type="button" @click="moveMonth(-1)">前へ</button>
<button type="button" @click="moveMonth(1)">次へ</button>
</div>
<canvas id="chart"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.2/dist/Chart.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
year: '',
month: '',
chart: null
},
methods: {
drawChart() {
const url = '/ajax/login_count?year='+ this.year +'&month='+ this.month;
axios.get(url)
.then(response => {
const data = response.data;
const ctx = document.getElementById('chart').getContext('2d');
let labels = [];
for(let i = 0 ; i < 24 ; i++) {
labels.push(i +'時');
}
if(this.chart) {
this.chart.destroy();
}
this.chart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'ログイン回数',
data: data
}]
},
options: {
scales: {
yAxes: [{
ticks: {
stepSize: 1
}
}]
}
}
});
});
},
moveMonth(number) {
this.month += number;
if(this.month === 0) {
this.year--;
this.month = 12;
} else if(this.month === 13) {
this.year++;
this.month = 1;
}
this.drawChart();
}
},
mounted() {
const date = new Date();
this.year = date.getFullYear();
this.month = date.getMonth() + 1;
this.drawChart();
}
});
</script>
</body>
</html>
少しコードが長いですが、重要なのはdrawChart()
の部分だけです。
この中では、まず先ほど作成した集計プログラムにAjax
でアクセスしてデータをとってきます。
そして、そのデータをChart.jsにセットをしています。
なお、以下の部分でChart.jsのインスタンスをクリアしていることに注目してください。こうしないと「年・月」を変更した場合に前のデータが残ってしまうことになるからです。(描画は上手く行きますが、ポップアップがおかしくなります)
if(this.chart) {
this.chart.destroy();
}
お疲れ様でした
テストしてみる
では、実際にテストしてみましょう!
まずは今月の統計チャートです。
そして、先月。
はい!
うまくいきました
ダウンロードする
今回実際に開発したソースコード一式を以下からダウンロードすることができます。
※ ただし、マイグレーションなどはご自身で実行していただく必要があります。
【Laravel】ログイン日時の統計をとって棒グラフにしてみるおわりに
ということで、今回はログインした日時を集計して棒グラフをつくってみました。
ちなみに、今回使ったChart.js
は棒グラフだけでなく、円グラフやレイダーチャートなど様々なチャート作成ができますので、もし興味がある方はこちらのサンプルページをご覧になってください。
なお、今回のケースではデータ量が多くなってくるとレスポンスが極端に遅くなることが想定されます。そのため、もしそうならないように対処したい場合は、例えばlogin_counts
テーブルを作ってcron
などで定期的にデータを集計するようにし、Ajax
の取得はこの集計テーブルから取得するようにするといいでしょう。
ぜひ皆さんも集計データのコンテンツを作ってみてくださいね。
ではでは〜!