2

I'm trying to find all the absolute URLs in a site (in wysiwyg content, url fields, config) to convert them into relative URLs later via manual administration. All the URLs in question are within the same Drupal site. I've managed to use PHPMyAdmin to find and record readily-accessible entities that contain absolute urls (nodes - /node/NID/edit, menu items - /admin/structure/menu/item/MLID/edit, etc.). However, I'm having a difficult time with Paragraphs.

  • The id of a paragraph field row is for a specific instance of the paragraph.
  • Without knowledge of the field hosting the paragraph, the instance could be anywhere.
  • The paragraph could be nested in another paragraph.

How would one go about solving this issue? How do I find the host node of paragraphs containing an absolute link?

  • I think what are looking for is something like the Reverse Entity Reference module – bleen Sep 14 '18 at 17:17
  • 3
    The host entity (node or another paragraph) is stored in the paragraph $paragraph->getParentEntity() – 4k4 Sep 14 '18 at 19:13
  • Can you elaborate a bit why exactly you'd recommend this module, and how exactly it will help OP to solve his issue? – leymannx Sep 15 '18 at 0:28
  • Paragraphs are attached to parent entities as entity references ... so to find out which parent entities reference a particular paragraph you will need to do a reverse reference lookup (e.g. SELECT all entites WHERE reference field = $paragraph_entity_id) ... this functionality is exactly what is provided by the Reverse Entity Reference module... – bleen Sep 17 '18 at 13:20
  • @bleen Paragraphs uses a different field type (entity_reference_revisions), does the reverse ER module work with that do you know? – Clive Sep 17 '18 at 13:34
2

It might be a bit easier to use the API rather than SQL for this. I'd be tempted to load all nodes, loop through all their fields and check values that way. If you hit a paragraph field, recurse and check its fields too.

This is a very rough example function, but it should get you going:

function entityHasFieldWithAbsUrl(\Drupal\Core\Entity\ContentEntityInterface $entity, array $types) {
  foreach ($entity->getFields() as $key => $field) {
    $field_type = $field->getFieldDefinition()->getType();
    $field_settings = $field->getSettings();
    if ($field_type == 'entity_reference_revisions' && $field_settings['target_type'] == 'paragraph') {
      foreach ($field as $item) {
        if ($item->entity instanceof \Drupal\paragraphs\ParagraphInterface) {
          if (entityHasFieldWithAbsUrl($item->entity, $types)) {
            return TRUE;
          }
        }
      }
    }
    elseif (in_array($field_type, $types)) {
      foreach ($field as $item) {
        // Change this to something better...
        if (strpos($item->value, 'https://www.example.com/') !== FALSE) {
          return TRUE;
        }
      }
    }
  }

  return FALSE;
}

And you'd use it something like this:

$nodes_with_abs_urls = [];
$nodes = \Drupal::entityTypeManager()->getStorage('node')->loadMultiple();
$types = ['text', 'text_long', 'text_with_summary', 'string'];
foreach ($nodes as $node) {
  if (entityHasFieldWithAbsUrl($node, $types)) {
    $nodes_with_abs_urls[] = $node->id();
  }
}

You probably need to tweak the types to suit your installation, but it should work at a basic level.

1

So what I ended up doing was searching for the URLs in PHPMyAdmin, picked out all the tables that belong to paragraph fields (they were prefixed paragraph__) and gathered all their entity_ids. Then using a temporary Drush command, I loaded all of these paragraphs and for each, recursively loaded $paragraph->getParentEntity() until it hit a desired entity type (in this case, nodes). Then I recorded the node ids.

function mymodule_drush_command() {
  $items = [];
  $items['mycommand'] = [
    'description' => 'Find the nids of the pids',
    'arguments' => [],
    'drupal dependencies' => ['mymodule'],
    'aliases' => [],
  ];
  return $items;
}

function drush_mymodule_mycommand() {
  // Could be improved by retrieving ids via query.
  $pids = [/* paragraph entity_ids */];

  // For each, recursively search ancestors.
  $nids = array_unique(array_filter(array_map(function($pid) {
    return _mymodule_find_node(\Drupal\paragraphs\Entity\Paragraph::load($pid));
  }, $pids)));

  // Print results
  drush_print_r(array_map(function($nid) {
    return '/node/' . $nid . '/edit';
  }, $nids));
}

function _mymodule_find_node($entity){
  // Could be improved by handling more than just nodes
  if ($entity instanceof \Drupal\node\NodeInterface) return $entity->id();
  // If still a paragraph, climb
  if ($entity instanceof \Drupal\paragraphs\ParagraphInterface) return _mymodule_find_node($entity->getParentEntity());
  // Otherwise, this is an entity we didn't want.
  return null;
}

Bulk of this operation could have been automated. It could have been improved to grab the entity ids via a query instead of PHPMyAdmin and handle different root types like blocks, terms, etc. It could also be more performant by not loading every single paragraph. But the above did what I needed at the moment, so I left it as is.

  • I really don't understand what you are trying to do here: You could simply get all bundles that containt Paragraph reference fields, and then query all the bundles to retrieve the nids.. But in general: I don't really understand why this is handy to you. – ssibal Sep 17 '18 at 14:30
  • @ssibal Then I'll refer you to the first part of my question: "I'm trying to find all the absolute URLs in a site (in wysiwyg content, url fields, config) to convert them into relative URLs later via manual administration.". Absolute links aren't ideal, especially if you move sites across domains/environments. "You could simply get all bundles that containt Paragraph reference fields, and then query all the bundles to retrieve the nids." - paragraphs can reference paragraphs. You cannot assume a node references a paragraph and ends there. – Joseph Sep 17 '18 at 14:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.