Answers.Body: <h2>Short hacky answer</h2>
  <p>Is this a one-time-operation? In this case, preparing the exported data manually is gonna be much simpler and quicker than any programmatic solution. Simply export both the entries and the categories. Then open both in Excel or Google Sheets and replace the IDs in the entries table with the slugs from the categories table using some Excel magic. Or, depending on the size of your dataset, do a quick search/replace in your code editor. Or even write a throway script to do it for you.</p>
  <h2>Long clean answer</h2>
  <p>Craft's built-in element exporter currently supports raw and expanded exports, but it's very easy to create a custom exporter type with a minimal amount of code. <a href="https://craftcms.com/docs/4.x/extend/element-exporter-types.html" rel="nofollow noreferrer">Custom exportes types are documented here.</a></p>
  <p>You don't need to write a complete custom exporter, you can just extend the existing exporters and modify their return value. Here's a simple exporter that will replace the IDs in the <code>answerCategory</code> field with slugs:</p>
  <pre class="lang-php prettyprint-override"><code>&lt;?php
  
  namespace modules\MyCustomModule\exporters;
  
  use Craft;
  use craft\elements\Category;
  use craft\elements\db\ElementQueryInterface;
  use craft\elements\exporters\Expanded;
  
  class ExpandedWithSlugs extends Expanded
  {
      public static function displayName(): string
      {
          return Craft::t('app', 'Expanded (relations as slugs)');
      }
  
      public function export(ElementQueryInterface $query): mixed
      {
          $data = parent::export($query);
          $converted = array_map(
              function (array $entry) {
                  $categories = $entry['answerCategory'];
                  if ($categories) {
                      $categories = Category::find()-&gt;id($categories)-&gt;status(null)-&gt;all();
                      $entry['answerCategory'] = array_column($categories, 'slug');
                  }
                  return $entry;
              },
              $data,
          );
          return $converted;
      }
  }
  </code></pre>
  <p>Put this exporter in a custom module and make sure to <a href="https://craftcms.com/docs/4.x/extend/element-exporter-types.html#registering-custom-exporter-types" rel="nofollow noreferrer">register it using the <code>Element::EVENT_REGISTER_EXPORTERS</code></a> event. Then you can select it from the element index page.</p>
  <p><a href="https://i.sstatic.net/13G0p.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/13G0p.png" alt="element export with custom exporter" /></a></p>
  <p>Note this exporter hardcodes the <code>answerCategory</code> field. A complete implementation could use the <a href="https://docs.craftcms.com/api/v4/craft-services-fields.html" rel="nofollow noreferrer">fields service</a> to get a list of all relations fields on your site and replace IDs with slugs for all of them.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-06-10T09:34:16.890Z
Answers.Text: Hi, thanks for your answer, the second option is what I did actually, more in abstract way, by detecting the field types.
  As I need this working for all types, this way not gonna be a good idea I think, because in entries, you can have categories, matrix, assets... Then in matrices, you can have also entries, categories... Lot of nested possibilities. That's why I was wondering if it could not be a feature to develop in Craft, to have the possibility to export by id or by slug (or UID, but FeedMe doesn't support UID).
Answers.UserDisplayName: emilie zawadzki
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-06-10T09:47:08.670Z
Answers.Text: @emiliezawadzki Well, for a full-blown export/import, IDs are what you want. Slugs aren't guaranteed to be unique, even within a single structure section, so this will cause problems at scale. If you import everything at once, you can just map old IDs to the newly imported content at import time. Another option would be to include the old IDs in your datamodel in the new site – as a hidden field that's not visible to editors. This way, you can continuously import content from the source site and map the relations cleanly to the imported content in the new site.
Answers.UserDisplayName: MoritzLost
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-06-09T14:59:18.443Z
Answers.LastActivityDate: 2022-06-09T14:59:18.443Z
Answers.UserDisplayName: MoritzLost
Body: <p>I am using the included exporter tools in Craft.
  I am trying to export content from a preprod to a production env.
  When I export content from preprod, I noticed that related element (entries, categories etc...) are referenced by id. Which is not a good idea to migrate these contents from one database to another one.
  Example of a structure of a categories related to my entry :</p>
  <pre><code>&quot;answerCategory&quot;: [
      52749,
      52754
  ]
  </code></pre>
  <p>So I was wondering if it is possible to get the slug instead of id for all related element.
  Then I could use feedMe to import by slug. Like this:</p>
  <pre><code>&quot;answerCategory&quot;: [
      &quot;this-is-cat-1&quot;,
      &quot;this-is-cat-2&quot;,
  ]
  </code></pre>
  <p>The only way I found for the moment is to write my own Exporter. But I need to request entries again, so not really efficient if we have lots of contents.</p>
  <p>Any suggestion ?</p>
  <p>Thanks</p>
  
Tags: <slug><entry-slug><export>
Title: Export related entries by slug - not id
ThreadText: # Export related entries by slug - not id
  
  (Asked by: username_0 on 2022-06-09)
  
  I am using the included exporter tools in Craft.
  I am trying to export content from a preprod to a production env.
  When I export content from preprod, I noticed that related element (entries, categories etc...) are referenced by id. Which is not a good idea to migrate these contents from one database to another one.
  Example of a structure of a categories related to my entry :
  
  ```
  "answerCategory": [
      52749,
      52754
  ]
  
  ```
  
  So I was wondering if it is possible to get the slug instead of id for all related element.
  Then I could use feedMe to import by slug. Like this:
  
  ```
  "answerCategory": [
      "this-is-cat-1",
      "this-is-cat-2",
  ]
  
  ```
  
  The only way I found for the moment is to write my own Exporter. But I need to request entries again, so not really efficient if we have lots of contents.
  
  Any suggestion ?
  
  Thanks
  
  **Tags:** slug, entry-slug, export
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2022-06-09. Score: 3
  
  ## Short hacky answer
  
  Is this a one-time-operation? In this case, preparing the exported data manually is gonna be much simpler and quicker than any programmatic solution. Simply export both the entries and the categories. Then open both in Excel or Google Sheets and replace the IDs in the entries table with the slugs from the categories table using some Excel magic. Or, depending on the size of your dataset, do a quick search/replace in your code editor. Or even write a throway script to do it for you.
  
  ## Long clean answer
  
  Craft's built-in element exporter currently supports raw and expanded exports, but it's very easy to create a custom exporter type with a minimal amount of code. [Custom exportes types are documented here.](https://craftcms.com/docs/4.x/extend/element-exporter-types.html)
  
  You don't need to write a complete custom exporter, you can just extend the existing exporters and modify their return value. Here's a simple exporter that will replace the IDs in the `answerCategory` field with slugs:
  
  ```php
  <?php
  
  namespace modules\MyCustomModule\exporters;
  
  use Craft;
  use craft\elements\Category;
  use craft\elements\db\ElementQueryInterface;
  use craft\elements\exporters\Expanded;
  
  class ExpandedWithSlugs extends Expanded
  {
      public static function displayName(): string
      {
          return Craft::t('app', 'Expanded (relations as slugs)');
      }
  
      public function export(ElementQueryInterface $query): mixed
      {
          $data = parent::export($query);
          $converted = array_map(
              function (array $entry) {
                  $categories = $entry['answerCategory'];
                  if ($categories) {
                      $categories = Category::find()->id($categories)->status(null)->all();
                      $entry['answerCategory'] = array_column($categories, 'slug');
                  }
                  return $entry;
              },
              $data,
          );
          return $converted;
      }
  }
  
  ```
  
  Put this exporter in a custom module and make sure to [register it using the `Element::EVENT_REGISTER_EXPORTERS`](https://craftcms.com/docs/4.x/extend/element-exporter-types.html#registering-custom-exporter-types) event. Then you can select it from the element index page.
  
  [![element export with custom exporter](https://i.sstatic.net/13G0p.png)](https://i.sstatic.net/13G0p.png)
  
  Note this exporter hardcodes the `answerCategory` field. A complete implementation could use the [fields service](https://docs.craftcms.com/api/v4/craft-services-fields.html) to get a list of all relations fields on your site and replace IDs with slugs for all of them.
  
  > **Comments:**
  > **username_0**: Hi, thanks for your answer, the second option is what I did actually, more in abstract way, by detecting the field types. As I need this working for all types, this way not gonna be a good idea I think, because in entries, you can have categories, matrix, assets... Then in matrices, you can have also entries, categories... Lot of nested possibilities. That's why I was wondering if it could not be a feature to develop in Craft, to have the possibility to export by id or by slug (or UID, but FeedMe doesn't support UID).  
  > **username_1**: @username_0 Well, for a full-blown export/import, IDs are what you want. Slugs aren't guaranteed to be unique, even within a single structure section, so this will cause problems at scale. If you import everything at once, you can just map old IDs to the newly imported content at import time. Another option would be to include the old IDs in your datamodel in the new site – as a hidden field that's not visible to editors. This way, you can continuously import content from the source site and map the relations cleanly to the imported content in the new site.  
  
  ---
  
  