AI(Copilot)でコードを作成してカレンダーを表示できましたが、今度は今月の月間カレンダーだけなく前月と翌月のボタンを追加してクリックしたら表示をリアクティブに変更できるようにします。

Livewireに変換

前回で作成したカレンダー生成の関数を用いることになりますが、今回もCopilotを使って開発します。

さあ、働いてもらいましょう!

> Laravel Livewireを用いて、先月と翌月を表示できるようにしてください

この結果としてLivewireのコンポーネント、それが使用するブレード、そのコンポーネントを含むブレード、とルートの定義が出力されるのですが、残念なことにこれがLivewireの最新のバージョン3ではなく、バージョン2の出力なのです。

おかしいと思い、問いかけます。

> どのバージョンのLivewireを知っていますか?
私はLivewireの最新バージョンであるLivewire 3を知っています。Livewire 3は、リアクティブなUIを構築するための最新の機能と改善を提供しています。

そうなら、

> そうなら、Livewire3を使って書き直してください

とプッシュ。しかし残念なことに出力される結果は先ほどと同じとなりました。

どうやらLivewireのバージョン3の知識はないみたいです。

ということで、Copilotで出力されたコードと私のLivewireの学習、つまり人間とのコラボにより以下のコード生成となりました。

コンポーネントの作成

最新のバージョンのLaravelでプロジェクトを作成して、以下で最新(バージョン3ですよ)のLivewireのパッケージをインストールします。

$ composer require livewire/livewire

以下でコンポーネントを作成します。

$ php artisan make:livewire Calendar

作成されるファイルは2つあります。

new file:   app/Livewire/Calendar.php
new file:   resources/views/livewire/calendar.blade.php

まず、Calendar.phpを編集します。ここではCopilotで出力されたコードを入れますが、注意するのは、namespaceがバージョン3のLivewireでは、App\Http\LivewireからApp\Livewireに変わっています。

namespace App\Livewire;

use Livewire\Component;
use Carbon\Carbon;

class Calendar extends Component
{
    public $year;
    public $month;
    public $calendar = [];

    public function mount($year = null, $month = null)
    {
        $this->year = $year ?? Carbon::now()->year;
        $this->month = $month ?? Carbon::now()->month;
        $this->generateCalendar();
    }

    public function generateCalendar()
    {
        $startOfMonth = Carbon::createFromDate($this->year, $this->month, 1);
        $endOfMonth = $startOfMonth->copy()->endOfMonth();
        $startDayOfWeek = $startOfMonth->dayOfWeek;
        $totalDays = $endOfMonth->day;

        $this->calendar = [];
        $week = [];

        for ($i = 0; $i < $startDayOfWeek; $i++) {
            $week[] = null;
        }

        for ($day = 1; $day <= $totalDays; $day++) {<strong>
            $week[] = $day;

            if (count($week) == 7) {
                $this->calendar[] = $week;
                $week = [];
            }
        }

        if (count($week) > 0) {
            while (count($week) < 7) {
                $week[] = null;
            }
            $this->calendar[] = $week;
        }
    }

    public function previousMonth()
    {
        $this->month--;
        if ($this->month < 1) {
            $this->month = 12;
            $this->year--;
        }
        $this->generateCalendar();
    }

    public function nextMonth()
    {
        $this->month++;
        if ($this->month > 12) {
            $this->month = 1;
            $this->year++;
        }
        $this->generateCalendar();
    }

    public function render()
    {
        return view('livewire.calendar');
    }
}

コンポーネントに含まれる、generateCalendar()は前回と似ていますが、このクラスのプロパティの$month, $yearにより返す値が変わるようになっています。また、前月を表示するpreviousMonth()と次月を表示するnextMonth()のメソッドが追加されています。

今度はブレードですが、これもCopilotの出力ををもとに英語から日本語に私が編集しました。wire:clickにおいて先ほど追加関数名が指定されています。これらはLivewireのためのコードで、これによりカレンダーで表示する月を変えることができます。

<div>
    <table>
        <caption>
            <button wire:click="previousMonth">前</button>
            <strong>{{ $year }}年{{ $month }}月</strong>
            <button wire:click="nextMonth">次</button>
        </caption>
        <thead>
            <tr>
                <th>日</th>
                <th>月</th>
                <th>火</th>
                <th>水</th>
                <th>木</th>
                <th>金</th>
                <th>土</th>
            </tr>
        </thead>
        <tbody>
            @foreach ($calendar as $week)
                <tr>
                    @foreach ($week as $day)
                        @if (is_null($day))
                            <td></td>
                        @else
                            <td>{{ $day }}</td>
                        @endif
                    @endforeach
                </tr>
            @endforeach
        </tbody>
    </table>
</div>

ルートの定義は以下となりますが、これはコンポーネントを全画面として表示する設定です。


use App\Livewire\Calendar;
use Illuminate\Support\Facades\Route;

Route::get('/calendar', Calendar::class);

そして、この全画面表示のためにレイアウトのブレードが必要です。

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>{{ $title ?? 'Page Title' }}</title>
    </head>
    <body>
        {{ $slot }}
    </body>
</html>

これで完了です。

う~ん。ちょっと醜いですね。Copilotに少し恰好良くしてもらいましょう。

> コンポーネントのブレードをTailwind cssを利用してベターなルックにしてください

以下のようにブレードは変わります。

<div class="container mx-auto p-4">
    <h1 class="text-2xl font-bold mb-4 text-center">{{ $year }}年{{ $month }}月</h1>
    <div class="flex justify-between mb-4">
        <button wire:click="previousMonth" class="bg-blue-500 text-white px-4 py-2 rounded">前</button>
        <button wire:click="nextMonth" class="bg-blue-500 text-white px-4 py-2 rounded">次</button>
    </div>
    <table class="min-w-full bg-white border border-gray-200">
        <thead>
            <tr>
                <th class="py-2 px-4 border border-gray-200 bg-gray-100">日</th>
                <th class="py-2 px-4 border border-gray-200 bg-gray-100">月</th>
                <th class="py-2 px-4 border border-gray-200 bg-gray-100">火</th>
                <th class="py-2 px-4 border border-gray-200 bg-gray-100">水</th>
                <th class="py-2 px-4 border border-gray-200 bg-gray-100">木</th>
                <th class="py-2 px-4 border border-gray-200 bg-gray-100">金</th>
                <th class="py-2 px-4 border border-gray-200 bg-gray-100">土</th>
            </tr>
        </thead>
        <tbody>
            @foreach ($calendar as $week)
                <tr>
                    @foreach ($week as $day)
                        @if (is_null($day))
                            <td class="py-2 px-4 border border-gray-200"></td>
                        @else
                            <td class="py-2 px-4 border border-gray-200">
                                {{ $day }}
                            </td>
                        @endif
                    @endforeach
                </tr>
            @endforeach
        </tbody>
    </table>
</div>

レイアウトの方は、以下となります。

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>{{ $title ?? 'Page Title' }}</title>
        <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">

    </head>
    <body class="bg-gray-100">
        <div class="container mx-auto p-4">
            {{ $slot }}
        </div>
    </body>
</html>

以下となりました。レスポンシブ対応ともなっています。

以上、Copilotと私のコラボの作品です。CopilotのLivewireの知識が最新でないのは残念ですが、知ったふりをしては欲しくないですね。

メルマガ購読の申し込みはこちらから。

By khino