/** * {@inheritdoc} */ public function process(ContextInterface $context) { /** @var ConfigContext $context */ /** @var array|null $definition */ $definition = $context->getResult(); if (empty($definition) || ConfigUtil::isExcludeAll($definition)) { // nothing to do return $definition; } $entityClass = $context->getClassName(); if (!$this->doctrineHelper->isManageableEntityClass($entityClass)) { return $definition; } $expandRelations = $context->get(ExpandRelatedEntitiesConfigExtra::NAME); $associations = $this->doctrineHelper->getEntityMetadataForClass($entityClass)->getAssociationMappings(); foreach ($associations as $fieldName => $mapping) { if (!$this->isAssociationCompletionRequired($fieldName, $definition)) { continue; } if (!in_array($fieldName, $expandRelations)) { continue; } $extras = array_filter($context->getExtras(), function ($item) { if ($item instanceof ExpandRelatedEntitiesConfigExtra || $item instanceof FilterFieldsConfigExtra) { return false; } return true; }); $config = $this->configProvider->getConfig($mapping['targetEntity'], $context->getVersion(), $context->getRequestType(), $extras); $definition[ConfigUtil::FIELDS][$fieldName] = $config; } $context->setResult([ConfigUtil::EXCLUSION_POLICY => ConfigUtil::EXCLUSION_POLICY_NONE, ConfigUtil::FIELDS => $definition[ConfigUtil::FIELDS]]); }
/** * @param array $definition * @param string $entityClass * * @return bool */ protected function updateRelations(array &$definition, $entityClass) { $hasChanges = false; $metadata = $this->doctrineHelper->getEntityMetadataForClass($entityClass); foreach ($definition[ConfigUtil::FIELDS] as $fieldName => &$fieldConfig) { if (!is_array($fieldConfig) || empty($fieldConfig[ConfigUtil::DEFINITION][ConfigUtil::FIELDS])) { continue; } $fieldDefinition = $fieldConfig[ConfigUtil::DEFINITION]; if (ConfigUtil::isExclude($fieldDefinition)) { continue; } $propertyPath = ConfigUtil::getPropertyPath($fieldDefinition, $fieldName); if (!$metadata->hasAssociation($propertyPath)) { continue; } $mapping = $metadata->getAssociationMapping($propertyPath); $targetMetadata = $this->doctrineHelper->getEntityMetadataForClass($mapping['targetEntity']); if ($this->isResourceForRelatedEntityAccessible($targetMetadata)) { continue; } $fieldDefinition[ConfigUtil::EXCLUDE] = true; $fieldConfig[ConfigUtil::DEFINITION] = $fieldDefinition; $hasChanges = true; } return $hasChanges; }
/** * @param array $definition * @param string $entityClass * * @return bool */ protected function updateRelations(array &$definition, $entityClass) { $hasChanges = false; $metadata = $this->doctrineHelper->getEntityMetadataForClass($entityClass); foreach ($definition[ConfigUtil::FIELDS] as $fieldName => &$fieldConfig) { if (!is_array($fieldConfig) || empty($fieldConfig[ConfigUtil::DEFINITION][ConfigUtil::FIELDS])) { continue; } $fieldDefinition = $fieldConfig[ConfigUtil::DEFINITION]; $propertyPath = ConfigUtil::getPropertyPath($fieldDefinition, $fieldName); if (!$metadata->hasAssociation($propertyPath)) { continue; } $mapping = $metadata->getAssociationMapping($propertyPath); $targetMetadata = $this->doctrineHelper->getEntityMetadataForClass($mapping['targetEntity']); if ($targetMetadata->inheritanceType === ClassMetadata::INHERITANCE_TYPE_NONE) { continue; } if (!is_array($fieldDefinition[ConfigUtil::FIELDS])) { $fieldDefinition[ConfigUtil::FIELDS] = [$fieldDefinition[ConfigUtil::FIELDS] => null]; } $fieldDefinition[ConfigUtil::FIELDS][ConfigUtil::CLASS_NAME] = null; $fieldConfig[ConfigUtil::DEFINITION] = $fieldDefinition; $hasChanges = true; } return $hasChanges; }
/** * {@inheritdoc} */ public function process(ContextInterface $context) { if ($context->hasResult()) { // a definition is already built return; } $context->setResult(ConfigUtil::getInitialConfig()); }
/** * {@inheritdoc} */ public function process(ContextInterface $context) { /** @var ConfigContext $context */ if ($context->hasFilters()) { // a filters' definition is already built return; } $context->setFilters(ConfigUtil::getInitialConfig()); }
/** * @param string $entityClass * @param string $filterKey * @param array $filterConfig * * @return ConfigInterface|null */ protected function findFieldConfig($entityClass, $filterKey, $filterConfig) { $path = ConfigUtil::explodePropertyPath(isset($filterConfig[ConfigUtil::PROPERTY_PATH]) ? $filterConfig[ConfigUtil::PROPERTY_PATH] : $filterKey); if (count($path) === 1) { return $this->getFieldConfig($entityClass, reset($path)); } $linkedProperty = array_pop($path); $classMetadata = $this->doctrineHelper->findEntityMetadataByPath($entityClass, $path); return null !== $classMetadata ? $this->getFieldConfig($classMetadata->name, $linkedProperty) : null; }
/** * @param EntityMetadata $entityMetadata * @param array $config */ protected function normalizeMetadata(EntityMetadata $entityMetadata, array $config) { $fields = ConfigUtil::getArrayValue($config, ConfigUtil::FIELDS); foreach ($fields as $fieldName => $fieldConfig) { if (!$entityMetadata->hasProperty($fieldName) && null !== $fieldConfig && isset($fieldConfig[ConfigUtil::PROPERTY_PATH])) { $path = ConfigUtil::explodePropertyPath($fieldConfig[ConfigUtil::PROPERTY_PATH]); if (count($path) > 1) { $this->addLinkedProperty($entityMetadata, $fieldName, $path); } } } }
public function testSorters() { $this->assertFalse($this->context->hasSorters()); $this->assertNull($this->context->getSorters()); $sorters = ConfigUtil::getInitialConfig(); $this->context->setSorters($sorters); $this->assertTrue($this->context->hasSorters()); $this->assertEquals($sorters, $this->context->getSorters()); $this->assertEquals($sorters, $this->context->get(SortersConfigExtra::NAME)); $this->context->setSorters(null); $this->assertTrue($this->context->hasSorters()); }
/** * @param array $filters * @param string $entityClass * @param array|null $config * * @return array */ protected function completeFilters(array $filters, $entityClass, $config) { $metadata = $this->doctrineHelper->getEntityMetadataForClass($entityClass); $filters = $this->getFieldFilters($filters, $metadata); $filters = $this->getAssociationFilters($filters, $metadata); if (!empty($config)) { foreach ($filters as $fieldName => &$fieldConfig) { if (ConfigUtil::isExcludedField($config, $fieldName)) { $fieldConfig[ConfigUtil::EXCLUDE] = true; } } } return $filters; }
/** * @param string $entityClass * @param string $version * * @return array|null */ protected function loadConfig($entityClass, $version) { $config = $this->configBag->getRelationConfig($entityClass, $version); if (empty($config) || ConfigUtil::isInherit($config)) { $parentClasses = $this->entityHierarchyProvider->getHierarchyForClassName($entityClass); foreach ($parentClasses as $parentClass) { $parentConfig = $this->configBag->getRelationConfig($parentClass, $version); if (!empty($parentConfig)) { $config = $this->mergeConfigs($parentConfig, $config); if (!ConfigUtil::isInherit($parentConfig)) { break; } } } } return !empty($config) ? $config : null; }
/** * @param array|null $config * * @return array */ protected function getAllowedFields($config) { $fields = []; if (!empty($config[ConfigUtil::FIELDS])) { if (is_array($config[ConfigUtil::FIELDS])) { foreach ($config[ConfigUtil::FIELDS] as $fieldName => $fieldConfig) { if (!is_array($fieldConfig) || !ConfigUtil::isExclude($fieldConfig)) { $propertyPath = ConfigUtil::getPropertyPath($fieldConfig, $fieldName); $fields[$propertyPath] = $fieldName; } } } elseif (is_string($config[ConfigUtil::FIELDS])) { $fields[$config[ConfigUtil::FIELDS]] = $config[ConfigUtil::FIELDS]; } } return $fields; }
/** * @param ConfigContext $context * @param array $fields * @param string $rootEntityClass * @param ClassMetadata $metadata * @param string|null $fieldPath */ protected function processFields(ConfigContext $context, array &$fields, $rootEntityClass, ClassMetadata $metadata, $fieldPath = null) { foreach ($fields as $fieldName => &$fieldConfig) { if (is_array($fieldConfig)) { $propertyPath = !empty($fieldConfig[ConfigUtil::PROPERTY_PATH]) ? $fieldConfig[ConfigUtil::PROPERTY_PATH] : $fieldName; $path = ConfigUtil::explodePropertyPath($propertyPath); if (count($path) === 1) { $this->setFieldCustomizationHandler($context, $fieldConfig, $metadata, $propertyPath, $rootEntityClass, $this->buildFieldPath($fieldName, $fieldPath)); } else { $linkedField = array_pop($path); $linkedMetadata = $this->doctrineHelper->findEntityMetadataByPath($metadata->name, $path); if (null !== $linkedMetadata) { $this->setFieldCustomizationHandler($context, $fieldConfig, $linkedMetadata, $linkedField, $rootEntityClass, $this->buildFieldPath($fieldName, $fieldPath)); } } } } }
/** * @param array $sorters * @param string $entityClass * @param array|null $config * * @return array */ protected function completeSorters(array $sorters, $entityClass, $config) { $metadata = $this->doctrineHelper->getEntityMetadataForClass($entityClass); $fields = array_merge(array_keys($this->doctrineHelper->getIndexedFields($metadata)), array_keys($this->doctrineHelper->getIndexedAssociations($metadata))); foreach ($fields as $fieldName) { if (array_key_exists($fieldName, $sorters)) { // already defined continue; } $sorters[$fieldName] = null; } if (!empty($config)) { foreach ($sorters as $fieldName => &$fieldConfig) { if (ConfigUtil::isExcludedField($config, $fieldName)) { $fieldConfig[ConfigUtil::EXCLUDE] = true; } } } return $sorters; }
/** * @param array $sectionConfig * @param string $sectionName * @param array $definition * @param array $childSectionConfig * @param string $fieldPrefix * @param string $pathPrefix * * @return array */ protected function collectNested(array &$sectionConfig, $sectionName, array $definition, array $childSectionConfig, $fieldPrefix, $pathPrefix) { $this->updatePropertyPath($childSectionConfig, $definition); $fields = ConfigUtil::getArrayValue($childSectionConfig, ConfigUtil::FIELDS); foreach ($fields as $fieldName => $config) { $fieldPath = !empty($config[ConfigUtil::PROPERTY_PATH]) ? $config[ConfigUtil::PROPERTY_PATH] : $fieldName; $field = $fieldPrefix . $fieldName; if (!isset($sectionConfig[ConfigUtil::FIELDS][$field])) { $path = $pathPrefix . $fieldPath; if ($path !== $field) { $config[ConfigUtil::PROPERTY_PATH] = $path; } elseif (is_array($config) && array_key_exists(ConfigUtil::PROPERTY_PATH, $config)) { unset($config[ConfigUtil::PROPERTY_PATH]); } $sectionConfig[ConfigUtil::FIELDS][$field] = $config; } if (is_array($definition) && !empty($definition[ConfigUtil::FIELDS][$fieldName][$sectionName])) { $this->collectNested($sectionConfig, $sectionName, ConfigUtil::getArrayValue($definition[ConfigUtil::FIELDS][$fieldName], ConfigUtil::DEFINITION), $definition[ConfigUtil::FIELDS][$fieldName][$sectionName], $this->buildPrefix($fieldName, $fieldPrefix), $this->buildPrefix($fieldPath, $pathPrefix)); } } }
/** * @param array $definition * @param string $entityClass * @param int $limit */ protected function setLimits(array &$definition, $entityClass, $limit) { if (isset($definition[ConfigUtil::FIELDS]) && is_array($definition[ConfigUtil::FIELDS])) { $metadata = $this->doctrineHelper->getEntityMetadataForClass($entityClass); foreach ($definition[ConfigUtil::FIELDS] as $fieldName => &$fieldConfig) { if (is_array($fieldConfig)) { $propertyPath = ConfigUtil::getPropertyPath($fieldConfig, $fieldName); $path = ConfigUtil::explodePropertyPath($propertyPath); if (count($path) === 1) { $this->setFieldLimit($fieldConfig, $metadata, $propertyPath, $limit); } else { $linkedField = array_pop($path); $linkedMetadata = $this->doctrineHelper->findEntityMetadataByPath($entityClass, $path); if (null !== $linkedMetadata) { $this->setFieldLimit($fieldConfig, $linkedMetadata, $linkedField, $limit); } } } } } }
/** * @param array $sectionConfig * @param string $entityClass * * @return array */ protected function removeDuplicates(array $sectionConfig, $entityClass) { if (empty($sectionConfig[ConfigUtil::FIELDS])) { return $sectionConfig; } $keys = array_keys($sectionConfig[ConfigUtil::FIELDS]); foreach ($keys as $key) { $fieldPath = !empty($sectionConfig[ConfigUtil::FIELDS][$key][ConfigUtil::PROPERTY_PATH]) ? $sectionConfig[ConfigUtil::FIELDS][$key][ConfigUtil::PROPERTY_PATH] : $key; $path = ConfigUtil::explodePropertyPath($fieldPath); if (count($path) === 1) { continue; } $fieldName = array_pop($path); if (array_key_exists(implode(ConfigUtil::PATH_DELIMITER, $path), $sectionConfig[ConfigUtil::FIELDS])) { $metadata = $this->doctrineHelper->findEntityMetadataByPath($entityClass, $path); if (null !== $metadata && in_array($fieldName, $metadata->getIdentifierFieldNames(), true)) { unset($sectionConfig[ConfigUtil::FIELDS][$key]); } } } return $sectionConfig; }
/** * @param EntityMetadata $entityMetadata * @param array $config */ protected function normalizeMetadata(EntityMetadata $entityMetadata, array $config) { $fields = ConfigUtil::getArrayValue($config, ConfigUtil::FIELDS); foreach ($fields as $fieldName => $fieldConfig) { if (null === $fieldConfig) { continue; } if (ConfigUtil::isExclude($fieldConfig)) { $entityMetadata->removeProperty($fieldName); } elseif (isset($fieldConfig[ConfigUtil::PROPERTY_PATH])) { $path = ConfigUtil::explodePropertyPath($fieldConfig[ConfigUtil::PROPERTY_PATH]); if (count($path) === 1) { $entityMetadata->renameProperty(reset($path), $fieldName); } } } if (ConfigUtil::isExcludeAll($config)) { $toRemoveFieldNames = array_diff(array_merge(array_keys($entityMetadata->getFields()), array_keys($entityMetadata->getAssociations())), array_keys($fields)); foreach ($toRemoveFieldNames as $fieldName) { $entityMetadata->removeProperty($fieldName); } } }
/** * {@inheritdoc} */ public function process(ContextInterface $context) { /** @var RelationConfigContext $context */ $config = $context->getResult(); if (null !== $config && ConfigUtil::isRelationInitialized($config)) { // a config already exists return; } $entityClass = $context->getClassName(); if (!$this->doctrineHelper->isManageableEntityClass($entityClass)) { // only manageable entities are supported return; } if (null === $config) { $config = []; } $targetIdFields = $this->doctrineHelper->getEntityIdentifierFieldNamesForClass($entityClass); if (!isset($config[ConfigUtil::EXCLUSION_POLICY])) { $config[ConfigUtil::EXCLUSION_POLICY] = ConfigUtil::EXCLUSION_POLICY_ALL; } $config[ConfigUtil::FIELDS] = count($targetIdFields) === 1 ? reset($targetIdFields) : array_fill_keys($targetIdFields, null); $context->setResult($config); }
/** * {@inheritdoc} */ public function process(ContextInterface $context) { /** @var GetListContext $context */ $configOfFilters = $context->getConfigOfFilters(); if (empty($configOfFilters)) { // a filters' configuration does not contains any data return; } if (!ConfigUtil::isExcludeAll($configOfFilters)) { // it seems that filters' configuration was not normalized throw new \RuntimeException(sprintf('Expected "all" exclusion policy for filters. Got: %s.', ConfigUtil::getExclusionPolicy($configOfFilters))); } $fields = ConfigUtil::getArrayValue($configOfFilters, ConfigUtil::FIELDS); $filters = $context->getFilters(); foreach ($fields as $field => $fieldConfig) { if ($filters->has($field)) { continue; } $filter = $this->createFilter(ConfigUtil::getPropertyPath($fieldConfig, $field), $fieldConfig); if (null !== $filter) { $filters->add($field, $filter); } } }
/** * Checks whether an entity has the given field and it is not marked with 'exclude' attribute. * * @param array $config The config of an entity * @param string $field The name of the field * * @return bool */ public static function isExcludedField(array $config, $field) { $result = false; if (isset($config[ConfigUtil::FIELDS])) { $fields = $config[ConfigUtil::FIELDS]; if (!array_key_exists($field, $fields)) { $result = true; } else { $fieldConfig = $fields[$field]; if (is_array($fieldConfig)) { if (array_key_exists(ConfigUtil::DEFINITION, $fieldConfig)) { $fieldConfig = $fieldConfig[ConfigUtil::DEFINITION]; } if (is_array($fieldConfig) && ConfigUtil::isExclude($fieldConfig)) { $result = true; } } } } return $result; }
/** * @param $fieldName * @param $definition * * @return bool|string */ protected function isAssociationCompletionRequired($fieldName, $definition) { if (!array_key_exists($fieldName, $definition)) { return true; } if (!is_array($definition[$fieldName])) { return false; } if (isset($definition[$fieldName][ConfigUtil::DEFINITION])) { if (null === $definition[$fieldName][ConfigUtil::DEFINITION]) { return true; } if (is_array($definition[$fieldName][ConfigUtil::DEFINITION])) { return false === ConfigUtil::isRelationInitialized($definition[$fieldName][ConfigUtil::DEFINITION]); } } return false === ConfigUtil::isRelationInitialized($definition[$fieldName]); }
/** * @param object $object * @param array $config * @param int $level * * @return array * * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function normalizeObjectByConfig($object, $config, $level) { if (!ConfigUtil::isExcludeAll($config)) { throw new \RuntimeException(sprintf('The "%s" must be "%s".', ConfigUtil::EXCLUSION_POLICY, ConfigUtil::EXCLUSION_POLICY_ALL)); } if (!array_key_exists(ConfigUtil::FIELDS, $config)) { throw new \RuntimeException(sprintf('The "%s" config does not exist.', ConfigUtil::FIELDS)); } $fields = $config[ConfigUtil::FIELDS]; if (!is_array($fields)) { throw new \RuntimeException(sprintf('The "%s" config must be an array.', ConfigUtil::FIELDS)); } $result = []; foreach ($fields as $fieldName => $fieldConfig) { $value = null; if (is_array($fieldConfig)) { if (ConfigUtil::isExclude($fieldConfig)) { continue; } $propertyPath = !empty($fieldConfig[ConfigUtil::PROPERTY_PATH]) ? $fieldConfig[ConfigUtil::PROPERTY_PATH] : $fieldName; if ($this->dataAccessor->tryGetValue($object, $propertyPath, $value) && null !== $value) { $childFields = isset($fieldConfig[ConfigUtil::FIELDS]) ? $fieldConfig[ConfigUtil::FIELDS] : null; if (is_string($childFields)) { if ($value instanceof \Traversable) { $childValue = []; foreach ($value as $val) { $childVal = null; $this->dataAccessor->tryGetValue($val, $childFields, $childVal); $childValue[] = $childVal; } } else { $childValue = null; if (!$this->dataAccessor->tryGetValue($value, $childFields, $childValue)) { continue; } } $value = $childValue; } elseif (is_array($childFields)) { $value = $this->normalizeObjectByConfig($value, $fieldConfig, $level + 1); } } } elseif (!$this->dataAccessor->tryGetValue($object, $fieldName, $value)) { continue; } $result[$fieldName] = $value; } if (isset($config[ConfigUtil::POST_SERIALIZE])) { $result = call_user_func($config[ConfigUtil::POST_SERIALIZE], $result); } return $result; }
public function testLoadMetadata() { $version = '1.1'; $requestType = 'rest'; $entityClass = 'Test\\Class'; $configExtras = [new TestConfigSection('section1'), new TestConfigSection('section2')]; $config = ConfigUtil::getInitialConfig(); $metadata = new EntityMetadata(); $metadataExtras = [new TestMetadataExtra('extra1')]; $this->context->setVersion($version); $this->context->setRequestType($requestType); $this->context->setConfigExtras($configExtras); $this->context->setMetadataExtras($metadataExtras); $this->context->setClassName($entityClass); $this->configProvider->expects($this->once())->method('getConfig')->with($entityClass, $version, [$requestType], $configExtras)->willReturn([ConfigUtil::DEFINITION => $config]); $this->metadataProvider->expects($this->once())->method('getMetadata')->with($entityClass, $version, [$requestType], $metadataExtras, $config)->willReturn($metadata); // test that metadata are not loaded yet $this->assertFalse($this->context->hasMetadata()); $this->assertSame($metadata, $this->context->getMetadata()); // load metadata $this->assertTrue($this->context->hasMetadata()); $this->assertTrue($this->context->has(Context::METADATA)); $this->assertSame($metadata, $this->context->get(Context::METADATA)); $this->assertEquals($config, $this->context->getConfig()); // test that metadata are loaded only once $this->assertSame($metadata, $this->context->getMetadata()); }
/** * @param string $entityClass * @param array $fieldsDefinition * @param array $filterFieldsConfig */ protected function filterAssociations($entityClass, &$fieldsDefinition, &$filterFieldsConfig) { $metadata = $this->doctrineHelper->getEntityMetadataForClass($entityClass); $associationsMapping = $metadata->getAssociationMappings(); foreach ($associationsMapping as $fieldName => $mapping) { $identifierFieldNames = $this->doctrineHelper->getEntityIdentifierFieldNamesForClass($mapping['targetEntity']); if (!isset($filterFieldsConfig[$fieldName]) || (!isset($fieldsDefinition[$fieldName][ConfigUtil::DEFINITION][ConfigUtil::FIELDS]) || !is_array($fieldsDefinition[$fieldName][ConfigUtil::DEFINITION][ConfigUtil::FIELDS]))) { continue; } $associationAllowedFields = $filterFieldsConfig[$fieldName]; foreach ($fieldsDefinition[$fieldName][ConfigUtil::DEFINITION][ConfigUtil::FIELDS] as $name => &$def) { if (in_array($name, $identifierFieldNames, true)) { continue; } if (!in_array($name, $associationAllowedFields, true) && !ConfigUtil::isMetadataProperty($name)) { if (is_array($def)) { $def = array_merge($def, [ConfigUtil::EXCLUDE => true]); } else { $def = [ConfigUtil::EXCLUDE => true]; } } } } }