LaravelでActionを切って躓いた話

概要

LaravelでControllerではなく、Actionというクラスを定義して、実装しようとして躓いた話をまとめます。

やりたかったこと

  • postsってルーティングにアクセスしたら、 App\Http\Controllers\Posts\IndexAction が呼ばれる

起きたこと

  • postAutoDumpで下記のエラーが発生した
UnexpectedValueException: Invalid route action: [App\Http\Controllers\Posts\IndexAction].

原因

Posts\IndexAction のメソッドを、 __invoke として居ないことが原因でした。

ルーティングでは、__invoke を前提に書いているのに、 index とメソッド定義していたので、エラーになっていたようでした。

Route::post('review', 'Review\\RegisterAction');

ちゃんと、 __invoke で定義することで解決しました。

参考

Guzzleを使ってAPIのテストを書く

概要

DBの単体テストは、PHPUnitを使うことで手軽に行うことができていました。
しかし、APIのテストはやり方がわかっておらず、テストコードの作成を行えていませんでした。

今回Guzzleを使って、モックを差せることがわかったので、実践したやり方をまとめてみます。

実践

本体

コードを書くときに、コンストラクタ内でオブジェクト生成するのではなく、外から生成したオブジェクトを渡すようにしました。
こうすることで、テストコードでモックを差すことができるからです。

class Hoge
{
    private $client;
    private $url;
    
    public function __construct(HttpClient $client, string $url)
    {
        $this->client = $client;
        $this->url = $url;
    }
    
    public function getList(string $channelId)
    {
        $response = $this->client->request(
            'POST',
            $this->url,
            [
                'headers' => [
                    'Content-Type'  => 'application/json',
                    'x-sora-target' => https://hoge.com/hogehoge,
                ],
            ]
        );
        return \GuzzleHttp\json_decode($response->getBody(), true);
    }
}

テストコード

次にテストコードです。
テストにはPHPUnitを使います。

最初に期待するAPIのレスポンスをResponseクラスを使って生成します。
次にそのResponseクラスを使って、handlerを生成します。 これをコードに渡してあげることで、APIリクエストを行う際に、モックから実行結果を受け取ってくれます。

こうすることで、APIのテストをモックを使って行うことができました。

class HogeTest extends TestCase
{
    /**
     * @test
     */
    public function testGetClientList()
    {
        $channelId = 'test';
        $mockResponse = new Response(200, [],
            json_encode(レスポンス)
        );
        $mock = new MockHandler([$mockResponse]);
        $handler = HandlerStack::create($mock);
        $mockClient = new HttpClient([
            'handler' => $handler,
        ]);
        $client = new Hoge($mockClient, 'http://localhost/');
        $actual = $client->getList($channelId);
        }
    }
}

ちなみに複数のレスポンスをモックで刺したい場合は、MockHandlerを生成するところで、複数渡せばOKです。

$mock = new MockHandler([$mockResponse, $mockResponse$]);

まとめ

Guzzleを使うことで、APIのテストコードを書くことができました。
こうすると、CIでAPIのテストも回す事ができますね。

注意しなければいけないのは、APIの振る舞いが変わったときに、モックも修正しなければいけないということです。
そうしないと、デプロイしたときにエラーが出てしまいます。

Laravelのtinkerを使えるとちょっと幸せになれる

概要

Laravelのtinkerでちょっと幸せになれたので、ここに残しておこうと思います。

tinker

説明

php aritisan tinker

のことで、Laravelを対話的に動かすことができるコマンドです。
Eloquentはもちろんのこと、自分で作成した独自クラスの読み込みも可能です。

動作

$ php aritisan tinker

を実行すると対話的に動かすことができるようになり、そこで動かしたいコマンドを実行します。

>>> Model::find($id)
>>> use 特定のクラス
>>> $hoge = new 読むこんだクラス
>>> $hoge->メソッド(引数)

などで簡単に動作を確認できます。

また、コンストラクタインジェクションなどで依存関係がある場合は下記で解決することができます。

resolve(クラス名)

このやり方なら、コンストラクタインジェクションで呼び出す先の依存関係も解決してもらえ、 tinke での確認が簡単になります。

まとめ

Laravelで何らの動作確認をするときは、 tinker を使うとプログラムがちゃんと動作していることが確認することができ、デバッグの助けになってくれました。

Laravel5.5のmigrationで、外部キー制約を貼ろうとして躓いた話

概要

業務でLaravel5.5を使っているので、家で学習がてらLaravelを触っていました。
そこで、外部キー制約を貼ったテーブルを作成しようとして躓いたので、忘れないように残しておきます。

やろうとしたこと

ブログ投稿を管理するpostsテーブルと、そのブログに投稿するコメントを管理するcommentsテーブルを作成しようとしました。
CakePHPのブログチュートリアルから持ってきました。

postsテーブルを作成するmigrationファイル

public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->increments('id');
        $table->string('title');
        $table->text('body');
        $table->timestamps();
    });
}

commentsテーブルを作成するmigrationファイル

public function up()
{
    Schema::create('comments', function (Blueprint $table) {
        $table->increments('id');
        $table->string('commenter');
        $table->text('body');
        $table->integer('posts_id');
        $table->foreign('posts_id')->references('id')->on('posts');
        $table->timestamps();
    });
}

結論

$table->integer('posts_id'); が原因でした。
この状態では、Laravelはint(11)で生成するようです。
しかし、postsテーブルの$table->increments('id');int(10) unsigned で生成するため、親テーブルと型が違う状態になり、エラーになっていました。

解決策としては、 $table->integer('posts_id')->unsigned(); としてやると、同じint(10) unsignedで生成してくれ、無事migrationできました。

public function up()
{
    Schema::create('comments', function (Blueprint $table) {
        $table->increments('id');
        $table->string('commenter');
        $table->text('body');
        $table->integer('posts_id')->unsigned();
        $table->foreign('posts_id')->references('id')->on('posts');
        $table->timestamps();
    });
}

エラー文言が出て、それで検索をかけて四苦八苦していましたが、テーブル構造を見たら一発で解決できました。
その視点は忘れないように、ここに残します。

PHPerKaigi2019に参加&LT登壇しました #phperkaigi2019

概要

juve534.hatenablog.com 前回初めてのカンファレンスとなり、最高の体験をすることができたPHPerKaigiが今年も開催されました。
しかも、今回は初めて登壇する方用に、ルーキーズLT という枠が設けられました。

「これはCfPするしかない!」ということで応募し、登壇することになりました!
また前回参加したことで、この1年でエンジニアとしてレベルアップできたので、今回は会社の後輩も数名連れていきました。

f:id:juve534:20190402021524j:plain

イベント全体

前回も参加者が楽しめるように工夫されている印象でしたが、今年もその印象は変わらないです。
PHPカンファレンスにもいくつか参加しましたが、PHPerKaigiは他とは一線を画する面白さだと思いました。

具体的に例を上げるのは難しいですが、トラックCで参加者同士がコミュニケーションを取れるようになっているのが良いですね。
IRTやアンカンファレンスはもちろんの事、Twitterで見かけたことある人とその場でコミュニケーションが取れる環境になっていると思いました。

また名札にTwitterアイコンを印刷してくれるので、声をかけてもらえることが多かったです。

セッション

今回は直近で役立つことより、尖ったセッションを重点的に聴くことにしました。
その中でも印象に残ったのは、下記の3つのセッションでした。

めもりーさんのセッションは、今回随一で尖っていたかなと思っています。
自分なら思いつかないですし、思いついてもやらないことで、本当にすごいと思いました。
また、郡山さんの大学講義を彷彿とさせるセッションや、うずらさんのLIVE感溢れるセッションも素晴らしかったです

登壇について

PHPerKaigi2018に参加し、いずれは登壇したいと思っていたので、ルーキーズLTに登壇しました。
自分は外部に登壇した経験が無かったため、スタッフの方々に事前に見てもらえるのは、チャンスだと思いました。

実際に資料を作って、練習会に参加し、3回の練習を行いました。
3回の練習で指摘された内容を取り込んでいくと、みるみる良くなっていくことがわかり、一人ではできないクオリティになったと感じました。

金沢から東京に行くのは金銭的に大変で、行くかどうか迷っていたのですが、行ってみてよかったです。 このPHPerKaigi2019で、初めて外部登壇を行えて本当に良かったと思います。

speakerdeck.com

まとめ

今回も素晴らしいカンファレンスでした。 スタッフ・スポンサー・スピーカーの皆様、ありがとうございました!!

P.S. 一緒に行った後輩たちにも、良い刺激を与えられたようで、その点でも良かったなと思っています。

グリー開発本部 Meetup #3 モニタリングに参加してきました #GDMeetup

概要

そーだいさんのPHPerのためのWebサービスのモニタリングの話を聴いて以来、モニタリングに注力していました。 今回は「入門監視」という素晴らしい本と、それに伴うイベントがあるということで参加してきました。

トークメモ

実践 自動復旧 / self healing - Speaker Deck

TL;DR

  • 入門監視にさらっと書いてある、自動復旧の話を掘り下げる
  • 自前の配信基盤Yururaで、複数のアラート(クリティカル、ワーミング、チケット)を投げ分けている
  • アラートの傾向を分析して、自動復旧へアプローチしていった
    • アラートの多くが、コマンド一発 or 数発で解決することがわかった
  • AWS System Managerで自動復旧させる
    • チャットの実行ログは残す
    • 最初は人間が確認していて、問題ないことを確認して、自動化した
    • アラートレポートで、アラートを可視化している
  • AWS環境なら、AWS System ManagerとかAWS Lambdaで大体できる

所感

自前でアラート配信基盤を作っているのは、強いの一言です。 自分は「複数のアラートを投げ分けたいと思いつつ、そこまで工数がかからないといいな…」と思いましたw

自動復旧に向かう前に、人間が介入するフェーズがあるのが良いですね。 補助輪を外すフェーズは、人間相手でも機械相手でも必要。

入門 監視のなにか(仮)

TL;DR

  • 監視の目的はなにか考える
  • バックアップと監視は問題が起きたときに、ちゃんとできているか気づく
    • ex.DBのバックアップを1日1回とる
      • この場合は、最大で24hデータが飛ぶ恐れがあるといえる
    • なんのためにバックアップを取るか考える
    • 監視も同様
      • 1日に何百のエラーメールがきて、それを無視する運用になっている
        • そのエラーメールは必要なのか?
  • 監視は専任の担当者を決めないほうが良い
    • 監視担当者より、開発をしている人の方が、適切な監視ができる
    • アプリケーションエンジニアも、監視をしようという話

所感

入門監視の内容について、トーク&補足という感じでした。 入門監視を全部読めていないですが、なんとかついていけてよかったです。

自分的には、モニタリングが属人化してしまっているので、なんとか改善せねばと思いました。

TL;DR

sysloadや監視などの話(仮)

TL;DR

  • エモすぎる話
  • モニタリング環境を自前で用意した
    • アクセスログから、社内のヘビーユーザを割り出し、ヒアリングした
    • gangliaを選定した
      • pythonでmoduleを追加できる
      • 拡張性が高そうだった
      • フロントエンドは自分で書いた
    • 数千台、数千メトリクスを取得している
      • 全ては見ることは不可能なので、重要な部分以外は後から書き出すようにした
  • サーバを減らす際は、減らしすぎないことに注意する
    • サーバを減らして障害になったらいけない
      • とはいえ、減らす事でコストダウンをしたい
    • サーバを減らす基準は、組織内で決めておくと良い
  • 今はSaaSでやった方が良い
    • 10年前はSaaSのコストが高かった
    • 今はコストダウンしている
      • ちなみに、SaaSの4分の1で運用できるので、Greeではgangliaを使っていく
  • モニタリングは学びのチャンス
  • 環境、OS、ミドルウェアの変化を見逃さないようにしよう

所感

低レイヤーを知っているエンジニアの強さを感じる。 MySQLのメトリクスだけで3桁の項目を観ているとか、すごすぎて言葉も出ない。

低レイヤーの知識は、10年経っても生きてくると思うので、自分も学ばねばと感じました。

感想

入門監視の松浦さん(doublemarket(@dblmkt)さん | Twitter)による、監視の話もグリーの監視事情も面白かったです。

グリーさんは、監視に結構力を入れていて、自分たちとの差を感じました。 自分たちも「アラートの分析をせねば」と感じ入る次第です。

スピーカーの方々、運営のグリーの方々、ありがとうございました!

PHPカンファレンス仙台2019に参加してきました #phpconsen

概要

PHPカンファレンス仙台 今回も例によって、行くかどうか迷ってましたが、PHPの現場を聞いて参戦を決めました。
先に結論から言うと、とても面白いイベントでした!!

セッションについて

今回が初開催らしいのですが、東京や関西に負けないボリュームでした。
また東京と比べると、マニアックな話も多かったかなと思います。

自分が参加したセッションのスライドを貼りますので、興味がある人はスライドをみてみてください〜

感想

前半から再発防止の話までは、私がいるチームの課題について話されており、早速持ち帰って展開しなければと思いました。
後半は純粋に自分が興味がある話題について聞いてきました。
CQRS、Elasticsearchと次の仕事で使えると良いなと思っています。

セッション以外で刺激を受けた点としては、 ダブスターズ さんが挙げられる。
PHPカンファレンス以外でも技術イベントを開けるような団体名にしたということでしたが、自分たちも金沢で技術コミュニテイを作ろうと思っているので、参考にさせてもらうと思います。

最後に

素晴らしいイベントを開いてくださった運営の皆様ありがとうございました!

余談

懇親会に参加しなかったので、牛タンを食べて帰りました。
牛タン最高ーーー!

利久の牛タン
牛タン