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

これでめでたくエイリアスが被るケースを回避できました。