self::F_COUNTRY, 'postal_code' => self::F_POSTAL_CODE, 'street' => self::F_STREET, 'building' => self::F_BUILDING, 'string' => self::F_AS_STRING, 'geom' => self::F_GEOM, 'attributes' => self::F_ATTRIBUTES, 'geographical_units' => self::F_GEOGRAPHICAL_UNITS, ]; private const COLUMN_MAPPING = [ 'country' => ['country'], 'postal_code' => ['postcode_code', 'postcode_name'], 'street' => ['street', 'streetNumber'], 'building' => ['buildingName', 'corridor', 'distribution', 'extra', 'flat', 'floor', 'steps'], 'string' => ['_as_string'], 'attributes' => ['isNoAddress', 'confidential', 'id'], 'geom' => ['_lat', '_lon'], 'geographical_units' => ['_unit_names', '_unit_refs'], ]; private AddressRender $addressRender; private AddressRepository $addressRepository; private GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository; private TranslatableStringHelperInterface $translatableStringHelper; /** * @var array>|null */ private ?array $unitNamesKeysCache = []; /** * @var array>|null */ private ?array $unitRefsKeysCache = []; public function __construct( AddressRender $addressRender, AddressRepository $addressRepository, GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository, TranslatableStringHelperInterface $translatableStringHelper ) { $this->addressRepository = $addressRepository; $this->geographicalUnitLayerRepository = $geographicalUnitLayerRepository; $this->translatableStringHelper = $translatableStringHelper; $this->addressRender = $addressRender; } public function addSelectClauses(int $params, QueryBuilder $queryBuilder, $entityName = 'address', $prefix = 'add') { foreach (self::ALL as $key => $bitmask) { if (($params & $bitmask) === $bitmask) { foreach (self::COLUMN_MAPPING[$key] as $field) { switch ($field) { case 'id': case '_as_string': $queryBuilder->addSelect(sprintf('%s.id AS %s%s', $entityName, $prefix, $field)); break; case 'street': case 'streetNumber': case 'floor': case 'corridor': case 'steps': case 'buildingName': case 'flat': case 'distribution': case 'extra': $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $entityName, $field, $prefix, $field)); break; case 'country': case 'postcode_name': case 'postcode_code': $postCodeAlias = sprintf('%spostcode_t', $prefix); if (!in_array($postCodeAlias, $queryBuilder->getAllAliases(), true)) { $queryBuilder->leftJoin($entityName . '.postcode', $postCodeAlias); } if ('postcode_name' === $field) { $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $postCodeAlias, 'name', $prefix, $field)); break; } if ('postcode_code' === $field) { $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $postCodeAlias, 'code', $prefix, $field)); break; } $countryAlias = sprintf('%scountry_t', $prefix); if (!in_array($countryAlias, $queryBuilder->getAllAliases(), true)) { $queryBuilder->leftJoin(sprintf('%s.country', $postCodeAlias), $countryAlias); } $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $countryAlias, 'name', $prefix, $field)); break; case 'isNoAddress': case 'confidential': $queryBuilder->addSelect(sprintf('CASE WHEN %s.%s = \'TRUE\' THEN 1 ELSE 0 END AS %s%s', $entityName, $field, $prefix, $field)); break; case '_lat': $queryBuilder->addSelect(sprintf('ST_Y(%s.point) AS %s%s', $entityName, $prefix, $field)); break; case '_lon': $queryBuilder->addSelect(sprintf('ST_X(%s.point) AS %s%s', $entityName, $prefix, $field)); break; case '_unit_names': foreach ($this->generateKeysForUnitsNames($prefix) as $alias => $layer) { $queryBuilder ->addSelect( sprintf( '(SELECT AGGREGATE(u_n_%s_%s.unitName) FROM %s u_n_%s_%s WHERE u_n_%s_%s MEMBER OF %s.geographicalUnits AND u_n_%s_%s.layer = :layer_%s_%s) AS %s', $prefix, $layer->getId(), GeographicalUnit::class, $prefix, $layer->getId(), $prefix, $layer->getId(), $entityName, $prefix, $layer->getId(), $prefix, $layer->getId(), $alias ) ) ->setParameter(sprintf('layer_%s_%s', $prefix, $layer->getId()), $layer); } break; case '_unit_refs': foreach ($this->generateKeysForUnitsRefs($prefix) as $alias => $layer) { $queryBuilder ->addSelect( sprintf( '(SELECT AGGREGATE(u_r_%s_%s.unitRefId) FROM %s u_r_%s_%s WHERE u_r_%s_%s MEMBER OF %s.geographicalUnits AND u_r_%s_%s.layer = :layer_%s_%s) AS %s', $prefix, $layer->getId(), GeographicalUnit::class, $prefix, $layer->getId(), $prefix, $layer->getId(), $entityName, $prefix, $layer->getId(), $prefix, $layer->getId(), $alias ) ) ->setParameter(sprintf('layer_%s_%s', $prefix, $layer->getId()), $layer); } break; default: throw new LogicException(sprintf('This key is not supported: %s, field %s', $key, $field)); } } } } } /** * @param self::F_* $params * * @return array|string[] */ public function getKeys(int $params, string $prefix = ''): array { $prefixes = []; foreach (self::ALL as $key => $bitmask) { if (($params & $bitmask) === $bitmask) { if ('geographical_units' === $key) { // geographical unit generate keys dynamically, depending on layers $prefixes = array_merge($prefixes, array_keys($this->generateKeysForUnitsNames($prefix)), array_keys($this->generateKeysForUnitsRefs($prefix))); continue; } $prefixes = array_merge( $prefixes, array_map( static function ($item) use ($prefix) { return $prefix . $item; }, self::COLUMN_MAPPING[$key] ) ); } } return $prefixes; } public function getLabel($key, array $values, $data, string $prefix = '', string $translationPrefix = 'export.address_helper.'): callable { $sanitizedKey = substr($key, strlen($prefix)); switch ($sanitizedKey) { case 'id': case 'street': case 'streetNumber': case 'buildingName': case 'corridor': case 'distribution': case 'extra': case 'flat': case 'floor': case '_lat': case '_lon': case 'steps': case 'postcode_code': case 'postcode_name': return static function ($value) use ($sanitizedKey, $translationPrefix) { if ('_header' === $value) { return $translationPrefix . $sanitizedKey; } if (null === $value) { return ''; } return $value; }; case 'country': return function ($value) use ($key) { if ('_header' === $value) { return 'export.list.acp' . $key; } if (null === $value) { return ''; } return $this->translatableStringHelper->localize(json_decode($value, true)); }; case 'isNoAddress': case 'confidential': return static function ($value) use ($sanitizedKey, $translationPrefix) { if ('_header' === $value) { return $translationPrefix . $sanitizedKey; } switch ($value) { case null: return ''; case true: return 1; case false: return 0; default: throw new LogicException('this value is not supported for ' . $sanitizedKey . ': ' . $value); } }; case '_as_string': return function ($value) use ($sanitizedKey, $translationPrefix) { if ('_header' === $value) { return $translationPrefix . $sanitizedKey; } if (null === $value) { return ''; } $address = $this->addressRepository->find($value); return $this->addressRender->renderString($address, []); }; default: $layerNamesKeys = array_merge($this->generateKeysForUnitsNames($prefix), $this->generateKeysForUnitsRefs($prefix)); if (array_key_exists($key, $layerNamesKeys)) { return function ($value) use ($key, $layerNamesKeys) { if ('_header' === $value) { $header = $this->translatableStringHelper->localize($layerNamesKeys[$key]->getName()); if (str_contains($key, 'unit_ref')) { $header .= ' (id)'; } return $header; } if (null === $value) { return ''; } $decodedValues = json_decode($value, true); switch (count($decodedValues)) { case 0: return ''; case 1: return $decodedValues[0]; default: return implode('|', $decodedValues); } }; } throw new LogicException('this key is not supported: ' . $sanitizedKey); } } /** * @return array */ private function generateKeysForUnitsNames(string $prefix): array { if (array_key_exists($prefix, $this->unitNamesKeysCache)) { return $this->unitNamesKeysCache[$prefix]; } $keys = []; foreach ($this->geographicalUnitLayerRepository->findAllHavingUnits() as $layer) { $keys[$prefix . 'unit_names_' . $layer->getId()] = $layer; } return $this->unitNamesKeysCache[$prefix] = $keys; } /** * @return array */ private function generateKeysForUnitsRefs(string $prefix): array { if (array_key_exists($prefix, $this->unitRefsKeysCache)) { return $this->unitRefsKeysCache[$prefix]; } $keys = []; foreach ($this->geographicalUnitLayerRepository->findAllHavingUnits() as $layer) { $keys[$prefix . 'unit_refs_' . $layer->getId()] = $layer; } return $this->unitRefsKeysCache[$prefix] = $keys; } }