Laravel PHP 消費税率の定義

# 消費税率の定義について

## 概要
システム構築の中で、消費税を扱う場面って結構ありますよね？
皆さんは消費税率を定義する場合どのようにしていますか？
今回は、設定ファイルに消費税率を期間付きで設定する方法をご紹介します。
(何度も同じようなプログラムを書いている私のためにw)

ちなみに、消費税率は頻繁に変わるものではないので、データベースを使って管理するほどではないと考えています。
消費税率変更のタイミングで設定ファイルに追記するだけで、その時期が来たら自動で消費税率が変わることを想定しています。

なお、自分の中では現状ベストな方法と勘違いしている可能性も高いので、他に良い方法をご存知の方はコメントに記載してもらえると嬉しいです。

## 環境
最近はよくLaravelを使うことが多いので、Laravel想定で紹介していきますがロジック自体はどの言語やフレームワークでも利用できると思います。

- PHP 7.4.4
- Laravel Framework 8.22.1

## プログラム作成
作成手順として、1~3まで用意していますが、3は検証用のユニットテストなので、不要な方は1と2のみでいいと思います。

### 1. 設定ファイルの用意
定数を設定するファイルがあれば、そちらに追記でもOKです。
今回は新規に作成の想定で`config/constants.php`を用意します。

```php:config/constants.php
<?php
return [
    'tax' => [
        'rates' => [
            // 日付の古いものから順に定義
            '2021-03-01' => 10,
            // 2021-05-01から税率15%にしたい場合は以下のように定義する
            '2021-05-01' => 15,
        ],
    ],
];
```

税率開始日をkeyにして、税率を定義しています。
コメントにも記載していますが、日付の古いものが上になるように定義してください。

### 2. 税率取得クラスの作成
1. で定義した消費税率を取得するクラスを作成します。
`app/Services/Common/TaxService.php`を作成します。

```php:app/Services/Common/TaxService.php
<?php


namespace App\Services\Common;


use Carbon\Carbon;

class TaxService
{
    /**
     * デフォルトの消費税率
     */
    private const DEFAULT_RATE = 10;

    /**
     * 消費税率を日付指定して取得
     *
     * @param Carbon $targetDate
     * @return int
     */
    public static function getRateByDate(Carbon $targetDate)
    {
        $rates = config('constants.tax.rates');
        $invertedRates = array_reverse($rates);
        foreach ($invertedRates as $date => $rate) {
            $rateDate = new Carbon($date);
            if ($targetDate->gte($rateDate)) {
                return $rate;
            }
        }
        return self::DEFAULT_RATE;
    }
}
```

処理を解説します。
1. 設定ファイルから税率の一覧を取得
2. 逆順(最新の日付順)に並び替え
3. 最新の日付から順ループ
4. \$targetDate(対象日) >= \$rateDate(税率開始日) に該当すれば\$rate(税率)を返却
5. 条件にマッチしない場合はデフォルト値を返却

### 3. ユニットテスト
念のためユニットテストを作成します。

```sh
php artisan make:test Services/Common/TaxServiceTest --unit
```



```php:tests/Unit/Services/Common/TaxServiceTest.php
<?php

namespace Tests\Unit\Services\Common;

use App\Services\Common\TaxService;
use Carbon\Carbon;
use Tests\TestCase;

class TaxServiceTest extends TestCase
{
    public function dataGetRateByDate()
    {
        return [
            'constantsで未定義の日付だった場合はデフォルト値が使われる' => [
                'targetDate' => '2021-02-01',
                'expected' => 10,
            ],
            '2021-03-05の場合は10%が返却される' => [
                'targetDate' => '2021-03-05',
                'expected' => 10,
            ],
            '2021-05-01の場合は15%が返却される' => [
                'targetDate' => '2021-05-01',
                'expected' => 15,
            ],
        ];
    }

    /**
     * @param $targetDate
     * @param $expected
     * @dataProvider dataGetRateByDate
     */
    public function testGetRateByDate($targetDate, $expected)
    {
        $result = TaxService::getRateByDate(new Carbon($targetDate));
        $this->assertEquals($expected, $result);
    }
}
```

ユニットテストは以下のコマンドで実行できます。

```sh
./vendor/bin/phpunit tests/Unit/Services/Common/TaxServiceTest.php 
```

結果

```sh
PHPUnit 9.5.1 by Sebastian Bergmann and contributors.

...                                                                 3 / 3 (100%)

Time: 00:00.562, Memory: 22.00 MB

OK (3 tests, 3 assertions)
```

想定通り動作していますね。

## 最後に
とある案件にて、、、
消費税率が処理している関数内で以下のように定義されていたのを見て震えました。(泣)

```php
$var = 10;
```

やめとけっ！！笑