Last active
December 28, 2022 18:47
-
-
Save mikemix/46583309e000ddc08336bba2b53a699c to your computer and use it in GitHub Desktop.
Symfony Form Data Mapper helper
This file contains hidden or 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\DataMapper; | |
use Symfony\Component\Form\DataMapperInterface; | |
use Symfony\Component\Form\Exception\UnexpectedTypeException; | |
use Symfony\Component\Form\FormInterface; | |
/** | |
* @template TObject of object | |
*/ | |
abstract class AbstractTypedDataMapper implements DataMapperInterface | |
{ | |
/** | |
* {@inheritDoc} | |
* @psalm-suppress MoreSpecificImplementedParamType | |
*/ | |
public function mapDataToForms(mixed $viewData, \Traversable $forms): void | |
{ | |
if (null === $viewData) { | |
return; | |
} | |
$expectedType = $this->getType(); | |
if (false === ($viewData instanceof $expectedType)) { | |
throw new UnexpectedTypeException($viewData, $this->getType()); | |
} | |
/** | |
* @var array<string, FormInterface> $form | |
* @psalm-suppress InvalidArgument | |
*/ | |
$form = \iterator_to_array($forms); | |
if ($this->isNew($viewData)) { | |
$this->populateEmptyFormWhenCreating($form); | |
} else { | |
$this->populateFormWhenUpdating($viewData, $form); | |
} | |
} | |
/** | |
* {@inheritDoc} | |
* @psalm-suppress MoreSpecificImplementedParamType | |
*/ | |
public function mapFormsToData(\Traversable $forms, mixed &$viewData): void | |
{ | |
$expectedType = $this->getType(); | |
if (false === ($viewData instanceof $expectedType)) { | |
throw new UnexpectedTypeException($viewData, $this->getType()); | |
} | |
/** | |
* @var array<string, FormInterface> $form | |
* @psalm-suppress InvalidArgument | |
*/ | |
$form = \iterator_to_array($forms); | |
if (false === $this->isNew($viewData)) { | |
$this->update($viewData, $form); | |
return; | |
} | |
if (null === ($new = $this->createNew($form))) { | |
return; | |
} | |
$viewData = $new; | |
} | |
/** @param TObject $viewData */ | |
protected function isNew(object $viewData): bool | |
{ | |
if (\method_exists($viewData, 'getId')) { | |
/** @psalm-suppress MixedMethodCall */ | |
return \in_array($viewData->getId(), [null, 0], true); | |
} | |
throw new \LogicException(\sprintf( | |
'Could not determine whether object is new. Overload method "%s" and fill with custom logic', | |
__METHOD__, | |
)); | |
} | |
/** @return class-string<TObject> */ | |
abstract protected function getType(): string; | |
/** | |
* @param array<string, FormInterface> $form | |
*/ | |
abstract protected function populateEmptyFormWhenCreating(array $form): void; | |
/** | |
* @param TObject $viewData | |
* @param array<string, FormInterface> $form | |
*/ | |
abstract protected function populateFormWhenUpdating(object $viewData, array $form): void; | |
/** | |
* @param array<string, FormInterface> $form | |
* @return TObject|null | |
*/ | |
abstract protected function createNew(array $form): ?object; | |
/** | |
* @param TObject $viewData | |
* @param array<string, FormInterface> $form | |
*/ | |
abstract protected function update(object $viewData, array $form): void; | |
} | |
// | |
// Example usage | |
// | |
/** @template-extends AbstractTypedDataMapper<Article> */ | |
final class NodeFormMapper extends AbstractTypedDataMapper | |
{ | |
/** @inheritDoc */ | |
protected function getType(): string | |
{ | |
return Article::class; | |
} | |
/** @inheritDoc */ | |
protected function populateEmptyFormWhenCreating(array $form): void | |
{ | |
$form['title']->setData(''); | |
$form['content']->setData(''); | |
} | |
/** @inheritDoc */ | |
protected function populateFormWhenUpdating(object $viewData, array $form): void | |
{ | |
$form['title']->setData($viewData->getTitle()); | |
$form['content']->setData($viewData->getContent()); | |
} | |
/** @inheritDoc */ | |
protected function createNew(array $form): ?object | |
{ | |
/** @var string $title */ | |
$title = $form['title']->getData(); | |
/** @var string $content */ | |
$content = $form['content']->getData(); | |
$article = new Article($this->userContext()->getUser()); | |
try { | |
$article->changeTitle($title); | |
} catch (AssertionFailedException $exception) { | |
$form['title']->addError(new FormError($exception->getMessage())); | |
return null; | |
} | |
try { | |
$article->changeContent($content); | |
} catch (AssertionFailedException $exception) { | |
$form['content']->addError(new FormError($exception->getMessage())); | |
return null; | |
} | |
return $article; | |
} | |
/** @inheritDoc */ | |
protected function update(object $viewData, array $form): void | |
{ | |
/** @var string $title */ | |
$title = $form['title']->getData(); | |
/** @var string $content */ | |
$content = $form['content']->getData(); | |
$viewData->changeTitle($title); | |
$viewData->changeContent($content); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment