我有这个
Alias
实体:
use Gedmo\Timestampable\Traits\TimestampableEntity;
class Alias
{
use IdentifierAutogeneratedTrait;
use TimestampableEntity;
use ActiveTrait;
/**
* @var string
* @ORM\Column(type="string", length=150)
*/
private $name;
/**
* Command associated to the alias.
*
* @var Command[]
* @ORM\ManyToMany(targetEntity="Command", mappedBy="aliases", cascade={"persist"})
*/
private $commands;
...
}
我有这个管理控制器:
class AliasAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name', null, array('required' => true))
->add('active', null, array('required' => false));
}
...
public function prePersist($alias)
{
// remove extra white spaces
$noWhiteSpaces = str_replace(' ', '', $alias->getName());
// split into several names
$aliasArr = explode(',', $noWhiteSpaces);
}
public function preUpdate($alias)
{
...
}
}
当我在视图上添加新的
Alias
时(即表单本身),我可以在 name
输入字段上写入一个值,例如: value1
或者我可以写入以逗号分隔的多个值:value1, value2, value3, value4
。如果我提交表单时 name
字段上只有一个值,那么不会有问题,但如果我有多个逗号分隔作为第二个示例,那么我应该在 prePersist/preUpdate
方法上检查它们以便拆分他们并为每个 Alias
创建一个 name
,我该怎么做?如果您查看 prePersist
方法,您会更清楚地看到我正在谈论的内容,即如何在新的 $aliasArr
中为每个名称创建新的别名,有什么帮助吗?
更新: TimestampableEntity 未处理,为什么?
在尝试了 @m-khalid-junaid 的解决方案后,由于
NOT NULL
约束,我以下面的错误结束,但我不知道为什么。看一下代码:
class AliasAdminController extends Controller
{
/**
* {@inheritdoc}
*/
public function createAction(Request $request = null)
{
// the key used to lookup the template
$templateKey = 'edit';
if (false === $this->admin->isGranted('CREATE')) {
throw new AccessDeniedException();
}
$object = $this->admin->getNewInstance();
$this->admin->setSubject($object);
/** @var $form \Symfony\Component\Form\Form */
$form = $this->admin->getForm();
$form->setData($object);
if ($this->getRestMethod() == 'POST') {
$form->submit($this->get('request'));
$isFormValid = $form->isValid();
// persist if the form was valid and if in preview mode the preview was approved
if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
if (false === $this->admin->isGranted('CREATE', $object)) {
throw new AccessDeniedException();
}
try {
$aliasArr = $this->toStrings($object->getName());
if (count($aliasArr) > 1) {
$object->setName($aliasArr[0]);
$object = $this->admin->create($object);
unset($aliasArr[0]);
$entityManager = $this->getDoctrine()->getManager();
foreach ($aliasArr as $alias) {
$newAlias = new Alias();
$newAlias->setName($alias);
$entityManager->persist($newAlias);
}
$entityManager->flush();
} else {
$object = $this->admin->create($object);
}
if ($this->isXmlHttpRequest()) {
return $this->renderJson(array(
'result' => 'ok',
'objectId' => $this->admin->getNormalizedIdentifier($object),
));
}
$this->addFlash(
'sonata_flash_success',
$this->admin->trans(
'flash_create_success',
array('%name%' => $this->escapeHtml($this->admin->toString($object))),
'SonataAdminBundle'
)
);
// redirect to edit mode
return $this->redirectTo($object);
} catch (ModelManagerException $e) {
$this->logModelManagerException($e);
$isFormValid = false;
}
}
// show an error message if the form failed validation
if (!$isFormValid) {
if (!$this->isXmlHttpRequest()) {
$this->addFlash(
'sonata_flash_error',
$this->admin->trans(
'flash_create_error',
array('%name%' => $this->escapeHtml($this->admin->toString($object))),
'SonataAdminBundle'
)
);
}
} elseif ($this->isPreviewRequested()) {
// pick the preview template if the form was valid and preview was requested
$templateKey = 'preview';
$this->admin->getShow();
}
}
$view = $form->createView();
// set the theme for the current Admin Form
$this->get('twig')->getExtension('form')->renderer->setTheme($view, $this->admin->getFormTheme());
return $this->render($this->admin->getTemplate($templateKey), array(
'action' => 'create',
'form' => $view,
'object' => $object,
));
}
/**
* {@inheritdoc}
*/
public function editAction($id = null)
{
// the key used to lookup the template
$templateKey = 'edit';
$id = $this->get('request')->get($this->admin->getIdParameter());
$object = $this->admin->getObject($id);
if (!$object) {
throw new NotFoundHttpException(sprintf('unable to find the object with id : %s', $id));
}
if (false === $this->admin->isGranted('EDIT', $object)) {
throw new AccessDeniedException();
}
$this->admin->setSubject($object);
/** @var $form \Symfony\Component\Form\Form */
$form = $this->admin->getForm();
$form->setData($object);
if ($this->getRestMethod() == 'POST') {
$form->submit($this->get('request'));
$isFormValid = $form->isValid();
// persist if the form was valid and if in preview mode the preview was approved
if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
try {
$aliasArr = $this->toStrings($object->getName());
if (count($aliasArr) > 1) {
$object->setName($aliasArr[0]);
$object = $this->admin->update($object);
unset($aliasArr[0]);
$entityManager = $this->getDoctrine()->getManager();
foreach ($aliasArr as $alias) {
$newAlias = new Alias();
$newAlias->setName($alias);
$entityManager->persist($newAlias);
}
$entityManager->flush();
} else {
$object = $this->admin->update($object);
}
if ($this->isXmlHttpRequest()) {
return $this->renderJson(array(
'result' => 'ok',
'objectId' => $this->admin->getNormalizedIdentifier($object),
));
}
$this->addFlash(
'sonata_flash_success',
$this->admin->trans(
'flash_edit_success',
array('%name%' => $this->escapeHtml($this->admin->toString($object))),
'SonataAdminBundle'
)
);
// redirect to edit mode
return $this->redirectTo($object);
} catch (ModelManagerException $e) {
$this->logModelManagerException($e);
$isFormValid = false;
}
}
// show an error message if the form failed validation
if (!$isFormValid) {
if (!$this->isXmlHttpRequest()) {
$this->addFlash(
'sonata_flash_error',
$this->admin->trans(
'flash_edit_error',
array('%name%' => $this->escapeHtml($this->admin->toString($object))),
'SonataAdminBundle'
)
);
}
} elseif ($this->isPreviewRequested()) {
// enable the preview template if the form was valid and preview was requested
$templateKey = 'preview';
$this->admin->getShow();
}
}
$view = $form->createView();
// set the theme for the current Admin Form
$this->get('twig')->getExtension('form')->renderer->setTheme($view, $this->admin->getFormTheme());
return $this->render($this->admin->getTemplate($templateKey), array(
'action' => 'edit',
'form' => $view,
'object' => $object,
));
}
private function logModelManagerException($e)
{
$context = array('exception' => $e);
if ($e->getPrevious()) {
$context['previous_exception_message'] = $e->getPrevious()->getMessage();
}
$this->getLogger()->error($e->getMessage(), $context);
}
private function toStrings($string)
{
$noWhiteSpaces = str_replace(' ', '', $string);
return explode(',', $noWhiteSpaces);
}
}
这是错误:
[2016-02-28 11:10:36] doctrine.DEBUG: "START TRANSACTION" [] []
[2016-02-28 11:10:36] doctrine.DEBUG: INSERT INTO cm_alias (name, created_at, updated_at, active) VALUES (?, ?, ?, ?) {"1":"alias1","2":null,"3":null,"4":true} []
[2016-02-28 11:10:36] doctrine.DEBUG: "ROLLBACK" [] []
[2016-02-28 11:10:36] app.ERROR: Failed to create object: PlatformAdminBundle\Entity\Alias {"exception":"[object] (Sonata\\AdminBundle\\Exception\\ModelManagerException(code: 0): Failed to create object: PlatformAdminBundle\\Entity\\Alias at /var/www/html/platform.sonata/vendor/sonata-project/doctrine-orm-admin-bundle/Model/ModelManager.php:142, Doctrine\\DBAL\\Exception\\NotNullConstraintViolationException(code: 0): An exception occurred while executing 'INSERT INTO cm_alias (name, created_at, updated_at, active) VALUES (?, ?, ?, ?)' with params [\"alias1\", null, null, 1]:\n\nSQLSTATE[23000]: Integrity constraint violation: 1048 Column 'created_at' cannot be null at /var/www/html/platform.sonata/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php:112, Doctrine\\DBAL\\Driver\\PDOException(code: 23000): SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'created_at' cannot be null at /var/www/html/platform.sonata/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:93, PDOException(code: 23000): SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'created_at' cannot be null at /var/www/html/platform.sonata/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:91)","previous_exception_message":"An exception occurred while executing 'INSERT INTO cm_alias (name, created_at, updated_at, active) VALUES (?, ?, ?, ?)' with params [\"alias1\", null, null, 1]:\n\nSQLSTATE[23000]: Integrity constraint violation: 1048 Column 'created_at' cannot be null"} []
为什么新的管理控制器不处理
TimestampableEntity
特征?我在这里缺少什么?
如果您想添加具有不同别名的相同对象,最好为您的管理类创建一个
CRUDController
控制器,一旦您拥有 CRUDController,您可以覆盖基类的 createAction
和 editAction
并在此处实现您的逻辑 prePresist/当前对象的 preUpdate 事件可以使用控制器为您的对象创建具有不同信息的副本。我在下面只提到了必要的代码,您可以从奏鸣曲的 CRUDController 复制并在 editAction
中实现相同的逻辑
public function createAction(Request $request = null)
{
// .. some code here copy from base class
try {
$noWhiteSpaces = str_replace(' ', '', $object->getName());
// split into several names
$aliases = explode(',', $noWhiteSpaces);
if (count($aliases) > 1) {
$object->setName($aliases[0]);
$object = $this->admin->create($object);
unset($aliases[0]);
$DM = $this->getDoctrine()->getManager();
foreach ($aliases as $alias) {
$newAlias = new Alias();
$newAlias->setName($alias);
$DM->persist($newAlias);
}
$DM->flush();
} else {
$object = $this->admin->create($object);
}
} catch (ModelManagerException $e) {
$this->handleModelManagerException($e);
$isFormValid = false;
}
// .. some code here copy from base class
}
编辑
created_at
非空错误
您需要在别名实体中定义一个构造函数,并使用
createdAt
对象初始化您的 DateTime
属性
/**
* Constructor
*/
public function __construct()
{
$this->createdAt = new \DateTime('now');
}
也许是这样的:
public function prePersist($alias)
{
// remove extra white spaces
$noWhiteSpaces = str_replace(' ', '', $alias->getName());
// split into several names
$aliasArr = explode(',', $noWhiteSpaces);
$currentAliasName = $aliasArr[0]; // Use the first name for the Alias created by your admin class.
$alias->setName($currentAliasName);
unset($aliasArr[0]);
$em = $this->getConfigurationPool()->getContainer()->getDoctrine()->getManager();
foreach ($aliasArr as $aliasName) {
$newAlias = new Alias();
$newAlias->setName($aliasName);
$newAlias->setActive(?);
$em->persist($newAlias);
$em->flush($newAlias);
}
}
虽然这是一个老问题,但答案发生了变化:Sonata Admin 现在支持保存挂钩。请参阅保存钩子@SonataAdminBundle Symfony 的文档。
支持的钩子有:
preValidate($object)
prePersist($object)
postPersist($object)
preValidate($object)
preUpdate($object)
postUpdate($object)
preRemove($object)
postRemove($object)
PHP 8.3 和 Symfony 7.0 实现示例是:
<?php
declare(strict_types=1);
namespace App\Admin;
use App\Entity\User;
use App\Enum\RoleEnum;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Form\FormMapper;
use Symfony\Component\Form\Extension\Core\Type\EnumType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Contracts\Service\Attribute\Required;
/**
* User document Sonata Admin backend class.
*
* @phpstan-extends AbstractAdmin<User>
*/
final class UserAdmin extends AbstractAdmin
{
protected ?UserPasswordHasherInterface $passwordHasher = null;
/**
* Set the service UserPasswordHasherInterface.
*/
#[Required]
public function setUserPasswordHasher(UserPasswordHasherInterface $passwordHasher): void
{
$this->passwordHasher = $passwordHasher;
}
/**
* Hash the User password before updating the database contents.
*/
#[\Override]
public function prePersist(object $object): void
{
$this->hashPassword($object);
}
/**
* Hash the User password before updating the database contents.
*/
#[\Override]
public function preUpdate(object $object): void
{
$this->hashPassword($object);
}
/**
* Canonicalize the User email before validating the form.
*/
#[\Override]
public function preValidate(object $object): void
{
$email = $object->getEmail();
$object->setEmailCanonical(null !== $email ? mb_strtolower($email) : null);
}
/**
* Update enabled field to not be required.
*
* @param FormMapper<User> $form the form view mapper
*/
#[\Override]
protected function configureFormFields(FormMapper $form): void
{
$form
->with('authentication', ['class' => 'col-md-4'])
->add('email')
->add('plainPassword', PasswordType::class, [
'required' => (!$this->hasSubject() || null === $this->getSubject()->getId()),
])
->add('enabled', null)
->end()
->with('roles', ['class' => 'col-md-8'])
->add('roles', EnumType::class, [
'label' => false,
'multiple' => true,
'expanded' => true,
'required' => false,
'class' => RoleEnum::class,
'translation_domain' => 'authentication',
])
->end();
parent::configureFormFields($form);
$form->get('enabled')->setRequired(false);
}
/**
* Hash the User password.
*/
protected function hashPassword(User $user): void
{
if (null === $this->passwordHasher) {
throw new \LogicException('The UserPasswordHasherInterface is not set.');
}
$plainPassword = $user->getPlainPassword();
if (null === $plainPassword) {
return;
}
$password = $this->passwordHasher->hashPassword($user, $plainPassword);
$user->setPassword($password);
}
}