15

教義の水分補給の速度を改善することを検討しています。私は以前に使用していましHYDRATE_OBJECTたが、多くの場合、作業が非常に重くなることがわかりました。

利用可能な最速のオプションが であることは承知していますがHYDRATE_ARRAY、エンティティ オブジェクトを操作することには多くのメリットがあります。エンティティ メソッドにビジネス ロジックがあるインスタンスでは、それが繰り返されますが、それは配列によって処理されます。

だから私が求めているのは、より安価なオブジェクト ハイドレーターです。いくつかの譲歩をして、速度の名の下にいくつかの機能を失うことを嬉しく思います。たとえば、読み取り専用になった場合でも問題ありません。同様に、遅延読み込みが問題でなければ、それも問題ありません。

この種のものは存在しますか、それとも私はあまりにも多くを求めていますか?

4

2 に答える 2

16

オブジェクトを操作する能力を失わずに高速化したい場合はObjectHydrator、独自のカスタム ハイドレーターを作成する必要があります。

そのためには、次の手順を実行する必要があります。

  1. Hydratorを拡張する独自のクラスを作成しますDoctrine\ORM\Internal\Hydration\AbstractHydrator。私の場合、ArrayHydratorエイリアスをオブジェクト変数にマッピングする手間を省くために拡張しています。

    use Doctrine\ORM\Internal\Hydration\ArrayHydrator;
    use Doctrine\ORM\Mapping\ClassMetadataInfo;
    use PDO;
    
    class Hydrator extends ArrayHydrator
    {
        const HYDRATE_SIMPLE_OBJECT = 55;
    
        protected function hydrateAllData()
        {
            $entityClassName = reset($this->_rsm->aliasMap);
            $entity = new $entityClassName();
            $entities = [];
            foreach (parent::hydrateAllData() as $data) {
                $entities[] = $this->hydrateEntity(clone $entity, $data);
            }
    
            return $entities;
        }
    
        protected function hydrateEntity(AbstractEntity $entity, array $data)
        {
            $classMetaData = $this->getClassMetadata(get_class($entity));
            foreach ($data as $fieldName => $value) {
                if ($classMetaData->hasAssociation($fieldName)) {
                    $associationData = $classMetaData->getAssociationMapping($fieldName);
                    switch ($associationData['type']) {
                        case ClassMetadataInfo::ONE_TO_ONE:
                        case ClassMetadataInfo::MANY_TO_ONE:
                            $data[$fieldName] = $this->hydrateEntity(new $associationData['targetEntity'](), $value);
                            break;
                        case ClassMetadataInfo::MANY_TO_MANY:
                        case ClassMetadataInfo::ONE_TO_MANY:
                            $entities = [];
                            $targetEntity = new $associationData['targetEntity']();
                            foreach ($value as $associatedEntityData) {
                                $entities[] = $this->hydrateEntity(clone $targetEntity, $associatedEntityData);
                            }
                            $data[$fieldName] = $entities;
                            break;
                        default:
                            throw new \RuntimeException('Unsupported association type');
                    }
                }
            }
            $entity->populate($data);
    
            return $entity;
        }
    }
    
  2. Doctrine 構成でハイドレーターを登録します。

    $config = new \Doctrine\ORM\Configuration()
    $config->addCustomHydrationMode(Hydrator::HYDRATE_SIMPLE_OBJECT, Hydrator::class);
    
  3. AbstractEntityエンティティを移入するメソッドを使用して作成します。私のサンプルでは、​​エンティティに既に作成されているセッター メソッドを使用して、エンティティにデータを入力しています。

    abstract class AbstractEntity
    {
        public function populate(Array $data)
        {
            foreach ($data as $field => $value) {
                $setter = 'set' . ucfirst($field);
                if (method_exists($this, $setter)) {
                    $this->{$setter}($value);
                }
            }
        }
    }
    

これらの 3 つの手順の後、クエリ メソッドHYDRATE_SIMPLE_OBJECTの代わりに渡すことができます。この実装は十分にテストされていませんが、より高度な機能のためにネストされたマッピングでも機能するはずであり、接続を実装しない限り、エンティティを簡単に保存/更新する機能が失われることに注意してください。オブジェクトは単なる単純なオブジェクトであるため、シリアライズしてキャッシュすることができます。HYDRATE_OBJECTgetResultHydrator::hydrateAllData()EntityManager

性能テスト

テストコード:

$hydrators = [
    'HYDRATE_OBJECT'        => \Doctrine\ORM\AbstractQuery::HYDRATE_OBJECT,
    'HYDRATE_ARRAY'         => \Doctrine\ORM\AbstractQuery::HYDRATE_ARRAY,
    'HYDRATE_SIMPLE_OBJECT' => Hydrator::HYDRATE_SIMPLE_OBJECT,
];

$queryBuilder = $repository->createQueryBuilder('u');
foreach ($hydrators as $name => $hydrator) {
    $start = microtime(true);
    $queryBuilder->getQuery()->getResult($hydrator);
    $end = microtime(true);
    printf('%s => %s <br/>', $name, $end - $start);
}

それぞれ 20~ 列の 940 レコードに基づく結果:

HYDRATE_OBJECT => 0.57511210441589
HYDRATE_ARRAY => 0.19534111022949
HYDRATE_SIMPLE_OBJECT => 0.37919402122498
于 2016-01-22T00:12:47.903 に答える