【PHP】月初～月末の日付配列と休祝日【Laravel10】

# 日付ごとの配列を作りたい
　日付ごとにデータを格納してViewに渡し、View側で日付ごとの表でデータを表示する、というパターンがよくある。そのために日付ごとの配列を生成して返すメソッドの配列パターンまとめ。Carbonを使うのでLaravelで使うことを想定。

```php:よく書き忘れる
use Carbon\Carbon;
```

```php:変数の初期化
$year = Carbon::now()->year;
$month = Carbon::now()->month;
```

# 1次元配列

```php:1次元配列の場合
public function generateDaysOfMonthArray($year, $month) {
    $start_day = Carbon::createFromDate($year, $month, 1)->startOfMonth();
    $end_day = $start_day->copy()->endOfMonth();
    $dateArray = [];
    for ($current_day = $start_day; $current_day <= $end_day; $current_day->addDay()) {
        $dateArray[$current_day->format('Y-m-d')] = $current_day;
    }
    return $dateArray;
}
```

```php
$targetDate = '2023-11-15'; // 例として、取り出したい日付を指定

// $targetDateに対応する$current_dayを取り出す
$specificCurrentDay = $dateArray[$targetDate];
```

# 2次元配列

```php:2次元配列の場合
public function generateDaysOfMonthArray($year, $month) {
    $start_day = Carbon::createFromDate($year, $month, 1)->startOfMonth();
    $end_day = $start_day->copy()->endOfMonth();
    $dateArray = [];
    for ($current_day = $start_day; $current_day <= $end_day; $current_day->addDay()) {
        $dateArray[$current_day->format('Y-m-d')] = [
            // $current_dayは文字列になるのでCarbonを入れとくと便利
            'carbonDate' => $current_day,
//          'other_key' => $value,
        ];
    }
    return $dateArray;
}
```

```php
$targetDate = '2023-11-15'; // 例として、取り出したい日付を指定

// $targetDateに対応する$current_dayを取り出す
$specificCurrentDay = $dateArray[$targetDate]['carbonDate'];
```

# 3次元配列

```php:3次元配列の場合
public function generateDaysOfMonthArray($year, $month) {
    $start_day = Carbon::createFromDate($year, $month, 1)->startOfMonth();
    $end_day = $start_day->copy()->endOfMonth();
    $dateArray = [];
    for ($current_day = $start_day; $current_day <= $end_day; $current_day->addDay()) {
        $dateArray[$current_day->format('Y-m-d')] = [
            'carbon_date_time' => [
                'carbonDate' => $current_day,
//              'key1' => $value1,
//              'key2' => $value2,
            ],
//            'other_key' => [
//                'key3' => $value3,
//                'key4' => $value4,
            ],
        ];
    }
    return $dateArray;
}
```

```php
$targetDate = '2023-11-15'; // 例として、取り出したい日付を指定

// $targetDateに対応する$current_dayを取り出す
$specificCurrentDay = $dateArray[$targetDate]['carbon_date_time']['carbonDate'];
```

# Controllerで使う
　たとえば2次元配列をコントローラーで使う。

 ```php
 public function showDataPage(Request $request) {
 // $yearと$monthは定義するなり$requestで受け取るなりする。

     $dateArray = generateDaysOfMonthArray($year, $month);

     foreach($dateArray as $date => $dataForDate) {
         $dataArray['key1'] = [
             // なんか定義する
         ];
     }

     return view('data_page', compact('dataArray'));
 }
 ```

# Viewで使う

```php
<table>
    <thead>
        <tr>
            <th> タイトル </th>
        </tr>
    </thead>
    <tbody>
        @foreach ($dateArray as $date => $dataForDate)
            <tr>
                <td>
                    <span>{{ $dataForDate['key1']['key2'] }}</span>
                </td>
            </tr>
        @endforeach
    </tbody>
</table>

```

　とこんな感じで各データをループ処理して表示する。


 # 休祝日を定義する

 　日付の配列生成時に休祝日の`true:false`や、判定できる曜日を入れておくと便利。テキスト色の指定は`tailwind css`の記述構文です。状況によってメソッドはstaticで定義するのもアリ。`$this`使ってるので書き直さないとstaticにはできません。

　`春分の日`、`秋分の日`、`天皇誕生日`は日付が変わる可能性があるので、別に仕組みを実装する必要がある。なんかのAPI使うか、毎年定義をコードで追記するかDBテーブルを作って日付を登録するかして読み込む。

```php
class Holiday extends Model {  // DBに登録するならモデルとして実装
    use HasFactory;
    protected $fillable = ['date', 'column1', 'column2', 'column3'];
    // その他リレーションとか必要な記述

    // 'YYYY-MM-DD'の日付を渡すと休祝日を判定するメソッド
    // 休祝日ならtrue、それ以外はfalseを返す
    public function isHoliday($carbonDate)
    {
        $carbonDate = Carbon::parse($carbonDate);

        // DBに休日が登録されている場合
        $holidayRecords = Holiday::where('date', $carbonDate->format('Y-m-d'))->exists();
        if ($holidayRecords) {
            return true;
        }
        
        // 日曜日
        if ($carbonDate->dayOfWeek == Carbon::SUNDAY) {
            return true;
        }

        // 年によって変わる休日の設定
        $yearUniequeHolidays = [
            // Carbon::parse('2023-1-9')->toDateString(),      // 2023_成人の日(1月第2月曜日)
            Carbon::parse('2023-2-23')->toDateString(),     // 2023_天皇誕生日(2018までは12-23)
            Carbon::parse('2023-3-21')->toDateString(),     // 2023_春分の日
            // Carbon::parse('2023-7-17')->toDateString(),     // 2023_海の日(7月第3月曜日)
            // Carbon::parse('2023-9-18')->toDateString(),     // 2023_敬老の日(9月第3月曜日)
            Carbon::parse('2023-9-23')->toDateString(),     // 2023_秋分の日
            // Carbon::parse('2023-10-9')->toDateString(),     // 2023_スポーツの日(10月第2月曜日)
        ];

        // 年によって変わる休日（設定済み）の判定
        if (in_array($carbonDate->toDateString(), $yearUniequeHolidays)) {
            return true;
        }

        // 年によって変わるが定義が存在する休日の判定
        if ($this->isSpecialMonday($carbonDate)) {
            return true;
        }

        // 年によって変わらない休日の設定
        $yearlyHolidays = [
            Carbon::create($carbonDate->year, 1, 1)->format('m-d'),    // 元日
            Carbon::create($carbonDate->year, 1, 2)->format('m-d'),    // 元日
            Carbon::create($carbonDate->year, 1, 3)->format('m-d'),    // 元日
            Carbon::create($carbonDate->year, 2, 11)->format('m-d'),   // 建国記念の日
            Carbon::create($carbonDate->year, 4, 29)->format('m-d'),   // 昭和の日(1989年から2006年までは"みどりの日")
            Carbon::create($carbonDate->year, 5, 3)->format('m-d'),    // 憲法記念日
            Carbon::create($carbonDate->year, 5, 4)->format('m-d'),    // みどりの日(2006年までは"国民の休日")
            Carbon::create($carbonDate->year, 5, 5)->format('m-d'),    // こどもの日
            Carbon::create($carbonDate->year, 8, 11)->format('m-d'),   // 山の日 (2016年に新設、特に山の意味はないらしい)
            Carbon::create($carbonDate->year, 11, 3)->format('m-d'),   // 文化の日
            Carbon::create($carbonDate->year, 11, 23)->format('m-d'),  // 勤労感謝の日
            Carbon::create($carbonDate->year, 12, 30)->format('m-d'),  // 年末休暇
            Carbon::create($carbonDate->year, 12, 31)->format('m-d'),  // 年末休暇
        ];

        // 年によって変わらない休日の判定
        if (in_array($carbonDate->format('m-d'), $yearlyHolidays)) {
            return true;
        }

        // 振替休日の判定
        if ($carbonDate->dayOfWeek == Carbon::MONDAY) {
            $prevDay = $carbonDate->copy()->addDay(-1);
            if (in_array($prevDay->format('m-d'), $yearlyHolidays)) {
                return true;
            }
        }

        // 上記の条件に当てはまらない場合は休日ではないと判定
        return false;
    }

    private function isSpecialMonday($carbonDate)
    {
        // 指定された日が1月の第2月曜日、7月の第3月曜日、9月の第3月曜日、または10月の第2月曜日に該当するかどうかを判定
        return (
            // 1月の第2月曜日
            ($carbonDate->month == 1 && $carbonDate->dayOfWeek == Carbon::MONDAY && $carbonDate->weekOfMonth == 2) ||
            // 7月の第3月曜日
            ($carbonDate->month == 7 && $carbonDate->dayOfWeek == Carbon::MONDAY && $carbonDate->weekOfMonth == 3) ||
            // 9月の第3月曜日
            ($carbonDate->month == 9 && $carbonDate->dayOfWeek == Carbon::MONDAY && $carbonDate->weekOfMonth == 3) ||
            // 10月の第2月曜日
            ($carbonDate->month == 10 && $carbonDate->dayOfWeek == Carbon::MONDAY && $carbonDate->weekOfMonth == 2)
        ) ?? false;
    }
}
```

　配列を生成する例。

 ```php
    private function generateDateArray($year, $month)
    {
        $startDate = Carbon::createFromDate($year, $month, 1)->startOfMonth();
        $endDate = $startDate->copy()->endOfMonth();
        $dateArray = [];
        $daysOfWeekInJapanese = ['日', '月', '火', '水', '木', '金', '土'];
        $myHoliday = new Holiday();    // isHolidayをstaticメソッドにする場合この行は不要

        // 日付をキーとした多次元配列を構築
        for ($currentDate = $startDate; $currentDate->lte($endDate); $currentDate->addDay()) {
            $dayOfWeek = $currentDate->dayOfWeek;                            // 曜日を数値で取得
            $holidayFlag = $myHoliday->isHoliday($currentDate);              // 休祝日フラグ
            $textColor = '';                                                 // viewで使うテキストの文字色

            if ($dayOfWeek == 0 || $holidayFlag == true) {
                $textColor = 'text-red-400';    // 休日は赤で表示
            } elseif ($dayOfWeek == 6) {
                $textColor = 'text-cyan-400';   // 土曜日は青で表示
            }

            $dateArray[$currentDate->format('Y-m-d')] = [
                'carbonDate' => Carbon::parse($currentDate),
                'stringDate' => Carbon::parse($currentDate)->format('Y-m-d'),
                'formattedDate' => Carbon::parse($currentDate)->format('Y-m-d (') . $daysOfWeekInJapanese[$dayOfWeek] . ')',
                'holiday_flag' => $holidayFlag,
                'dayOfWeek' => $dayOfWeek,
                'textColor' => $textColor,
            ];
        }

        return $dateArray;
    }
```

　Viewでのテキスト色指定の例。

```php
<span class="{{ $dataForDate['textColour'] }}">テキスト<span>
```