はいさい(N回目)
というわけで、9/16に PHPカンファレンス沖縄2023 で登壇してきました。
オフラインでの登壇は初めての経験だったということもあり、せっかくなので記憶が薄れる前に記録に残しておこうと思います。
登壇する前までマジで地獄を見たのですが、今となってはそれもいい思い出です。はい。
発表資料
発表資料はこちらになります。(Google Slidesをpdfにするとフォントの問題なのか崩れたり見づらくなるのどうにかならんのか)
Unit of Workパターンで永続化とトランザクションを制御する – Speaker Deck
動画はこちら。Track B の3人目くらいです。
発表時の様子はこちら。
X(旧Twitter)での紹介。
神が息づいている島。
なぜ登壇しようと思ったのか
去年の春頃に転職をしたのですが、そこでは息を吐くようにプロポーザルを出して登壇する人が一定数存在しておりました。
自分はこういったカンファレンスで登壇する人って雲の上の人なんだろうなぁと正直思っていたのですが、身近にそういった人がいたというのが一番大きかったと思います。あれ?気軽にチャレンジしてもいいのでは!?、というところですかね。
そこで思い切って、ペチコン福岡にプロポーザルを出してみたのですが撃沈。いやあれは無理や。
結局自分には縁のない話なんだよなぁと思っていたところ、まさかのペチコン沖縄開催決定。魂の置いてある場所。これは魂を拾いに行かねばなるまいと意気込んでプロポーザルを出したところ採択された感じです。
※ 魂の置いてある場所とありますが、わたし沖縄生まれなのです
あと、内弁慶でいるのも何か違うんだよなぁと常々思っていたのもあります。いつかは外で発信してみたいなぁとひっそり夢見ていたので自らチャレンジできたのは良かったと思っています。
登壇してよかったこと
1. 違う風景を見れたこと
生粋の引きこもり/ボッチ気質である自分にとって、外部に向けて、ましてやオフラインでの発表をするのはかなり勇気のいる行動だったのですが、それと引き換えに今まで見たことのなかった風景を見ることが出来ました。
どんな風景なんだ?と聞かれるとうまく説明できませんが、登壇する前の緊迫感、登壇中の緊張感、登壇した後の開放感、それぞれの場面での自分の目を通して見える風景というんですかね、そこでの感じ方とも言うんでしょうか、そういう普段とは違う体験・経験が出来たと思っています。
はじめて経験することに対して一歩踏み込むのは勇気がいりましたが、この一歩を踏み出せたのは今後に活きるかなぁと。
つーか、踏み出してみればなんとかなるんで、大したことないっすわ。なので、みんなも踏み込んでいこっ!?
2. コミュニティに参加できたこと
前夜祭と懇親会にも思い切って参加してみましたが、それも良かった。ネット上でしか観測できていなかった著名な方々があちらにもこちらにもいる状態でした。
ボッチになるのは覚悟していて前夜祭もほぼボッチだったのですが、しめじ(@smeghead)さん が話しかけてくれてマジで助かりました感謝です。
懇親会では他の方たちと積極的に話すことは出来ませんでしたが、話を盗み聞いているとナチュラルに技術的な話をずっとしていて、おぉ!これがコミュニティってやつか!と、ひとりで感動していました。
(@katzchumさんとはActiveRecordの話、ちゃまほり@tyamahoriさんとは髪型がめちゃくちゃ被っている話ができました)
ただ、基本ボッチというのには変わりなかったので、こういうところで積極的に飛び込んでいくというのは今後の課題かもしれません。ことみん(@kotomin_m)さんに昼飯時カジュアルに声かけてもらった記憶があるけど、あれくらいのコミュ力があればなぁと思います。
いやー、無理かなぁ。無理だなぁ。
3. 声をかけてもらえたこと
登壇を終えた後の休憩中だったかな?トイレで「発表、めちゃくちゃ勉強になりました」と声をかけてもらえました。
めちゃくちゃ嬉しかったです。名前とか聞けなかったのですが、その節はありがとうございました。
あと、X(Twitter)でも、めちゃくちゃ分かりやすかったとメンションをもらえたりもしました。
本当に資料を作るのに苦労したので、こうやって褒めてくれる、声をかけてくれる人がいてマジで救われました。感謝感激雨嵐。
4. ソウルフードをめちゃくちゃ食べた
沖縄そば、タコス、ポーク玉子、油みそ、ブルーシール、さんぴん茶は絶対に飲み食いしようと決めてたので、それらをちゃんと食べれたのは良かったです。
なかでも、てぃしらじそば (汀志良次) – 首里/沖縄そば | 食べログはマジで美味かった。優勝でした。
反省点
今となっては反省するところなどないと思っているのですが、強いて挙げるとするならば、やはりスライドを完成させるのが遅かったというところですかね〜。
この先、ちょっと雑に書いていきます。
構想〜初稿完成まで
まずは@hanhan1978さんに手助けしてもらったときの図。
現実逃避をずっとしていて、そろそろ取り掛かるかぁと思ったのが9月に入ってから。
そこからはネタのためのコードを書きまくってました。こういうことを喋れたらいいなぁというのは考えつつ、それを裏付けるためのコードを書いていった感じです。
これは、Doctrineを試しだした時の図。
で、大体これくらいのネタがあれば書けるかなぁと思ったのが9/7。
頭の中では、これを喋ろうと思っていたのはあったが、いざスライドに落とそうと思うと何をどう書いていけばいいのかまったくわからんの図。
なんとなく構想が固まってきたところの図(自分はこんな感じで発表資料は付箋とかでアウトラインとか概要を決めています)。
でだ、弊社隔週の金曜日に勉強会(カタリバっていうんだけどね)がありまして、9/8に素振りの場を自ら用意していたのもあり、なんとしてでもいったんは仕上げないといけない状況だったので、ここから徹夜ですよ。はい。
おじさん久しぶりに徹夜しちゃった。つらかったーーー。徹夜つれぇーーー。
発表の素振り
そして、まぁまぁ話す内容もグダグダのまま素振りを実行。
素振りって本当に大事で、話しを実際にしてみて、伝えたいことがこれだと伝わらない、何か足りない、みたいなことが分かるわけですよ。ほいで、ここからがある意味本番で、後はひたすら足りなさそうな部分、逆に削ったほうがいい部分を調整していった感じです。
(なので、絶対に素振りはしたほうがいいです。もしそういう場がなくても、誰にでもいいから実際に実演してみるのが大事だと思います。)
完成
なんやかんやで、9/14にだいたい出来上がって、それを社内の人たちに見てもらいました。前乗りで15日に沖縄出発だったのでガチで直前。
(こういう素振りの場所とか壁打ちができる環境があって弊社素晴らしいなって思います)
直前に頂いていたフィードバックを、空港や飛行機の中で反映してフィニッシュ。
※ フィードバック自体は、内容をゴロッと変えるとかではなく、こういう見せ方/書き方のほうがいいのでは?みたいなやつで良かった
※ 直前にも関わらずフィードバックをくれた同僚には感謝しかない!!
前夜祭に参加してホテルに戻ってから、2〜3回練習して当日を迎えるというなかなかハードな感じでした。
ここで良かったこととしては、練習時点で大体30分前後で終わっていたこと。奇跡。これはなかなか良かった。実際の本番でも時間を守れた。こればかりは褒めてやりたい。
という感じで、ほんとうに直前でスライドが完成したので本番は当たって砕けろ精神で挑みましたね。いぇい。
(だからこそ、開き直って思い切ってやれたという説がある)
なので、また何か登壇のチャンスがあれば
- 1ヶ月前にはスライドを書き出す
- 2週間前には何かしらの素振りでフィードバックをもらう
- 1週間前にはスライド完成させる
- 後は本番まで練習、調整
みたいな大人のスケジュールってやつを組んで挑みたいですね。いや、絶対に無理だな。
あと、最後の最後チェックインに失敗したのでこれは反省。
スライドに書ければよかったこと
時間がなさすぎて、スライドに書ければよかったことを補足しておきます。
Identity Map
IdentityMap – Qiita
うーん、これは書ければ良かったかもしれない。気づくのが遅くて入れられなかった。
これ、Doctrineは体現していて、いい具合にクエリを実行してくれるのくだりで入れられたら良かったかも。そうすればコマンドパターンの説得感もあがったかもしれない。
How is Doctrine 2 different to Eloquent? | Culttt にもあるのですが、要は一度エンティティを生成しておくと何度問い合わせても、そのインスタンスを再利用して返してくれるというものになります。
簡単なサンプルを書いてみます。
サンプル
<?php declare(strict_types = 1);
use App\Entities\Member;
use Doctrine\ORM\EntityManagerInterface;
// EntityManager
$em = app()->make(EntityManagerInterface::class);
// 取得
$member = $em->find(Member::class, 1);
echo $member->getProfile(); // 'hoge' と出力
// 更新
$member->setProfile('updated');
// $memberは監視済みなのでpersistを明示する必要はない
// $em->persist($member);
// 再取得
$updatedMember = $em->find(Member::class, 1);
echo $updatedMember->getProfile(); // 'updated' と出力
// 保存
$em->flush($member);これを行うと流れるクエリは以下の2本になります。
Select1回
{
"sql": "SELECT t0.id AS id_1, t0.name AS name_2, t0.profile AS profile_3 FROM members t0 WHERE t0.id = ?",
"params": {
"1": 1
},
"types": {
"1": 1
}
}Update1回
{
"sql": "UPDATE members SET profile = ? WHERE id = ?",
"params": {
"1": "updated",
"2": 1
},
"types": {
"1": 2,
"2": 1
}
}つまり、再取得のところで行っているfindではクエリは実行されていないということになります。
// 再取得
$updatedMember = $em->find(Member::class, 1); // ここでクエリは実行されていない一度生成されたEntityが再利用されていることが分かるかと思います。
更新の順番を意識する必要はあるかもしれない?けど、状態を取ってくる場面において無駄なクエリは流れなくなるんじゃないかなぁと。こいつはすごい。
Eloquent(ActiveRecord)の場合
同じようなことをするサンプルをEloquent(ActiveRecord)で書いてみます。
<?php declare(strict_types = 1);
use App\Models\Member;
// 取得
$member = Member::find(1);
echo $member->profile; // 'hoge' と出力
// 更新
$member->profile = 'updated';
$member->save(); // saveしないとhogeのまま
// 再取得
$updatedMember = Member::find(1);
echo $updatedMember->profile; // 'updated' と出力Eloquentの場合は、以下3本のクエリが流れます。
-- 取得のところ
select * from `members` where `members`.`id` = 1 limit 1;
-- 更新のところ
update `members` set `profile` = updated where `id` = 1;
-- 再取得のところ
select * from `members` where `members`.`id` = 1 limit 1;やはり、クエリの発行を減らすには工夫が必要そうです。
アノテーションにAttributesを使う
@77webさんに補足していただいたのですが、Entityに記述するアノテーションの形式はPHP8を使っているのであればannotationsではなく、attributesで書いておけばよかったですね。
Annotations
Annotationsはこういう形式で、自分がスライドでチラッと見せたやつです。
use App\Infrastructure\Repository\MemberRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=MemberRepository::class)
* @ORM\Table(name="members")
*/
class Member
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
<pre><code>// 略
</code></pre>Attributes
で、Attributesがこういう形式です。こっちのほうがええな!
use App\Infrastructure\Repository\MemberRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: MemberRepository::class)]
#[ORM\Table(name: "members")]
class Member
{
#[ORM\Id, ORM\Column(type: "integer"), ORM\GeneratedValue()]
private $id;
<pre><code>// 略
</code></pre>※ [orm] Meta Data – Laravel Doctrine も参照してください
注意点
で、これもしかしたら自分の環境のせいなのかもしれませんが、laravel-doctrineを入れた場合、config/doctrine.phpのDOCTRINE_METADATAの値をattributesに変えないとClass "App\Entities\Member" is not a valid entity or mapped super class.と怒られてしまいました。
※ config/doctrine.phpについては、ここらへん を参照してください
なので、
'managers' => [
'default' => [
'dev' => env('APP_DEBUG', true),
'meta' => env('DOCTRINE_METADATA', 'attributes'), // デフォルトだと、<code>annotations</code>なので<code>attributes</code>に変える
'connection' => env('DB_CONNECTION', 'mysql'),
'paths' => [
base_path('app/Entities')
],こんな感じで、env('DOCTRINE_METADATA', 'attributes')のようにする必要があるかもしれません。
テストコード
これまた、@77webさんに補足していただいたのですが、EntityはPOPOであるがゆえのテスタビリティの良さ(DB接続不要)についても触れられたらよかったですね。
これも時間がなさすぎた。。
このへんの、スライドに書ければよかったことは別途どこかで記事を書きたいなと思います。
おわりに
なんだかんだ長くなってしまいました。
どの発表も素晴らしく、ペチコン沖縄最高でした!!
魂を拾いに沖縄へ行ったはずですが、魂はやっぱり置いたままにしておきます!!!
P.S. 懇親会終わりに琉球大学でさまよって最終バスを乗り過ごし、結局タクシーとゆいれーるで、しめじさんと一緒に帰ったのはいい思い出です

