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

こんにちは!Symfonyアドベントカレンダー 6日目です :christmas_tree::crescent_moon:

初参戦です!よろしくお願いします :bow:

さて、私自身Symfonyを使い始めて早5ヶ月が経ちました。そこで、よく使っている便利なSymfonyコマンドを紹介しようと思います。

初心者向けですが、もしかしたら知らないコマンドがあるかもしれません!新しい発見があると嬉しいです :muscle:

※ Symfony4用に bin/consoleとしていますが、app/consoleに置き換えれば大体がSymfony2でも使えると思います。

おなじみ系

まずは、Symfonyを使う上で絶対通るであろう「おなじみ系」コマンドです。

「なんか、更新されないなー」ってときは、とりあえずcache:clearしてます。 (省略してc:cでもできちゃいます)

$ bin/console cache:clear …キャッシュを削除する

$ bin/console list または $ bin/console …使用可能コマンド一覧を表示する(設定ファイルにエラーがないかの確認にも使えます :ok_woman:

lint系

次に、ファイルの構文チェックをしてくれる「lint系」コマンドです。

オフライン環境でブラウザチェックができない時には、使えるコマンドだと思います。

$ bin/console lint:yaml [file_name] …yamlファイルのlintチェックをする

  • NGな時(下記エラーでは、1行目のインデントがおかしいのが一目瞭然ですね)
$ bin/console lint:yaml config/routes.yaml

  ERROR  in config/routes.yaml
  >> Unable to parse at line 1 (near "   index:").


 [WARNING] 0 YAML files have valid syntax and 1 contain errors.

  • OKな時
$ bin/console lint:yaml config/routes.yaml

 [OK] All 1 YAML files contain valid syntax.

$ bin/console lint:twig [file_name] …twigファイルのlintチェックをする

  • NGな時
$ bin/console lint:twig templates/base.html.twig

  ERROR  in templates/base.html.twig (line 5)
      3          <head>
      4              <meta charset="UTF-8">
  >>  5              <title>Welcome!{% endblock %}</title>
  >> Unknown "endblock" tag.
      6              {% block stylesheets %}{% endblock %}
      7          </head>

OKな時は、link:twigと同じような感じです。

debug系

次は、「debug系」コマンドです。

新たにサービスやパラメータ、ルートを登録した際に、きちんと意図した通り登録できているかをチェックする時に私はよく使います。

他にも、パス → 該当コントローラクラスまたはその逆を調べる時などに便利です。

$ bin/console debug:container [sevice_id] …サービスID → サービス情報(クラスなど)を確認する

$ bin/console debug:container doctrine.orm.entity_manager

 // This service is a public alias for the service doctrine.orm.default_entity_manager

Information for Service "doctrine.orm.default_entity_manager"
=============================================================

 The EntityManager is the central access point to ORM functionality.

 ---------------- -------------------------------------
  Option           Value
 ---------------- -------------------------------------
  Service ID       doctrine.orm.default_entity_manager
  Class            Doctrine\ORM\EntityManager
  Tags             -
  Public           yes
  Synthetic        no
  Lazy             yes
  Shared           yes
  Abstract         no
  Autowired        no
  Autoconfigured   no
  Factory Class    Doctrine\ORM\EntityManager
  Factory Method   create
 ---------------- -------------------------------------

$ bin/console debug:container --parameter=[parameter_name] …パラメータ値を取得する

$ bin/console debug:container --parameter=kernel.cache_dir
 ------------------ -------------------------------------------------------
  Parameter          Value
 ------------------ -------------------------------------------------------
  kernel.cache_dir   /path/to/project/var/cache/dev
 ------------------ -------------------------------------------------------

$ bin/console debug:route [route_name] …ルート名 → パスやメソッド、クラスを確認する

$ bin/console debug:route _twig_error_test
+--------------+--------------------------------------------------------------------+
| Property     | Value                                                              |
+--------------+--------------------------------------------------------------------+
| Route Name   | _twig_error_test                                                   |
| Path         | /_error/{code}.{_format}                                           |
| Path Regex   | #^/_error/(?P<code>\d+)(?:\.(?P<_format>[^/]++))?$#sDu             |
| Host         | ANY                                                                |
| Host Regex   |                                                                    |
| Scheme       | ANY                                                                |
| Method       | ANY                                                                |
| Requirements | code: \d+                                                          |
| Class        | Symfony\Component\Routing\Route                                    |
| Defaults     | _controller: twig.controller.preview_error::previewErrorPageAction |
|              | _format: html                                                      |
| Options      | compiler_class: Symfony\Component\Routing\RouteCompiler            |
|              | utf8: true                                                         |
+--------------+--------------------------------------------------------------------+

$ bin/console router:match router:match [path] …パス → ルート名やメソッド、クラスを確認する

$ bin/console router:match /_error/123.csv



 [OK] Route "_twig_error_test" matches


+--------------+--------------------------------------------------------------------+
| Property     | Value                                                              |
+--------------+--------------------------------------------------------------------+
| Route Name   | _twig_error_test                                                   |
| Path         | /_error/{code}.{_format}                                           |
| Path Regex   | #^/_error/(?P<code>\d+)(?:\.(?P<_format>[^/]++))?$#sDu             |
| Host         | ANY                                                                |
| Host Regex   |                                                                    |
| Scheme       | ANY                                                                |
| Method       | ANY                                                                |
| Requirements | code: \d+                                                          |
| Class        | Symfony\Component\Routing\Route                                    |
| Defaults     | _controller: twig.controller.preview_error::previewErrorPageAction |
|              | _format: html                                                      |
| Options      | compiler_class: Symfony\Component\Routing\RouteCompiler            |
|              | utf8: true                                                         |
+--------------+--------------------------------------------------------------------+

$ bin/console debug:twig --filter=[word] …twigで使える関数を確認する

例) twigでdump関数が使えるのかを知りたい → dump()が表示されたので使えそうですね :bulb:

$ bin/console debug:twig --filter=dump

Functions
---------

 * dump()
 * profiler_dump(data, maxDepth = 0)
 * profiler_dump_log(message, context = null)

...

doctrine系

最後に、「doctrine系」コマンドです。

$ bin/console doctrine:schema:validate …エンティティとDBが同期されているかを調べる

  • 同期されている時
$ bin/console doctrine:schema:validate

Mapping
-------


 [OK] The mapping files are correct.


Database
--------


 [OK] The database schema is in sync with the mapping files.

  • DBが同期されていない時
$ php bin/console doctrine:schema:validate
              
Mapping
-------

                                                                                                                      
[OK] The mapping files are correct.                                                                                    
                                                                                                                      

Database
--------

                                                                                                                      
[ERROR] The database schema is not in sync with the current mapping file.
  

$ php bin/console doctrine:schema:update …エンティティとDBを同期する、差分を確認する

  • エンティティとDBを同期する前に、差分(SQL)を確認したい時
$ php bin/console doctrine:schema:update  --dump-sql

 The following SQL statements will be executed:

     CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(180) NOT NULL, ...

  • 強制的に同期したい時
$ php bin/console doctrine:schema:update  --force

 Updating database schema...

     1 query was executed

                                                                                                                        
 [OK] Database schema updated successfully!                                                                             
                                                                                                                        

さいごに

いかがでしたでしょうか、全部知っていたらごめんなさいw

よく使うけど覚えられないものはエイリアスを作ったりして、日々の業務の効率化につながればいいなと思います :pray:

カスタムで自分だけのコマンドを作るも楽しそうですね:sunglasses:


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

Symfony Advent Calendar 2019 4日目の記事です。
昨日はtamakiiさんの Docker for Mac でも快適な Symfony 開発環境を作りたい でした!

皆さんは再利用可能なバンドルを作ったことはありますか?
Symfony2時代からSymfonyに触れている方は、アプリケーションの機能を置く場所としてのバンドルは多数開発したことがあると思います。
再利用可能なバンドルとは、モノリシックなアプリ内に置く機能の分類としてのバンドルではなく、複数のアプリケーションから composer require して使われるバンドルという意味です。
オープンソースの便利なバンドル、 FOSUserBundleSonataAdminBundle なんかがそうですね。オープンソースにしなくても、社内で開発する複数のアプリケーションに共通する機能を再利用可能なバンドルとして開発すると非常に便利です。コピペコードも減って精神的にもとても楽になります。

そもそも再利用可能なバンドルのファンクショナルテストはどうするか

弊社開発部ブログに過去記事があります :relaxed:
https://tech.quartetcom.co.jp/2016/12/19/functional-testing-syfony-bundle/

ファンクショナルテストが無いと、バンドル導入時に受け入れ側のアプリケーションでバンドルの機能に対してテストを書かないと安心して使えないので、テストできるようになっているSymfonyにはホントに助けられています。

フレームワークとしての振る舞いの変更やサービス定義のテストであれば、この過去記事の内容で十分なのですが、問題は再利用可能なバンドルにコントローラアクションを作った場合です。
この場合、Kernelだけを差し替えてもアクションのテストを書くことができません。

再利用可能なバンドルのコントローラアクションのテスト

再利用可能なバンドル内のコントローラアクションに対してファンクショナルテストを書くためには、まず下記のような内容で Tests/Functional/config/routes.yml を設置します。
※ AcmeDemoBundleのところは実際に開発中のバンドルの名前を入れてください。

_my_actions:
    resource: "@AcmeDemoBundle/Resources/config/routing.yml"

次に、 Tests/Functional/config/config.yml に、設置したroutes.ymlを読み込む設定を追加します。

framework:
    router:
        resource: "%kernel.root_dir%/config/routes.yml"

これで準備は完了です。
自分のバンドルのファンクショナルテスト用の KernelTestCase を使い、 static::createClient() ではなく self::$container->get('test.client') によって KernelBrowser を取得することで、普通のアプリケーション内のバンドルを開発するときと同様に、コントローラアクションに対するファンクショナルテストを書くことができます。

おまけ: コントローラに対してDIしているとき(Symfony4+)

Symfony4 以上を使っていてコントローラクラスに対してSymfonyのDI機能で依存を注入させたいときは、Controllerを public: true でサービスとして登録する必要があるので注意してください。
一見DIできないように見えてエラーと戦うのですが、 ContainerControllerResolver クラスのコードを読むとわかります。

まとめ

以前 Twitterで呟いた とおり、カルテット開発部では「フレームワークはどうでもいい」のですが、「なんでも良い」わけでなくドメインのコードを邪魔しないフレームワークとしては、いまのところSymfonyが最強だと思っています。
※ 個人の意見です(笑)


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

こんにちは!バックエンド担当の永井です!

はじめに

PHPの場合、特定の型のオブジェクトの集合を表現する時に配列を使うことが多いと思うのですが、どうせ同じ型の集まりなのだから、他の言語でも良くあるような 型を制限 して扱いたいなと日頃から思っていました。
SPL辺りで用意してくれても良いのになと思っていたのですが、なかなか実装されないので自分で作ろうかと思っていたところに、素敵なライブラリ ramsey/collection を見つけたのでご紹介したいと思います。

ramsey/collection

Java Collection Framework にインスパイアされたというこのライブラリは、PHP7.2以上で動作します。
個人的に、JavaのCollection Frameworkはよく出来ていて良いなぁと思っていたので、この記述を見た時は期待が高まりました!
ただ、JavaのCollection Frameworkと比べるとまだまだ揃っているクラスが少ないので、今後に期待って感じかなと思います。

インストール方法

composerでサクッといけます。

$ composer require ramsey/collection

Collectionの使い方

まずは普通のCollectionクラスはこんな感じで使います。

今回はMemberクラスというのを用意して、こいつをコレクションとして扱いたいと思います。

<?php

namespace Qcmnagai\CollectionSample;

use DateTimeInterface;

class Member
{
    private $name;
    private $email;
    private $joinedAt;

    /**
     * Member constructor.
     * @param string $name
     * @param string $email
     * @param DateTimeInterface $joinedAt
     */
    public function __construct(string $name, string $email, DateTimeInterface $joinedAt)
    {
        $this->name = $name;
        $this->email = $email;
        $this->joinedAt = $joinedAt;
    }

    /**
     * @return mixed
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @return mixed
     */
    public function getEmail()
    {
        return $this->email;
    }

    /**
     * @return DateTimeInterface
     */
    public function getJoinedAt(): DateTimeInterface
    {
        return $this->joinedAt;
    }
}

なんてことないValueObjectなクラスですね。
こいつのコレクションクラスを作る場合はこんな感じです。

<?php

$memberNagai = new Member('nagai', 'nagai@example.com', new \DateTime('2015-07-01'));
$memberShiga = new Member('shiga', 'shiga@example.com', new \DateTime('2019-09-01'));
$memberSawai = new Member('sawai', 'sawai@example.com', new \DateTime('2017-10-01'));

$members = new Collection(Member::class);
$members->add($memberNagai);
$members->add($memberShiga);
$members->add($memberSawai);

分かりやすいですね。
ちなみに、add()を使わずに最初にガーッと追加することも出来ます。

<?php

$memberNagai = new Member('nagai', 'nagai@example.com', new \DateTime('2015-07-01'));
$memberShiga = new Member('shiga', 'shiga@example.com', new \DateTime('2019-09-01'));
$memberSawai = new Member('sawai', 'sawai@example.com', new \DateTime('2017-10-01'));

$members = new Collection(Member::class, [$memberNagai, $memberShiga, $memberSawai]);

最初に指定した型じゃないものを追加しようとすると、ちゃんと例外投げてくれます。

<?php

$members = new Collection(Member::class);
$members->add(new \DateTime());
Ramsey\Collection\Exception\InvalidArgumentException : Value must be of type Path\To\Member;

親切!

クラスじゃなくて、スカラー型にも対応しております。
文字列の場合は、こんな感じ。

<?php

$members = new Collection('string');

便利っす。
文字列の他にも PHPマニュアルの型のページに載っているほとんどの型には対応してそうです。
mixed にも対応していて、個人的にPHPっぽくて良いなって思いました。

Collection以外のクラスたち

  • 値の重複を許さないSetクラス群
  • PHPでいう連想配列的なMapクラス群
  • いわゆるキュー構造のQueueクラス群

全て型による制限を付けられるようになっています。
個人的に Java Collection Framework の Sorted 系があるともっと嬉しいなと思っているのですが、今後に期待しつつ、時間を見つけて自分でも試しに作ってみたいなと思いました。

おわりに

いかがでしたでしょうか。
クラスの内部で使うだけだったら、単なる配列でも良いかなと思いますが、色んな所で使われるようなものであれば、こういったライブラリを利用しても良いんじゃないかなと思います。