Before this change, custom account cancellation methods were implemented via hook_user_cancel(). However, this method wasn't reliable for the reasons described in #3135592: Cannot implement a custom user cancellation method. This change deprecates the hook_user_cancel() hook and it schedules for removal in the next major Drupal release.
Third-party code that need to act when an account is cancelled, should listen to the \Drupal\user\Event\AccountCancelEvent event and add its own cancellation logic. There are cases when the third-party module wants to avoid the default Drupal core's handling of account cancellation. In this case, the custom subscriber should, act before the core subscribers, by using a proper priority and should use AccountCancelEvent::stopPropagation() method to avoid downstream subscribers.
Currently, Drupal core exposes the following account cancellation subscribers:
| Subscriber | Priority |
|---|---|
\Drupal\comment\EventSubscriber\CommentAccountCancelSubscriber::onUserAccountCancel() |
20 |
\Drupal\history\EventSubscriber\HistoryAccountCancelSubscriber::onUserAccountCancel() |
20 |
\Drupal\node\EventSubscriber\NodeAccountCancelSubscriber::onUserAccountCancel() |
20 |
\Drupal\user\EventSubscriber\AccountCancelSubscriber::::onUserAccountCancel() |
0 |
Example
In the following example we pretend that third-party code has defined a custom account cancellation method custom_user_cancel, via hook_user_cancel_methods_alter():
Before
/**
* Implements hook_user_cancel().
*/
function mymodule_user_cancel(array $edit, UserInterface $account, string $method): void {
if ($method === 'custom_user_cancel') {
\Drupal::messenger()->addStatus('Custom user cancel method executed.');
}
}
But note that, because of the bug from #3135592: Cannot implement a custom user cancellation method, the logic of user_cancel_block cancellation method is also executed.
After
mymodule.services.yml:
services:
mymodule.account_cancel:
class: Drupal\mymodule\EventSubscriber\MyModuleAccountCancelSubscriber
tags:
- { name: event_subscriber }
Drupal\mymodule\EventSubscriber\MyModuleAccountCancelSubscriber:
class MyModuleAccountCancelSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents(): array {
return [
// Act before all Drupal core account cancellation subscribers.
AccountCancelEvent::class => ['onAccountCancel' => 50],
];
}
public function onUserAccountCancel(AccountCancelEvent $event): void {
if ($event->getMethod() === 'custom_user_cancel') {
// Do own logic here.
\Drupal::messenger()->addStatus('Custom user cancel method executed.');
// Prevent *all* Drupal core account cancellation subscribers to act.
$event->stopPropagation();
}
}
}
Other changes
Constructor of UserController, UserCancelForm and UserMultipleCancelConfirm classes should receive the user.account_cancellation service as an additional parameter. Not passing this service as parameter is deprecated and the parameter will be mandatory in the next Drupal major version.