このエントリーをはてなブックマークに追加

明けましておめでとうございます!永井です!
本年も「カルテットコミュニケーションズ開発部ブログ」を、宜しくお願い致します。

はじめに

弊社では会社の公休日というのをGoogleカレンダーを利用して全社員に公開されています。
2019年1月現在、2020年末ごろまで登録されていますが、年初の時点で今年中の予定が公開されているというのは、一社員としてはとても助かりますよね。
折角、Googleカレンダーというアクセスしやすい形で公開されているので、プログラムで「特定の日が休みかどうか」を判定できると、いろいろ便利かなと思い、実際に検証してみました。

検証

前提

弊社は完全週休2日制で、祝日も休みとなりますので、

  • 土日
  • 祝日
  • 会社が定める公休日

という条件で、その日が休みかどうかを判定することができそうです。
ちなみに会社の公休日は主に年末年始とお盆休みが定められており、年間休日120日以上は確保されています。詳細はこちらをごらんください → 募集要項

土日の判定

これは結構簡単に判定できますよね。
例えばこんな感じ。

<?php

function isWeekend(\DateTimeInterface $target): boolean
{
    return in_array($target->format('w'), ['0', '6'], true);
}

祝日と会社の公休日の判定

祝日の判定は、77web/Japanese.Holidayという素敵なライブラリがあるのですが、今回はGoogleカレンダーをつかうことになりますので、祝日も日本の祝日というGoogleカレンダーで判定することにします。
Googleカレンダーの情報を取得するには、APIを利用したいと思いますが、G Suite配下のプライベートなカレンダーへアクセスする場合には、 サービスアカウントを利用する方がお手軽なので、今回はこちらを利用して進めていきます。

GCPのプロジェクトを作成する

GCPのWebコンソールへアクセスして、プロジェクトを作成してください。

gcp1

GCPのWEBコンソールで、Google Calendar APIを有効にする

API とサービス ページから、Google Calendar API を有効にしてください。

gcp2

サービスアカウントの作成

  • 役割は特に指定しなくてもOK
  • キーのタイプは特にこだわりがなければJSON

というで進めていきます。

service account

会社の公休日カレンダーのサービスアカウントへの共有とIDの確認

カレンダーの詳細ページにいくと、特定のユーザーとの共有で先程追加したサービスアカウントが選べるようになっていますので、閲覧権限(すべての予定の詳細)で共有します。
ちなみに 予定の時間枠のみ表示(詳細は非表示) でも大丈夫かなと試してみましたが、権限が不足しているみたいでした。

また、カレンダーIDも確認してください。

calendar id

日本の祝日カレンダーのIDの確認

日本の祝日 は既に全ユーザーに共有されていますので、何もしなくても利用できます。
IDは ja.japanese#holiday@group.v.calendar.google.com になります。

利用するライブラリ

Googleが用意してくれているgoogleapis/google-api-php-clientを利用します。
composerで簡単にインストールできます。

$ composer require google/apiclient:"^2.0"

特定の日付に予定があるか判定するコード

以下のような関数を用意してみました。

<?php

function hasItems($calendarId, \DateTimeInterface $date)
{
    $credentialsPath = 'service-account-key.json';
    putenv('GOOGLE_APPLICATION_CREDENTIALS='.$credentialsPath);

    $client = new Google_Client();
    $client->useApplicationDefaultCredentials();

    $client->addScope(Google_Service_Calendar::CALENDAR_EVENTS_READONLY);
    $service = new Google_Service_Calendar($client);

    $optParams = [
        'orderBy' => 'startTime',
        'singleEvents' => TRUE,
        'timeMin' => (new \DateTime($date->format('Y-m-d 00:00:00')))->format(DATE_RFC3339),
        'timeMax' => (new \DateTime($date->format('Y-m-d 23:59:59')))->format(DATE_RFC3339),
        'timeZone' => 'Asia/Tokyo',
    ];
    $results = $service->events->listEvents($calendarId, $optParams);

    return \count($results->getItems()) > 0;
}

見ていただければわかると思いますが、

特定の日付に1件以上予定があるかどうか

というロジックになります。

気をつける点はタイムゾーンの設定です。
PHPのタイムゾーン、カレンダーのタイムゾーンなど色々ありますので、それぞれ適切に設定する必要があります。

また、Googleクライアントにはスコープを指定する必要があります。
Google_Service_Calendar::CALENDAR_EVENTS_READONLY または Google_Service_Calendar::CALENDAR_READONLY を指定する事でカレンダーおよびそのイベントにアクセスする事ができます。

最終的なコード

<?php

date_default_timezone_set('Asia/Tokyo');
require_once 'vendor/autoload.php';

$japaneseHolidayCalendarId = 'ja.japanese#holiday@group.v.calendar.google.com';
$quartetDayOffCalendarId = '会社の公休日のカレンダーID';

// 2019年の1月1日から31日を判定して出力する
foreach (range(1, 31) as $day) {
    $target = (new \DateTime())->setDate(2019, 01, $day)->setTime(0,0,0);
    if (
        isWeekend($target) ||
        hasItems($japaneseHolidayCalendarId, $target) ||
        hasItems($quartetDayOffCalendarId, $target)
    ) {
        printf('%s is day off.'.PHP_EOL, $target->format('Y-m-d'));
    } else {
        printf('%s is work day.'.PHP_EOL, $target->format('Y-m-d'));
    }
}

function isWeekend(\DateTimeInterface $date)
{
    return in_array($date->format('w'), [0,6], false);
}

function hasItems($calendarId, \DateTimeInterface $date)
{
    $credentialsPath = 'service-account-key.json';
    putenv('GOOGLE_APPLICATION_CREDENTIALS='.$credentialsPath);

    $client = new Google_Client();
    $client->useApplicationDefaultCredentials();

    $client->addScope(Google_Service_Calendar::CALENDAR_EVENTS_READONLY);
    $service = new Google_Service_Calendar($client);

    $optParams = [
        'orderBy' => 'startTime',
        'singleEvents' => TRUE,
        'timeMin' => (new \DateTime($date->format('Y-m-d 00:00:00')))->format(DATE_RFC3339),
        'timeMax' => (new \DateTime($date->format('Y-m-d 23:59:59')))->format(DATE_RFC3339),
        'timeZone' => 'Asia/Tokyo',
    ];
    $results = $service->events->listEvents($calendarId, $optParams);

    return \count($results->getItems()) > 0;
}

実行した結果は以下の通り。

2019-01-01 is day off.
2019-01-02 is day off.
2019-01-03 is day off.
2019-01-04 is work day.
2019-01-05 is day off.
2019-01-06 is day off.
2019-01-07 is work day.
2019-01-08 is work day.
2019-01-09 is work day.
2019-01-10 is work day.
2019-01-11 is work day.
2019-01-12 is day off.
2019-01-13 is day off.
2019-01-14 is day off.
2019-01-15 is work day.
2019-01-16 is work day.
2019-01-17 is work day.
2019-01-18 is work day.
2019-01-19 is day off.
2019-01-20 is day off.
2019-01-21 is work day.
2019-01-22 is work day.
2019-01-23 is work day.
2019-01-24 is work day.
2019-01-25 is work day.
2019-01-26 is day off.
2019-01-27 is day off.
2019-01-28 is work day.
2019-01-29 is work day.
2019-01-30 is work day.
2019-01-31 is work day.

意図したとおり実行されました!

さいごに

いかがでしたでしょうか。
簡単ではありますが、いろんなところで活躍する場面がありそうですよね。
とても小さい機能ですが、色んな所で使い所がありそうなので、ライブラリ化して社内で公開できると良いかなと思っています!


このエントリーをはてなブックマークに追加

はじめに

Nagoya.phpとは、名古屋で不定期に開催しているPHPの勉強会です。

もともと有志の個人によって運営されていた勉強会でしたが、立ち上げメンバーが次々にカルテットに入社した結果、ここ数年はなんとなくカルテット主催のイベントという感じになっています(笑)

去る2018/12/26(水)、通算13回目(9ヶ月振り)のNagoya.phpを弊社セミナールームにて開催しました :elephant: :sparkles:

本記事では、Nagoya.php #13 の様子を簡単にレポートしたいと思います :muscle:

Nagoya.phpの特徴

Nagoya.phpは、よくあるLT中心の勉強会ではなく、「プログラミング問題を解いてみよう」というコーナーを中心にした内容になっています。

これは、ちょっとしたプログラミング問題を用意して、参加者それぞれで実際にコードを書いて解いてみるというものです。

問題は主に、@Nabetani氏が開催されている横浜へなちょこプログラミング勉強会という勉強会で出題されたものを拝借しています :pray:

乾杯&自己紹介タイム

19:00のオープニングとともに、早速全員で乾杯 :beers: :sparkles:

時間も限られていたので一人30秒制限で全員に簡単な自己紹介をしていただきました。

自己紹介の題材としてPHP歴を発表していただいたのですが、始めたばかりという初心者の方もいれば、PHP歴15年以上といった大ベテランの方もいて、いつもながら非常にバラエティに富んだ参加者層でした :+1:

プログラミング問題タイム

自己紹介が済んだら、早速プログラミング問題タイムです。(制限時間は40分ほど)

今回使わせていただいた問題は、縦横ローテーションというものでした。

過去のNagoya.phpは基本的に週末のお昼で開催していたので、プログラミング問題を解く時間として贅沢に2時間ぐらい使っていたのですが、今回は初めての平日開催ということでかなり時間が限られていたため、比較的簡単そうな問題を選美ました。

が、皆さん仕事終わりで頭が疲れていた様子で、想定していたよりやや苦戦されていました :sweat_smile:

答え合わせ&交流タイム

20:00からは、ピザ :pizza: を片手にプログラミング問題の答え合わせをしつつ、交流タイムです。

まずは参考までに私の解答例を発表させていただきました。

ここから先は、参加者の皆さんの中で時間内に解けた方や、最後までは終わらなかったけど大枠は作れたという方による、「私はこんな感じで解きました」の発表タイム(通称「自慢タイム」)です :sparkles:

こんな感じで、何名かの方に、前に出て自分のコードの設計や実装の工夫点、苦労したポイントなどを簡単に解説していただきました。

なお、参加者の皆さんの解答例がTwitterのハッシュタグ #nagoyaphp で見られたりもしますので、興味のある方は覗いてみてください :smile:

おわりに

今回は実に9ヶ月ぶりの開催になってしまいましたが、2019年は隔月開催を目標に頑張って運営していこうと思っています(次回は2019年2月開催予定)ので、名古屋近郊にお住いのPHPerさんはぜひ気軽に遊びに来ていただけると嬉しいです。

今回、クロージングの前に参加者の皆さんに「今日楽しかった人ー!?」という問いを投げかけてみたのですが、ほぼ全員が手を挙げてくださいました :flushed:

この調子で皆さんに楽しんでいただける場にしつつ、プログラミング問題のレベル設定やスムーズな進行など、運営側として改善できるところは頑張って改善していきますので、引き続きよろしくお願いします! :raised_hands:


このエントリーをはてなブックマークに追加

Symfony Advent Calendar 2018 24日目の記事です。(ちょっと遅くなってしまいました)

はじめに

Symfony2系はサポート終了しました。(2.8のセキュリティサポートはあと1年ぐらいあります)
でも、Symfony2で作られたプロジェクトは生きていて、そんなに簡単にSymfony3, Symfony4の新しい構造のプロジェクトに移行はできません。

そこにSymfony経験のない新入社員が来たら…どうします?

フロントエンド界隈の変化ほどじゃないですが、Symfony2→Symfony3→Symfony4はデフォルトのプロジェクト構造にかなり変化があります。
実際にSymfonyを使って開発する人にとっては書く内容は大して変わってないですし、もともとSymfony2で開発していた人がSymfony3, Symfony4に移行するのはそれほど難しくありません。なんといってもSymfonyは2→4の間に複雑さを除く方向に進化したので。

しかし、Symfony未経験の人がチュートリアルとしてSymfony3やSymfony4に入門して、その後にSymfony2の構造で動いているプロジェクトを触るのは、難しいのではないかと思っています。
不要なものとして後に切り捨てられた複雑性を必要とする世界に、不要な世界から戻らされることになります。

そこで、なんとかして 新人さんにはSymfony3.4を使うけどSymfony2.8と同じ構造でSymfonyに入門してほしい と思い、方法を考えました。

Symfonyのバージョンは3.4だけど構造はSymfony2スタイルのプロジェクトを作る

結論から言うと、方法とは、 Symfony2.8のプロジェクトを作ってSymfony3.4にバージョンアップする です。

Symfony2.8のプロジェクトを作る

symfony/framework-standard-edition を使い、まずは2.8を指定してプロジェクトを作ります。

$ composer create-project symfony/framework-standard-edition:"~2.8" myproject

Symfony2.8→Symfony3.4バージョンアップの準備

myproject にできあがるのは、ただのSymfony2.8の空のプロジェクトです。
これをSymfony3.4にバージョンアップしたいのですが、そのためには準備が必要です。

PHPのバージョン変更

composer.json上で require.phpconfig.platform.php を修正して、依存するPHPのバージョンを変更します。
Symfony3.4のcomposer.json を見る限り5.5.9以上、7.0.8以上なら大丈夫そうです。
※ 私の手元の環境で一番古いのがPHP7.1.11だったので、仮で 7.1 にしてあります。

# composer.json
"require": {
- "php": ">=5.3.9"
+ "php": ">=7.1",
// ...
},
# composer.json
"config": {
  "platform": {
-     "php": "5.6"
+     "php": "7.1",
  }
// ...
},

sensio/distribution-bundleのバージョン変更 4.x→5.x

sensio/distribution-bundle の4.xはSymfony3に対応していないので、5.xに上げます。

$ composer reqiure sensio/distribution-bundle:"~5.0"

Symfony2.8→Symfony3.4バージョンアップ

いよいよSymfonyをバージョンアップします。composer.jsonを編集してからcomposer updateすればOKです。

# composer.json
- "symfony/symfony": "2.8.*",
+ "symfony/symfony": "3.4.*",
$ composer update symfony/symfony

これで完成です!

まとめ

巨大なプロジェクトをどのようにSymfony4に適合させていくか、まだまだ検討中です。
Symfony3.4でしばらく時間を稼ぎたいところです。

サンプルプロジェクト https://github.com/77web/Symfony-update-test