Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A way to keep original values around when a Symfony form is submitted with an empty value. Useful for password fields especially
{
"name": "pmg/keep-value-listener",
"description": "Example code for a blog post",
"license": "MIT",
"require": {
"symfony/form": "~3.0"
},
"require-dev": {
"phpunit/phpunit": "~5.0"
},
"autoload": {
"files": [
"KeepValueListener.php"
]
}
}
<?php
/**
* Copyright (c) 2016 PMG <https://www.pmg.com>
*
* License: MIT
*/
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Use inside your form types.
*
* public function buildForm(FormBuilderInterface $build, array $options)
* {
* $build->add('password', PasswordType::class);
* $build->add('remove_password', CheckboxType::class, [
* 'mapped' => false,
* ]);
* $build->addEventSubscriber(new KeepValueListener('password', 'remove_password'));
* }
*/
final class KeepValueListener implements EventSubscriberInterface
{
/**
* The name of a field whose value should be kept if an
* empty value is submitted.
*
* @var string
*/
private $keepField;
/**
* The boolean (checkbox) field that tells the listener to clear
* the field.
*
* @var string
*/
private $clearField;
/**
* @var PropertyAccessorInterface
*/
private $accessor;
public function __construct($keepField, $clearField=null, PropertyAccessorInterface $accessor=null)
{
$this->keepField = $keepField;
$this->clearField = $clearField;
$this->accessor = $accessor ?: PropertyAccess::createPropertyAccessor();
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return [
FormEvents::PRE_SUBMIT => 'onPreSubmit',
];
}
public function onPreSubmit(FormEvent $event)
{
$field = $event->getForm()->get($this->keepField);
$submitData = $event->getData();
$erase = false;
if ($this->clearField && isset($submitData[$this->clearField])) {
$erase = self::asBool($submitData[$this->clearField]);
}
if ($erase) {
$submitData[$this->keepField] = $field->getConfig()->getEmptyData();
$event->setData($submitData);
return;
}
if (empty($submitData[$this->keepField])) {
$submitData[$this->keepField] = $this->accessor->getValue(
$event->getForm()->getData(),
$field->getPropertyPath()
);
$event->setData($submitData);
}
}
private static function asBool($value)
{
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
}
<?php
/**
* Copyright (c) 2016 PMG <https://www.pmg.com>
*
* License: MIT
*/
use Symfony\Component\Form\Forms;
use Symfony\Component\Form\Extension\Core\Type as CoreTypes;
class KeepValueListenerTest extends \PHPUnit_Framework_TestCase
{
private $formFactory;
public function testFormSubmissionWithClearFieldSetRemovesTheValue()
{
$form = $this->createForm([]);
$form->submit([
'password' => 'test',
'clear_password' => '1',
]);
$data = $form->getData();
$this->assertNull($data['password']);
}
public function testEmptyFieldValueInSubmissionKeepsTheOriginalValueAround()
{
$form = $this->createForm([
'password' => 'original',
]);
$form->submit([
'password' => '',
]);
$data = $form->getData();
$this->assertEquals('original', $data['password']);
}
public function testNonEmptyValueInSubmissionReplacesTheExistingValue()
{
$form = $this->createForm([
'password' => 'original',
]);
$form->submit([
'password' => 'changed',
]);
$data = $form->getData();
$this->assertEquals('changed', $data['password']);
}
protected function setUp()
{
$this->formFactory = Forms::createFormFactory();
}
private function createForm($data)
{
return $this->formFactory->createBuilder(CoreTypes\FormType::class, $data)
->add('password', CoreTypes\PasswordType::class, [
'empty_data' => null,
])
->add('clear_password', CoreTypes\CheckboxType::class)
->addEventSubscriber(new KeepValueListener('password', 'clear_password'))
->getForm();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.