Last active
March 15, 2018 19:00
-
-
Save Xymanek/23feec43b9faf5e0b8f70c2604fb8eda to your computer and use it in GitHub Desktop.
Sonata admin custom form action RFC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Admin\FormAction; | |
use Sonata\AdminBundle\Admin\AdminInterface; | |
abstract class AbstractFormAction implements FormActionInterface, AdminAwareInterface | |
{ | |
/** | |
* @var AdminInterface | |
*/ | |
protected $admin; | |
public function setAdmin (AdminInterface $admin = null) | |
{ | |
$this->admin = $admin; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Admin\FormAction; | |
use Sonata\AdminBundle\Admin\AdminInterface; | |
interface AdminAwareInterface | |
{ | |
public function setAdmin (AdminInterface $admin = null); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Admin\FormAction; | |
use Sonata\AdminBundle\Form\FormMapper; | |
interface FormActionInterface | |
{ | |
/** | |
* Transforms the model object (eg. entity) to form DTO | |
* | |
* @param null|object $object The model object or null if no id specified in request (eg. create action) | |
* @return object The DTO that will be passed to form as $data argument | |
*/ | |
public function transformToForm ($object); | |
/** | |
* Transforms the DTO back to model. This method MUST return the model object even if the updates were done in-place | |
* This method will not be called unless \Symfony\Component\Form\FormInterface::isValid() returns true | |
* | |
* @param object $data The DTO that you returned in transformToForm() | |
* @return object The model that needs to be persisted to database | |
*/ | |
public function transformToModel ($data); | |
/** | |
* Same as \Sonata\AdminBundle\Admin\AbstractAdmin::configureFormFields() | |
* | |
* @param FormMapper $form | |
* @return void | |
*/ | |
public function configureFormFields (FormMapper $form); | |
/** | |
* Returns the name that will be used of the route that will be used to display this action | |
* | |
* @return string | |
*/ | |
public function getRouteName (); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Admin\FormAction; | |
use App\Form\Data\NewUserFormData; | |
use Sonata\AdminBundle\Form\FormMapper; | |
class NewUserAction extends AbstractFormAction | |
{ | |
public function transformToForm ($object): object | |
{ | |
if ($object !== null) { | |
throw new \InvalidArgumentException('This action is intended only for creation of new entities'); | |
} | |
return new NewUserFormData(); | |
} | |
public function transformToModel ($data): object | |
{ | |
if (!$data instanceof NewUserFormData) { | |
throw new \TypeError(); | |
} | |
return $data->toUser(); | |
} | |
public function configureFormFields (FormMapper $form): void | |
{ | |
$form | |
->add('username') | |
->add('password'); | |
} | |
public function getRouteName (): string | |
{ | |
return 'custom_create'; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Admin\FormAction; | |
use App\Form\Data\NewUserFormData; | |
use AutoMapperPlus\AutoMapperInterface; | |
use Sonata\AdminBundle\Form\FormMapper; | |
class NewUserActionWithDependency extends AbstractFormAction | |
{ | |
/** | |
* @var AutoMapperInterface | |
*/ | |
private $autoMapper; | |
public function __construct (AutoMapperInterface $autoMapper) | |
{ | |
$this->autoMapper = $autoMapper; | |
} | |
public function transformToForm ($object): object | |
{ | |
if ($object !== null) { | |
throw new \InvalidArgumentException('This action is intended only for creation of new entities'); | |
} | |
return new NewUserFormData(); | |
} | |
public function transformToModel ($data): object | |
{ | |
if (!$data instanceof NewUserFormData) { | |
throw new \TypeError(); | |
} | |
return $this->autoMapper->mapToObject($data, $this->admin->getSubject()); | |
} | |
public function configureFormFields (FormMapper $form): void | |
{ | |
$form | |
->add('username') | |
->add('password'); | |
} | |
public function getRouteName (): string | |
{ | |
return 'custom_create'; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Form\Data; | |
use App\Entity\User; | |
use Symfony\Component\Validator\Constraints as Assert; | |
class NewUserFormData | |
{ | |
/** | |
* @var string|null | |
* | |
* @Assert\NotNull() | |
* @Assert\NotBlank() | |
* @Assert\Length(min=3) | |
*/ | |
private $username; | |
/** | |
* @var string|null | |
* | |
* @Assert\NotBlank() | |
*/ | |
private $password; | |
public function toUser () : User | |
{ | |
if ($this->username === null) { | |
throw new \DomainException('Cannot transform to user when no username set'); | |
} | |
$user = new User($this->username); | |
if ($this->password !== null) { | |
// This is EXTREMELY STUPID from security perspective and is done only for demonstration purposes | |
// Do not try at home | |
$user->changePasswordHash(md5($this->password)); | |
} | |
return $user; | |
} | |
public function getUsername (): ?string | |
{ | |
return $this->username; | |
} | |
public function setUsername (?string $username): void | |
{ | |
$this->username = $username; | |
} | |
public function getPassword (): ?string | |
{ | |
return $this->password; | |
} | |
public function setPassword (?string $password): void | |
{ | |
$this->password = $password; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
services: | |
App\Admin\FormAction\NewUserAction: | |
autowire: true | |
autoconfigure: true | |
tags: | |
- { name: sonata.admin.form_action, admin: App\Admin\UserAdmin } # admin's service, can be multiple tags/admins |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Entity; | |
use Doctrine\ORM\Mapping as ORM; | |
/** | |
* @ORM\Entity() | |
*/ | |
class User | |
{ | |
/** | |
* @var int|null | |
* | |
* @ORM\Id() | |
* @ORM\Column(type="integer") | |
*/ | |
private $id; | |
/** | |
* @var string | |
* | |
* @ORM\Column(unique=true) | |
*/ | |
private $username; | |
/** | |
* @var string|null | |
* | |
* @ORM\Column(name="password", nullable=true) | |
*/ | |
private $passwordHash; | |
public function __construct (string $username) | |
{ | |
$this->username = $username; | |
} | |
public function getId (): ?int | |
{ | |
return $this->id; | |
} | |
public function getUsername (): string | |
{ | |
return $this->username; | |
} | |
public function getPasswordHash (): ?string | |
{ | |
return $this->passwordHash; | |
} | |
public function changePasswordHash (?string $passwordHash): void | |
{ | |
$this->passwordHash = $passwordHash; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Admin; | |
use Sonata\AdminBundle\Admin\AbstractAdmin; | |
use Sonata\AdminBundle\Route\RouteCollection; | |
class UserAdmin extends AbstractAdmin | |
{ | |
protected function configureRoutes (RouteCollection $collection) | |
{ | |
$collection | |
->remove('create') | |
->add('custom_create', '/create'); | |
$collection->add('password_change', $this->getRouterIdParameter() . '/password-change'); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Admin\FormAction; | |
use App\Entity\User; | |
use App\Form\Data\UserPasswordChangeFormData; | |
use Sonata\AdminBundle\Form\FormMapper; | |
class UserPasswordChangeAction extends AbstractFormAction | |
{ | |
public function transformToForm ($object): object | |
{ | |
if (!$object instanceof User) { | |
throw new \TypeError(); | |
} | |
return new UserPasswordChangeFormData($object); | |
} | |
public function transformToModel ($data): object | |
{ | |
if (!$data instanceof UserPasswordChangeFormData) { | |
throw new \TypeError(); | |
} | |
$data->applyChanges(); | |
return $data->getUser(); | |
} | |
public function configureFormFields (FormMapper $form): void | |
{ | |
$form | |
->add('password'); | |
} | |
public function getRouteName (): string | |
{ | |
return 'password_change'; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Form\Data; | |
use App\Entity\User; | |
class UserPasswordChangeFormData | |
{ | |
/** | |
* @var User | |
*/ | |
private $user; | |
/** | |
* @var string|null | |
* | |
* @Assert\NotBlank() | |
*/ | |
private $password; | |
public function __construct (User $user) | |
{ | |
$this->user = $user; | |
} | |
public function applyChanges (): void | |
{ | |
if ($this->password !== null) { | |
// This is EXTREMELY STUPID from security perspective and is done only for demonstration purposes | |
// Do not try at home | |
$this->user->changePasswordHash(md5($this->password)); | |
} else { | |
$this->user->changePasswordHash(null); | |
} | |
} | |
public function getUser (): User | |
{ | |
return $this->user; | |
} | |
public function getPassword (): ?string | |
{ | |
return $this->password; | |
} | |
public function setPassword (?string $password): void | |
{ | |
$this->password = $password; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment