Skip to content
Snippets Groups Projects
Commit 06a7a10f authored by Jacob Rockowitz's avatar Jacob Rockowitz
Browse files

Merge branch '3426422-performance-improvements' into '1.0.x'

Resolve #3426422 "Performance improvements"

See merge request !113
parents d1d89adc dee7e036
No related branches found
No related tags found
No related merge requests found
Pipeline #119111 failed
......@@ -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']
......
......@@ -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.
......
......@@ -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'];
}
/**
......
......@@ -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.
*/
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment