Skip to content

Commit

Permalink
A more versatile solution to field naming (#149)
Browse files Browse the repository at this point in the history
* Added rename.xml for custom field name support
* Added getFieldName()
* Updated tests so they don't break when renames are in effect
* Cleaned up configuration (release warning: prefix enabled by default)
* Scoping fix
* Fixed rename config issue with commenting
* Removed example prefix as default
* Batch support for getFieldName(). Added normalizeFormData() to reverse the effects of custom field name support
* Added test for normalizeFormData
* Removed mock functionality and updated test
  • Loading branch information
zanderwar authored and bummzack committed Nov 9, 2016
1 parent 08e8df4 commit 6715307
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 31 deletions.
16 changes: 16 additions & 0 deletions _config/rename.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
Name: omnipay-rename
---
# You can define custom names globally, or on a per gateway basis, if you define your gateway here, it will take priority
# and then will fall back to any of the global renames defined below (if set). The example below shows how you would rename
# the default fields for the Stripe payment gateway, and how to rename field names globally

GatewayFieldsFactory:
rename:
prefix: '' # e.g "card_"
#name: 'myName'
#type: 'myType'
#number: 'myNumber'
Stripe:
name: 'name' # e.g change to newName
#number: 'myStripeNumber'
153 changes: 126 additions & 27 deletions code/GatewayFieldsFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,21 +96,21 @@ public function getCardFields()
$expiryrange = range($year, date('Y', strtotime("+$range years")));

$fields = array(
'type' => \DropdownField::create('type', _t('PaymentForm.Type', 'Type'), $this->getCardTypes()),
'name' => \TextField::create('name', _t('PaymentForm.Name', 'Name on Card')),
'number' => \TextField::create('number', _t('PaymentForm.Number', 'Card Number'))
'type' => \DropdownField::create($this->getFieldName('type'), _t('PaymentForm.Type', 'Type'), $this->getCardTypes()),
'name' => \TextField::create($this->getFieldName('name'), _t('PaymentForm.Name', 'Name on Card')),
'number' => \TextField::create($this->getFieldName('number'), _t('PaymentForm.Number', 'Card Number'))
->setDescription(_t('PaymentForm.NumberDescription', 'no dashes or spaces')),
'startMonth' => \DropdownField::create('startMonth', _t('PaymentForm.StartMonth', 'Month'), $months),
'startYear' => \DropdownField::create('startYear', _t('PaymentForm.StartYear', 'Year'),
array_combine($startrange, $startrange), $year
'startMonth' => \DropdownField::create($this->getFieldName('startMonth'), _t('PaymentForm.StartMonth', 'Month'), $months),
'startYear' => \DropdownField::create($this->getFieldName('startYear'), _t('PaymentForm.StartYear', 'Year'),
array_combine($startrange, $startrange), $year
),
'expiryMonth' => \DropdownField::create('expiryMonth', _t('PaymentForm.ExpiryMonth', 'Month'), $months),
'expiryYear' => \DropdownField::create('expiryYear', _t('PaymentForm.ExpiryYear', 'Year'),
array_combine($expiryrange, $expiryrange), $year
'expiryMonth' => \DropdownField::create($this->getFieldName('expiryMonth'), _t('PaymentForm.ExpiryMonth', 'Month'), $months),
'expiryYear' => \DropdownField::create($this->getFieldName('expiryYear'), _t('PaymentForm.ExpiryYear', 'Year'),
array_combine($expiryrange, $expiryrange), $year
),
'cvv' => \TextField::create('cvv', _t('PaymentForm.CVV', 'Security Code'))
'cvv' => \TextField::create($this->getFieldName('cvv'), _t('PaymentForm.CVV', 'Security Code'))
->setMaxLength(5),
'issueNumber' => \TextField::create('issueNumber', _t('PaymentForm.IssueNumber', 'Issue Number'))
'issueNumber' => \TextField::create($this->getFieldName('issueNumber'), _t('PaymentForm.IssueNumber', 'Issue Number'))
);

$this->cullForGateway($fields);
Expand Down Expand Up @@ -158,13 +158,13 @@ public function getCardTypes()
public function getBillingFields()
{
$fields = array(
'billingAddress1' => \TextField::create('billingAddress1', _t('PaymentForm.BillingAddress1', 'Address')),
'billingAddress2' => \TextField::create('billingAddress2', _t('PaymentForm.BillingAddress2', 'Address line 2')),
'city' => \TextField::create('billingCity', _t('PaymentForm.BillingCity', 'City')),
'postcode' => \TextField::create('billingPostcode', _t('PaymentForm.BillingPostcode', 'Postcode')),
'state' => \TextField::create('billingState', _t('PaymentForm.BillingState', 'State')),
'country' => \TextField::create('billingCountry', _t('PaymentForm.BillingCountry', 'Country')),
'phone' => \PhoneNumberField::create('billingPhone', _t('PaymentForm.BillingPhone', 'Phone'))
'billingAddress1' => \TextField::create($this->getFieldName('billingAddress1'), _t('PaymentForm.BillingAddress1', 'Address')),
'billingAddress2' => \TextField::create($this->getFieldName('billingAddress2'), _t('PaymentForm.BillingAddress2', 'Address line 2')),
'city' => \TextField::create($this->getFieldName('billingCity'), _t('PaymentForm.BillingCity', 'City')),
'postcode' => \TextField::create($this->getFieldName('billingPostcode'), _t('PaymentForm.BillingPostcode', 'Postcode')),
'state' => \TextField::create($this->getFieldName('billingState'), _t('PaymentForm.BillingState', 'State')),
'country' => \TextField::create($this->getFieldName('billingCountry'), _t('PaymentForm.BillingCountry', 'Country')),
'phone' => \PhoneNumberField::create($this->getFieldName('billingPhone'), _t('PaymentForm.BillingPhone', 'Phone'))
);
$this->cullForGateway($fields);

Expand All @@ -179,16 +179,16 @@ public function getShippingFields()
{
$fields = array(
'shippingAddress1' => \TextField::create(
'shippingAddress1', _t('PaymentForm.ShippingAddress1', 'Shipping Address')
$this->getFieldName('shippingAddress1'), _t('PaymentForm.ShippingAddress1', 'Shipping Address')
),
'shippingAddress2' => \TextField::create(
'shippingAddress2', _t('PaymentForm.ShippingAddress2', 'Shipping Address 2')
$this->getFieldName('shippingAddress2'), _t('PaymentForm.ShippingAddress2', 'Shipping Address 2')
),
'city' => \TextField::create('shippingCity', _t('PaymentForm.ShippingCity', 'Shipping City')),
'postcode' => \TextField::create('shippingPostcode', _t('PaymentForm.ShippingPostcode', 'Shipping Postcode')),
'state' => \TextField::create('shippingState', _t('PaymentForm.ShippingState', 'Shipping State')),
'country' => \TextField::create('shippingCountry', _t('PaymentForm.ShippingCountry', 'Shipping Country')),
'phone' => \PhoneNumberField::create('shippingPhone', _t('PaymentForm.ShippingPhone', 'Shipping Phone'))
'city' => \TextField::create($this->getFieldName('shippingCity'), _t('PaymentForm.ShippingCity', 'Shipping City')),
'postcode' => \TextField::create($this->getFieldName('shippingPostcode'), _t('PaymentForm.ShippingPostcode', 'Shipping Postcode')),
'state' => \TextField::create($this->getFieldName('shippingState'), _t('PaymentForm.ShippingState', 'Shipping State')),
'country' => \TextField::create($this->getFieldName('shippingCountry'), _t('PaymentForm.ShippingCountry', 'Shipping Country')),
'phone' => \PhoneNumberField::create($this->getFieldName('shippingPhone'), _t('PaymentForm.ShippingPhone', 'Shipping Phone'))
);
$this->cullForGateway($fields);

Expand All @@ -202,7 +202,7 @@ public function getShippingFields()
public function getEmailFields()
{
$fields = array(
'email' => \EmailField::create('email', _t('PaymentForm.Email', 'Email'))
'email' => \EmailField::create($this->getFieldName('email'), _t('PaymentForm.Email', 'Email'))
);
$this->cullForGateway($fields);

Expand All @@ -216,13 +216,60 @@ public function getEmailFields()
public function getCompanyFields()
{
$fields = array(
'company' => \TextField::create('company', _t('PaymentForm.Company', 'Company'))
'company' => \TextField::create($this->getFieldName('company'), _t('PaymentForm.Company', 'Company'))
);
$this->cullForGateway($fields);

return \FieldList::create($fields);
}

/**
* Attempts to find a custom field name and/or prefix defined in rename.yml, otherwise returns the same input
* that it was given
*
* @param string|array $defaultName The default name of the field
* @param null|bool $skipGateway Used in recursion
*
* @return string|array
*/
public function getFieldName($defaultName, $skipGateway = null) {
// batch support
if (is_array($defaultName)) {
$stack = array();
foreach ($defaultName as $name) {
$stack[] = $this->getFieldName($name, $skipGateway);
}

return $stack;
}

$renameMap = \Config::inst()->get('GatewayFieldsFactory', 'rename');

if (!$renameMap || empty($renameMap)) {
return $defaultName;
}

// allows support for custom field names that are dependent on the gateway provider
// see rename.yml configuration file for an example
if (array_key_exists($this->gateway, $renameMap) && !empty($renameMap[$this->gateway]) && !$skipGateway) {
$gatewayMap = $renameMap[$this->gateway];

// if not in gatewayMap, run this function again but skip this gateway check
if (!array_key_exists($defaultName, $gatewayMap)) {
return $this->getFieldName($defaultName, true);
}

// a name has been found to replace $defaultName with, switch map over to the gateway map
$renameMap = $gatewayMap;
}

if (!array_key_exists($defaultName, $renameMap)) {
return (array_key_exists('prefix', $renameMap)) ? $renameMap['prefix'] . $defaultName : $defaultName;
}

return (array_key_exists('prefix', $renameMap)) ? $renameMap['prefix'] . $renameMap[$defaultName] : $renameMap[$defaultName];
}

/**
* Clear all fields that are not required by the gateway. Does nothing if gateway is null
* @param $fields
Expand All @@ -241,4 +288,56 @@ protected function cullForGateway(&$fields, $defaults = array())
}
}
}

/**
* Normalizes form data keys to map to their respective Omnipay parameters (in other words: reverses the effects
* from the custom field name support)
*
* @param array $data The form data consisting of key value pairs
*
* @return array
*/
public function normalizeFormData(array $data) {

$renameMap = \Config::inst()->get('GatewayFieldsFactory', 'rename');

if (!$renameMap || empty($renameMap)) {
return $data;
}

$hasPrefix = false;

if (array_key_exists('prefix', $renameMap)) {
$hasPrefix = true;
}

// Fix for: array_flip(): Can only flip STRING and INTEGER values!
// This would occur if you have defined Gateways in rename.yml
foreach($globalMap = $renameMap as $key => $value) {
if (is_array($value)) {
unset($globalMap[$key]);
}
}

$stack = array(array_flip($globalMap));

if (array_key_exists($this->gateway, $renameMap) && !empty($renameMap[$this->gateway])) {
$stack[] = $gatewayMap = array_flip($renameMap[$this->gateway]);
}

foreach($stack as $map) {
foreach ($map as $otherName => $defaultName) {
if ($hasPrefix) {
$otherName = $renameMap['prefix'] . $otherName;
}
if (array_key_exists($otherName, $data)) {
$value = $data[$otherName];
unset($data[$otherName]);
$data[$defaultName] = $value;
}
}
}

return $data;
}
}
64 changes: 60 additions & 4 deletions tests/GatewayFieldsFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,20 @@ public function setUp()
'Company',
'Email'
));

// caters for custom field names so that tests pass even if user has defined custom names
$fieldSets = array(
&$this->ccFields,
&$this->billingFields,
&$this->shippingFields,
&$this->companyFields,
&$this->emailFields,
);
foreach ($fieldSets as &$fieldSet) {
foreach ($fieldSet as &$field) {
$field = $this->factory->getFieldName($field);
}
}
}

public function testAllFieldGroups()
Expand Down Expand Up @@ -188,7 +202,7 @@ public function testRequiredFields()

$fields = $factory->getFields();

$this->assertEquals(array(
$defaults = array(
// default required CC fields for gateways that aren't manual and aren't offsite
'name',
'number',
Expand All @@ -203,7 +217,9 @@ public function testRequiredFields()
'shippingCountry',
'company',
'email'
), array_keys($fields->dataFields()));
);

$this->assertEquals($this->factory->getFieldName($defaults), array_keys($fields->dataFields()));

// Same procedure with offsite gateway should not return the CC fields

Expand All @@ -217,14 +233,54 @@ public function testRequiredFields()

$fields = $factory->getFields();

$this->assertEquals(array(
$pxDefaults = array(
'billingAddress1',
'billingCity',
'billingCountry',
'shippingCity',
'shippingCountry',
'company',
'email'
), array_keys($fields->dataFields()));
);

$this->assertEquals($this->factory->getFieldName($pxDefaults), array_keys($fields->dataFields()));
}

public function testNormalizeFormData() {
$data = array(
'prefix_testName' => 'Reece Alexander',
'prefix_testNumber' => '4242424242424242',
'prefix_testExpiryMonth' => '11',
'prefix_testExpiryYear' => '2016'
);

Config::inst()->update('GatewayFieldsFactory', 'rename', array(
'prefix' => 'prefix_',
'name' => 'testName',
'number' => 'testNumber',
'expiryMonth' => 'testExpiryMonth',
'expiryYear' => 'testExpiryYear'
));

$factory = new GatewayFieldsFactory();

$this->assertEquals(
array_keys($factory->normalizeFormData($data, true)),
array(
'name',
'number',
'expiryMonth',
'expiryYear'
)
);
}

public function renameWalk(&$array) {
return $array = array_map(
function ($name) {
return $this->factory->getFieldName($name);
},
$array
);
}
}

0 comments on commit 6715307

Please sign in to comment.