[#365] Added creation hint registry for entity stub aliases.#369
[#365] Added creation hint registry for entity stub aliases.#369AlexSkrypnyk wants to merge 11 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR implements creation hints as first-class, discoverable artifacts. Drivers expose named hints for entity types; hints run in pre-create or post-create pipelines, mutate stubs or apply side-effects, and unresolved hints throw CreationHintResolutionException. Core, DrupalDriver, and DrushDriver are wired to register and dispatch these hints; tests and UPGRADING.md were updated. ChangesCreation Hints First-Class System
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Code coverage (threshold: 95%) Per-class coverage |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/Drupal/Driver/DrushDriver.php (1)
265-275:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFail fast when UID parsing fails before post-create hint dispatch
At Line 266,
$uidcan beNULL, but Lines 272–275 still dispatch post-create hints with a null-backed entity object. That can produce partial side effects and brittle behavior for hints that require a persisted identity.Suggested fix
$result = $this->drush('user-create', $arguments, $options); $uid = $this->parseUserId($result); -if ($uid) { - $stub->setValue('uid', $uid); -} +if ($uid === NULL) { + throw new \RuntimeException('Unable to parse user id from drush user-create output.'); +} +$stub->setValue('uid', $uid); $account = new \stdClass(); $account->uid = $uid; $this->applyPostCreateHints($stub, $account, 'user');🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Drupal/Driver/DrushDriver.php` around lines 265 - 275, The parseUserId() call can return NULL and you must fail fast before creating a null-backed account and invoking post-create hooks; update the logic in DrushDriver (around where $result = $this->drush('user-create', ...) is called) to check the $uid returned by $this->parseUserId($result) and if it is null/false, abort the flow (throw an exception or return an error) instead of proceeding to set $stub->setValue('uid', ...), creating $account and calling $this->applyPostCreateHints($stub, $account, 'user'); ensure the guard references parseUserId, $stub and applyPostCreateHints so only a valid persisted uid triggers the post-create hint dispatch.src/Drupal/Driver/Core/Core.php (1)
761-765:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUpdate missing-vocabulary error text to match new resolution paths
termCreate()now accepts vocabulary via bundle orvid(possibly filled by hints), but the exception only mentionsvocabulary_machine_name, which can mislead callers.Suggested fix
- throw new \InvalidArgumentException("Cannot create term because it is missing the required property 'vocabulary_machine_name'."); + throw new \InvalidArgumentException("Cannot create term because vocabulary is missing. Provide a bundle, 'vid', or 'vocabulary_machine_name'.");🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Drupal/Driver/Core/Core.php` around lines 761 - 765, The exception in termCreate() checks $vocabulary (resolved from $stub->getBundle() or $stub->getValue('vid')) but the message only mentions 'vocabulary_machine_name'; update that InvalidArgumentException text to reflect the new resolution paths by referencing the accepted inputs (bundle or vid) — e.g. say the term is missing a vocabulary (bundle or vid / vocabulary_machine_name) or that neither bundle nor vid was provided (and vid may be supplied via hints) so callers aren't misled; modify the throw in the block where $vocabulary is empty to use the new message.
🧹 Nitpick comments (3)
src/Drupal/Driver/Hint/RolesHint.php (1)
64-66: ⚡ Quick winValidate each role value before casting
At Line 65, unconditional
(string) $rolecan turn invalid values into misleading role names (e.g.,"Array"). Adding lightweight validation here will keep failures explicit and easier to debug.Suggested refactor
foreach ($roles as $role) { - $this->driver->userAddRole($stub, (string) $role); + if (!is_scalar($role) || trim((string) $role) === '') { + continue; + } + $this->driver->userAddRole($stub, trim((string) $role)); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Drupal/Driver/Hint/RolesHint.php` around lines 64 - 66, The loop in RolesHint.php unconditionally casts each $role to string before calling $this->driver->userAddRole($stub, (string) $role), which can hide invalid types (e.g., arrays become "Array"); update the loop to validate each $role first—check is_string($role) or is_scalar($role) and that the trimmed string is non-empty, log or throw a clear error for invalid values, and only then call $this->driver->userAddRole($stub, (string) $role); reference the foreach over $roles and the call to userAddRole to locate where to add the validation and error handling.tests/Drupal/Tests/Driver/Unit/Core/Hint/ParentTermHintTest.php (1)
67-120: ⚡ Quick winAdd explicit test coverage for missing vocabulary resolution path
Please add a case where bundle is
NULLandvidis absent/empty, assertingCreationHintResolutionException. That branch is part of the new strict failure behavior and is worth pinning directly in this test suite.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/Drupal/Tests/Driver/Unit/Core/Hint/ParentTermHintTest.php` around lines 67 - 120, Add a new test case that exercises the missing-vocabulary branch: instantiate ParentTermHint and an EntityStub with bundle set to NULL and no 'vid' key (or an empty 'vid'), call ParentTermHint::applyToStub($stub) and assert it throws CreationHintResolutionException, also assert the original 'parent' value remains unchanged on the stub (use the same pattern as testApplyToStubThrowsOnUnknownParent to catch the exception and assert message contains identifying info and that EntityStub::getValue('parent') still returns the original value).tests/Drupal/Tests/Driver/Unit/Core/Hint/AuthorHintTest.php (1)
100-103: ⚡ Quick winStrengthen coercion test with non-string inputs
dataProviderApplyToStubCoercesValueToString()currently uses string-only cases. Add at least one non-string value (e.g.,7) to verify actual coercion behavior end-to-end.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/Drupal/Tests/Driver/Unit/Core/Hint/AuthorHintTest.php` around lines 100 - 103, The data provider dataProviderApplyToStubCoercesValueToString() only supplies string inputs; add a non-string case to confirm coercion occurs end-to-end by yielding an integer input and the expected string output (for example add a case like 'integer input' => [7, '7']) so the test exercises actual type coercion when AuthorHint::applyToStub is used.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/Drupal/Driver/Core/Hint/AuthorHint.php`:
- Around line 75-82: The lookup result from ($this->userLookup) stored in $user
may be an object without an id() method; before calling $user->id() in
AuthorHint.php (around the AuthorHint class handling), validate that $user is an
object exposing id() (e.g. method_exists($user, 'id') or instanceof the expected
user class) and if it does not, throw a CreationHintResolutionException with a
clear message similar to the existing "user ... does not exist" error; only call
$user->id() and proceed to $stub->setValue('uid', ...) and
$stub->removeValue('author') when the guard passes.
In `@src/Drupal/Driver/Core/Hint/ParentTermHint.php`:
- Around line 43-51: The parent lookup closure in ParentTermHint currently
returns reset($tids) and silently accepts ambiguous matches; modify the
$this->parentLookup closure so after executing the taxonomy_term entity query it
checks if count($tids) > 1 and, if so, throws a CreationHintResolutionException
with a clear ambiguity message including $parent_name and $vid; otherwise
proceed to return NULL if empty or the single tid (reset($tids)). Ensure you
reference the ParentTermHint::$parentLookup closure and throw
CreationHintResolutionException when multiple matches are detected.
- Around line 79-91: Validate that the values returned from
$stub->getValue('parent') and $stub->getBundle()/ $stub->getValue('vid') are
scalars before casting to string: retrieve them into variables (e.g. $parent_raw
and $vid_raw), check is_scalar($parent_raw) and is_scalar($vid_raw) (or null
allowed for bundle), and if not throw a CreationHintResolutionException with a
clear message referencing the offending field; only then cast to string to
produce $parent_name and $vid and call $this->parentLookup((string)$parent_name,
(string)$vid).
In `@src/Drupal/Driver/DrupalDriver.php`:
- Around line 152-159: getCreationHints currently calls getCore() which can fail
if $this->core is not initialized; update getCreationHints to safely handle an
uninitialized core by catching failures from getCore (or checking $this->core
first) and returning an empty array when core is missing, then only call
$core->getCreationHints($entity_type) if $core is an instance of
CreationHintCapabilityInterface; reference getCreationHints(), getCore(),
CreationHintCapabilityInterface and $this->core when applying the guard.
In `@UPGRADING.md`:
- Around line 413-415: The migration guide currently references the wrong
dispatcher method names; update the text to use the actual method names
runPreCreateHints() and runPostCreateHints() instead of applyPreCreateHints() /
applyPostCreateHints() so the docs match the implementation (also check nearby
mentions of registerCreationHint() and getCreationHints() to ensure
consistency).
---
Outside diff comments:
In `@src/Drupal/Driver/Core/Core.php`:
- Around line 761-765: The exception in termCreate() checks $vocabulary
(resolved from $stub->getBundle() or $stub->getValue('vid')) but the message
only mentions 'vocabulary_machine_name'; update that InvalidArgumentException
text to reflect the new resolution paths by referencing the accepted inputs
(bundle or vid) — e.g. say the term is missing a vocabulary (bundle or vid /
vocabulary_machine_name) or that neither bundle nor vid was provided (and vid
may be supplied via hints) so callers aren't misled; modify the throw in the
block where $vocabulary is empty to use the new message.
In `@src/Drupal/Driver/DrushDriver.php`:
- Around line 265-275: The parseUserId() call can return NULL and you must fail
fast before creating a null-backed account and invoking post-create hooks;
update the logic in DrushDriver (around where $result =
$this->drush('user-create', ...) is called) to check the $uid returned by
$this->parseUserId($result) and if it is null/false, abort the flow (throw an
exception or return an error) instead of proceeding to set
$stub->setValue('uid', ...), creating $account and calling
$this->applyPostCreateHints($stub, $account, 'user'); ensure the guard
references parseUserId, $stub and applyPostCreateHints so only a valid persisted
uid triggers the post-create hint dispatch.
---
Nitpick comments:
In `@src/Drupal/Driver/Hint/RolesHint.php`:
- Around line 64-66: The loop in RolesHint.php unconditionally casts each $role
to string before calling $this->driver->userAddRole($stub, (string) $role),
which can hide invalid types (e.g., arrays become "Array"); update the loop to
validate each $role first—check is_string($role) or is_scalar($role) and that
the trimmed string is non-empty, log or throw a clear error for invalid values,
and only then call $this->driver->userAddRole($stub, (string) $role); reference
the foreach over $roles and the call to userAddRole to locate where to add the
validation and error handling.
In `@tests/Drupal/Tests/Driver/Unit/Core/Hint/AuthorHintTest.php`:
- Around line 100-103: The data provider
dataProviderApplyToStubCoercesValueToString() only supplies string inputs; add a
non-string case to confirm coercion occurs end-to-end by yielding an integer
input and the expected string output (for example add a case like 'integer
input' => [7, '7']) so the test exercises actual type coercion when
AuthorHint::applyToStub is used.
In `@tests/Drupal/Tests/Driver/Unit/Core/Hint/ParentTermHintTest.php`:
- Around line 67-120: Add a new test case that exercises the missing-vocabulary
branch: instantiate ParentTermHint and an EntityStub with bundle set to NULL and
no 'vid' key (or an empty 'vid'), call ParentTermHint::applyToStub($stub) and
assert it throws CreationHintResolutionException, also assert the original
'parent' value remains unchanged on the stub (use the same pattern as
testApplyToStubThrowsOnUnknownParent to catch the exception and assert message
contains identifying info and that EntityStub::getValue('parent') still returns
the original value).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 88767511-3875-4ade-b120-48e1fb78d691
📒 Files selected for processing (27)
UPGRADING.mdsrc/Drupal/Driver/Capability/CreationHintCapabilityInterface.phpsrc/Drupal/Driver/Core/Core.phpsrc/Drupal/Driver/Core/Hint/AuthorHint.phpsrc/Drupal/Driver/Core/Hint/ParentTermHint.phpsrc/Drupal/Driver/Core/Hint/VocabularyMachineNameHint.phpsrc/Drupal/Driver/DrupalDriver.phpsrc/Drupal/Driver/DrushDriver.phpsrc/Drupal/Driver/Exception/CreationHintResolutionException.phpsrc/Drupal/Driver/Hint/CreationHintInterface.phpsrc/Drupal/Driver/Hint/CreationHintRegistryTrait.phpsrc/Drupal/Driver/Hint/PostCreateHintInterface.phpsrc/Drupal/Driver/Hint/PreCreateHintInterface.phpsrc/Drupal/Driver/Hint/RolesHint.phptests/Drupal/Tests/Driver/Kernel/Core/CoreNodeMethodsKernelTest.phptests/Drupal/Tests/Driver/Kernel/Core/CoreTermMethodsKernelTest.phptests/Drupal/Tests/Driver/Kernel/Core/CoreUserMethodsKernelTest.phptests/Drupal/Tests/Driver/Unit/BlackboxDriverCreationHintsTest.phptests/Drupal/Tests/Driver/Unit/Core/Hint/AuthorHintTest.phptests/Drupal/Tests/Driver/Unit/Core/Hint/ParentTermHintTest.phptests/Drupal/Tests/Driver/Unit/Core/Hint/VocabularyMachineNameHintTest.phptests/Drupal/Tests/Driver/Unit/DrupalDriverCreationHintsTest.phptests/Drupal/Tests/Driver/Unit/DrushDriverCreationHintsTest.phptests/Drupal/Tests/Driver/Unit/Fixtures/FakeUser.phptests/Drupal/Tests/Driver/Unit/Fixtures/HintCapableCoreInterface.phptests/Drupal/Tests/Driver/Unit/Fixtures/RecordingUserCapability.phptests/Drupal/Tests/Driver/Unit/Hint/RolesHintTest.php
| `registerCreationHint()`, `getCreationHints()`, and two protected | ||
| dispatchers (`applyPreCreateHints()`, `applyPostCreateHints()`) for free. | ||
|
|
There was a problem hiding this comment.
Use the actual dispatcher method names in the migration guide.
This section references applyPreCreateHints() / applyPostCreateHints(), but the PR objective defines runPreCreateHints() / runPostCreateHints(). Keeping these names aligned avoids upgrade-time confusion.
📘 Proposed docs fix
- dispatchers (`applyPreCreateHints()`, `applyPostCreateHints()`) for free.
+ dispatchers (`runPreCreateHints()`, `runPostCreateHints()`) for free.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| `registerCreationHint()`, `getCreationHints()`, and two protected | |
| dispatchers (`applyPreCreateHints()`, `applyPostCreateHints()`) for free. | |
| `registerCreationHint()`, `getCreationHints()`, and two protected | |
| dispatchers (`runPreCreateHints()`, `runPostCreateHints()`) for free. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@UPGRADING.md` around lines 413 - 415, The migration guide currently
references the wrong dispatcher method names; update the text to use the actual
method names runPreCreateHints() and runPostCreateHints() instead of
applyPreCreateHints() / applyPostCreateHints() so the docs match the
implementation (also check nearby mentions of registerCreationHint() and
getCreationHints() to ensure consistency).
There was a problem hiding this comment.
Not adopting. The current implementation in CreationHintRegistryTrait uses applyPreCreateHints() and applyPostCreateHints(), so the UPGRADING.md text matches the code as shipped. Renaming to runPre/PostCreateHints() would make the documentation diverge from the actual method names.
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify actual method names in CreationHintRegistryTrait
rg -n "function apply|function run" --type=php src/Drupal/Driver/Hint/CreationHintRegistryTrait.php
Length of output: 217
@AlexSkrypnyk, you're absolutely right — the script confirms the trait exposes applyPreCreateHints() (line 64) and applyPostCreateHints() (line 88), so UPGRADING.md is accurate as written. My original comment was based on the PR objective description rather than the actual method names in the code. No change needed here, sorry for the noise!
There was a problem hiding this comment.
🧹 Nitpick comments (2)
tests/Drupal/Tests/Driver/Unit/Core/Hint/AuthorHintTest.php (2)
102-105: ⚡ Quick winCoercion test name over-promises current coverage.
At Line 102, the provider only sends strings, so it doesn’t actually validate non-string coercion. Add at least one numeric case.
Proposed update
public static function dataProviderApplyToStubCoercesValueToString(): iterable { yield 'plain string' => ['alice', 'alice']; yield 'integer-like string' => ['7', '7']; + yield 'integer' => [7, '7']; + yield 'float' => [7.5, '7.5']; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/Drupal/Tests/Driver/Unit/Core/Hint/AuthorHintTest.php` around lines 102 - 105, The data provider dataProviderApplyToStubCoercesValueToString currently only yields string inputs so the test name is misleading; add at least one non-string numeric case (e.g., yield 'integer' => [7, '7']) so the test actually verifies that applyToStub coerces numeric values to strings; update the provider in AuthorHintTest::dataProviderApplyToStubCoercesValueToString to include this numeric example alongside the existing string cases.
121-128: ⚡ Quick winAssert lookup callback is not called for empty author values.
At Line 121, this test can be stricter by failing immediately if the resolver callback is invoked, proving the empty-value guard runs before lookup.
Proposed update
- $hint = new AuthorHint(static fn (): object => new FakeUser(1)); + $hint = new AuthorHint(static function (): object { + self::fail('Lookup callback must not run when author is empty.'); + return new FakeUser(1); + });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/Drupal/Tests/Driver/Unit/Core/Hint/AuthorHintTest.php` around lines 121 - 128, Make the test ensure the AuthorHint resolver callback is NOT invoked for empty author values by replacing the current resolver (static fn (): object => new FakeUser(1)) with a callback that will immediately fail or throw if executed; keep the stub creation via EntityStub('node','article',['author' => $author]) and the existing expectException/expectExceptionMessageMatches assertions, then call AuthorHint->applyToStub($stub) to verify the guard prevents the lookup callback from being called.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/Drupal/Tests/Driver/Unit/Core/Hint/AuthorHintTest.php`:
- Around line 102-105: The data provider
dataProviderApplyToStubCoercesValueToString currently only yields string inputs
so the test name is misleading; add at least one non-string numeric case (e.g.,
yield 'integer' => [7, '7']) so the test actually verifies that applyToStub
coerces numeric values to strings; update the provider in
AuthorHintTest::dataProviderApplyToStubCoercesValueToString to include this
numeric example alongside the existing string cases.
- Around line 121-128: Make the test ensure the AuthorHint resolver callback is
NOT invoked for empty author values by replacing the current resolver (static fn
(): object => new FakeUser(1)) with a callback that will immediately fail or
throw if executed; keep the stub creation via
EntityStub('node','article',['author' => $author]) and the existing
expectException/expectExceptionMessageMatches assertions, then call
AuthorHint->applyToStub($stub) to verify the guard prevents the lookup callback
from being called.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: df0233f5-7ec2-45ae-8cd3-c10279c88941
📒 Files selected for processing (4)
tests/Drupal/Tests/Driver/Unit/Core/Hint/AuthorHintTest.phptests/Drupal/Tests/Driver/Unit/Core/Hint/ParentTermHintTest.phptests/Drupal/Tests/Driver/Unit/Core/Hint/VocabularyMachineNameHintTest.phptests/Drupal/Tests/Driver/Unit/Hint/RolesHintTest.php
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/Drupal/Tests/Driver/Unit/Hint/RolesHintTest.php
…Driver::getCreationHints()'.
Closes #365
Summary
Prior to this change, ergonomic stub properties such as
authoron nodes,parenton terms, androleson users were resolved by scattered inlineif ($stub->hasValue(...))blocks insideCore.phpandDrushDriver.phpwith no discoverable contract. This PR introduces a first-class creation hint system: aCreationHintCapabilityInterfacethat exposes a typed registry per entity type, two lifecycle interfaces (PreCreateHintInterfaceandPostCreateHintInterface), and aCreationHintRegistryTraitthat both drivers now use to run pre- and post-save dispatch. As a 3.0 behavioural break, unresolvable hints (non-existent author, empty author, missing vocabulary on a parent term) now throwCreationHintResolutionExceptioninstead of silently producing wrong values.Changes
Capability interface
Drupal\Driver\Capability\CreationHintCapabilityInterfacewithgetCreationHints(string $entity_type): array. Composite driver interfaces (DrupalDriverInterface,DrushDriverInterface) are intentionally not updated - hand-rolled implementations remain valid.BlackboxDriverdeliberately does not implement the capability.Hint interfaces and exception
Drupal\Driver\Hint\CreationHintInterface(base contract:getAlias(),getDescription()).Drupal\Driver\Hint\PreCreateHintInterface- mutates the stub beforeEntityInterface::save().Drupal\Driver\Hint\PostCreateHintInterface- acts on the entity after save.Drupal\Driver\Exception\CreationHintResolutionExceptionextendingInvalidArgumentException.Hint implementations
Core\Hint\AuthorHint- resolvesauthorstub alias to a Drupal user UID (pre-create).Core\Hint\VocabularyMachineNameHint- resolvesvocabularyalias tovidon term stubs (pre-create).Core\Hint\ParentTermHint- resolvesparentterm name to a TID (pre-create).Hint\RolesHint- assignsrolesarray after user save (post-create), shared byCoreandDrushDriver.Registry trait and dispatcher wiring
Drupal\Driver\Hint\CreationHintRegistryTraitwithgetCreationHints(),runPreCreateHints(), andrunPostCreateHints()helpers.Core,DrupalDriver, andDrushDrivernow use the trait. Inline alias blocks inCore::nodeCreate(),Core::termCreate(),Core::userCreate(),DrushDriver::termCreate(), andDrushDriver::userCreate()replaced with dispatcher calls.Core::userCreate()now honoursroleson the stub (symmetry fix -DrushDriveralready did).Tests
AuthorHintTest,VocabularyMachineNameHintTest,ParentTermHintTest, andRolesHintTest.DrupalDriverCreationHintsTest,DrushDriverCreationHintsTest,BlackboxDriverCreationHintsTest.Documentation
UPGRADING.mdsection for the 3.0 behavioural break:CreationHintResolutionExceptionon badauthor, emptyauthor, and missing vocabulary when usingparent.Error message improvements
VocabularyMachineNameHint::applyToStub()guarded against a missing alias.ParentTermHintthrows with a clear message when the vocabulary cannot be resolved.Core::nodeCreate()throws a clear error on emptyauthor.Before / After
Summary by CodeRabbit
New Features
Bug Fixes
Documentation