Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion islandora.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ services:
arguments: ['@entity_type.manager', '@current_user', '@language_manager', '@file_system', '@islandora.utils']
islandora.utils:
class: Drupal\islandora\IslandoraUtils
arguments: ['@entity_type.manager', '@entity_field.manager', '@context.manager', '@flysystem_factory', '@language_manager', '@current_user']
arguments: ['@entity_type.manager', '@entity_field.manager', '@context.manager', '@flysystem_factory', '@language_manager', '@current_user', '@database', '@logger.channel.islandora']
islandora.entity_mapper:
class: Islandora\EntityMapper\EntityMapper
islandora.stomp.auth_header_listener:
Expand Down
191 changes: 113 additions & 78 deletions src/IslandoraUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
namespace Drupal\islandora;

use Drupal\context\ContextManager;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\Query\QueryException;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Session\AccountInterface;
Expand All @@ -23,11 +25,13 @@
use Drupal\media\MediaInterface;
use Drupal\node\NodeInterface;
use Drupal\taxonomy\TermInterface;
use Psr\Log\LoggerInterface;

/**
* Utility functions for figuring out when to fire derivative reactions.
*/
class IslandoraUtils {
use DependencySerializationTrait;
use StringTranslationTrait;
const EXTERNAL_URI_FIELD = 'field_external_uri';

Expand Down Expand Up @@ -96,14 +100,20 @@ class IslandoraUtils {
* Language manager.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
* @param \Drupal\Core\Database\Connection $database
* Drupal's database service.
* @param \Psr\Log\LoggerInterface $logger
* Islandora's logger service.
*/
public function __construct(
EntityTypeManagerInterface $entity_type_manager,
EntityFieldManagerInterface $entity_field_manager,
ContextManager $context_manager,
FlysystemFactory $flysystem_factory,
LanguageManagerInterface $language_manager,
AccountInterface $current_user
AccountInterface $current_user,
protected Connection $database,
protected LoggerInterface $logger,
) {
$this->entityTypeManager = $entity_type_manager;
$this->entityFieldManager = $entity_field_manager;
Expand Down Expand Up @@ -189,7 +199,8 @@ public function getMedia(NodeInterface $node) {
* Calling getStorage() throws if the storage handler couldn't be loaded.
*/
public function getMediaWithTerm(NodeInterface $node, TermInterface $term) {
$mids = $this->getMediaReferencingNodeAndTerm($node, $term);
$query = $this->getMediaReferencingNodeAndTermQuery($node, $term);
$mids = $query?->range(0, 1)->execute() ?? [];
if (empty($mids)) {
return NULL;
}
Expand All @@ -214,25 +225,29 @@ public function getReferencingMedia($fid) {
// Get media fields that reference files.
$fields = $this->getReferencingFields('media', 'file');

// Process field names, stripping off 'media.' and appending 'target_id'.
$conditions = array_map(
function ($field) {
return ltrim($field, 'media.') . '.target_id';
},
$fields
);
if (empty($fields)) {
$this->logger->debug('No media fields found referencing a file.');
return [];
}

// Query for media that reference this file.
$query = $this->entityTypeManager->getStorage('media')->getQuery();
$query->accessCheck(TRUE);
$group = $query->orConditionGroup();
foreach ($conditions as $condition) {
$group->condition($condition, $fid);
$queries = [];
foreach ($fields as $full_field) {
// Process field names, stripping off 'media.' and appending 'target_id'.
assert(str_starts_with($full_field, 'media.'), 'Has expected prefix.');
$trimmed_field = substr($full_field, strlen('media.'));
$queries[] = $this->database->select("media__{$trimmed_field}", 'f')
->fields('f', ['entity_id'])
->condition("f.{$trimmed_field}_target_id", $fid);
}
$query->condition($group);

return $this->entityTypeManager->getStorage('media')
->loadMultiple($query->execute());
$media_storage = $this->entityTypeManager->getStorage('media');

// Query for media that reference this file.
$query = $media_storage->getQuery()
->accessCheck(TRUE)
->condition('mid', array_reduce($queries, static::unionReduction(...)), 'IN');

return $media_storage->loadMultiple($query->execute());
}

/**
Expand Down Expand Up @@ -261,24 +276,37 @@ public function getTermForUri($uri) {
// Add field_external_uri.
$fields[] = self::EXTERNAL_URI_FIELD;

$query = $this->entityTypeManager->getStorage('taxonomy_term')->getQuery();

$orGroup = $query->orConditionGroup();
foreach ($fields as $field) {
$orGroup->condition("$field.uri", $uri);
$queries = [];
foreach ($fields as $field_name) {
$queries[] = $this->database->select("taxonomy_term__{$field_name}", 'f')
->fields('f', ['entity_id'])
->condition("f.{$field_name}_uri", $uri);
}

$results = $query
/** @var \Drupal\Core\Database\Query\SelectInterface $query */
$query = array_reduce($queries, static::unionReduction(...));

$term_storage = $this->entityTypeManager->getStorage('taxonomy_term');
$results = $term_storage->getQuery()
->accessCheck(TRUE)
->condition($orGroup)
->condition('tid', $query, 'IN')
->range(0, 1)
->execute();

if (empty($results)) {
return NULL;
return $results ?
$term_storage->load(reset($results)) :
NULL;
}

/**
* Callback for array_reduce(); "join" queries with "UNION".
*/
private static function unionReduction(?SelectInterface $carry, SelectInterface $item) : SelectInterface {
if ($carry === NULL) {
return $item;
}

return $this->entityTypeManager->getStorage('taxonomy_term')
->load(reset($results));
return $carry->union($item);
}

/**
Expand Down Expand Up @@ -486,48 +514,75 @@ public function getFilesystemSchemes() {
}

/**
* Get array of media ids that have fields that reference $node and $term.
* Build out query for selecting media related to a given node and term.
*
* @param \Drupal\node\NodeInterface $node
* The node to reference.
* The node in question.
* @param \Drupal\taxonomy\TermInterface $term
* The term to reference.
* The term in question.
*
* @return array|int|null
* Array of media IDs or NULL.
* @return \Drupal\Core\Entity\Query\QueryInterface|null
* An entity query over media, constrained to those relating to the given
* node and term. NULL if a field does not exist to relate media to either
* nodes or terms.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function getMediaReferencingNodeAndTerm(NodeInterface $node, TermInterface $term) {
private function getMediaReferencingNodeAndTermQuery(NodeInterface $node, TermInterface $term) : ?QueryInterface {
$term_fields = $this->getReferencingFields('media', 'taxonomy_term');
if (count($term_fields) <= 0) {
\Drupal::logger("No media fields reference a taxonomy term");
if (empty($term_fields)) {
$this->logger->debug("No media fields found referencing a taxonomy term.");
return NULL;
}
$node_fields = $this->getReferencingFields('media', 'node');
if (count($node_fields) <= 0) {
\Drupal::logger("No media fields reference a node.");
if (empty($node_fields)) {
$this->logger->debug("No media fields found referencing a node.");
return NULL;
}

$remove_entity = function (&$o) {
$remove_entity = static function (&$o) {
$o = substr($o, strpos($o, '.') + 1);
};
array_walk($term_fields, $remove_entity);
array_walk($node_fields, $remove_entity);

$query = $this->entityTypeManager->getStorage('media')->getQuery();
$query->accessCheck(TRUE);
$taxon_condition = $this->getEntityQueryOrCondition($query, $term_fields, $term->id());
$query->condition($taxon_condition);
$node_condition = $this->getEntityQueryOrCondition($query, $node_fields, $node->id());
$query->condition($node_condition);
// Does the tags field exist?
try {
$mids = $query->execute();
}
catch (QueryException $e) {
$mids = [];
}
return $mids;
$map_reference_field = function (int $value) : callable {
return function (string $field) use ($value) : SelectInterface {
return $this->database->select("media__{$field}", 'f')
->fields('f', ['entity_id'])
->condition("f.{$field}_target_id", $value);
};
};

$term_query = array_reduce(
array_map($map_reference_field($term->id()), $term_fields),
static::unionReduction(...),
);
$node_query = array_reduce(
array_map($map_reference_field($node->id()), $node_fields),
static::unionReduction(...),
);

return $this->entityTypeManager->getStorage('media')->getQuery()
->accessCheck(TRUE)
->condition('mid', $term_query, 'IN')
->condition('mid', $node_query, 'IN');
}

/**
* Get array of media ids that have fields that reference $node and $term.
*
* @param \Drupal\node\NodeInterface $node
* The node to reference.
* @param \Drupal\taxonomy\TermInterface $term
* The term to reference.
*
* @return array
* Array of media IDs.
*/
public function getMediaReferencingNodeAndTerm(NodeInterface $node, TermInterface $term) {
return $this->getMediaReferencingNodeAndTermQuery($node, $term)?->execute() ?? [];
}

/**
Expand All @@ -553,27 +608,6 @@ public function getReferencingFields($entity_type, $target_type) {
return $fields;
}

/**
* Make an OR condition for an array of fields and a value.
*
* @param \Drupal\Core\Entity\Query\QueryInterface $query
* The QueryInterface for the query.
* @param array $fields
* The array of field names.
* @param string $value
* The value to search the fields for.
*
* @return \Drupal\Core\Entity\Query\ConditionInterface
* The OR condition to add to your query.
*/
private function getEntityQueryOrCondition(QueryInterface $query, array $fields, $value) {
$condition = $query->orConditionGroup();
foreach ($fields as $field) {
$condition->condition($field, $value);
}
return $condition;
}

/**
* Gets the id URL of an entity.
*
Expand Down Expand Up @@ -761,11 +795,12 @@ protected function getParentsByEntityReference(ContentEntityInterface $entity, a
if ($entity->hasField($field)) {
$reference_field = $entity->get($field);
if (!$reference_field->isEmpty()) {
$parents = array_merge($parents, $reference_field->referencedEntities());
$parents[] = $reference_field->referencedEntities();
}
}
}
return $parents;

return array_merge(...$parents);
}

/**
Expand Down
Loading