我可以在 Sonata Admin 控制器中使用 prePersist/preUpdate 来保存多个对象吗?

问题描述 投票:0回答:3

我有这个

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
特征?我在这里缺少什么?

php symfony doctrine-orm sonata-admin sonata
3个回答
1
投票

如果您想添加具有不同别名的相同对象,最好为您的管理类创建一个

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');
}

1
投票

也许是这样的:

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);
    }
}

0
投票

虽然这是一个老问题,但答案发生了变化: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);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.