简体   繁体   English

快速实体主义水化器

[英]Fast entity Doctrine hydrator

I'm looking at improving the speed of doctrine hydration. 我正在寻求提高学说水合的速度。 I've previously been using HYDRATE_OBJECT but can see that in many instances, that can be quite heavy to work with. 我以前一直在使用HYDRATE_OBJECT但是在很多情况下可以看到,这可能会非常繁重。

I'm aware that the fastest option available is HYDRATE_ARRAY but then I give away a lot of benefits of working with entity objects. 我知道可用的最快选项是HYDRATE_ARRAY但是我给出了使用实体对象的很多好处。 In instances where there's business logic in an entity method, that's going to be repeated for however that's handled by arrays. 在实体方法中存在业务逻辑的情况下,这将被重复,但是由数组处理。

So what I'm after is a cheaper object hydrator. 所以我所追求的是更便宜的物体保湿剂。 I'm happy to make some concessions and loose some functionality in the name of speed. 我很高兴以速度的名义做出一些让步并放松一些功能。 For instance if it ended up being read only, that'd be ok. 例如,如果它最终只是被读取,那就没问题了。 Equally, if lazy loading wasn't a thing, that would be ok too. 同样,如果延迟加载不是一件事,那也没关系。

Does this sort of thing exist or am I asking too much? 这种事情是存在还是我要求太多?

If you want faster ObjectHydrator without losing the ability to work with objects then you will have to create your own custom hydrator. 如果您想要更快的ObjectHydrator而不会失去使用对象的能力,那么您将不得不创建自己的自定义水合器。

To do so you have to do following steps: 为此,您必须执行以下步骤:

  1. Create your own Hydrator class which extends Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator . 创建自己的Hydrator类,扩展Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator In my case I am extending ArrayHydrator as it saves me trouble of mapping aliases to object variables: 在我的情况下,我正在扩展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. Register hydrator in Doctrine configuration: 在Doctrine配置中注册水化器:

     $config = new \\Doctrine\\ORM\\Configuration() $config->addCustomHydrationMode(Hydrator::HYDRATE_SIMPLE_OBJECT, Hydrator::class); 
  3. Create AbstractEntity with method for populating the entity. 使用填充实体的方法创建AbstractEntity In my sample I am using already created setter methods in the entity to populate it: 在我的示例中,我使用已在实体中创建的setter方法来填充它:

     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); } } } } 

After those three steps you can pass HYDRATE_SIMPLE_OBJECT instead of HYDRATE_OBJECT to getResult query method. 在这三个步骤之后,您可以将HYDRATE_SIMPLE_OBJECT而不是HYDRATE_OBJECTgetResult查询方法。 Keep in mind this implementation was not heavily tested but should work even with nested mappings for more advanced functionality you will have to improve Hydrator::hydrateAllData() and unless you implement connection to EntityManager you will lose the ability to easily save / update entities, while on the other hand because these objects are just mere simple objects, you will be able to serialize and cache them. 请记住,这个实现没有经过严格测试,但是即使使用嵌套映射来实现更高级的功能,你也必须改进Hydrator::hydrateAllData() ,除非你实现与EntityManager连接,否则你将失去轻松保存/更新实体的能力,而另一方面,因为这些对象只是简单的对象,您将能够序列化和缓存它们。

Performance test 性能测试

Test code: 测试代码:

$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);
}

Result based on 940 records with 20~ columns each: 结果基于940条记录,每条记录20~列:

HYDRATE_OBJECT => 0.57511210441589
HYDRATE_ARRAY => 0.19534111022949
HYDRATE_SIMPLE_OBJECT => 0.37919402122498

You might be looking for a way for Doctrine to hydrate DTO's ( Data Transfer Object ). 您可能正在寻找Doctrine水合DTO( 数据传输对象 )的方法。 These are not real entities, but simple read-only objects meant to pass data around. 这些不是真正的实体,而是简单的只读对象,用于传递数据。

Since Doctrine 2.4 it has native support for such hydration using the NEW operator in DQL. 自从Doctrine 2.4以来,它使用DQL中的NEW运算符对这种水合作用进行原生支持。

When you have class like this: 当你有这样的课:

class CustomerDTO
{
    private $name;
    private $email;
    private $city;

    public function __construct($name, $email, $city)
    {
        $this->name  = $name;
        $this->email = $email;
        $this->city  = $city;
    }

    // getters ...
}

You can use SQL like this: 您可以像这样使用SQL:

$query     = $em->createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city) FROM Customer c JOIN c.email e JOIN c.address a');
$customers = $query->getResult();

$customers will then contain an array of CustomerDTO objects. $customers将包含一组CustomerDTO对象。

You can find it here in the documentation . 您可以在文档中找到它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM