View source
<?php
namespace Drupal\typed_data;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Render\AttachmentsInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
use Drupal\Core\TypedData\ComplexDataInterface;
use Drupal\Core\TypedData\DataDefinitionInterface;
use Drupal\Core\TypedData\DataReferenceDefinitionInterface;
use Drupal\Core\TypedData\DataReferenceInterface;
use Drupal\Core\TypedData\Exception\MissingDataException;
use Drupal\Core\TypedData\ListDataDefinitionInterface;
use Drupal\Core\TypedData\ListInterface;
use Drupal\Core\TypedData\PrimitiveInterface;
use Drupal\Core\TypedData\TranslatableInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\typed_data\Exception\InvalidArgumentException;
class DataFetcher implements DataFetcherInterface {
public function fetchDataByPropertyPath(TypedDataInterface $typed_data, $property_path, BubbleableMetadata $bubbleable_metadata = NULL, $langcode = NULL) {
$sub_paths = explode('.', $property_path);
return $this
->fetchDataBySubPaths($typed_data, $sub_paths, $bubbleable_metadata, $langcode);
}
public function fetchDataBySubPaths(TypedDataInterface $typed_data, array $sub_paths, BubbleableMetadata $bubbleable_metadata = NULL, $langcode = NULL) {
$current_selector = [];
$bubbleable_metadata = $bubbleable_metadata ?: new BubbleableMetadata();
try {
foreach ($sub_paths as $name) {
$current_selector[] = $name;
if ($typed_data instanceof DataReferenceInterface) {
$this
->addBubbleableMetadata($typed_data, $bubbleable_metadata);
$typed_data = $typed_data
->getTarget();
if ($typed_data === NULL) {
throw new MissingDataException("The specified reference is NULL.");
}
}
if (isset($langcode) && $typed_data instanceof TranslatableInterface) {
if ($typed_data
->hasTranslation($langcode)) {
$typed_data = $typed_data
->getTranslation($langcode);
}
}
if ($typed_data instanceof ListInterface && !ctype_digit($name)) {
$this
->addBubbleableMetadata($typed_data, $bubbleable_metadata);
$typed_data = $typed_data
->get(0);
}
if ($typed_data instanceof ListInterface || $typed_data instanceof ComplexDataInterface) {
$this
->addBubbleableMetadata($typed_data, $bubbleable_metadata);
$typed_data = $typed_data
->get($name);
}
else {
$current_selector_string = implode('.', $current_selector);
throw new InvalidArgumentException("The parent property is not a list or a complex structure at '{$current_selector_string}'.");
}
if (!isset($typed_data)) {
$selector_string = implode('.', $sub_paths);
$current_selector_string = implode('.', $current_selector);
throw new MissingDataException("Unable to apply data selector '{$selector_string}' at '{$current_selector_string}'");
}
}
$this
->addBubbleableMetadata($typed_data, $bubbleable_metadata);
return $typed_data;
} catch (MissingDataException $e) {
$selector = implode('.', $sub_paths);
$current_selector = implode('.', $current_selector);
throw new MissingDataException("Unable to apply data selector '{$selector}' at '{$current_selector}': " . $e
->getMessage());
} catch (\InvalidArgumentException $e) {
$selector = implode('.', $sub_paths);
$current_selector = implode('.', $current_selector);
throw new InvalidArgumentException("Unable to apply data selector '{$selector}' at '{$current_selector}': " . $e
->getMessage());
}
}
public function fetchDefinitionByPropertyPath(DataDefinitionInterface $data_definition, $property_path, $langcode = NULL) {
$sub_paths = explode('.', $property_path);
return $this
->fetchDefinitionBySubPaths($data_definition, $sub_paths, $langcode);
}
public function fetchDefinitionBySubPaths(DataDefinitionInterface $data_definition, array $sub_paths, $langcode = NULL) {
$current_selector = [];
foreach ($sub_paths as $name) {
$current_selector[] = $name;
if ($data_definition instanceof DataReferenceDefinitionInterface) {
$data_definition = $data_definition
->getTargetDefinition();
}
if ($data_definition instanceof ListDataDefinitionInterface && !ctype_digit($name)) {
$data_definition = $data_definition
->getItemDefinition();
}
if ($data_definition instanceof ComplexDataDefinitionInterface) {
$data_definition = $data_definition
->getPropertyDefinition($name);
}
elseif ($data_definition instanceof ListDataDefinitionInterface) {
$data_definition = $data_definition
->getItemDefinition();
}
else {
$current_selector_string = implode('.', $current_selector);
if (count($current_selector) > 1) {
$parent_property = $current_selector[count($current_selector) - 2];
throw new InvalidArgumentException("The data selector '{$current_selector_string}' cannot be applied because the parent property '{$parent_property}' is not a list or a complex structure");
}
else {
$type = $data_definition
->getDataType();
throw new InvalidArgumentException("The data selector '{$current_selector_string}' cannot be applied because the definition of type '{$type}' is not a list or a complex structure");
}
}
if (!isset($data_definition)) {
$selector_string = implode('.', $sub_paths);
$current_selector_string = implode('.', $current_selector);
throw new InvalidArgumentException("Unable to apply data selector '{$selector_string}' at '{$current_selector_string}'");
}
}
return $data_definition;
}
public function autocompletePropertyPath(array $data_definitions, $partial_property_path) {
if ($partial_property_path == '') {
return array_keys($data_definitions);
}
$results = [];
foreach ($data_definitions as $variable_name => $data_definition) {
if (stripos($variable_name, $partial_property_path) === 0) {
$results = array_merge($results, $this
->getAutocompleteSuggestion($data_definition, $variable_name));
}
}
if (!empty($results)) {
return $results;
}
$colon = strpos($partial_property_path, ':');
if ($colon === FALSE) {
$parts = explode('.', $partial_property_path);
$first_part = array_shift($parts);
}
else {
$parts = explode('.', substr($partial_property_path, $colon + 1));
$first_part = substr($partial_property_path, 0, $colon + 1) . array_shift($parts);
}
if (!isset($data_definitions[$first_part])) {
return [];
}
$last_part = array_pop($parts);
$middle_path = implode('.', $parts);
if ($middle_path === '') {
$variable_definition = $data_definitions[$first_part];
}
else {
try {
$variable_definition = $this
->fetchDefinitionByPropertyPath($data_definitions[$first_part], $middle_path);
} catch (InvalidArgumentException $e) {
return [];
}
}
if ($variable_definition instanceof DataReferenceDefinitionInterface) {
$variable_definition = $variable_definition
->getTargetDefinition();
}
if ($variable_definition instanceof ListDataDefinitionInterface) {
if ($last_part === '' && !($variable_definition instanceof FieldDefinitionInterface && $variable_definition
->getFieldStorageDefinition()
->getCardinality() === 1)) {
if ($middle_path === '') {
$property_path = $first_part;
}
else {
$property_path = "{$first_part}.{$middle_path}";
}
$item_definition = $variable_definition
->getItemDefinition();
for ($i = 0; $i < 3; $i++) {
$results = array_merge($results, $this
->getAutocompleteSuggestion($item_definition, "{$property_path}.{$i}"));
}
}
if (!ctype_digit($last_part)) {
$variable_definition = $variable_definition
->getItemDefinition();
}
}
if ($variable_definition instanceof ComplexDataDefinitionInterface) {
foreach ($variable_definition
->getPropertyDefinitions() as $property_name => $property_definition) {
if (stripos($property_name, $last_part) === 0 || $last_part === '') {
if ($middle_path === '') {
$property_path = "{$first_part}.{$property_name}";
}
else {
$property_path = "{$first_part}.{$middle_path}.{$property_name}";
}
$results = array_merge($results, $this
->getAutocompleteSuggestion($property_definition, $property_path));
}
}
}
usort($results, function ($a, $b) {
return strnatcasecmp($a['value'], $b['value']);
});
return $results;
}
protected function getAutocompleteSuggestion(DataDefinitionInterface $data_definition, $variable_name) {
$label = $variable_name;
if ($data_label = $data_definition
->getLabel()) {
$label .= " ({$data_label})";
}
$results[] = [
'value' => $variable_name,
'label' => $label,
];
if ($data_definition instanceof DataReferenceDefinitionInterface) {
$data_definition = $data_definition
->getTargetDefinition();
}
if ($data_definition instanceof ListDataDefinitionInterface || $data_definition instanceof ComplexDataDefinitionInterface) {
$label = "{$variable_name}...";
if ($data_label) {
$label .= " ({$data_label})";
}
$results[] = [
'value' => "{$variable_name}.",
'label' => $label,
];
}
return $results;
}
protected function addBubbleableMetadata(TypedDataInterface $data, BubbleableMetadata $bubbleable_metadata) {
if ($data instanceof PrimitiveInterface) {
return;
}
$value = $data
->getValue();
if ($value instanceof CacheableDependencyInterface || $value instanceof AttachmentsInterface) {
$bubbleable_metadata
->addCacheableDependency($value);
}
}
}