Skip to content

Instantly share code, notes, and snippets.

@mikemix
Last active December 28, 2022 18:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikemix/46583309e000ddc08336bba2b53a699c to your computer and use it in GitHub Desktop.
Save mikemix/46583309e000ddc08336bba2b53a699c to your computer and use it in GitHub Desktop.
Symfony Form Data Mapper helper
<?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