Skip to content

Commit

Permalink
Merge pull request #2 from iMi-digital/feature/configurable-input-len…
Browse files Browse the repository at this point in the history
…gth-validation

Add configurable input length validation
  • Loading branch information
amenk authored Jan 27, 2020
2 parents d1ab3f6 + 8651d7a commit f8eda12
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 24 deletions.
8 changes: 8 additions & 0 deletions Api/Data/CustomFieldsInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ interface CustomFieldsInterface
const CHECKOUT_GOODS_MARK = 'checkout_goods_mark';
const CHECKOUT_COMMENT = 'checkout_comment';

const ATTRIBUTES = [
self::CHECKOUT_BUYER_EMAIL,
self::CHECKOUT_BUYER_NAME,
self::CHECKOUT_COMMENT,
self::CHECKOUT_GOODS_MARK,
self::CHECKOUT_PURCHASE_ORDER_NO,
];

/**
* Get checkout buyer name
*
Expand Down
77 changes: 77 additions & 0 deletions Helper/Config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace Bodak\CheckoutCustomForm\Helper;


use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface;
use Psr\Log\LoggerInterface;

class Config
{
const LIMIT_NOT_SET = -1;

/**
* @var ScopeConfigInterface
*/
private $config;

/**
* @var StoreManagerInterface
*/
private $storeManager;

/**
* @var LoggerInterface
*/
private $logger;

/**
* Config constructor.
*
* @param ScopeConfigInterface $config
* @param StoreManagerInterface $storeManager
* @param LoggerInterface $logger
*/
public function __construct(
ScopeConfigInterface $config,
StoreManagerInterface $storeManager,
LoggerInterface $logger
) {
$this->config = $config;
$this->storeManager = $storeManager;
$this->logger = $logger;
}

/**
* @return array
*/
public function getEnabledFields()
{
return explode(',',
$this->config->getValue('bodak/checkout/enabled_fields',
ScopeInterface::SCOPE_STORE));
}

/**
* @param $attribute
*
* @return int
*/
public function getAllowedLength($attribute)
{
$configPath = 'checkout/general/' . $attribute . '_limit';
try {
$allowedLength = $this->config->getValue($configPath, ScopeInterface::SCOPE_STORES,
$this->storeManager->getStore()->getId());
} catch (NoSuchEntityException $e) {
$this->logger->error('Cannot get allowed length for custom field. Falling back to default scope value.');
$this->logger->error($e->getMessage(), ['exception' => $e]);
$allowedLength = $this->config->getValue($configPath);
}

return (int)$allowedLength ?: self::LIMIT_NOT_SET;
}
}
57 changes: 38 additions & 19 deletions Model/Checkout/LayoutProcessor/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,18 @@
namespace Bodak\CheckoutCustomForm\Model\Checkout\LayoutProcessor;

use Bodak\CheckoutCustomForm\Api\Data\CustomFieldsInterface;
use Bodak\CheckoutCustomForm\Helper\Config;

class Plugin
{
/**
* @var Config
*/
private $config;

/**
* @var array
*/
private $fields = [
[
'dataScopeName' => CustomFieldsInterface::CHECKOUT_BUYER_NAME,
Expand All @@ -28,8 +37,8 @@ class Plugin
],
'config' => [
'tooltip' => [
'description' => 'We will send an order confirmation to this email address'
]
'description' => 'We will send an order confirmation to this email address',
],
],
],
[
Expand All @@ -46,48 +55,45 @@ class Plugin
'config' => [
'cols' => 15,
'rows' => 2,
'maxlength' => 80,
'maxlength' => null,
'elementTmpl' => 'Bodak_CheckoutCustomForm/form/element/textarea',
],
'showTitle' => false,
],
];

/**
* @var \Magento\Framework\App\Config\ScopeConfigInterface
*/
protected $_scopeConfig;

/**
* LayoutProcessor constructor.
*
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* @param Config $config
*/
public function __construct(\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig)
public function __construct(Config $config)
{
$this->_scopeConfig = $scopeConfig;
$this->config = $config;
}

/**
* @param \Magento\Checkout\Block\Checkout\LayoutProcessor $subject
* @param array $jsLayout
*
* @see \Magento\Checkout\Block\Checkout\LayoutProcessor::process
* @return array
* @see \Magento\Checkout\Block\Checkout\LayoutProcessor::process
*/
public function afterProcess(
\Magento\Checkout\Block\Checkout\LayoutProcessor $subject,
array $jsLayout
) {
$config = explode(',',
$this->_scopeConfig->getValue('bodak/checkout/enabled_fields',
\Magento\Store\Model\ScopeInterface::SCOPE_STORE));
$config = $this->config->getEnabledFields();

$this->applyLengthLimitToFields();

foreach ($this->fields as $sortOrder => $field) {
if ( ! in_array($field['dataScopeName'], $config)) {
if (!in_array($field['dataScopeName'], $config)) {
continue;
}
if(!isset($field['showTitle'])) $field['showTitle'] = true;
if (!isset($field['showTitle'])) {
$field['showTitle'] = true;
}

$formField = [
'component' => 'Magento_Ui/js/form/element/abstract',
Expand All @@ -112,10 +118,23 @@ public function afterProcess(
}

$jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']
['children']['shippingAddress']['children']['custom-checkout-form-container']
['children']['custom-checkout-form-fieldset']['children'][$field['dataScopeName']] = $formField;
['children']['shippingAddress']['children']['custom-checkout-form-container']
['children']['custom-checkout-form-fieldset']['children'][$field['dataScopeName']] = $formField;
}

return $jsLayout;
}

private function applyLengthLimitToFields()
{
foreach ($this->fields as $key => $field) {
$fieldName = $field['dataScopeName'];
$allowedLength = $this->config->getAllowedLength($fieldName);
if ($allowedLength === Config::LIMIT_NOT_SET) {
continue;
}
$this->fields[$key]['config']['maxlength'] = $allowedLength;
$this->fields[$key]['validation']['max_text_length'] = $allowedLength;
}
}
}
99 changes: 99 additions & 0 deletions Model/CustomFields/Validator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

namespace Bodak\CheckoutCustomForm\Model\CustomFields;

use Bodak\CheckoutCustomForm\Api\Data\CustomFieldsInterface;
use Bodak\CheckoutCustomForm\Helper\Config;
use Magento\Framework\Validator\AbstractValidator;
use Zend_Validate_Exception;
use Zend\Filter\Word\UnderscoreToCamelCase;


class Validator extends AbstractValidator
{

/**
* @var CustomFieldsInterface
*/
private $value;

/**
* @var Config
*/
private $config;

/**
* @var UnderscoreToCamelCase
*/
private $underscoreToCamelCase;

/**
* Validator constructor.
*
* @param Config $config
* @param UnderscoreToCamelCase $underscoreToCamelCase
*/
public function __construct(Config $config, UnderscoreToCamelCase $underscoreToCamelCase)
{
$this->config = $config;
$this->underscoreToCamelCase = $underscoreToCamelCase;
}

/**
* Returns true if and only if $value meets the validation requirements
*
* If $value fails validation, then this method returns false, and
* getMessages() will return an array of messages that explain why the
* validation failed.
*
* @param mixed $value
*
* @return boolean
* @throws Zend_Validate_Exception If validation of $value is impossible
*/
public function isValid($value)
{
$valid = true;
if (!($value instanceof CustomFieldsInterface)) {
throw new Zend_Validate_Exception('Expected value to be instance of \Bodak\CheckoutCustomForm\Api\Data\CustomFieldsInterface');
}

$this->setValue($value);

foreach (CustomFieldsInterface::ATTRIBUTES as $attribute) {
if (!$this->lengthIsValid($attribute)) {
$valid = false;
$this->_addMessages([sprintf('Field %s is to long.', $attribute)]);
}
}

return $valid;
}

/**
* @param $value
*/
private function setValue($value)
{
$this->value = $value;
}

/**
* @param $attribute
*
* @return bool
*/
private function lengthIsValid($attribute)
{
$allowedLength = $this->config->getAllowedLength($attribute);

if ($allowedLength === Config::LIMIT_NOT_SET) {
return true;
}

$function = $this->underscoreToCamelCase->filter('get_' . $attribute);
$value = call_user_func([$this->value, $function]);

return mb_strlen($value) <= $allowedLength;
}
}
27 changes: 22 additions & 5 deletions Model/CustomFieldsRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

namespace Bodak\CheckoutCustomForm\Model;

use Bodak\CheckoutCustomForm\Model\CustomFields\Validator;
use Magento\Framework\Exception\LocalizedException;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Exception\NoSuchEntityException;
Expand Down Expand Up @@ -47,31 +49,42 @@ class CustomFieldsRepository implements CustomFieldsRepositoryInterface
*/
protected $customFields;

/**
* @var Validator
*/
private $validator;

/**
* CustomFieldsRepository constructor.
*
* @param CartRepositoryInterface $cartRepository CartRepositoryInterface
* @param ScopeConfigInterface $scopeConfig ScopeConfigInterface
* @param CustomFieldsInterface $customFields CustomFieldsInterface
* @param ScopeConfigInterface $scopeConfig ScopeConfigInterface
* @param CustomFieldsInterface $customFields CustomFieldsInterface
* @param Validator $validator
*/
public function __construct(
CartRepositoryInterface $cartRepository,
ScopeConfigInterface $scopeConfig,
CustomFieldsInterface $customFields
CustomFieldsInterface $customFields,
Validator $validator
) {
$this->cartRepository = $cartRepository;
$this->scopeConfig = $scopeConfig;
$this->customFields = $customFields;
$this->validator = $validator;
}

/**
* Save checkout custom fields
*
* @param int $cartId Cart id
* @param int $cartId Cart id
* @param \Bodak\CheckoutCustomForm\Api\Data\CustomFieldsInterface $customFields Custom fields
*
* @return \Bodak\CheckoutCustomForm\Api\Data\CustomFieldsInterface
* @return \Bodak\CheckoutCustomForm\Api\Data\CustomFieldsInterface|string
* @throws CouldNotSaveException
* @throws NoSuchEntityException
* @throws \Zend_Validate_Exception
* @throws LocalizedException
*/
public function saveCustomFields(
int $cartId,
Expand All @@ -82,6 +95,10 @@ public function saveCustomFields(
throw new NoSuchEntityException(__('Cart %1 is empty', $cartId));
}

if (!$this->validator->isValid($customFields)) {
throw new LocalizedException(__('Custom fields contain invalid values.'));
}

try {
$cart->setData(
CustomFieldsInterface::CHECKOUT_BUYER_NAME,
Expand Down
Loading

0 comments on commit f8eda12

Please sign in to comment.