Problem/Motivation

Core is about to add a default content export system: #3532694: Add a command-line utility to export content in YAML format

It is extensible and modules can integrate with it by means of an event. Pathauto should integrate with this new system.

Proposed resolution

It is possible that nothing will actually be necessary; core naïvely exports the Path field items, only removing the non-portable path ID.

But if it turns out that core is not exporting the Pathauto flag, then Pathauto should subscribe to the new PreExportEvent and wrap around the core callback for path field items, ensuring that the flag is preserved on export.

Issue fork pathauto-3535498

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

phenaproxima created an issue. See original summary.

phenaproxima’s picture

Title: [PP-1] Integrate with core's default content exporter » Integrate with core's default content exporter
Status: Postponed » Active

The core blocker is in!

dieterholvoet’s picture

FYI, it seems like core is not exporting the Pathauto flag by default.

phenaproxima’s picture

That's because it is a computed property, which core ignores by default.

Pathauto could force the property to be exported by overriding the export callback for path field items. That's doable by subscribing to PreExportEvent.

dieterholvoet’s picture

Status: Active » Needs review

dieterholvoet’s picture

Pathauto seems to be causing issues with default content. Drupal\pathauto\PathautoItem::postSave() makes it so its parent method, Drupal\path\Plugin\Field\FieldType\PathItem::postSave(), is never triggered when pathauto is enabled. This causes aliases of translations to sometimes not be imported.

dieterholvoet’s picture

It seems like #2973478: Aliases are not created for affected translations fixes this problem. With a patch from that issue, aliases for all translations are generated during import. That does mean that the exported alias is not used and instead a new alias is generated, but that might be acceptable in context of Pathauto.

csakiistvan’s picture

@dieterholvoet — the MR itself tests out fine, but could you clarify the last sentence of comment #8? "The exported alias is not used and instead a new alias is generated" only seems to hold for items with pathauto: 1. For pathauto: 0 items the exported alias is used verbatim (see the round trip below), and that is precisely what exporting the flag buys us — so as a general statement it appears to argue against the MR's own purpose. Did you mean it strictly for the automatic case? Also, should #2973478 be marked as a dependency here rather than just related, if the translation import fix is required?

Environment

  • Drupal: 11.4.4
  • PHP: 8.5.5
  • Database: MariaDB 10.11.16
  • DDEV: v1.25.2
  • Pathauto: 8.x-1.15

Not reproducible in the browser — the core default content exporter is a CLI/API feature. The core drupal content:export script does not bootstrap in a docroot-based composer layout, so the Drupal\Core\DefaultContent\Exporter service (which that command wraps) is called directly.

Prerequisites

  • Drupal 11.4+ (core default content export/import, #3532694).
  • Pathauto enabled with a pattern for content:
ddev drush en pathauto -y
ddev drush php:eval '\Drupal::entityTypeManager()->getStorage("pathauto_pattern")->create([
  "id" => "content", "label" => "Content",
  "type" => "canonical_entities:node",
  "pattern" => "/[node:content-type]/[node:title]",
])->save();'
  • Two article nodes, covering both pathauto states:
    • an ordinary node with an automatically generated alias (pathauto: 1);
    • a node with a hand-written alias and pathauto turned off (pathauto: 0).
ddev drush php:eval '
$a = \Drupal\node\Entity\Node::create(["type"=>"article","title"=>"Pathauto Export Test","uid"=>1,"status"=>1]);
$a->save();
$b = \Drupal\node\Entity\Node::create(["type"=>"article","title"=>"Manual Alias Test","uid"=>1,"status"=>1]);
$b->set("path", ["alias" => "/my-hand-made-alias", "pathauto" => \Drupal\pathauto\PathautoState::SKIP]);
$b->save();'

Steps

  1. Export both nodes and look at the path field in the YAML output. Note that no pathauto key is present for either node.
  2. Apply the fix from MR !185: it adds Drupal\pathauto\EventSubscriber\DefaultContentSubscriber, which subscribes to PreExportEvent and registers an export callback for field_item:path that drops the non-portable pid and adds the computed pathauto state to the exported values.
  3. Rebuild caches: ddev drush cr
  4. Export both nodes again and check the path field in the YAML output.
  5. Export both nodes to files, delete the nodes, then import the exported files back and check the resulting aliases.
ddev drush php:eval '
foreach ([5, 6] as $id) {
  $node = \Drupal\node\Entity\Node::load($id);
  print strstr((string) \Drupal::service(\Drupal\Core\DefaultContent\Exporter::class)->export($node), "path:");
}'

Expected results

  • Before the fix: the exported path field contains only alias and langcode; the pathauto state is missing, because it is a computed property and core skips those.
  • After the fix: the exported path field also contains pathauto: 1 for the automatically aliased node and pathauto: 0 for the hand-aliased node.
  • After the fix, a round trip (export → delete → import) restores /my-hand-made-alias unchanged for the hand-aliased node, and regenerates the pattern-based alias for the other one.
  • No pid value in the export.

Actual results

Before the fix both nodes exported as alias plus langcode only, with no pathauto state. After applying MR !185 and rebuilding caches, the export contained pathauto: 1 for "Pathauto Export Test" and pathauto: 0 for "Manual Alias Test", and no pid. The round trip confirmed the practical effect: with the flag exported, the re-imported "Manual Alias Test" kept /my-hand-made-alias, while "Pathauto Export Test" was regenerated to /article/pathauto-export-test. Importing the same YAML with the pathauto lines stripped out (i.e. the pre-fix export) silently replaced the hand-written alias with the generated /article/manual-alias-test, so without this fix the manual alias is lost on import. Works as intended.

Remarks

  • Comment #8 states that on import "the exported alias is not used and instead a new alias is generated, but that might be acceptable in context of Pathauto". That holds only for items with pathauto: 1. For pathauto: 0 items, PathautoItem::postSave() defers to PathItem::postSave() and the exported alias is used verbatim — which is exactly what this MR makes possible, and losing it would be data loss rather than acceptable behaviour. The wording in comment #8 reads as more general than it is.
  • Comment #7 reports that PathautoItem::postSave() prevents translation aliases from being imported, and comment #8 says a patch from #2973478 fixes it. That issue is currently only listed as related; if the fix is required for correct multilingual import it should be marked as a dependency of this issue. The translation scenario was not covered by this test.

Testing produced with the assistance of an LLM.

dieterholvoet’s picture

Did you mean it strictly for the automatic case?

Yes

should #2973478 be marked as a dependency here rather than just related, if the translation import fix is required?

Yes, it's needed when dealing with translations, but that doesn't mean we can't merge this before that one.