| Total Complexity | 59 |
| Total Lines | 245 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like EagerLoadingExtension often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use EagerLoadingExtension, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 41 | final class EagerLoadingExtension implements ContextAwareQueryCollectionExtensionInterface, QueryItemExtensionInterface |
||
| 42 | { |
||
| 43 | use EagerLoadingTrait; |
||
|
|
|||
| 44 | |||
| 45 | private $propertyNameCollectionFactory; |
||
| 46 | private $propertyMetadataFactory; |
||
| 47 | private $classMetadataFactory; |
||
| 48 | private $maxJoins; |
||
| 49 | private $serializerContextBuilder; |
||
| 50 | private $requestStack; |
||
| 51 | |||
| 52 | /** |
||
| 53 | * @TODO move $fetchPartial after $forceEager (@soyuka) in 3.0 |
||
| 54 | */ |
||
| 55 | public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, int $maxJoins = 30, bool $forceEager = true, RequestStack $requestStack = null, SerializerContextBuilderInterface $serializerContextBuilder = null, bool $fetchPartial = false, ClassMetadataFactoryInterface $classMetadataFactory = null) |
||
| 56 | { |
||
| 57 | if (null !== $this->requestStack) { |
||
| 58 | @trigger_error(sprintf('Passing an instance of "%s" is deprecated since version 2.2 and will be removed in 3.0. Use the data provider\'s context instead.', RequestStack::class), E_USER_DEPRECATED); |
||
| 59 | } |
||
| 60 | if (null !== $this->serializerContextBuilder) { |
||
| 61 | @trigger_error(sprintf('Passing an instance of "%s" is deprecated since version 2.2 and will be removed in 3.0. Use the data provider\'s context instead.', SerializerContextBuilderInterface::class), E_USER_DEPRECATED); |
||
| 62 | } |
||
| 63 | |||
| 64 | $this->propertyNameCollectionFactory = $propertyNameCollectionFactory; |
||
| 65 | $this->propertyMetadataFactory = $propertyMetadataFactory; |
||
| 66 | $this->resourceMetadataFactory = $resourceMetadataFactory; |
||
| 67 | $this->classMetadataFactory = $classMetadataFactory; |
||
| 68 | $this->maxJoins = $maxJoins; |
||
| 69 | $this->forceEager = $forceEager; |
||
| 70 | $this->fetchPartial = $fetchPartial; |
||
| 71 | $this->serializerContextBuilder = $serializerContextBuilder; |
||
| 72 | $this->requestStack = $requestStack; |
||
| 73 | } |
||
| 74 | |||
| 75 | /** |
||
| 76 | * {@inheritdoc} |
||
| 77 | */ |
||
| 78 | public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass = null, string $operationName = null, array $context = []) |
||
| 79 | { |
||
| 80 | $this->apply(true, $queryBuilder, $queryNameGenerator, $resourceClass, $operationName, $context); |
||
| 81 | } |
||
| 82 | |||
| 83 | /** |
||
| 84 | * The context may contain serialization groups which helps defining joined entities that are readable. |
||
| 85 | */ |
||
| 86 | public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, string $operationName = null, array $context = []) |
||
| 87 | { |
||
| 88 | $this->apply(false, $queryBuilder, $queryNameGenerator, $resourceClass, $operationName, $context); |
||
| 89 | } |
||
| 90 | |||
| 91 | private function apply(bool $collection, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass = null, string $operationName = null, array $context) |
||
| 111 | } |
||
| 112 | |||
| 113 | /** |
||
| 114 | * Joins relations to eager load. |
||
| 115 | * |
||
| 116 | * @param bool $wasLeftJoin if the relation containing the new one had a left join, we have to force the new one to left join too |
||
| 117 | * @param int $joinCount the number of joins |
||
| 118 | * @param int $currentDepth the current max depth |
||
| 119 | * |
||
| 120 | * @throws RuntimeException when the max number of joins has been reached |
||
| 121 | */ |
||
| 122 | private function joinRelations(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, bool $forceEager, bool $fetchPartial, string $parentAlias, array $options = [], array $normalizationContext = [], bool $wasLeftJoin = false, int &$joinCount = 0, int $currentDepth = null) |
||
| 221 | } |
||
| 222 | } |
||
| 223 | |||
| 224 | private function addSelect(QueryBuilder $queryBuilder, string $entity, string $associationAlias, array $propertyMetadataOptions) |
||
| 225 | { |
||
| 226 | $select = []; |
||
| 227 | $entityManager = $queryBuilder->getEntityManager(); |
||
| 228 | $targetClassMetadata = $entityManager->getClassMetadata($entity); |
||
| 229 | if (!empty($targetClassMetadata->subClasses)) { |
||
| 230 | $queryBuilder->addSelect($associationAlias); |
||
| 231 | |||
| 232 | return; |
||
| 233 | } |
||
| 234 | |||
| 235 | foreach ($this->propertyNameCollectionFactory->create($entity) as $property) { |
||
| 236 | $propertyMetadata = $this->propertyMetadataFactory->create($entity, $property, $propertyMetadataOptions); |
||
| 237 | |||
| 238 | if (true === $propertyMetadata->isIdentifier()) { |
||
| 239 | $select[] = $property; |
||
| 240 | continue; |
||
| 241 | } |
||
| 242 | |||
| 243 | //the field test allows to add methods to a Resource which do not reflect real database fields |
||
| 244 | if ($targetClassMetadata->hasField($property) && (true === $propertyMetadata->getAttribute('fetchable') || $propertyMetadata->isReadable())) { |
||
| 245 | $select[] = $property; |
||
| 246 | } |
||
| 247 | |||
| 248 | if (!array_key_exists($property, $targetClassMetadata->embeddedClasses)) { |
||
| 249 | continue; |
||
| 250 | } |
||
| 251 | |||
| 252 | foreach ($this->propertyNameCollectionFactory->create($targetClassMetadata->embeddedClasses[$property]['class']) as $embeddedProperty) { |
||
| 253 | $propertyMetadata = $this->propertyMetadataFactory->create($entity, $property, $propertyMetadataOptions); |
||
| 254 | $propertyName = "$property.$embeddedProperty"; |
||
| 255 | if ($targetClassMetadata->hasField($propertyName) && (true === $propertyMetadata->getAttribute('fetchable') || $propertyMetadata->isReadable())) { |
||
| 256 | $select[] = $propertyName; |
||
| 257 | } |
||
| 258 | } |
||
| 259 | } |
||
| 260 | |||
| 261 | $queryBuilder->addSelect(sprintf('partial %s.{%s}', $associationAlias, implode(',', $select))); |
||
| 262 | } |
||
| 263 | |||
| 264 | /** |
||
| 265 | * Gets the serializer context. |
||
| 266 | * |
||
| 267 | * @param string $contextType normalization_context or denormalization_context |
||
| 268 | * @param array $options represents the operation name so that groups are the one of the specific operation |
||
| 269 | */ |
||
| 270 | private function getNormalizationContext(string $resourceClass, string $contextType, array $options): array |
||
| 286 | } |
||
| 287 | } |
||
| 288 |