Skip to content

Commit

Permalink
Transform form data in a DataTransformer (schmittjoh#186)
Browse files Browse the repository at this point in the history
  • Loading branch information
psrpinto authored Oct 17, 2016
1 parent 6e706a4 commit 473c49f
Showing 4 changed files with 307 additions and 70 deletions.
86 changes: 16 additions & 70 deletions Form/ChoosePaymentMethodType.php
Original file line number Diff line number Diff line change
@@ -2,13 +2,12 @@

namespace JMS\Payment\CoreBundle\Form;

use JMS\Payment\CoreBundle\Entity\ExtendedData;
use JMS\Payment\CoreBundle\Entity\PaymentInstruction;
use JMS\Payment\CoreBundle\Form\Transformer\ChoosePaymentMethodTransformer;
use JMS\Payment\CoreBundle\PluginController\PluginControllerInterface;
use JMS\Payment\CoreBundle\PluginController\Result;
use JMS\Payment\CoreBundle\Util\Legacy;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
@@ -26,6 +25,7 @@ class ChoosePaymentMethodType extends AbstractType
{
private $pluginController;
private $paymentMethods;
private $transformer;

public function __construct(PluginControllerInterface $pluginController, array $paymentMethods)
{
@@ -37,6 +37,11 @@ public function __construct(PluginControllerInterface $pluginController, array $
$this->paymentMethods = $paymentMethods;
}

public function setDataTransformer(DataTransformerInterface $transformer)
{
$this->transformer = $transformer;
}

public function buildForm(FormBuilderInterface $builder, array $options)
{
$options['available_methods'] = $this->getPaymentMethods($options['allowed_methods']);
@@ -74,65 +79,15 @@ public function buildForm(FormBuilderInterface $builder, array $options)
$self->validate($form, $options);
});

$builder->addModelTransformer(new CallbackTransformer(
function ($data) use ($self, $options) {
return $self->transform($data, $options);
},
function ($data) use ($self, $options) {
return $self->reverseTransform($data, $options);
}
), true);
}

public function transform($data, array $options)
{
if (null === $data) {
return null;
}

if ($data instanceof PaymentInstruction) {
$method = $data->getPaymentSystemName();

$methodData = array_map(function ($v) {
return $v[0];
}, $data->getExtendedData()->all());

if (isset($options['predefined_data'][$method])) {
$methodData = array_diff_key($methodData, $options['predefined_data'][$method]);
}

return array(
'method' => $method,
'data_'.$method => $methodData,
);
}

throw new \RuntimeException(sprintf('Unsupported data of type "%s".', ('object' === $type = gettype($data)) ? get_class($data) : $type));
}

public function reverseTransform($data, array $options)
{
$method = isset($data['method']) ? $data['method'] : null;
$data = isset($data['data_'.$method]) ? $data['data_'.$method] : array();

$extendedData = new ExtendedData();
foreach ($data as $k => $v) {
$extendedData->set($k, $v);
}

if (isset($options['predefined_data'][$method])) {
if (!is_array($options['predefined_data'][$method])) {
throw new \RuntimeException(sprintf('"predefined_data" is expected to be an array for each method, but got "%s" for method "%s".', json_encode($options['extra_data'][$method]), $method));
}

foreach ($options['predefined_data'][$method] as $k => $v) {
$extendedData->set($k, $v);
}
}

$amount = $this->computeAmount($options['amount'], $options['currency'], $method, $extendedData);
// To maintain BC, we instantiate a new ChoosePaymentMethodTransformer in
// case it hasn't been supplied.
$transformer = $this->transformer
? $this->transformer
: new ChoosePaymentMethodTransformer()
;

return new PaymentInstruction($amount, $options['currency'], $method, $extendedData);
$transformer->setOptions($options);
$builder->addModelTransformer($transformer);
}

public function validate(FormEvent $event, array $options)
@@ -245,15 +200,6 @@ private function applyErrorsToForm(FormInterface $form, Result $result)
}
}

private function computeAmount($amount, $currency, $method, ExtendedData $extendedData)
{
if ($amount instanceof \Closure) {
return $amount($currency, $method, $extendedData);
}

return $amount;
}

private function getPaymentMethods($allowedMethods = array())
{
$allowAllMethods = !count($allowedMethods);
110 changes: 110 additions & 0 deletions Form/Transformer/ChoosePaymentMethodTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

namespace JMS\Payment\CoreBundle\Form\Transformer;

use JMS\Payment\CoreBundle\Entity\ExtendedData;
use JMS\Payment\CoreBundle\Entity\PaymentInstruction;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;

class ChoosePaymentMethodTransformer implements DataTransformerInterface
{
/**
* @var array
*/
protected $options;

public function setOptions(array $options)
{
$this->options = $options;
}

/**
* {@inheritdoc}
*/
public function transform($data)
{
if (null === $data) {
return null;
}

if (!$data instanceof PaymentInstruction) {
throw new TransformationFailedException(sprintf('Unsupported data of type "%s".', $this->getDataType($data)));
}

$method = $data->getPaymentSystemName();

$methodData = array_map(function ($v) {
return $v[0];
}, $data->getExtendedData()->all());

if (isset($this->options['predefined_data'][$method])) {
$methodData = array_diff_key($methodData, $this->options['predefined_data'][$method]);
}

return array(
'method' => $method,
'data_'.$method => $methodData,
);
}

/**
* {@inheritdoc}
*/
public function reverseTransform($data)
{
if (null === $data) {
return null;
}

if (!is_array($data)) {
throw new TransformationFailedException(sprintf('Unsupported data of type "%s".', $this->getDataType($data)));
}

if (!isset($this->options['amount'])) {
throw new TransformationFailedException("The 'amount' option must be supplied to the form");
}

if (!isset($this->options['currency'])) {
throw new TransformationFailedException("The 'currency' option must be supplied to the form");
}

$method = isset($data['method']) ? $data['method'] : null;

if (isset($this->options['predefined_data'][$method])) {
if (!is_array($this->options['predefined_data'][$method])) {
throw new TransformationFailedException(sprintf('"predefined_data" is expected to be an array for each method, but got "%s" for method "%s".', $this->getDataType($this->options['predefined_data'][$method]), $method));
}
}

$data = isset($data['data_'.$method]) ? $data['data_'.$method] : array();
$extendedData = new ExtendedData();
foreach ($data as $key => $value) {
$extendedData->set($key, $value);
}

if (isset($this->options['predefined_data'][$method])) {
foreach ($this->options['predefined_data'][$method] as $key => $value) {
$extendedData->set($key, $value);
}
}

$amount = $this->options['amount'];
if ($amount instanceof \Closure) {
$amount = $amount($this->options['currency'], $method, $extendedData);
}

return new PaymentInstruction($amount, $this->options['currency'], $method, $extendedData);
}

private function getDataType($data)
{
$type = gettype($data);

if ($type === 'object') {
$type = get_class($data);
}

return $type;
}
}
7 changes: 7 additions & 0 deletions Resources/config/payment.xml
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
<parameter key="payment.plugin_controller.entity.options.credit_class">JMS\Payment\CoreBundle\Entity\Credit</parameter>

<parameter key="payment.form.choose_payment_method_type.class">JMS\Payment\CoreBundle\Form\ChoosePaymentMethodType</parameter>
<parameter key="payment.form.choose_payment_method_transformer.class">JMS\Payment\CoreBundle\Form\Transformer\ChoosePaymentMethodTransformer</parameter>

<parameter key="payment.encryption_service.class">JMS\Payment\CoreBundle\Cryptography\MCryptEncryptionService</parameter>
<parameter key="payment.encryption_service.secret"></parameter>
@@ -43,9 +44,15 @@
<argument>%payment.encryption_service.mode%</argument>
</service>

<service id="payment.form.choose_payment_method_transformer" class="%payment.form.choose_payment_method_transformer.class%" />

<service id="payment.form.choose_payment_method_type" class="%payment.form.choose_payment_method_type.class%">
<argument type="service" id="payment.plugin_controller" />
<tag name="form.type" alias="jms_choose_payment_method" />
<call method="setDataTransformer">
<argument type="service" id="payment.form.choose_payment_method_transformer" />
</call>
</service>

</services>
</container>
Loading

0 comments on commit 473c49f

Please sign in to comment.