- Issue created by @mxr576
- ππΊHungary mxr576 Hungary
/** * {@inheritdoc} */ public function getBaseFormId() { // Assign ENTITY_TYPE_form as base form ID to invoke corresponding // hook_form_alter(), #validate, #submit, and #theme callbacks, but only if // it is different from the actual form ID, since callbacks would be invoked // twice otherwise. $base_form_id = $this->entity->getEntityTypeId() . '_form'; if ($base_form_id == $this->getFormId()) { $base_form_id = NULL; } return $base_form_id; }
Just found this workaround in
\Drupal\Core\Entity\EntityForm::getBaseFormId()
so somebody might have tried to address this problem before. - πΊπΈUnited States nicxvan
We'll definitely need sign off on collapsing the alters.
I think it's best, but we need to ensure that there isn't some time that that behavior is essential.
I cannot think of any off the top of my head, but why was baseform trying to solve it there instead of in the alter in the first place.
- π©πͺGermany donquixote
The workaround exists since Drupal 9.0.* (or earlier).
Behavior in older Drupal versions
In Drupal prior to 11.2.x, when calling ->alter(['a', 'a'], ..) with the same alter type repeated, the implementations would simply be repeated.
The order was by module and then by alter type:class HookAlterTest extends KernelTestBase { protected static $modules = [ 'procedural_hook_test', 'system', ]; public function testAlter(): void { $this->assertAlterCallOrder([ 'procedural_hook_test_test_main_alter', 'procedural_hook_test_test_other_alter', 'procedural_hook_test_test_main_alter', 'procedural_hook_test_test_main_alter', 'procedural_hook_test_test_other_alter', 'system_test_main_alter', 'system_test_main_alter', 'system_test_main_alter', ], ['test_main', 'test_other', 'test_main', 'test_main', 'test_other', 'test_x']); } [...] (not sharing the full class here)
Behavior since 11.2.x
Since 11.2.x, every implementation is only executed once, and we see the notice as reported here.
public function testAlter(): void { $this->assertAlterCallOrder([ 'procedural_hook_test_test_main_alter', 'procedural_hook_test_test_other_alter', 'system_test_main_alter', ], ['test_main', 'test_other', 'test_main', 'test_main', 'test_other', 'test_x']); }
However, both the deduplication and the notice were actually meant for a different case, where the same method is registered for two distinct alter hooks, which are then executed together with ->alter().
E.g. ->alter(['a', 'b'], ..) with #[Hook('a_alter')] and #[Hook('b_alter')] on the same method.
The case with ->alter(['a', 'a'], ..) was not really considered at the time. This is why the notice looks so strange.Solution
Now we need to decide what should be the "correct" behavior.
I think it makes sense to execute every implementation only once.
But we don't want the same notice, if the duplication is caused by a duplicate alter type passed to ->alter().The easiest might be to simply call array_unique() in ->alter().
class ModuleHandler ... { [..] public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { [..] if (is_array($type)) { $type = array_unique($type); $cid = implode(',', $type); } else { $cid = $type; }
- π©πͺGermany donquixote
We'll definitely need sign off on collapsing the alters.
We should have asked for this before 11.2.0 release.
(And we should have added a test for this case)
Now if we change it back to have implementations repeated, we break with the behavior of 11.2.0. - πΊπΈUnited States nicxvan
Alternatively if we change it back we
fix
the behavior of 11.2.However, since we have the ability to have multiple implementations there are ways to execute your alters multiple times if you really want to.
I suspect this was just a hidden misfeature for many years.
Calling the same alter multiple times similarly seems fundamentally wrong. You could likely achieve that with a second call but I fail to see the use case of duplicates alter runs.
I would advocate for array_unique but that is forest impression without deep consideration.