PHPで各種カレンダーへの予定追加機能を実装する

# はじめに
イベント予約時にカレンダーへの予定追加を可能とする機能を実装する機会があり学習したのでまとめる。
カレンダーへの予定追加の方法としては以下２通り。
<br>

<b>
①icsファイルのダウンロード<br>
②パラメーターを含んだリンクの設置（Googleカレンダー、Yahoo!カレンダー、Outlook）
</b>

※Outlookのみパラメーターを利用すると一部不備があるため、実際にはicsファイルのダウンロードとした。

尚、icsファイルの書き方自体は他にもたくさん記事があるため詳細については割愛し、PHPでの実装時の流れや詰まった点、パラメーターの種類について主に記載していく。

# icsファイルとは
カレンダーにインポートすると記載された内容でカレンダーに登録される。
Googleカレンダー、Outlookなどにインポート可能。（Yahoo!カレンダーは不可）
iCalendarはicsファイルを開くと自動的にカレンダーへの追加画面となる。
特徴は以下。

- MIMEタイプは「text/calendar」
- 拡張子は「.ics」
- 中身はプレーンテキストで、文字コードはutf-8が標準で用いられる。

ファイルの中身は以下のように特殊な形式となっている。

```
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//A company//X product//NONSGML v1.0//JP
BEGIN:VEVENT
DTSTART;TZID=Asia/Tokyo:20220302T173000
DTEND;TZID=Asia/Tokyo:20220302T201000
SUMMARY:タイトル
DESCRIPTION:詳細
END:VEVENT
END:VCALENDAR
```
上から簡単に見ていく。

#### BEGIN
これはicsファイルを作成する上でのお作法のようなものだ。
４行目のBEGIN:VEVENTも同じで、ここからカレンダーですよ〜、ここからイベントですよ〜と示すためのもので、下のENDとそれぞれ対になっている。

#### VERSION
現状では特に気にせず2.0を指定すれば良い。

#### PRODID
作成者の情報を記載する。
決まったフォーマットはなく、「-//会社名//個人名(プロダクト名)//国名」がよく用いられる形式のようだ。

#### DTSTART;TZID / DTEND;TZID
予定の開始・終了日時。
yyyymmddThhmmss形式となる。
ISO8601で定められたフォーマットで、間の"T"はセパレータというものらしくとにかく必須。

#### SUMMARY
予定のタイトル。

#### DESCRIPTION
予定の詳細。
改行も可能。※後述


上に挙げたのはあくまで設定項目の一例で、他にもアラームの設定など可能となる。
ここでは他の項目について言及しないので、気になる場合は別の記事を探していただくことをオススメする。

# PHPでicsファイルのダウンロード機能を実装する
まずはicsファイルをダウンロードするためのリンクを設置する。
```php:views/calendar.blade.php
<a href="/addCalendar/{{ $id }}">
```

続いて、Controller側でicsファイルを生成していく。
ざっと中身を見ていただくと以下のようになる。
```php:Controller/Calendar.php
public function addCalendar(Request $request)
{
	$headers = array(
		'Content-Type' => 'text/calendar',
        'Content-Disposition' => 'attachment; filename=calendar.ics',
    );

                             ## 中略 ##

    $description = "カレンダーへの予定追加機能について".¥¥r¥¥n."icsファイルとパラメーター形式"//後述

    $fp = fopen('php://temp', 'w+b');
	$text = 
	   	"BEGIN:VCALENDAR\r\n".
	    "VERSION:2.0\r\n".
    	"PRODID:-//".config('custom.paramater.company_name')."/".config('custom.paramater.site_name')."//NONSGML v1.0//JP\r\n".
		"BEGIN:VEVENT\r\n".
	   	"DTSTART;TZID=Asia/Tokyo:".$seminarStart->format('Ymd').'T'.$seminarStart->format('His')."\r\n".
    	"DTEND;TZID=Asia/Tokyo:".$seminaEnd->format('Ymd').'T'.$seminarEnd->format('His')."\r\n".
		"SUMMARY:".$title."\r\n".
	   	"DESCRIPTION:".$description."\r\n".
	    "END:VEVENT\r\n".
	    "END:VCALENDAR\r\n";
	fwrite($fp, $text);
	rewind($fp);
	$ics = stream_get_contents($fp);
	fclose($fp);
	return Response::make($ics, 200, $headers);
```
なんとなく概要を掴んでいただいた上で、手順を分けて記載していく。（ざっくり分けると２つの手順のみ）

#### ①headerを指定する
初めにダウンロードするファイルのheaderを指定する。
前述の通り、`Content-Type`は`text/calendar`とする。

`Content-Type`や`Content-Disposition`、またこれから記載するファイルそのものの生成方法（icsファイルに限らず）については<a href="https://qiita.com/charita/items/5d92f17c5873a7c82b1a">こちら</a>に記載しているので、そちらを確認していただけると幸いです。

#### ②icsファイルの中身を作成する
変数`$text`にicsファイルの中身をテキストとして格納し、それをtempファイルに書き込んでいく。
そしてそれをダウンロードできる形式にしてブラウザに返してやる、というのが本当にざっくりした流れだ。
タイトルや日時指定をし、予定概要にただ一行の文を記載すれば良いのであればこれで完了になる。

ただし予定概要の部分（DESCRIPTION）を改行したいのであれば、少し注意が必要だ。
実際この部分で手間取り時間を食ってしまった。

やり方としては、変数の中で改行されるような形にしてそれを当て込むというやり方が良い。
仮に以下のようにして改行をしようとするとうまくいかない。
```php
$text = 
	         ## 中略 ##
	   	"DESCRIPTION:カレンダーへの予定追加機能について"."\r\n"."icsファイルとパラメーター形式"."\r\n".
	    "END:VEVENT\r\n".
	         ## 中略 ##
```
おそらくこの場合、「カレンダーへの予定追加機能について」で改行され次の行の頭が「icsファイルとパラメーター形式」となるが、icsファイル内のキー（DESCRIPTIONなど）として認識されてしまいエラーとなるのだと思う。
そのため、`$description`という変数の中で改行をしている。

そしてもう一つ気をつけるのが、変数内の改行部分を同じように`¥r¥n`とすると、結局変数を当て込んだときに改行処理が走ってしまい、上記と同じことになってしまう。
```php
$description = "カレンダーへの予定追加機能について".¥r¥n."icsファイルとパラメーター形式" //変数$textの中で改行処理が走ってしまう
```
そこで、改行コード前にバックスラッシュをつけることで、`$text`の中で改行されないようにする必要がある。
よって以下のようにすると正しく改行されて予定に反映される。
```php
$description = "カレンダーへの予定追加機能について".¥¥r¥¥n."icsファイルとパラメーター形式" //変数$textの中で改行処理が走らず、icsファイル展開時に改行処理が走る
```

# パラメーターを使用した各種カレンダー追加
icsファイルのダウンロードとは異なり、リンクを踏むとそのリンクに該当するカレンダーの予定追加画面に自動で遷移する方式だ。
以下リンクから一度試していただけるとイメージがつくと思う。

##### Googleカレンダー
http://www.google.com/calendar/event?action=TEMPLATE&text=タイトル&dates=20220302T180000/20220302T190000&details=予定概要&add=test-user@gmail.com

##### Yahoo!カレンダー
https://calendar.yahoo.co.jp/?V=60&TITLE=タイトル&ST=20220302T180000&ET=20220302T190000&DESC=予定概要&URL=https://www.google.com/

##### Outlook
https://outlook.live.com/owa/?path=/calendar/action/compose&subject=タイトル&startdt=2022-03-02T18:00:00&enddt=2022-03-02T19:00:00&body=予定概要

※前置きしたようにOutloolのみ一部不備？があり、一度目にリンクを踏んだ際のみ文字化けしてしまう。（二度目以降は正しく表示）

このように、カレンダーごとにパラメーターが指定されており、そこに値を入れることでカレンダーへの予定追加が可能となる。
カレンダーごとのパラメーターは以下。
基本となるURLに続けてパラメーターをカスタマイズして作成可能。
#### ▼Googleカレンダー
基本URL
http://www.google.com/calendar/event?action=TEMPLATE&
|パラメーター|説明|
|---------|-----|
|text     | タイトル  |
|dates    |開始日時/終了日時  |
|details  |予定の詳細  |
|location |場所  |
|add      |招待するメールアドレス|

その他のパラメーターが気になる方はこちら
https://interactiondesignfoundation.github.io/add-event-to-calendar-docs/services/google.html

上記サイト最下部にgeneratorなるものがあり、これを使うとリンクを自動生成してくれるらしい、、、便利。
http://kalinka.tardate.com/
http://output.jsbin.com/xujuluw

#### ▼Yahoo!カレンダー
基本URL
https://calendar.yahoo.co.jp/?V=60&

|パラメーター  |説明   |
|-------|----------|
|TITLE  |タイトル    |
|ST     |開始日時   |
|ET     |終了日時   |
|DUR    |予定の時間 |
|IN_LOC |場所      |
|DESC   |予定の詳細 |
|ENC    |文字コード  |
|URL    |URL

その他のパラメーターが気になる方はこちら
https://interactiondesignfoundation.github.io/add-event-to-calendar-docs/services/yahoo.html

#### ▼Outlook
基本URL
https://outlook.live.com/owa/?path=/calendar/action/compose&

※Outlookのみ日時のフォーマットが異なる

|パラメーター|説明                     |
|---------|------------------------|
|subject  |タイトル                   |
|body     |予定の詳細                |
|location |場所                     |
|startdt  |開始日時(yyyy-mm-ddTH:i:s)|
|enddt    |終了日時(yyyy-mm-ddTH:i:s)|
|to       |出席者のメールアドレス        |
|allday   |終日（値はtrue / false）    |



以上のようにして、各種カレンダーへの予定追加機能を実装する。
今までにない実装経験だったのでシンプルにおもしろかった。