今日もヤバさをI/O中。

(物理的)大型エンジニアのブログです。基本的に何かが足りません。

LaravelのwhereRawで安易にOR句を使ってはいけない

この記事は、Qiita の Laravel Advent Calendar 2020 20日目の記事です。

qiita.com

LaravelのEloquentやQueryBuilderで使えるwhereRawですが、安易にOR句を使うと落とし穴にはまることに気がつきました。

今回はその落とし穴と回避策について解説します。

laravel.com

TL;DR

  • LaravelのwhereRawでOR句を使うと、SoftDeleteが考慮されなくなったり意図しない条件が実行される可能性がある
  • whereRawでは括弧を自動的には追加しないため、whereRaw("A OR B and C") と記述した時に発行されるクエリは WHERE A or (B and C)となってしまう
  • OR句を使うときはクロージャを使う。どうしてもwhereRawを使いたいときはwhereRaw("(A OR B)")と記述する

なぜwhereRawでOR句を使ってはいけないのか?

例えばwhereRaw('A OR B')というクエリを実行すると、WHERE A OR BというSQL文が発行されます。括弧を記述していないため、WHERE (A OR B)というSQL文は発行されません。

whereRawは記述されたSQL文をそのまま実行しています。なので当然といえば当然ですが、括弧を自動的に追加するなんてことはしません。

この仕様が思わぬ副作用を生むため、安易にOR句を使ってはいけないと結論づけています。

whereRawでOR句を使った時に何が起こるか?

今回使用した動作環境のPHP/Laravelのバージョンは以下の通りです。

PHP8.0 Laravel 8.19

SoftDeleteが考慮されない可能性がある

deleted_atが存在するusersテーブルで、LaravelのSoftDeleteトレイトを使った時の場合を例にします。

クロージャを使ったケースと、whereRawでOR句を使ったケースで、実際に発行されるクエリは以下のようになります。

User::where(function ($query) {
    $query->where('is_admin', 1)
    ->orWhere('is_admin', 1);
})->toSql();
>>> "select * from `users` where (`is_admin` = ? or `is_admin` = ?) and `users`.`deleted_at` is null"

User::whereRaw('is_admin = 1 or is_client = 1')->toSql();
>>> "select * from `users` where is_admin = 1 or is_client = 1 and `users`.`deleted_at` is null"

同じクエリになるかと思いきや、違うクエリになります。クロージャの方では括弧が自動的に挿入されますが、whereRawでは挿入されません。

結果としてwhereRawでOR句を使ったケースで発行されるクエリでは

"select * from `users` where is_admin = 1 or (is_client = 1 and `users`.`deleted_at` is null)"

と同等のSQL文が実行されてしまい、is_admin = 1 and deleted_at is not nullのレコードを抽出してしまうような条件式になってしまいます。

意図しない条件が実行される可能性がある

where句でis_admin = 1 or is_client = 1 and is_corporation という条件を実行します。

orWhereを使ったケースと、whereRawでOR句を使ったケースで、発行されるクエリは以下のようになります。

User::where(function ($query) {
    $query->where('is_admin', 1)
    ->orWhere('is_admin', 1);
})->where('is_corporation', 1)->toSql();
>>>  "select * from `users` where (`is_admin` = 1 or `is_admin` = 1) and `is_corporation` = 1"

User::whereRaw('is_admin = 1 or is_client = 1 and is_corporation = 1')->toSql();
>>> "select * from `users` where is_admin = 1 or is_client = 1 and is_corporation = 1"

こちらも、whereRawでOR句を使ったケースで発行されるでは括弧が挿入されないため、

select * from `users` where is_admin = 1 or (is_client = 1 and is_corporation = 1)

と同等のクエリが実行されてしまい、is_admin = 1 and is_corporation = 0の条件式レコードを抽出してしまうような条件式になってしまいます。

回避策

回避策は幾つがありますが、ここでは2つの例をご紹介します。

クロージャを使う

丁寧に書くのであれば、クロージャを使うと良いです。

User::where(function ($query) {
    $query->where('is_admin', 1)
    ->orWhere('is_client', 1);
})->where('is_corporation', 1)->toSql();

>>>  "select * from `users` where (`is_admin` = 1 or `is_admin` = 1) and `is_corporation` = 1"

クロージャ内でwhereRawを使っても良いです。ただし、発行されるクエリがクロージャ内でorWhereを使った場合と厳密には違うので気をつけましょう。

User::where(function ($query) {
    $query->whereRaw('is_admin = 1 or is_client = 1');
})->where('is_corporation', 1)->toSql();

>>> "select * from `users` where (is_admin = 1 or is_client = 1) and `is_corporation` = 1"

whereRaw("(A OR B)")と記述する

どうしてもwhereRaw単品で使いたい場合は、括弧を明示してあげると上手くいきます。

User::whereRaw('(is_admin = 1 or is_client = 1) and is_corporation = 1')->toSql();

>>> "select * from `users` where (is_admin = 1 or is_client = 1) and is_corporation = 1"

おまけ:一応プルリクを出したが…

whereRaw("A OR B")と記述していても WHERE (A OR B)と自動的に括弧を追加する、という旨の改修をしてプルリクエストを出してみましたが、残念ながらマージされませんでした。

github.com

実際既存機能にどのくらい影響があるのか誰も(コミッターでも)想像つかないので、既存サービスへ影響を出さないことを優先したと考えれば納得です。

ならばドキュメントに注意書きを追加するのはどうだろう!と思ってプルリクエストを出してみましたが、こちらも残念ながらマージされませんでした。

github.com

できればこの記事を見なくても解決できる形にしたい気持ちがありましたが、残念です…

最後に

考えてみればそりゃそうなるよねって感じの仕様だとは思いますが、コードレビューだけではなかなか気づけない落とし穴だと思います。

また、OR句以外でもwhereRawで落とし穴がありそう…そんな予感がします。見つけた方は是非記事にして共有して頂けると嬉しいです!

Javascript 製のE2Eテスト(puppeteer)に纏わるちょっとした工夫

この記事は、ソフトウェアテストの小ネタ Qiita Advent Calendar 2019 の24日目の記事です。 puppeteer を触っているときにちょっとした工夫を2つほど思いついたので、コード例と共に記事にしようと思います。

テスト部分を try ~ catch ~ finally で括ると、テストに落ちた時や終了した時の処理をカスタマイズできる

puppeteer は node 製のライブラリなので、javascript で使われている try ~ catch ~ finally句が使用できます。

テスト部分を try ~ catch ~ finally 句で括ると、catch 句ではテストに落ちた時の処理を、finally 句では終了した時の処理をカスタマイズできます。

コード例

import Puppeteer from 'puppeteer';
import test from 'ava';

test.serial('ログイン_成功', async (t) => {
    const browser = await Puppeteer.launch({
        "ignoreHTTPSErrors" : true,
    });
    const page = await browser.newPage();

      try {
        await page.goto("https://hoge.com");
        await page.type(".mail-address", "hoge@huga.jp");
        await page.waitFor(500);
        await page.type('.password', "test");
        await page.waitFor(500);
        await page.click('.login');
        await page.waitFor('.logout');
        const result = await page.evaluate(() => {
            return document.querySelector('.logout').innerHTML;
        });
        t.true(result.includes('ログアウト'));
    } catch (e) {
        // テストに失敗したら、スクリーンショットを撮って保存する
        const todayDateTime = new Date();
        const formatTodayDateTime = String(todayDateTime.getFullYear())
            + String(todayDateTime.getMonth() + 1)
            + String(todayDateTime.getDate())
            + String(todayDateTime.getHours())
            + String(todayDateTime.getMinutes());
        + String(todayDateTime.getSeconds());

        await page.screenshot({
            path: 'screenshot/' + formatTodayDateTime + '_error_login.png',
            fullPage: true
        });

        t.fail('ログインテストに失敗しました。スクリーンショットをご確認ください。\nエラーメッセージ内容:' + e.message);
    } finally {
        // テストに成功しようが失敗しようがブラウザは閉じる
        await browser.close();
    } 
});

私は上記のように、「失敗した時はスクリーンショットを取って保存する」「テスト終了した時は必ずブラウザを閉じる」処理を追加しています。

環境によって違う値は、定数ファイルに保存しておくと管理が楽

ログインIDやパスワードといった環境ごとに値が違う場合は、定数ファイルにまとめて保存すると管理が楽になります。 仮にテストデータのリセットが行われたとしても、1カ所直すだけですむためです。

私は configと呼ばれる、環境変数(env)によって定数の値を変えるライブラリを使っています。

コード例

test.js

import Puppeteer from 'puppeteer';
import test from 'ava';

test.serial('ログイン_成功', async (t) => {
    const browser = await Puppeteer.launch({
        "ignoreHTTPSErrors" : true,
    });
    const page = await browser.newPage();

    await page.goto("https://hoge.com");
    await page.type(".mail-address", config.get("front.user.mail")
);
    await page.waitFor(500);
    await page.type('.password', config.get("front.user.password"));
    await page.waitFor(500);
    await page.click('.login');
    await page.waitFor('.logout');
    const result = await page.evaluate(() => {
            return document.querySelector('.logout').innerHTML;
    });

    t.true(result.includes('ログアウト'));
    await browser.close();
});

testing.json

{
  "front": {
    "user": {
      "mail": "hoge@huga.jp",
      "password": "Test2"
    },
}

staging.json

{
  "front": {
    "user": {
      "mail": "piyo@foo.jp",
      "password": "Test2"
    },
}

package.json

{
    "name": "e2e-test",
    "version": "1.0.0",
    "description": "",
    "scripts": {
        "test_testing": "NODE_ENV=testing ava --verbose",
        "test_staging": "NODE_ENV=staging ava --verbose"
    },
    "devDependencies": {
        "ava": "*",
        "config": "*",
        "puppeteer": "*"
    }
}

テスト実行時に NODE_ENV という環境変数を使って、使用する定数ファイルを指定しています。

改めて Laravel/framework のコントリビュートガイドを読んでみた

Qiita Laravel #2 Advent Calendar 2019 23日目の記事です。

過去に Laravel/framework にてコントリビュートをしたことがありましたが、
割と雰囲気でコントリビュートしていた感があったので改めてコントリビュートガイドを読み込んでみようと思い立った次第でございます。

ドキュメントを読み込む

Laravel/framework にはコントリビュートガイドが存在するので、コントリビュートガイドを読みつつ個人的に補足すべきところを補足してみます。

laravel.com

貢献ガイド 6.x Laravel

Bug Reports

不具合に関する報告は各 Repository に Pull Request を作成することで対応します。

ただ、Pull Request と言ってもバグ票代わりになるものなので、他のコントリビューターでも簡単にバグが再現・修正できるよう明確にしなければなりません。
「不具合の修正には、バグの報告者とコントリビューターたちで力を合わせて解決する」というスタンスのようですね。

当たり前といえば当たり前ですが、新規機能作成や仕様変更はバグレポートに含まれません。その場合は Core Development Discussion を読みましょう。

Pull Request をざっと眺めてみると、いきなり解決案となるコミットを提示していることが多いです。
「不具合を見つけたけど、どう解決するかまではわからんな…?」と思ったら、以下のように Issue を立ててみると良いかもしれません。

github.com

あるいは Laravel Discord server の #internals チャンネル で話してみるのも良いでしょう!

Support Questions

Laravel の使い方に関する質問や相談は、 Laracasts ForumsLaravel.io Forums などで受け付けるようです。

Issue をざっと眺めると、Laravel の使い方に関する質問や相談のパターンで issue が結構作成されています。(そのあと以下のように他のコントリビューターに Laracasts Forums や Laravel.io Forums などへ促された後、closed されるケースが多いです)

github.com

Core Development Discussion

新規機能作成や仕様変更の場合は、 Pull Request の作成より前に議論を行う流れがあります。

具体的なコード例がある場合は、laravel/ideasで issue を立て、議論しましょう。

github.com

実装できるのであれば、以下のように Pull Request も同時に作ると良いです。
議論が行われてからマージされると思いますので、他の人が Pull Request を作成する前に作成しましょう。(テストコードの実装も忘れずに!)

github.com

具体的なコードがないうちは、Laravel Discord server の #internals チャンネルで話してみましょう!
Taylor さんに話しかける良いチャンスでもあるので、話しかけてみるのもアリですね!

Which Branch?

基本的にコントリビュートで使うブランチは主に3種類です。

  • 現行の LTS のブランチ:サポートポリシーを確認しましょう
  • 最新の安定ブランチ:数値表記されたブランチの中で一番最新のブランチ(default ブランチに設定されていることが多いです)
  • 現在開発中のブランチ:master ブランチ

不具合修正は、基本的に最新の安定ブランチ or LTSブランチ
後方互換性があるマイナー機能は、最新の安定ブランチ
それ以外は、現在開発中のブランチ
ということになります!

これもわからなければ、 Laravel Discord server の #internals チャンネルで話してみましょう!

Compiled Assets

コンパイル済みのファイルをメンテナ(taylor さんなど)がレビューしない以上、
Pull request 上でコンパイル済みの大量コードを含むファイルが表示されるのは無意味です。
少しでもレビューしやすくするため、コンパイル済みのファイルを含めないようにしましょう。

Security Vulnerabilities

セキュリティ脆弱性を見つけたときは脆弱性を突かれる前になるべく早く修正しなければならない他、
対応が完了するまで大っぴらにするわけにはいきません。 そのため、セキュリティ脆弱性に関しては taylor さんにメールを送信するという方法を用いているわけですね。

Coding Style

Laravelは PSR-2 コーディング規約と PSR-4 オートローディング規約に準拠しています。
ただし、StyleCI によって自動で修正されるようなので、あまり気にしなくても良いかと思います。

Code of Conduct

こちらで特に補足することはありません。コントリビュートする際のマインドとして、大切なことなのでしっかり読みましょう。

最後に

私も含めコントリビュートガイドを見る前に、いきなりコントリビュートしてしまう人は多々います。 コントリビュートをする前に、まずはドキュメントと当記事を落ち着いて読むことをオススメします!(自戒の意味も込めて😇)

PHPerKaigi 2019 に当日スタッフとスピーカーとして参加してきたので振り返ったった

PHPerKaigi 2019?

phperkaigi.jp

2019年3月29日(金)〜 3月31日(日)はPHPerたちのお祭りです! この一文につきると思います!

他の技術系イベントと比べても参加者を楽しませるコンテンツが多数用意してあり、(いい意味で)異色の技術系イベントです!

発表内容共有

今回は LT スピーカーとしても参加したので、振り返りの前にスライドの共有をします!

speakerdeck.com

speakerdeck.com

後日発表時の動画もアップロードされるはずなので、アップロードされ次第こちらの記事にURLを貼り付けます!

振り返り

それじゃあまた振り返りをします!振り返り方法は、例のごとく YKPWT で行います

ただ、前回の振り返りだと詳細に書きすぎたので、少し簡潔にします!

やったこと

  • 当日スタッフ
    • 会場設営
    • ノベルティを用意する作業
    • トラックA の進行
  • LTスピーカー
    • 本編 LT
    • 懇親会 LT
  • 参加者
    • セッション聴講
    • 懇親会参加
      • PHPreParty, PHPer茶会, 懇親会, PHPostParty

Keep

当日スタッフ

  • 3日間という初めての長期間スタッフだったが、無事終えられた
  • 指示された作業よりも、自分で考えて(工夫して)作業した内容の方が多かった
  • 一部の当日スタッフらと後日ボルタリングへ行く約束をした

LTスピーカー

  • 2,300人という大人数の舞台で初めて登壇した
    • というか、応募時点では社外で LT をしたことがなかったが無事終えた
  • 時間内にきっちり喋り終えることができた
  • ちょっとしたハプニングがあったし、緊張もしていたが、素振りで練習した内容通りのことを話せた

参加者

  • やはり無料でセッションや懇親会に出れるのは最高(当日スタッフ・スピーカー特典)
  • 全日程の懇親会を通して交流を深められたので、通常のイベントの3倍(3日分)の繋がりが増えた

Problem

当日スタッフ

  • 明らかに最終日に近づくにつれて、疲れが隠せなくなっていた(小さなミスがいくつかあった)

LTスピーカー

  • 懇親会 LT で割とすべった
  • pdfpc と呼ばれる、pdf のプレゼンツールを使ったが会場の設備との相性が悪くて使えなかった
  • 事前に予告していた内容と少し違う内容になった

参加者

  • 当日スタッフの仕事やスライド作成・修正に時間をかけすぎて、一部積極的に参加できなかったコンテンツがあった(PHPerチャレンジとか)

わかったこと

  • 「最悪コアスタッフに相談すれば大丈夫」という気持ちがあると、色々いい感じに対応することができる
    • そういう安心感を抱かせてくれるコアスタッフの皆さんマジすごい
  • LT に問わず発表の素振り、何回もするの大事
  • LT、すべることもある。そういうこともある
  • 当日スタッフとはいえ、3日分も参加すると達成感とスタッフ同士の繋がり度合いが半端ない

次やること

  • 自分がイベント開催者になるときは、自分がうまく立ち回るのではなく他のイベントスタッフが動きやすいように立ち回る
  • 発表の素振り、何回もやっていき💪
    • 特にスライドが pdf だったら、スライドの内容を覚えこむまでやる(そうでなかったら、大人しく pdf ではなく keynote とかにする)
  • 次は PHP カンファレンス北海道のCfPにレギュラートーク枠で応募する
    • そのためにネタを考える・作る
  • 日頃からダジャレを言って冷たい目線を向けられることに慣れ、すべっても動じない心を身につける
  • 来年の PHPerKaigi も参加する

最後に

関係者、スタッフの皆さま、参加者の皆さまお疲れ様でした&ありがとうございました!

これだけ参加者を楽しませる取り組みがすごいカンファレンスは、そうそう無いと思います!!(現に知人とも「イベント運営面でお手本にしたいけど、正直再現性がわからんし、すぐ真似できなそう」って話をしました)

また次の PHPerKaigi でお会いしましょう!

avgメソッド Collectionメソッドソースコードリーディング100本ノック2本目

メソッド概要

コレクション 5.8 Laravel

avgメソッドは、指定したキーの平均値を返します。

<?php

$average = collect([['foo' => 10], ['foo' => 10], ['foo' => 20], ['foo' => 40]])->avg('foo');

// 20

$average = collect([1, 1, 2, 4])->avg();

// 2

average メソッドも同じ処理です。

ソースコードリーディング

avg メソッド

では早速、ソースコードリーディングをやって行きましょう。

<?php

/**
 * Get the average value of a given key.
 *
 * @param  callable|string|null  $callback
 * @return mixed
 */
public function avg($callback = null)
{
    $callback = $this->valueRetriever($callback);

    $items = $this->map(function ($value) use ($callback) {
        return $callback($value);
    })->filter(function ($value) {
        return ! is_null($value);
    });

    if ($count = $items->count()) {
        return $items->sum() / $count;
    }
}

後で解説しますが valueRetriever メソッドは、$callback をコールバックとして読み取れる値に整形するメソッドです。

$callback の戻り値を、 map メソッドを使って Collection へ整形します。

戻り値が null だった時は、 Collection に含めません。 null を平均の対象へと含めないようにする意図のようですね。

最後に Collection 内の値の平均値を返す…といった処理になります。

参考:

github.com

valueRetriever メソッド

<?php

/**
 * Get a value retrieving callback.
 *
 * @param  string  $value
 * @return callable
 */
protected function valueRetriever($value)
{
    if ($this->useAsCallable($value)) {
        return $value;
    }

    return function ($item) use ($value) {
        return data_get($item, $value);
    };
}

$callback を、コールバックとして読み取れる値に整形するメソッドです。

最初にそもそもコールバックとして読み取れる値だったら、そのまま値を返します。

コールバックとして読み取れない値だったら、コールバックとして読み取れる無名関数を返します。例えば以下のようなコードがあったとします。

$callback = $this->valueRetriever($value) 
$callback($item)

この場合、 $callback($item) は以下のようになります。

$callback($item) =
    function ($item) use ($value) {
        return data_get($item, $value);
    }

data_get は、配列やドット区切りのオブジェクトから要素(配列 or データ型の値)を取り出すヘルパメソッドになります(data_get ヘルパメソッドについては、別の記事にて解説します)。

$item が配列だったり Collection だった時のために、このような処理になっているようです。

参考:

github.com

github.com

useAsCallable メソッド

関数として呼び出し可能 かつ 文字列ではないかを返します。

呼び出し可能な関数名ではなく、関数そのものか判別したい時に使うようです。

<?php

/**
 * Determine if the given value is callable, but not a string.
 *
 * @param  mixed  $value
 * @return bool
 */
protected function useAsCallable($value)
{
    return ! is_string($value) && is_callable($value);
}

最後に

この記事を書いているうちに、 Laravel 5.7 -> 5.8 へとバージョンアップしました。

もたもたしているとすぐバージョンが上がってしまい、過去に書いた Collection メソッドをまた再度検証する…なんてことになってしまうので他のメソッドもどんどん書いていこうと思います!

次は chunk メソッドです!

LaravelJPConference 当日スタッフとして人生初参加してきたので、振り返ってみた!

2/16 開催の LaravelJPConference に当日スタッフとして参加してきました! conference2019.laravel.jp

人生で初めて大規模イベントの(当日)スタッフ参加です…!当日は進行を担当していました!

もちろん LaravelJPConference も、今年初の開催なので初めての参加ですw

ですが、スタッフも含め参加者は334名だったそうです! 初開催なのにすごい!

当日の様子やセッションについては、以下を見ていただければと思います!

ハッシュタグ#laraveljpcon

Laravel JP Conference 2019 スライドまとめ - Qiita

振り返り(YKPWT)

YKPWT を使って個人的な振り返りをしました! cocoeyes02.hatenadiary.jp

以下やったこと、Keep、Problem、わかったこと、次やることになります!

やったこと

  • Laravel Track の準備
    • 機械設備の仕様把握(PCの規格に合わせた表示変更、マイク音量調節、照明調節、空調など)
    • 設営(スピーカー台設置と後方のパネルの組み立て)
  • Laravel Track の進行
    • スピーカー対応
      • お水配布
      • 疎通チェック(スピーカーのPC に合わせて)
    • 司会
      • アナウンス(開始前 / 開始後)
      • 質疑応答
    • タイムキーパー
    • 機械設備操作
    • 質疑応答時のマイク運び
  • Closing Talk 後の片付け
    • 現状復旧
    • ゴミ片付け

Keep

当日スタッフとして
  • 連絡を見逃さないよう、定期的に slack で当日スタッフ用のチャンネルを見た
  • 誰も機械整備の仕様について詳しく知らなかったので、分かったことを随時 slack や口頭にて共有した
  • アナウンス内容を変える対応が何回かあったが、なんとか対応することができた
  • 設営担当の撤去の手伝いなど、特に割り振られていない仕事も少しやった
  • 初開催の LaravelJPConference だったが、大成功かつ無事終えることができた
  • イベント運営の経験を経て、対応力やナレッジなど得られるものがあった
一参加者として
  • Laravel や PHP 問わず知見や知識が深まった
    • しかもタダで!(今回当日スタッフはチケット代無料でした )
  • PHPer 界隈の知人がめっちゃ増えた(フォロワーも40人ぐらい増えた)
  • 自分の OSS 活動が評価されたようにも感じたし、他の人への勧め方など知見を得られた(ういろうさん @nyamucoro ありがとうございます!)
  • お昼御飯がめっちゃおいしかった(ランチサポンサーの株式会社デザインワン・ジャパンさんありがとうございます!)

Problem

当日スタッフとして
  • 事前にアナウンスのテキストを用意されていたので、iPad で確認できるよう持ってきていたが当日に iPad が初期化されていたことに気がついた 😇
  • アナウンス担当時に緊張し過ぎて、その時やってたセッションの内容が頭に入ってこなかった
  • Track がほぼ満員だった時、空き席への案内などの対応が満足にできなかった(コアスタッフの人に任せてしまった)
  • 事前説明会で言われたタスク以上のことが、あまりできなかった気がする
  • スマホの電池残量がギリギリだった(イベント終了時に7%)
一参加者として
  • 恒例(?)のじゃんけんで決勝まで上がったが最後で負けた :kanashii:

分かったこと

当日スタッフとして
  • イベントスタッフ用のワークスペースを用意する時、緊急連絡用のチャンネルがあるとすぐ報告できるし安心
  • 持ち場が固定のスタッフは持ち場以外の状況等が把握できないので、小さな出来事でも slack で共有してあげると良い
  • 会場の設備について、まだ把握していないのであればできるだけ早く理解して共有することが吉
  • イベントスタッフの経験豊富な人がいると、いろいろと円滑に進む(ありがたい。。。)
  • やっぱり午前中にトラブルが起きがち(逆に午後は安定する)
  • 定期的に slack を見たり、ツイートしたらすぐにスマホの電池残量が減る
  • 司会やばいぐらい緊張する
  • 当日スタッフとして参加すると、普通の参加以上に界隈の人と仲良くなれるチャンスがある
一参加者として
  • php 系のカンファレンスでやるじゃんけん大会は、前でじゃんけんをする人がなんの手を出すか予告するので、途中まではその手に勝つ手を出す
    • どこで裏切られるかを推測するのが勝利の鍵
その他
  • 自分のイベントアドリブ対応力の低さが露呈した
    • ホスピタリティが足りない
    • 経験値が足りない
  • デブなので立ち仕事やると、すぐに足に疲れが出てくる

次やること

  • 他のイベントにも参加して、イベントスタッフ経験を積む
    • 今度は自分が進行を円滑に進められるような人へ…
    • 自社のイベントスタッフやコアスタッフになることにも繋がるはず
  • 小さなことでもいいので、なるべく slack に流して共有する
    • ただし、緊急時の内容は、他の共有内容で流れてしまうとまずいので専用のチャンネルで共有する
  • スマホ充電用のモバイルバッテリーを持っていく
    • 持ち歩くことになるので、できるだけ小型の方が良い
  • 前日に自分が持っていくデバイスが正常に起動しているか確認する
  • 次のじゃんけん大会こそ、どこで裏切られるかを推測して景品をゲットする!!!!
  • 運動しよう…

最後に

拙い個人的な振り返りの共有になってしまいましたが、全体を振り返ってみても当日スタッフ参加はとても得るものが多いです!!

他のPHPカンファレンスでも当日スタッフを募集していると思いますので、参加されたことが無い方は是非参加をお勧めします!!!

2019年に開催される全国のPHPカンファレンスのまとめ - Qiita

日報の形式は「YKPWT」がオススメ

TL;DR

  • 日報は「YKPWT (やったこと Keep Problem 分かったこと 次やること)」がオススメだよ
  • KPT より優れている点
    • 当日の業務内容を報告できるよ
    • 良かったことの深掘りができるよ
  • YWT より優れている点
    • 良かったことを書いた方が、自身の成長を実感できるよ
    • やったこと→分かったことをいきなり書くより、やったこと→ Keep Problem →分かったことの順で書いた方が、日報に慣れていない人でも書きやすいし説明しやすいよ
  • あくまで個人的なオススメだから、実際に使ってみて自分に合ってるか判断してね

YKPWT とは

YKPWT は

  • Y やったこと
  • K Keep
  • P Problem
  • W 分かったこと
  • T 次やること

の略です。役割としては

  • Y → その日の業務、事実報告
  • K・P → 所感をまとめる
  • W 分かったこと → 所感から内省、分析
  • T 次やること → 得られた学びを行動につなげる

という構成になります。少し項目が多い感じは否めないですね。

YKPWT は内容から見てわかる通り YWT と KPT を混合させたフレームワークです。ただ混ぜただけのように思われるかもしれませんが、日報に関しては YWT や KPT よりも YKPWT の方が良いと個人的に感じます。

ちなみに YWT と KPT については、以下の記事でよくまとめられています。

yoshitsugumi.hatenablog.com

KPT より優れている点

その日の業務内容が書ける

日報は個人の振り返りだけでなく、報告先(上司やメンターなど)へその日何をやったのか報告する意味もあります。

KPT で振り返るとその日の業務内容を書く枠がなく、その日何をやったのか報告することができません。

良かったことの深掘りができる

日報に限らず KPT でありがちなことですが、Problem と Problem を解決する Try に意識を向けすぎて、 Keep の深堀りが足りないことがあります。「じゃあ良かったことはそのまま続けていただいて…」で終わってしまう例を、何度も見たことがあります。

Keep も深堀り次第では型化のキッカケを見つけたり、良かったことを見つけるスキルの向上になったりします。

YWT より優れている点

自分自身を褒める要素がある

日報のモチベーションの観点から、自身の成長を感じることは大事だと思います。

YWT は振り返りのフレームワークとしては優秀ですが、学びを得ることに特化しすぎている気がします。もちろん学びを得ることは、自身の成長に繋がる大事な点です。ですが学びを得ることだけでなく、その日の良かったことを確認するだけでも自身の成長を感じることが出来ます。

  • 他の人には当たり前のことでも、自分が今までできなかったことが出来た!
  • 学びになるようなことじゃないけど、いい感じの動きができた!

これらを学びにならないからといって、書かないのはもったいないです。

Y→W の難しさを緩和している

個人的に YWT で一番難しいことは、やったことから分かったことを考えることだと思っています。やったことから分かったことを加筆するとき、よく起きやすいのが…

  • 時間がかかっている、気がついたら 30 分近くも経っていた
  • やったことからどういう背景で分かったことが出てきたのか、報告先にうまく説明できない・伝わらない

だと思っています。

これはやったことから、分かったこと(≒ 学び)をいきなり思いつこうとするのが難しいからだと感じます。

YKPWT では、やったことと分かったことの間に「やったことの結果」を書く作業を追加しました。それは Keep と Problem です。

まずは良かった点と反省点を整理して、次に良かった点と反省点から学びを得るフローになっています。良かった点と反省点を出すことはそこまで敷居が高くないことと、自然と分かったことの理由づけができるので、質の高い Y→W を書くことが出来ます。

やったこと

管理画面の E2E テスト(nightmareJS)作成 6.5h

Keep

Problem

  • 間違って git reset をしてしまい、3時間分の作業が消えてしまった
  • nightmareJSで、ファイルのアップロードができなかった

わかったこと

  • CUI 以上に、GUI で全ファイルの git reset は簡単に出来てしまう
  • Vue インスタンスのプロパティの値を変えないと、nightmareJS を使って input の value を変えてもテストできない
  • nightmareJS 単体では、テスト中にファイルのアップロードができない
  • nightmareJS で悩んでいることを、nightmareJS の処理で解決する必要はない

次やること

  • GUI 上で git reset するときは、全ファイルではなく1ファイルずつ git reset する
  • vue 周りのテストをするときは、なるべく vue でテスト用のデータを用意する methods を作成する
  • ライブラリである nightmare-upload を使ってみる

最後に

この YKPWT は個人的なオススメです。人や環境によっては別のやり方のほうがあっているかもしれません。

ですが、日報をどう書けば良いか悩んでいる人は、まずこの YKPWT を試してみましょう。それから自分のやり方にあっているか考え、改良するか別のやり方にするか検討してはいかがでしょうか。