diff --git a/schemadotorg.services.yml b/schemadotorg.services.yml index d6af1100764bdff2279906c60801c623a7b28c27..2e7202d5dc2412b6b7bd49ee1238aef5cff17bdd 100644 --- a/schemadotorg.services.yml +++ b/schemadotorg.services.yml @@ -10,7 +10,7 @@ services: arguments: ['@config.factory', '@config.typed', '@entity_type.manager', '@schemadotorg.schema_type_manager'] schemadotorg.schema_type_manager: class: Drupal\schemadotorg\SchemaDotOrgSchemaTypeManager - arguments: ['@database', '@schemadotorg.names'] + arguments: ['@database', '@cache.data', '@schemadotorg.names'] schemadotorg.schema_type_builder: class: Drupal\schemadotorg\SchemaDotOrgSchemaTypeBuilder arguments: ['@module_handler', '@current_user', '@schemadotorg.schema_type_manager'] diff --git a/src/Plugin/EntityReferenceSelection/SchemaDotOrgEntityReferenceSelection.php b/src/Plugin/EntityReferenceSelection/SchemaDotOrgEntityReferenceSelection.php index e70b838905a628c7b3fb9c37a650ac79e9fc5e97..3e127370d81c5a9bc63114314bb0a1af32b4ae9a 100644 --- a/src/Plugin/EntityReferenceSelection/SchemaDotOrgEntityReferenceSelection.php +++ b/src/Plugin/EntityReferenceSelection/SchemaDotOrgEntityReferenceSelection.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Drupal\schemadotorg\Plugin\EntityReferenceSelection; use Drupal\Component\Utility\Html; +use Drupal\Core\Config\Entity\ConfigEntityStorageInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginBase; use Drupal\Core\Entity\EntityRepositoryInterface; @@ -59,7 +60,7 @@ class SchemaDotOrgEntityReferenceSelection extends SelectionPluginBase implement /** * The mapping storage. */ - protected SchemaDotOrgMappingStorageInterface $mappingStorage; + protected SchemaDotOrgMappingStorageInterface|ConfigEntityStorageInterface $mappingStorage; /** * The entity repository. diff --git a/src/SchemaDotOrgSchemaTypeManager.php b/src/SchemaDotOrgSchemaTypeManager.php index 2e796adfc11c78c92240f00a1e721eb82466c09f..b5d2a52d3cc9e1dc77b8eac54621d12b6b4ce55b 100644 --- a/src/SchemaDotOrgSchemaTypeManager.php +++ b/src/SchemaDotOrgSchemaTypeManager.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Drupal\schemadotorg; +use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Database\Connection; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslatableMarkup; @@ -33,21 +34,19 @@ class SchemaDotOrgSchemaTypeManager implements SchemaDotOrgSchemaTypeManagerInte */ protected array $itemsCache = []; - /** - * Schema.org superseded cache. - */ - protected array $supersededCache; - /** * Constructs a SchemaDotOrgSchemaTypeManager object. * * @param \Drupal\Core\Database\Connection $database * The database connection. + * @param \Drupal\Core\Cache\CacheBackendInterface $cacheBackend + * Cache backend instance to use. * @param \Drupal\schemadotorg\SchemaDotOrgNamesInterface $schemaNames * The Schema.org names service. */ public function __construct( protected Connection $database, + protected CacheBackendInterface $cacheBackend, protected SchemaDotOrgNamesInterface $schemaNames ) {} @@ -58,32 +57,141 @@ class SchemaDotOrgSchemaTypeManager implements SchemaDotOrgSchemaTypeManagerInte return static::URI . $id; } + /** + * Get information about Schema.org types and properties. + * + * @return array + * An associative array keyed by ids containing + * information about Schema.org types and properties. + */ + protected function getInfo(): array { + if ($cache = $this->cacheBackend->get('schemadotorg_info')) { + return $cache->data; + } + + $info = [ + static::SCHEMA_TYPES => [], + static::SCHEMA_PROPERTIES => [], + ]; + + // Types. + $result = $this->database->select('schemadotorg_types', 't') + ->fields('t', ['id', 'enumerationtype', 'properties', 'superseded_by', 'sub_type_of']) + ->orderBy('id') + ->execute(); + $types = []; + while ($record = $result->fetchAssoc()) { + $id = str_replace('https://schema.org/', '', $record['id']); + $types[$id] = $record; + + $info[self::SCHEMA_TYPES][$id] = []; + + // Superseded. + if (!empty($record['superseded_by'])) { + $info[self::SCHEMA_TYPES][$id][static::SCHEMA_SUPERSEDED] = TRUE; + } + + $breadcrumbs = $this->getTypeBreadcrumbs($id); + foreach ($breadcrumbs as $breadcrumb => $items) { + // Enumeration. + if (str_starts_with($breadcrumb, 'Thing/Intangible/Enumeration')) { + $info[self::SCHEMA_TYPES][$id][static::SCHEMA_INTANGIBLE] = TRUE; + $info[self::SCHEMA_TYPES][$id][static::SCHEMA_ENUMERATION] = TRUE; + if ($record['enumerationtype']) { + $info[self::SCHEMA_TYPES][$id][static::SCHEMA_ENUMERATION_VALUE] = TRUE; + } + else { + $info[self::SCHEMA_TYPES][$id][static::SCHEMA_ENUMERATION_TYPE] = TRUE; + } + } + // Intangible. + elseif (str_starts_with($breadcrumb, 'Thing/Intangible')) { + $info[self::SCHEMA_TYPES][$id][static::SCHEMA_INTANGIBLE] = TRUE; + } + // Thing. + elseif (str_starts_with($breadcrumb, 'Thing')) { + if (!empty($record['properties'])) { + $info[self::SCHEMA_TYPES][$id][static::SCHEMA_THING] = TRUE; + } + } + // Data type. + else { + $info[self::SCHEMA_TYPES][$id][static::SCHEMA_DATA_TYPE] = TRUE; + } + } + } + + // Properties. + $result = $this->database->select('schemadotorg_properties', 't') + ->fields('t', ['id', 'superseded_by', 'sub_property_of', 'range_includes']) + ->orderBy('id') + ->execute(); + while ($record = $result->fetchAssoc()) { + $id = str_replace('https://schema.org/', '', $record['id']); + + $info[static::SCHEMA_PROPERTIES][$id] = []; + if (!empty($record['superseded_by'])) { + $info[static::SCHEMA_PROPERTIES][$id][static::SCHEMA_SUPERSEDED] = TRUE; + } + if (!empty($record['sub_property_of'])) { + $info[static::SCHEMA_PROPERTIES][$id]['sub_property_of'] = str_replace('https://schema.org/', '', $record['sub_property_of']); + } + + $default_types = $this->parseIds($record['range_includes']); + $info[static::SCHEMA_PROPERTIES][$id]['default_type'] = NULL; + $sub_types_of_thing = []; + foreach ($default_types as $default_type) { + // If there is a data type, then we return no default type. + if (isset($info[self::SCHEMA_TYPES][$default_type][static::SCHEMA_DATA_TYPE])) { + $default_types = []; + $sub_types_of_thing = []; + break; + } + // If the default type is a subtype of a schema.org thing use that first. + elseif ($types[$default_type]['sub_type_of'] === 'https://schema.org/Thing') { + $sub_types_of_thing[$default_type] = $default_types; + unset($default_types[$default_type]); + } + // If the full type is not a thing, remove it. + elseif (!isset($info[self::SCHEMA_TYPES][$default_type][static::SCHEMA_THING])) { + unset($default_types[$default_type]); + } + } + + $info[static::SCHEMA_PROPERTIES][$id]['default_type'] = ($sub_types_of_thing || $default_types) + ? array_key_first($sub_types_of_thing + $default_types) + : NULL; + + } + + $this->cacheBackend->set('schemadotorg_info', $info); + + return $info; + } + /** * {@inheritdoc} */ public function isId(string $table, string $id): bool { - $label = $this->database->select('schemadotorg_' . $table, 't') - ->fields('t', ['label']) - ->condition('label', $id) - ->execute() - ->fetchField(); - // Making sure the 'label' (aka type or property id) is exact match because - // SQL queries are case-insensitive. - return ($label === $id); + $info = $this->getInfo(); + return isset($info[$table][$id]); } /** * {@inheritdoc} */ public function isItem(string $id): bool { - return $this->isType($id) || $this->isProperty($id); + $info = $this->getInfo(); + return isset($info[static::SCHEMA_TYPES][$id]) + || isset($info[static::SCHEMA_PROPERTIES][$id]); } /** * {@inheritdoc} */ public function isType(string $id): bool { - return $this->isId(static::SCHEMA_TYPES, $id); + $info = $this->getInfo(); + return isset($info[static::SCHEMA_TYPES][$id]); } /** @@ -107,68 +215,48 @@ class SchemaDotOrgSchemaTypeManager implements SchemaDotOrgSchemaTypeManagerInte * {@inheritdoc} */ public function isThing(string $id): bool { - $type_definition = $this->getType($id); - return (!empty($type_definition) - && $type_definition['label'] === $id - && !empty($type_definition['properties']) - && !in_array($id, ['Enumeration', 'Intangible']) - ); + $info = $this->getInfo(); + return isset($info[static::SCHEMA_TYPES][$id][static::SCHEMA_THING]); } /** * {@inheritdoc} */ public function isDataType(string $id): bool { - $data_types = $this->getDataTypes(); - return (isset($data_types[$id])); + $info = $this->getInfo(); + return isset($info[static::SCHEMA_TYPES][$id][static::SCHEMA_DATA_TYPE]); } /** * {@inheritdoc} */ public function isIntangible(string $id): bool { - if (!$this->isType($id)) { - return FALSE; - } - elseif ($id === 'Intangible') { - return TRUE; - } - else { - $breadcrumbs = $this->getTypeBreadcrumbs($id); - foreach ($breadcrumbs as $breadcrumb => $items) { - if (str_contains($breadcrumb, '/Intangible/')) { - return TRUE; - } - } - return FALSE; - } + $info = $this->getInfo(); + return isset($info[static::SCHEMA_TYPES][$id][static::SCHEMA_INTANGIBLE]); } /** * {@inheritdoc} */ public function isEnumerationType(string $id): bool { - return (boolean) $this->database->select('schemadotorg_types', 'types') - ->fields('types', ['id']) - ->condition('enumerationtype', $this->getUri($id)) - ->countQuery() - ->execute() - ->fetchField(); + $info = $this->getInfo(); + return isset($info[static::SCHEMA_TYPES][$id][static::SCHEMA_ENUMERATION_TYPE]); } /** * {@inheritdoc} */ public function isEnumerationValue(string $id): bool { - $item = $this->getItem(static::SCHEMA_TYPES, $id); - return (!empty($item['enumerationtype'])); + $info = $this->getInfo(); + return isset($info[static::SCHEMA_TYPES][$id][static::SCHEMA_ENUMERATION_VALUE]); } /** * {@inheritdoc} */ public function isProperty(string $id): bool { - return $this->isId(static::SCHEMA_PROPERTIES, $id); + $info = $this->getInfo(); + return isset($info[static::SCHEMA_PROPERTIES][$id]); } /** @@ -183,18 +271,9 @@ class SchemaDotOrgSchemaTypeManager implements SchemaDotOrgSchemaTypeManagerInte * {@inheritdoc} */ public function isSuperseded(string $id): bool { - if (!isset($this->supersededCache)) { - $this->supersededCache = []; - foreach ([static::SCHEMA_TYPES, static::SCHEMA_PROPERTIES] as $table) { - $ids = $this->database->select('schemadotorg_' . $table, $table) - ->fields($table, ['label']) - ->condition('superseded_by', '', '<>') - ->execute() - ->fetchCol(); - $this->supersededCache += array_combine($ids, $ids); - } - } - return !empty($this->supersededCache[$id]); + $info = $this->getInfo(); + return isset($info[static::SCHEMA_TYPES][$id][static::SCHEMA_SUPERSEDED]) + || isset($info[static::SCHEMA_PROPERTIES][$id][static::SCHEMA_SUPERSEDED]); } /** @@ -274,40 +353,8 @@ class SchemaDotOrgSchemaTypeManager implements SchemaDotOrgSchemaTypeManagerInte * The Schema.org property's default Schema.org type from range_includes. */ public function getPropertyDefaultType(string $property): ?string { - $property_definition = $this->getProperty($property); - if (!$property_definition) { - return NULL; - } - - $range_includes = $this->parseIds($property_definition['range_includes']); - $type_definitions = $this->getTypes($range_includes); - - $sub_types_of_thing = []; - foreach ($type_definitions as $type => $type_definition) { - // Remove all types definitions without properties - // (i.e. Enumerations and Data types). - if (empty($type_definition['properties'])) { - // If the property range can be a simple data type, do not return a - // default type. - if ($this->isDataType($type)) { - return NULL; - } - // Otherwise. unset the enumeration. - else { - unset($type_definitions[$type]); - } - } - // Track subtypes of Thing. - elseif ($type_definition['sub_type_of'] === 'https://schema.org/Thing') { - $sub_types_of_thing[$type] = $type; - } - } - - // Make sure subtypes of Thing comes first. - $type_definitions = $sub_types_of_thing + $type_definitions; - - // Finally, return the first type in range_includes type definitions. - return array_key_first($type_definitions); + $info = $this->getInfo(); + return $info[static::SCHEMA_PROPERTIES][$property]['default_type']; } /** diff --git a/src/SchemaDotOrgSchemaTypeManagerInterface.php b/src/SchemaDotOrgSchemaTypeManagerInterface.php index 80d3f40035648698e8e466b090eef33f817e2f68..ab539db52b1cacaf1039a963265a33ff6a286137 100644 --- a/src/SchemaDotOrgSchemaTypeManagerInterface.php +++ b/src/SchemaDotOrgSchemaTypeManagerInterface.php @@ -12,15 +12,65 @@ use Drupal\Core\StringTranslation\TranslatableMarkup; interface SchemaDotOrgSchemaTypeManagerInterface { /** - * Schema.org type. + * Schema.org types. */ const SCHEMA_TYPES = 'types'; /** - * Schema.org property. + * Schema.org type. + */ + const SCHEMA_TYPE = 'type'; + + /** + * Schema.org properties. */ const SCHEMA_PROPERTIES = 'properties'; + /** + * Schema.org property. + */ + const SCHEMA_PROPERTY = 'property'; + + /** + * Schema.org thing. + */ + const SCHEMA_THING = 'thing'; + + /** + * Schema.org intangible. + */ + const SCHEMA_INTANGIBLE = 'intangible'; + + /** + * Schema.org data type. + */ + const SCHEMA_DATA_TYPE = 'data_type'; + + /** + * Schema.org enumeration. + */ + const SCHEMA_ENUMERATION = 'enumeration'; + + /** + * Schema.org enumeration type. + */ + const SCHEMA_ENUMERATION_TYPE = 'enumeration_type'; + + /** + * Schema.org enumeration value. + */ + const SCHEMA_ENUMERATION_VALUE = 'enumeration_value'; + + /** + * Schema.org superseded. + */ + const SCHEMA_SUPERSEDED = 'superseded'; + + /** + * Schema.org default type. + */ + const SCHEMA_DEFAULT_TYPE = 'default_type'; + /** * The Schema.org base URI. */