Drush comand to export all items from entity with defined criteria

Created on 18 August 2016, about 8 years ago
Updated 23 August 2024, 3 months ago

I added simple Drush command to export all items from an entity instead of item with the ability to set defined criteria to limit exported items based on where conditions.

Use case:

I need to export all menu links for the main menu in order to deploy easily and limit the effort and time needed to create them from the interface or upload the database as I have 2 languages.

the command takes 3 parameters
entity_type: required
property name: optional
property name: optional

the below piece of code summerizes the idea, I also prevent export 0 $ids and superadmin uid=1

$properties = $property_name ? [$property_name => $property_value] : [];
$entities = \Drupal::entityManager()->getStorage($entity_type)->loadByProperties($properties);

drush_default_content_export_references($entity_type, $id);
Feature request
Status

Needs work

Version

2.0

Component

Code

Created by

🇪🇬Egypt mhmd Riydah

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.1 & MySQL 5.7
    last update over 1 year ago
    13 pass
  • I remove the changes in the drush.inc files, since we discussed that they will be removed anyway in another issue.

    I also abort the execution of the command, if no entity id is provided but properties were given.
    I ended up not throwing an exception, since the check is made in the command itself, so I just stop the execution after displaying an error via the logger.

    If this is not the best way to go about it, let me know and I'll change it.

  • Status changed to Needs work over 1 year ago
  • 🇨🇭Switzerland berdir Switzerland

    Added two more comments, then I think it's ready to go.

  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.1 & MySQL 5.7
    last update over 1 year ago
    13 pass
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.1 & MySQL 5.7
    last update over 1 year ago
    13 pass
  • Status changed to Needs review over 1 year ago
  • I made both the changes requested, changing it back to needs review.

  • Also I am not sure If I am the one who should mark the gitlab comments as resolved or the reviewer :)

  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.1 & MySQL 5.7
    last update about 1 year ago
    13 pass
  • 🇧🇯Benin delacosta456

    hi @flyke
    i was reading this discussion to find a solution for my case and started being confuse when i read your comment.
    Please what did you exactly mean by the below

    UPDATE: never mind, figured it out:
    drush dcer menu_link_content menu_name main --folder=modules/custom/my_custom_module/content

    I have this patch applied but not really sure on how to make your command work.

    Thanks

  • 🇧🇪Belgium flyke

    Hi @delacosta456,
    In the 4 years since that comment of me, I haven't really used the default_content module anymore.
    drush dcer menu_link_content menu_name main --folder=modules/custom/my_custom_module/content
    You need to literally use (copy/paste) this part:
    drush dcer menu_link_content menu_name
    You need to replace main with the actual menu id you want to export from.
    For example, If you edit the menu you like, and the url in your browser for the menu edit page is something like: https://mywebsite.com/admin/structure/menu/manage/footer-main Then the last part from that url, footer-main, is your menu id. So in the drush command you need to replace main with footer-main so it becomes:
    drush dcer menu_link_content menu_name footer-main --folder=modules/custom/my_custom_module/content

    The last part you need to replace is this:
    --folder=modules/custom/my_custom_module/content
    If you have a custom module, located at modules/custom/my_custom_module, you need to create a folder 'content' in there. That is where the exported items will be placed after running the drush command.

  • 🇧🇯Benin delacosta456

    hi @flyke
    Thanks for your clarification.
    However following your suggestion returned an error of too many argument

    Below is the drush command i try to use :

    drush dcer menu_link_content menu_name main --folder=modules/custom/brains_mtcb_default_content/content

    and this return the error message below

    Too many arguments to "dcer" command, expected arguments "entity_type_id" "entity_id".

    i am i missing something ?
    Thanks

  • 🇧🇪Belgium flyke

    Hi @delacosta456,

    As I said, it's been four years, so I probably got it wrong.

    I did retrieve my personal notes in a readme file from a custom module in one of our projects from years ago, maybe this still works and can help you:

    EXPORT block content:
    drush dcer block_content 1 --folder=modules/custom/my_custom_module/content
    -> change '11' to the desired block id. You can see blockid in url when editing a block.
    -> a file with a unique id string as filename will be generated, like: 55489d08-4eac-4818-9cea-e3cb38b70195.json
    -> change file name to something more descriptive, likke: block-about-us.json

    EXPORT menu items:
    drush dcer menu_link_content 45 --folder=modules/custom/my_custom_module/content
    -> change '45' to the desired menulink id. You can see the id in url when editing a menulink.
    -> a file with a unique id string as filename will be generated, like: 55489d08-4eac-4818-9cea-e3cb38b70195.json
    -> change file name to something more descriptive, like: menulink-contact.json

    EXPORT node:
    drush dcer node 1 --folder=modules/custom/my_custom_module/content
    -> change '1' to the desired node id. You can see the id in url when editing a node.
    -> a file with a unique id string as filename will be generated, like: 55489d08-4eac-4818-9cea-e3cb38b70195.json
    -> change file name to something more descriptive, like: node-homepage.json

    EXPORT user:
    lando drush dcer user 14 --folder=modules/custom/my_custom_module/content
    -> change '1' to the desired user id. You can see the id in url when editing a user.
    -> a file with a unique id string as filename will be generated, like: 55489d08-4eac-4818-9cea-e3cb38b70195.json
    -> change file name to something more descriptive, like: user-contentmanager.json

    Export menu items:
    - create the menuitem (locally)
    - edit it to see menu item id: admin/structure/menu/item/20/edit -> id = 20
    - drush dcer menu_link_content 45 --folder=modules/custom/my_custom_module/content
    - If that does not work, you have an older version of default_content and you should use:
    - drush dce menu_link_content 45 --file=modules/custom/my_custom_module/content/menu_link_content/menu-link-overzicht-tweedehands.json
    - repeat for all the menulinks you want to have (do not add menulinks that are automatically added via views pages)

    And then lastly, I had code for importing contant via a update hook like this, but remember, this is from some years ago:

    /**
     * Custom function to import content that was exported via default_content.
     */
    function _my_custom_module_import($path_to_content_json) {
      list($entity_type_id, $filename) = explode('/', $path_to_content_json);
      $module_handler = \Drupal::service('module_handler');
      $module_path = $module_handler->getModule('my_custom_module')->getPath();
    
      $encoded_content = file_get_contents($module_path . '/content/' . $path_to_content_json);
    
      $serializer = \Drupal::service('serializer');
      $content = $serializer->decode($encoded_content, 'hal_json');
    
      // If entity already exists, do nothing.
      $entity_id = FALSE;
      if (isset($content["id"])) {
        $entity_id = $content["id"][0]["value"];
      }
      if (isset($content["nid"])) {
        $entity_id = $content["nid"][0]["value"];
      }
      if (isset($content["tid"])) {
        $entity_id = $content["tid"][0]["value"];
      }
      if (isset($content["uid"])) {
        $entity_id = $content["uid"][0]["value"];
      }
      if ($entity_id) {
        $entity_storage = \Drupal::entityTypeManager()->getStorage($entity_type_id);
        $existing_entity = $entity_storage->load($entity_id);
        if ($existing_entity !== NULL) {
          echo 'Skipping importing content because it already exists: ' . $existing_entity->label() . "\n";
          return;
        }
      }
    
      // If entity does not exist, make sure there are no revisions of it either, otherwise we still get error when saving.
      unset($content["revision_id"]);
      unset($content["revision_created"]);
      unset($content["revision_translation_affected"]);
    
      global $base_url;
      $url = $base_url . base_path();
      $content['_links']['type']['href'] = str_replace('http://drupal.org/', $url, $content['_links']['type']['href']);
      $contents = $serializer->encode($content, 'hal_json');
      $class = \Drupal::entityTypeManager()->getDefinition($entity_type_id)->getClass();
      $entity = $serializer->deserialize($contents, $class, 'hal_json', ['request_method' => 'POST']);
    
      $entity->enforceIsNew(TRUE);
      $entity->save();
    }
    
    /**
     * Add exported content via update hooks.
     *
     * Implements hook_update_N().
     */
    functionmy_custom_module_update_8001() {
      _my_custom_module_import_all();
    }
    
    /**
     * Import all content that is inside this modules /content folder.
     */
    function _my_custom_module_import_all() {
      $directory = \Drupal::service('extension.list.module')->getPath('my_custom_module') . '/content';
      foreach (\Drupal::service('file_system')->scanDirectory($directory, '/\.json$/') as $filename => $file) {
        // $filename = modules/custom/my_custom_module/content/block_content/footeraddress.json
        // remove module folder so $file = content/block_content/footeraddress.json
        $file = str_replace($directory . '/', '', $filename);
        _my_custom_module_import($file);
      }
    }
  • 🇨🇭Switzerland berdir Switzerland

    berdir changed the visibility of the branch 8.x-1.x to hidden.

  • Status changed to Needs work 3 months ago
  • 🇨🇭Switzerland berdir Switzerland

    Another review. I'm quite conflicted between this and Add `drush dcer --bundles` option to limit export Needs review . It doesn't make sense to add both, and while this is more flexible, it doesn't support some things like multiple bundles that the other issue supports and it's more complex to use.

    I think the menu use case is a good example why just bundles aren't sufficient, maybe we can expand it to also support comma separated values and add that as an IN condition to the entity query and take that from the other issue.

    To be honest, not entirely sold on the new method. It needs a lot of work on docs, API changes on ExporterInterface and now that the old drush changes are removed, is really only used once. Instead of addressing all the review points, it might be easier to just inline the entity query into the drush command again and add the condition there. It doesn't even need a loop then since it really only supports a single condition.

    @delacosta456: I recommend reading the built-in documentation that the drush command automatically provides, that would tell you that the property name and value are no longer arguments but options that you need to pass with --property-name and --property-value.

Production build 0.71.5 2024