PosAddressConfigBuilder.php
Tue Oct 18 2022 22:11:26 GMT+0000 (Coordinated Universal Time)
<?php
namespace Drupal\pos_field_address;
use Drupal\Core\Cache\Cache;
use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Messenger\MessengerInterface;
/**
* Class PosAddressConfigBuilder.
*/
class PosAddressConfigBuilder {
const COUNTRIES = 'countries.yml';
/**
* Cache backend.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
/**
* Messenger.
*
* @var \Drupal\Core\Messenger\MessengerInterface
*/
protected $messenger;
/**
* PosAddressConfigBuilder constructor.
*
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* Cache.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* Messenger.
*/
public function __construct(CacheBackendInterface $cache, MessengerInterface $messenger) {
$this->cache = $cache;
$this->messenger = $messenger;
}
/**
* Parse file.
*
* @param string $file
* Filename.
*
* @return bool|mixed
* File content.
*/
private function parseFile($file) {
$cache_name = 'address__' . str_replace('.yml', '', $file);
if ($cached = $this->cache->get($cache_name)) {
return $cached->data;
}
$path = realpath(__DIR__) . '/../values/' . $file;
if (!is_file($path)) {
return FALSE;
}
$content = file_get_contents($path);
if ($content === FALSE) {
return $content;
}
$content = Yaml::decode($content);
$this->cache->set($cache_name, $content, Cache::PERMANENT);
return $content;
}
/**
* Get country codes.
*
* @return array
* Country codes.
*/
public function getCountryCodes() {
return array_keys($this->getCountriesDetailed());
}
/**
* Get country list.
*
* @param array $allowed_countries
* Allowed countries.
*
* @return array|bool|mixed
* Countries.
*/
public function getCountries(array $allowed_countries = []) {
$countries = $this->getCountriesDetailed();
foreach ($countries as $country_code => $country) {
if (empty($allowed_countries) || in_array($country_code, $allowed_countries)) {
$countries[$country_code] = $country['country'];
}
else {
unset($countries[$country_code]);
}
}
return $countries;
}
/**
* Detailed country data.
*
* @return array|bool|mixed
* Data.
*/
public function getCountriesDetailed() {
$countries_detailed = &drupal_static('address_countries_detailed');
if (!isset($countries_detailed)) {
$countries_detailed = $this->parseFile(self::COUNTRIES);
}
return $countries_detailed;
}
/**
* Country schema.
*
* @param string $country_code
* Country code.
*
* @return bool|mixed
* Schema.
*/
public function getCountrySchema($country_code, $force_input_schema = FALSE) {
$countries = $this->getCountriesDetailed();
if ($countries === FALSE || !array_key_exists($country_code, $countries)) {
return FALSE;
}
if ($force_input_schema) {
return ['input' => t('Input')];
}
return $countries[$country_code]['schema'];
}
public function isOriginallyInputCountrySchema($country_code) {
$schema = $this->getCountrySchema($country_code);
if ($schema === FALSE) {
return TRUE;
}
return array_key_exists('input', $schema);
}
/**
* Check if country schema exists.
*
* @param string $country_code
* Country code.
*
* @return bool
* Does schema exist?
*/
public function countrySchemaExists($country_code) {
$countries = $this->getCountriesDetailed();
if ($countries === FALSE || !array_key_exists($country_code, $countries)) {
return FALSE;
}
return TRUE;
}
/**
* Returns tree for single country.
*/
public function getTree($country_code) {
$tree = &drupal_static('address_countries_schema_' . $country_code);
if (!isset($tree)) {
$tree = $this->parseFile($country_code . '.yml');
}
if ($tree === FALSE) {
return [];
}
return $tree === FALSE ? [] : $tree;
}
/**
* Returns data at depth.
*/
public function getCurrentDepthData($country_code, $data = [], $filter = FALSE, $filter_cache_context = FALSE, $force_input_schema = FALSE) {
$microtime = microtime(TRUE);
$tree = $this->cache->get($filter_cache_context);
if ($filter !== FALSE && $filter_cache_context !== FALSE && $tree) {
$tree = $tree->data;
}
else {
$tree = $this->getTree($country_code);
if ($filter !== FALSE) {
$this->filterTree($tree, $filter, $country_code);
if ($filter_cache_context !== FALSE) {
$this->cache->set($filter_cache_context, $tree);
}
}
}
$schema = $this->getCountrySchema($country_code, $force_input_schema);
if ($schema === FALSE) {
return FALSE;
}
$return_data = [];
$current_depth_data = $tree;
$return_data['country'] = $tree;
if (empty($schema)) {
return [];
}
elseif (key($schema) == 'input') {
return $return_data['country'];
}
do {
if (current($data) !== FALSE) {
$current_code = current($data);
}
else {
$current_code = key($current_depth_data[key($schema)]);
}
$current_depth_data = $current_depth_data[key($schema)];
if (!array_key_exists($current_code, $current_depth_data)) {
$current_code = key($current_depth_data);
}
$return_data[key($schema)] = $current_depth_data[$current_code];
$return_data[key($schema)]['code'] = $current_code;
$current_depth_data = $current_depth_data[$current_code];
if ((microtime(TRUE) - $microtime) > 2) {
$this->messenger->addMessage('Report this problem to administrator');
break;
}
next($data);
} while (next($schema) !== FALSE);
return $return_data;
}
/**
* Filters tree.
*/
private function filterTree(&$tree, $filter, $country_code) {
if (!is_array($filter)) {
return $tree;
}
$schema = $this->getCountrySchema($country_code);
foreach ($filter as $key => $value) {
if ($value[0] !== $country_code) {
unset($filter[$key]);
}
}
foreach ($schema as $schema_key => $label) {
$this->unsetUnexisting($tree, $filter, $schema, $schema_key);
}
}
/**
* Deletes locations not in filter.
*/
private function unsetUnexisting(&$tree, $filter, $schema, $schema_key) {
$tree_copy = $tree;
$filter_key = 1;
reset($schema);
$this->goToCurrentDepth($tree[key($schema)], $filter, $schema, $filter_key, $schema_key);
return $tree_copy;
}
/**
* Go through the depth.
*
* @param array $tree
* Tree.
* @param mixed $filter
* Filter.
* @param array $schema
* Schema.
* @param mixed $filter_key
* Filter key.
* @param mixed $schema_key
* Schema key.
*/
private function goToCurrentDepth(array &$tree, &$filter, array &$schema, &$filter_key, $schema_key) {
if (key($schema) === $schema_key) {
foreach ($tree as $key => $value) {
$found = FALSE;
foreach ($filter as $filter_code) {
if ($filter_code[$filter_key] == $key) {
$found = TRUE;
break;
}
}
if (!$found) {
unset($tree[$key]);
}
}
}
else {
next($schema);
$current_key = key($schema);
$filter_key++;
$current_filter = $filter_key;
foreach ($tree as $code_key => $code) {
$this->goToCurrentDepth($tree[$code_key][$current_key], $filter, $schema, $filter_key, $schema_key);
$filter_key = $current_filter;
reset($schema);
while (key($schema) !== $current_key) {
next($schema);
}
}
}
}
/**
* Get select schema.
*
* @param string $country_code
* Country code.
*
* @return array
* Schema.
*/
public function getSelectSchema($country_code) {
$tree = $this->getTree($country_code);
$schema = $this->getCountrySchema($country_code);
$values = [];
$codes = [];
end($schema);
$this->getSubnames($tree, key($schema), $values, $codes, TRUE, $country_code);
return array_combine($codes, $values);
}
/**
* Get subnames.
*
* @param array $current_tree
* Current tree.
* @param mixed $last_schema_key
* Last schema key.
* @param array $values
* Values.
* @param array $codes
* Codes.
* @param mixed $first
* First.
* @param string $country_code
* Country code.
*
* @return array
* Subnames.
*/
private function getSubnames(array $current_tree, $last_schema_key, array &$values, array &$codes, $first, $country_code = '') {
$keys = array_keys($current_tree);
if (!array_key_exists($country_code, $this->getCountriesDetailed())) {
$country_name = '';
}
else {
$country_name = $this->getCountriesDetailed()[$country_code]['country'];
}
$schema_key = FALSE;
foreach ($keys as $key) {
if ($key != 'name') {
$schema_key = $key;
}
}
$subnames = [];
if (empty($current_tree)) {
return [];
}
foreach ($current_tree[$schema_key] as $code => $tree) {
if ($first) {
$subnames = $this->getSubnames($tree, $last_schema_key, $values, $codes, FALSE);
foreach ($subnames as $subname) {
$values[] = $country_name . '->' . $tree['name'] . '->' . $subname['name'];
$codes[] = $country_code . '{||}' . $code . '{||}' . $subname['code'];
}
}
elseif ($last_schema_key === $schema_key) {
$subnames[] = ['name' => $tree['name'], 'code' => $code];
}
else {
$return_value = $this->getSubnames($tree, $last_schema_key, $values, $codes, FALSE);
foreach ($return_value as $subname) {
$subnames[] = [
'name' => $tree['name'] . '->' . $subname['name'],
'code' => $code . '{||}' . $subname['code'],
];
}
}
}
if (!$first) {
return $subnames;
}
}
/**
* Get all options for country schema element.
*
* @param string $country_code
* Country code.
* @param mixed $schema_key
* Schema key.
*
* @return array
* Options.
*/
public function getAllOptionsForCountrySchemaElement($country_code, $schema_key) {
$tree = $this->getTree($country_code);
$country_schema = $this->getCountrySchema($country_code);
$values = [];
if (!empty($country_schema)) {
$this->getAllValuesToGivenDepth($tree[key($country_schema)], $country_schema, $schema_key, $values);
}
return $values;
}
/**
* Get all values to given depth.
*
* @param array $tree
* Tree.
* @param array $country_schema
* Schema.
* @param mixed $schema_key
* Schema key.
* @param array $values
* Values.
* @param array $current_depth_value
* Current value.
*/
public function getAllValuesToGivenDepth(array $tree, array &$country_schema, $schema_key, array &$values, array $current_depth_value = []) {
$current_key_schema = key($country_schema);
next($country_schema);
foreach ($tree as $value => $sub_tree) {
$current_depth_value[$current_key_schema] = (string) $value;
if ($schema_key === $current_key_schema) {
$values[] = [
'name' => $sub_tree['name'],
'value' => $current_depth_value,
];
}
else {
$this->getAllValuesToGivenDepth($sub_tree[key($country_schema)], $country_schema, $schema_key, $values, $current_depth_value);
}
}
prev($country_schema);
}
/**
* Validates if data is valid.
*/
public function validateValue($country_code, $data) {
$tree = $this->getTree($country_code);
$schema = $this->getCountrySchema($country_code);
if (key($schema) === 'input' && is_string($data)) {
return TRUE;
}
if (count($data) == 0 && !empty($tree)) {
return TRUE;
}
elseif (count($data) == 0 && empty($tree)) {
return FALSE;
}
foreach ($schema as $schema_key => $label) {
if (array_key_exists(current($data), $tree[$schema_key])) {
$tree = $tree[$schema_key][current($data)];
}
else {
return FALSE;
}
if (next($data) === FALSE) {
break;
}
}
return TRUE;
}
/**
* Returns all schema keys that exists.
*/
public function getAllSchemaKeys() {
$detailed_countries = $this->getCountriesDetailed();
$all_schema_keys = [];
foreach ($detailed_countries as $detailed_country) {
foreach ($detailed_country['schema'] as $key => $label) {
if (!in_array($key, $all_schema_keys)) {
$all_schema_keys[] = $key;
}
}
}
return $all_schema_keys;
}
}



Comments