mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
352 lines
9.7 KiB
PHP
352 lines
9.7 KiB
PHP
<?php
|
|
|
|
namespace SilverStripe\Forms;
|
|
|
|
use InvalidArgumentException;
|
|
use SilverStripe\ORM\ArrayLib;
|
|
use SilverStripe\ORM\FieldType\DBMoney;
|
|
use SilverStripe\ORM\DataObjectInterface;
|
|
|
|
/**
|
|
* A form field that can save into a {@link Money} database field.
|
|
* See {@link CurrencyField} for a similar implementation
|
|
* that can save into a single float database field without indicating the currency.
|
|
*
|
|
* @author Ingo Schommer, SilverStripe Ltd. (<firstname>@silverstripe.com)
|
|
*/
|
|
class MoneyField extends FormField
|
|
{
|
|
|
|
// TODO replace with `FormField::SCHEMA_DATA_TYPE_TEXT` when MoneyField is implemented
|
|
/** @skipUpgrade */
|
|
protected $schemaDataType = 'MoneyField';
|
|
|
|
/**
|
|
* Limit the currencies
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $allowedCurrencies = [];
|
|
|
|
/**
|
|
* @var NumericField
|
|
*/
|
|
protected $fieldAmount = null;
|
|
|
|
/**
|
|
* @var FormField
|
|
*/
|
|
protected $fieldCurrency = null;
|
|
|
|
/**
|
|
* Gets field for the currency selector
|
|
*
|
|
* @return FormField
|
|
*/
|
|
public function getCurrencyField()
|
|
{
|
|
return $this->fieldCurrency;
|
|
}
|
|
|
|
/**
|
|
* Gets field for the amount input
|
|
*
|
|
* @return NumericField
|
|
*/
|
|
public function getAmountField()
|
|
{
|
|
return $this->fieldAmount;
|
|
}
|
|
|
|
public function __construct($name, $title = null, $value = "")
|
|
{
|
|
$this->setName($name);
|
|
$this->fieldAmount = NumericField::create(
|
|
"{$name}[Amount]",
|
|
_t('SilverStripe\\Forms\\MoneyField.FIELDLABELAMOUNT', 'Amount')
|
|
)
|
|
->setScale(2);
|
|
$this->buildCurrencyField();
|
|
|
|
parent::__construct($name, $title, $value);
|
|
}
|
|
|
|
public function __clone()
|
|
{
|
|
$this->fieldAmount = clone $this->fieldAmount;
|
|
$this->fieldCurrency = clone $this->fieldCurrency;
|
|
}
|
|
|
|
/**
|
|
* Builds a new currency field based on the allowed currencies configured
|
|
*
|
|
* @return FormField
|
|
*/
|
|
protected function buildCurrencyField()
|
|
{
|
|
$name = $this->getName();
|
|
|
|
// Validate allowed currencies
|
|
$currencyValue = $this->fieldCurrency ? $this->fieldCurrency->dataValue() : null;
|
|
$allowedCurrencies = $this->getAllowedCurrencies();
|
|
if (count($allowedCurrencies) === 1) {
|
|
// Hidden field for single currency
|
|
$field = HiddenField::create("{$name}[Currency]");
|
|
reset($allowedCurrencies);
|
|
$currencyValue = key($allowedCurrencies);
|
|
} elseif ($allowedCurrencies) {
|
|
// Dropdown field for multiple currencies
|
|
$field = DropdownField::create(
|
|
"{$name}[Currency]",
|
|
_t('SilverStripe\\Forms\\MoneyField.FIELDLABELCURRENCY', 'Currency'),
|
|
$allowedCurrencies
|
|
);
|
|
} else {
|
|
// Free-text entry for currency value
|
|
$field = TextField::create(
|
|
"{$name}[Currency]",
|
|
_t('SilverStripe\\Forms\\MoneyField.FIELDLABELCURRENCY', 'Currency')
|
|
);
|
|
}
|
|
|
|
$field->setReadonly($this->isReadonly());
|
|
$field->setDisabled($this->isDisabled());
|
|
if ($currencyValue) {
|
|
$field->setValue($currencyValue);
|
|
}
|
|
$this->fieldCurrency = $field;
|
|
return $field;
|
|
}
|
|
|
|
public function setSubmittedValue($value, $data = null)
|
|
{
|
|
if (empty($value)) {
|
|
$this->value = null;
|
|
$this->fieldCurrency->setValue(null);
|
|
$this->fieldAmount->setValue(null);
|
|
return $this;
|
|
}
|
|
|
|
// Handle submitted array value
|
|
if (!is_array($value)) {
|
|
throw new InvalidArgumentException("Value is not submitted array");
|
|
}
|
|
|
|
// Update each field
|
|
$this->fieldCurrency->setSubmittedValue($value['Currency'], $value);
|
|
$this->fieldAmount->setSubmittedValue($value['Amount'], $value);
|
|
|
|
// Get data value
|
|
$this->value = $this->dataValue();
|
|
return $this;
|
|
}
|
|
|
|
public function setValue($value, $data = null)
|
|
{
|
|
if (empty($value)) {
|
|
$this->value = null;
|
|
$this->fieldCurrency->setValue(null);
|
|
$this->fieldAmount->setValue(null);
|
|
return $this;
|
|
}
|
|
|
|
// Convert string to array
|
|
// E.g. `44.00 NZD`
|
|
if (is_string($value) &&
|
|
preg_match('/^(?<amount>[\\d\\.]+)( (?<currency>\w{3}))?$/i', $value, $matches)
|
|
) {
|
|
$currency = isset($matches['currency']) ? strtoupper($matches['currency']) : null;
|
|
$value = [
|
|
'Currency' => $currency,
|
|
'Amount' => (float)$matches['amount'],
|
|
];
|
|
} elseif ($value instanceof DBMoney) {
|
|
$value = [
|
|
'Currency' => $value->getCurrency(),
|
|
'Amount' => $value->getAmount(),
|
|
];
|
|
} elseif (!is_array($value)) {
|
|
throw new InvalidArgumentException("Invalid currency format");
|
|
}
|
|
|
|
// Save value
|
|
$this->fieldCurrency->setValue($value['Currency'], $value);
|
|
$this->fieldAmount->setValue($value['Amount'], $value);
|
|
$this->value = $this->dataValue();
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get value as DBMoney object useful for formatting the number
|
|
*
|
|
* @return DBMoney
|
|
*/
|
|
protected function getDBMoney()
|
|
{
|
|
return DBMoney::create_field('Money', [
|
|
'Currency' => $this->fieldCurrency->dataValue(),
|
|
'Amount' => $this->fieldAmount->dataValue()
|
|
])
|
|
->setLocale($this->getLocale());
|
|
}
|
|
|
|
public function dataValue()
|
|
{
|
|
// Non-localised money
|
|
return $this->getDBMoney()->getValue();
|
|
}
|
|
|
|
public function Value()
|
|
{
|
|
// Localised money
|
|
return $this->getDBMoney()->Nice();
|
|
}
|
|
|
|
/**
|
|
* 30/06/2009 - Enhancement:
|
|
* SaveInto checks if set-methods are available and use them
|
|
* instead of setting the values in the money class directly. saveInto
|
|
* initiates a new Money class object to pass through the values to the setter
|
|
* method.
|
|
*
|
|
* (see @link MoneyFieldTest_CustomSetter_Object for more information)
|
|
*
|
|
* @param DataObjectInterface|Object $dataObject
|
|
*/
|
|
public function saveInto(DataObjectInterface $dataObject)
|
|
{
|
|
$fieldName = $this->getName();
|
|
if ($dataObject->hasMethod("set$fieldName")) {
|
|
$dataObject->$fieldName = $this->getDBMoney();
|
|
} else {
|
|
$currencyField = "{$fieldName}Currency";
|
|
$amountField = "{$fieldName}Amount";
|
|
|
|
$dataObject->$currencyField = $this->fieldCurrency->dataValue();
|
|
$dataObject->$amountField = $this->fieldAmount->dataValue();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a readonly version of this field.
|
|
*/
|
|
public function performReadonlyTransformation()
|
|
{
|
|
$clone = clone $this;
|
|
$clone->setReadonly(true);
|
|
return $clone;
|
|
}
|
|
|
|
public function setReadonly($bool)
|
|
{
|
|
parent::setReadonly($bool);
|
|
|
|
$this->fieldAmount->setReadonly($bool);
|
|
$this->fieldCurrency->setReadonly($bool);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setDisabled($bool)
|
|
{
|
|
parent::setDisabled($bool);
|
|
|
|
$this->fieldAmount->setDisabled($bool);
|
|
$this->fieldCurrency->setDisabled($bool);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set list of currencies. Currencies should be in the 3-letter ISO 4217 currency code.
|
|
*
|
|
* @param array $currencies
|
|
* @return $this
|
|
*/
|
|
public function setAllowedCurrencies($currencies)
|
|
{
|
|
if (empty($currencies)) {
|
|
$currencies = [];
|
|
} elseif (is_string($currencies)) {
|
|
$currencies = [
|
|
$currencies => $currencies
|
|
];
|
|
} elseif (!is_array($currencies)) {
|
|
throw new InvalidArgumentException("Invalid currency list");
|
|
} elseif (!ArrayLib::is_associative($currencies)) {
|
|
$currencies = array_combine($currencies, $currencies);
|
|
}
|
|
|
|
$this->allowedCurrencies = $currencies;
|
|
|
|
// Rebuild currency field
|
|
$this->buildCurrencyField();
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getAllowedCurrencies()
|
|
{
|
|
return $this->allowedCurrencies;
|
|
}
|
|
|
|
/**
|
|
* Assign locale to format this currency in
|
|
*
|
|
* @param string $locale
|
|
* @return $this
|
|
*/
|
|
public function setLocale($locale)
|
|
{
|
|
$this->fieldAmount->setLocale($locale);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get locale to format this currency in.
|
|
* Defaults to current locale.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getLocale()
|
|
{
|
|
return $this->fieldAmount->getLocale();
|
|
}
|
|
|
|
/**
|
|
* Validate this field
|
|
*
|
|
* @param Validator $validator
|
|
* @return bool
|
|
*/
|
|
public function validate($validator)
|
|
{
|
|
// Validate currency
|
|
$currencies = $this->getAllowedCurrencies();
|
|
$currency = $this->fieldCurrency->dataValue();
|
|
if ($currency && $currencies && !in_array($currency, $currencies)) {
|
|
$validator->validationError(
|
|
$this->getName(),
|
|
_t(
|
|
__CLASS__ . '.INVALID_CURRENCY',
|
|
'Currency {currency} is not in the list of allowed currencies',
|
|
['currency' => $currency]
|
|
)
|
|
);
|
|
return false;
|
|
}
|
|
|
|
// Field-specific validation
|
|
return $this->fieldAmount->validate($validator) && $this->fieldCurrency->validate($validator);
|
|
}
|
|
|
|
public function setForm($form)
|
|
{
|
|
$this->fieldCurrency->setForm($form);
|
|
$this->fieldAmount->setForm($form);
|
|
return parent::setForm($form);
|
|
}
|
|
}
|