LaravelのCarbonでカレンダーを作ってみる

### 要件定義
カレンダーの要件定義をしてみます。
通常カレンダーは、日曜日から始まり、土曜日で終わります。
月初の週の日曜日から月末の週の土曜日までの日付をレンダリングすればカレンダーができると考えます。

知りたいのは、３つ。
1．　月初の日付が日曜日ではないときの、追加する前月カレンダーの日付。
2．　当月の日付
3．　月末の日付が土曜日ではないときの、追加する翌月カレンダーの日付。
この３つの日付の情報を集めてくるロジックを作れば良い。

デフォルト表示のカレンダーは、当月表示。
ページ送りで、前月、翌月表示をしたい。

### 要件定義をコントローラーに表現していきます。
Carbonをフル活用して書いていきます。
daysInMonthってのが便利。その月に何日、日があるかわかるという。
``` php:CalendarController.php
    use Illuminate\Support\Carbon;

    public function calendar(Request $request){
        //表示したい年（デフォルトは今日の年）
        $year = $request->input('year')??Carbon::today()->format('Y');
        //表示したい月（デフォルトは今日の月）
        $month = $request->input('month')??Carbon::today()->format('m');
        //表示する年月の初日
        $calendarYm = Carbon::Create($year, $month, 01, 00, 00, 00);

        //カレンダーの日付を$calendarDaysの配列に集める
        $calendarDays = [];

        //１．　月初の日付が日曜日ではないときの、追加する前月カレンダーの日付。
        if($calendarYm->dayOfWeek != 0){
            $calendarStartDay = $calendarYm->copy()->subDays($calendarYm->dayOfWeek);
            for ($i = 0; $i < $calendarYm->dayOfWeek; $i++) {
                $calendarDays[] = ['date'=>$calendarStartDay->copy()->addDays($i),'show'=>false,'status'=>false];
            }
        }

        //２． 当月の日付
        for ($i = 0; $i < $calendarYm->daysInMonth; $i++) {
            if($calendarYm->copy()->addDays($i)>=Carbon::now()){
                $show = true;
                $status = true;
            } else {
                $show = true;
                $status = false;
            }
            $calendarDays[] = ['date'=>$calendarYm->copy()->addDays($i),'show'=>$show,'status'=>$status];
        }

        //３.　月末の日付が土曜日ではないときの、追加する翌月カレンダーの日付。
        if($calendarYm->copy()->endOfMonth()->dayOfWeek != 6){
            for ($i = $calendarYm->copy()->endOfMonth()->dayOfWeek+1; $i < 7; $i++) {
                $calendarDays[] = ['date'=>$calendarYm->copy()->addDays($i),'show'=>false,'status'=>false];
            }
        }


        return view('front.calendar', [
            'calendarDays' => $calendarDays//集めた日付
            'previousMonth' => $calendarYm->copy()->subMonth(),//前月
            'nextMonth' => $calendarYm->copy()->addMonth(),//翌月
            'thisMonth' => $calendarYm,//当月
        ]);
    }
```

### カレンダーのように並ぶようにCSSを作る
display:gridで7列で折り返しするようにすると簡単です。
他にも方法はあるとは思います。

``` css:calendar.css
        .calendar-grid{
            display: grid;
            grid-template-columns: repeat(7, 1fr);
            width:350px;
            margin: 0 auto;
        }
        .week-block{
            width: 50px;
            height: 28px;
            text-align: center;
            font-weight: bold;
        }
        .day-block{
            width: 50px;
            height: 50px;
        }
        .button-day{
            width: 45px;
            height: 45px;
            padding: 0;
            text-align: center;
            background: white;
            border: 1px solid red;
        }
        .d-flex {
            display: flex;
        }
        .justify-content-between {
            justify-content: space-between;
        }
        .text-center{
            text-align: center;
        }
        .my-3 {
            margin-bottom: 1.5rem;
        }
```
### Viewを作ります
上から順に、翌月、前月のリンクと、当月の年月表示。
カレンダー曜日、日付とレンタリングしていきます。
``` php:calendar.blade.php
<div class="d-flex justify-content-between">
    <a href="{{url()->current().'?year='.$previousMonth->format('Y').'&month='.$previousMonth->format('m')}}">前月</a>
    <div class="text-center">
        <strong>{{$thisMonth->format('Y年n月')}}</strong>
    </div>
    <a href="{{url()->current().'?year='.$nextMonth->format('Y').'&month='.$nextMonth->format('m')}}">翌月</a>
</div>
<div class="my-3">
    <div class="calendar-grid">
        @foreach(['(日)', '(月)', '(火)', '(水)', '(木)', '(金)', '(土)'] as $weekName)
        <div class="week-block">
            {{$weekName}}
        </div>
        @endforeach
        @foreach($calendarDays as $calendarDay)
            @if($calendarDay['show'])
                <div class="day-block">
                    <button class="button-day" type="button" data-date="{{$calendarDay['date']->format('Y-m-d')}}">{{$calendarDay['date']->format('j')}}</button>
                </div>
            @else
                <div class="day-block"></div>
            @endif
        @endforeach
    </div>
</div>
```
### 完成のスクショ
私の場合、こんな感じになりました。
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309843/7a9e4336-feff-7fbb-462b-20306817cf5d.png)

作ってみましたが、いかがでしょうか。
用途に合わせて、改造して使ってもいいかもしれません。
参考までに。