Featured image of post nelmio/aliceでcurrentの値をintegerとして扱う方法について

nelmio/aliceでcurrentの値をintegerとして扱う方法について

Twitter ツイート Hatena Bookmark ブックマーク

ちょっと古いプロジェクトで、SymfonyのバージョンやPHPのバージョンを上げたいと思いましたがテストがないのでこわいなということで、テストを書き始めました。
nelmio/aliceを使ってテスト用のダミーデータを作っていましたが current まわりでちょっとつまずいてしまいました。

どのような問題が起きたのか?

コンストラクタに1から順に番号をもつ値が必要だったので current の値を使おうと考えていました。
以下のようなコンストラクタです。

1
2
3
4
    public function __construct(private Map $map, private int $number, float $lat, float $lng)
    {
        $this->location = new LatLng($lng, $lat);
    }

これに対してこのようなFixtureの定義をしました。

1
2
3
4
5
6
7
8
9
App\Entity\Map:
  map1:
    name: 'テスト'
    aliasName: 'test1'
    open: true

App\Entity\Shop:
  distance{1..300}:
    __construct: ['@map1', '<current()>', 35.684062887918, 139.77446354018]

この状態でFixtureをロードすると以下のようなエラーが発生します。

1
 TypeError: Argument 2 passed to App\Entity\Shop::__construct() must be of the type int, string given, called in /app/vendor/nelmio/alice/src/Generator/Instantiator/Chainable/NoCallerMethodCallInstantiator.php on line 51

これは <current()> メソッドがstringの値を返してしまうので型が不一致になってエラーになっています。

キャストしてみる

aliceにはキャスト関数が用意されています。
https://github.com/nelmio/alice/blob/master/doc/customizing-data-generation.md#cast

<current()>のキャストしてしまえばいいのでは?と思ったので以下のようにFixtureの定義を修正しました。

1
2
3
4
5
6
7
8
9
App\Entity\Map:
  map1:
    name: 'テスト'
    aliasName: 'test1'
    open: true

App\Entity\Shop:
  distance{1..300}:
    __construct: ['@map1', '<intval(current())>', 35.684062887918, 139.77446354018]

実行してもaliceではエラーになりません。
ただしこれにも問題があって、必ず値が 0 になってしまいcurrentの値が使えるわけではないです。

どうやらaliceでは複数の関数を重ねる書き方がサポートされていないようです。

$currentをキャストする

そもそもaliceにはIdentityという機能(関数)が用意されていて、phpのコードをまぜて書くことができます。
<identity(1 * 2)> みたいなカタチでphpのコードを混ぜられます。
そしてidentityは省略可能で <()><identity()> は同じです。 https://github.com/nelmio/alice/blob/master/doc/customizing-data-generation.md#identity

つまり <(new \DateTime('2000-01-01'))> みたいに書くこともできちゃうわけです。

さらに、current() はidentityでは使用できませんが、identityの中で使う場合$current という変数として展開されています。 ドキュメントに書かれています。

The value current (result of <current()>) with the variable $current https://github.com/nelmio/alice/blob/master/doc/advanced-guide.md#identity

ここまでくれば $current をキャストしてしまえばいいわけです。

1
2
3
4
5
6
7
8
9
App\Entity\Map:
  map1:
    name: 'テスト'
    aliasName: 'test1'
    open: true

App\Entity\Shop:
  distance{1..300}:
    __construct: ['@map1', '<((int)$current)>', 35.684062887918, 139.77446354018]

まとめ

  • <current()> はstringで値を受け取ってしまう
  • <((int)$current)> でintとして扱える

最後に

blueskyでポストして反応していろいろアドバイスをくれた @ttskchさんありがとうございます!!!

参考

comments powered by Disqus
Built with Hugo
テーマ StackJimmy によって設計されています。