[英]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: 为此,您必须执行以下步骤:
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; } }
Register hydrator in Doctrine configuration: 在Doctrine配置中注册水化器:
$config = new \\Doctrine\\ORM\\Configuration() $config->addCustomHydrationMode(Hydrator::HYDRATE_SIMPLE_OBJECT, Hydrator::class);
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_OBJECT
给getResult
查询方法。 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
连接,否则你将失去轻松保存/更新实体的能力,而另一方面,因为这些对象只是简单的对象,您将能够序列化和缓存它们。
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.