保存钩子
当 SonataAdmin 被提交处理时,会调用一些事件。一个是在任何持久层交互之前,另一个是在之后。此外,在提交和验证编辑和创建操作之间,会调用 preValidate
事件。事件命名如下
- 新对象:
preValidate($object)
/prePersist($object)
/postPersist($object)
- 编辑的对象:
preValidate($object)
/preUpdate($object)
/postUpdate($object)
- 删除的对象:
preRemove($object)
/postRemove($object)
值得注意的是,无论是否存在任何实际的持久层事件,只要 Admin 成功提交,就会调用更新事件。这与 DoctrineORM 中的 preUpdate
和 postUpdate
事件以及可能其他一些持久层的使用不同。
例如:如果你提交一个编辑表单,而没有更改表单上的任何值,那么数据库中没有任何内容需要更改,并且 DoctrineORM 不会触发 Entity 类自身的 preUpdate
和 postUpdate
事件。但是,你的 Admin 类的 preUpdate
和 postUpdate
方法会被调用,这可以被用来发挥你的优势。
注意
当在一个 Admin 中嵌入另一个 Admin 时,例如使用 sonata_type_admin
字段类型,子 Admin 的钩子不会被触发。
与 SonataUserBundle 一起使用的示例
SonataUserBundle
为你的 Symfony 项目提供身份验证功能,并与 Doctrine ORM、Doctrine ODM 兼容。
用户管理系统需要在更新用户密码或用户名时执行特定的调用。这就是如何使用 Admin 捆绑包通过使用 preUpdate
保存钩子来解决问题的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
namespace Sonata\UserBundle\Admin\Entity;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Form\Type\ModelType;
use Sonata\UserBundle\Form\Type\SecurityRolesType;
use Sonata\UserBundle\Model\UserManagerInterface;
final class UserAdmin extends AbstractAdmin
{
private UserManagerInterface $userManager;
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
}
protected function configureFormFields(FormMapper $form): void
{
$form
->with('General')
->add('username')
->add('email')
->add('plainPassword', 'text')
->end()
->with('Groups')
->add('groups', ModelType::class, ['required' => false])
->end()
->with('Management')
->add('roles', SecurityRolesType::class, ['multiple' => true])
->add('locked', null, ['required' => false])
->add('expired', null, ['required' => false])
->add('enabled', null, ['required' => false])
->add('credentialsExpired', null, ['required' => false])
->end()
;
}
public function preUpdate(object $user): void
{
$this->userManager->updateCanonicalFields($user);
$this->userManager->updatePassword($user);
}
}
在控制器中挂钩
你可能已经注意到,Admin 中存在的钩子不允许你与删除过程进行交互:你无法取消它。为了实现这一点,你应该知道还有一种方法可以在控制器中挂钩操作。
如果你定义一个继承自 CRUDController
的自定义控制器,你可以重新定义以下方法
- 新对象:
preCreate($object)
- 编辑的对象:
preEdit($object)
- 删除的对象:
preDelete($object)
- 显示对象:
preShow($object)
- 列表对象:
preList($object)
如果这些方法返回 Response,则该过程将被中断,并且响应将由控制器按原样返回(如果它返回 null,则该过程继续)。你可以使用 redirectTo($object)
方法生成重定向到对象显示页面的重定向。
注意
如果你需要禁止删除特定项目,你可以在 preDelete($object)
方法中进行检查。