Doctrine2でSQLを発行する際のカラムのエイリアス変更
久しぶりのブログ更新です。
はい、忙しさにかまけて更新をさぼっていました。すみません。
今回は、Symfony2+Doctrineで開発を行っていた際に困った現象と回避方法をについて
Doctrine2ではDQLやQueryBuilderを使用してSQLを発行するとカラム名+連番のエイリアスをつけて発行します。
例) id , name, tel, faxの4つのカラムを持つperson テーブルの場合
SELECT * FROM person; ↓ DQLで発行すると SELECT p0_.id AS id0, p0_.name AS name1, p0_.tel AS tel2, p0_.fax AS fax3 FROM person AS p0_;
通常はこれで被ることはないだろうという設計なのでしょうが、たまたまこの連番の部分が被ってしまいSQLの結果がおかしくなるケースが発生。
※ ちなみにDoctrine1の場合は、「テーブルエイリアス名+アンダーバー2個+カラム名 」となっています。
今更テーブル構造の見直しを行うのも無理なので、エイリアスの文字列を作成する部分をカスタマイズすることに。
エイリアス名を整形しているクラスは以下。
Doctrine\ORM\Mapping\DefaultQuoteStrategy
ちなみに、Doctrineのconfigクラスから上記クラスを再設定できるようになっているのですが、Symfony2側からはconfigクラスが隠ぺいされてどうしようもなかったのでEntityManagerも独自クラスに変更することに。
1. まずは、DefaultQuoteStrategyを継承したクラスを作成し、getColumnAliasをoverride。
use Doctrine\ORM\Mapping\DefaultQuoteStrategy; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\DBAL\Platforms\AbstractPlatform; class KzlQuoteStrategy extends DefaultQuoteStrategy { /** * {@inheritdoc} */ public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null) { // Trim the column alias to the maximum identifier length of the platform. // If the alias is to long, characters are cut off from the beginning. // And strip non alphanumeric characters // 後ろに連番をつけるだけでは、カラム名がかぶってしまうので間にアンダーバー入れる。 //$columnName = $columnName . $counter; $columnName = $columnName . "__". $counter; $columnName = substr($columnName, -$platform->getMaxIdentifierLength()); $columnName = preg_replace('/[^A-Za-z0-9_]/', '', $columnName); return $platform->getSQLResultCasing($columnName); } }
2. 次に、EntityManagerを継承したクラスを作成し、QuoteStrategyで使用するクラスを変更。
use Doctrine\ORM\EntityManager; use Doctrine\ORM\Configuration; use Doctrine\Common\EventManager; class KzlEntityManager extends EntityManager { /** * * {@inheritdoc} */ public static function create($conn, Configuration $config, EventManager $eventManager = null) { // カラム名のエイリアスを変更するために以下設定を上書き $config->setQuoteStrategy(new KzlQuoteStrategy()); return parent::create($conn, $config, $eventManager); } }
3. 後は、上記EntityMamagerを使用するようにSymfony2の設定を変更します。
app/config/config.ymlに以下を追加
parameters: # EntityManagerを入れ替え doctrine.orm.entity_manager.class: ネームスペース\KzlEntityManager
これでめでたくエイリアスが被るケースを回避できました。