Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add configurable input length validation #2

Merged
merged 3 commits into from
Jan 27, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
}
}
109 changes: 109 additions & 0 deletions Model/CustomFields/Validator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?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->convertSnakeToCamelCase('get_' . $attribute);
$value = call_user_func([$this->value, $function]);

return mb_strlen($value) <= $allowedLength;
}

/**
* @param $string
*
* @return string
*/
private function convertSnakeToCamelCase($string)
amenk marked this conversation as resolved.
Show resolved Hide resolved
{
return $this->underscoreToCamelCase->filter($string);
}
}
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