LoginSignup
11
15

More than 5 years have passed since last update.

【Laravel】Carbonを使って日時をDBに格納する打刻システム

Last updated at Posted at 2019-01-31

Vue.jsとの共同開発にて、出退勤の時間を打刻するアプリを作成中です。
DBに時間を格納する方法にはCarbonを使えばいいと聞き、不明点が多くLaravelのみで動作検証したかったため試してみました。

DBに時間を格納し、扱うのが初めてでデータ型をどうしようと思ったのですが、dateTime型で成功しました。

以下を参考にしました。
Carbon:Introduction
PHPで日付時刻処理を書くならCarbonを使うべき

環境

  • Laravel 5.7.19
  • MySQL 8.0.12

Carbonを使った日時データの格納

LaravelにはCarbonが用意されてます。ない場合は以下の方法でComposer使ってインストール。

composer require nesbot/carbon

DB設計について

スクリーンショット 2019-01-17 22.01.15.png

本筋から外れてしまいますがログインについてはメールアドレスを使ってではなく、ログインIDでのログインとします。

Usersテーブル

database/migration/create_users_table.php
<?php

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

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('loginid')->unique()->comment('ログインID');
            $table->string('password');
            $table->tinyInteger('role')->unsigned()->default(10)->comment('権限0:system  5:admin  10:user');
            $table->rememberToken();
            $table->timestamps();
        });
    }

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

上記に伴い、app/Http/Controllers/Auth/LoginController.phpを変更します。

参照:app/Http/Controllers/Auth/LoginController.php

Timestampsテーブル

次に打刻用データベースです。

出勤の打刻と同時にカラムを作成、退勤時間はNullに設定しました。もちろん、Usersテーブルのidと外部キー制約でまとめます。

database/migration/create_timestamps_table.php
<?php

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

class CreateTimestampsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('timestamps', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned()->index();
            $table->dateTime('punchIn');
            $table->dateTime('punchOut')->nullable();
            $table->timestamps();

            $table->foreign('user_id')->references('id')->on('users');
        });
    }

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

1対多リレーション

UserモデルとTimestampモデルを用意し、一対多の構造用意。

app/User
<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'loginid', 'password'
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token', 'role',
    ];

    /**
     * Timestamp関連付け
     * 1対多
     */
    public function timestamp()
    {
        return $this->hasMany(Timestamp::class);
    }
}
app/Timestamp.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Timestamp extends Model
{
    protected $fillable = ['user_id', 'punchIn', 'punchOut'];

    /**
     * ユーザー関連付け
     * 1対多
     */
    public function user()
    {
        $this->belongsTo(User::class);
    }
}

ルート

ルーティングは以下のようにコーディングしました。
AuthServiceProviderを使った認証でページのアクセスをadminとuserで制限しています。

参照:app/Providers/AuthServiceProvider.php

routes/web.php

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


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


Route::group(['middleware' => ['auth', 'can:admin']], function() {
    Route::get('admin/user/index', 'UserController@index')->name('admin/user/index');
    Route::get('admin/user/show/{id}', 'UserController@show')->name('admin/user/show');
});

Route::group(['middleware' => 'auth'], function() {
    Route::post('/punchin', 'TimestampsController@punchIn')->name('timestamp/punchin');
    Route::post('/punchout', 'TimestampsController@punchOut')->name('timestamp/punchout');
});

コントローラー

以下のような挙動を考えて設計しました。

  • 出勤ボタンを連続して押せない(同日中)
  • 出勤打刻がない状態で退勤打刻を押せない

(2019/02/07 TimestampsController一部修正)

app/Http.TimestampsController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Auth;
use Carbon\Carbon;
use App\User;
use App\Timestamp;

class TimestampsController extends Controller
{
    public function punchIn()
    {
        $user = Auth::user();

        /**
         * 打刻は1日一回までにしたい 
         * DB
         */
        $oldTimestamp = Timestamp::where('user_id', $user->id)->latest()->first();
        if ($oldTimestamp) {
            $oldTimestampPunchIn = new Carbon($oldTimestamp->punchIn);
            $oldTimestampDay = $oldTimestampPunchIn->startOfDay();
        }

        $newTimestampDay = Carbon::today();

        /**
         * 日付を比較する。同日付の出勤打刻で、かつ直前のTimestampの退勤打刻がされていない場合エラーを吐き出す。
         */
        if (($oldTimestampDay == $newTimestampDay) && (empty($oldTimestamp->punchOut))){
            return redirect()->back()->with('error', 'すでに出勤打刻がされています');
        }

        $timestamp = Timestamp::create([
            'user_id' => $user->id,
            'punchIn' => Carbon::now(),
        ]);

        return redirect()->back()->with('my_status', '出勤打刻が完了しました');
    }

    public function punchOut()
    {
        $user = Auth::user();
        $timestamp = Timestamp::where('user_id', $user->id)->latest()->first();

        if( !empty($timestamp->punchOut)) {
            return redirect()->back()->with('error', '既に退勤の打刻がされているか、出勤打刻されていません');
        }
        $timestamp->update([
            'punchOut' => Carbon::now()
        ]);

        return redirect()->back()->with('my_status', '退勤打刻が完了しました');
    }
}

View周りはGitHubをご覧ください。

11
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
15