Vlastní FormControl – Vlastní formát čísla – Jak na to?

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
jasir
Člen | 746
+
0
-

Ahoj, snažím se vytvořit si vlastní FormControl pro prezentování peněžní hodnoty.
Jedná se vlastně o text input, ve kterém má uživatel možnost zapsat číselnou hodnotu.
Ta může být zapsáná:

  1. s oddělovačem tisíců 100.000,00 (sto tisíc)
  2. i bez oddělovačů tisíců jako float, tj 100000,00

Chtěl bych, aby setValue(), getValue() vracelo číselnou hodnotu (bez oddělovačů).

<?php
$money = new MoneyInput(..)
$money->setValue(100000); /* Formulář bude zobrazovat 100,000.00 */
$val = $money->getValue(); /* Vrátí hodnotu bez oddělovačů */
?>

Nějak na to nemůžu přijít, zatím mám jenom:

<?php
class MoneyInput extends TextInput
{
	public function __construct  ($label = NULL, $message = NULL, $cols = NULL, $maxLength = NULL)
	{
		parent::__construct($label,$cols,$maxLength);
		if ($message===NULL) {
			$message = 'Musí být číselná hodnota, jako oddělovač tisíců lze použít tečku.';
		}
		$this->addRule(array($this, 'validateMoney'), $message);
	}

	/**
	* Validates money value.
	* Allows format 100.000,00 (cents are optional) or
	* without thousands separators 1000000.
	*
	* @param MoneyInput $control
	* @param mixed $arg
	*/
	public function validateMoney(MoneyInput $control, $arg)
	{
		$value = $control->getValue();
		if (preg_match('/\\A\\b[0-9]{1,3}(?:.?[0-9]{3})*(?:\\,[0-9]{2})?\\b\\z/', $value)) {
			return TRUE;
		}
		return $this->validateFloat($control);
	}
}
?>

Napadlo mě přepsat setValue() (nastavení zobrazení) a getValue() (odstranění/konverze oddělovačů),
ale validator si volá getValue().
Zatím to řeším že při vytváření formu do setValue(), setDefaults() pošlu číslo v požadovaném formátu a po
$form->getValues provedu konverzi, ale chtěl bych, aby to umělo samo.

EDIT: Mohu použít proměnnou tmpValue ? To by mi to asi vyřešilo, validoval bych podle tmpValue, a setValue() a getValue() by pak mohli provádět konverze číselného formátu.

Editoval jasir (5. 5. 2009 13:49)

insider
Člen | 31
+
0
-

Zdravim,

co prepsat nejak takhle

<?php
public function getValue($number = true){
	if($number)
		return str_replace(array(',','.'),array('',''),$this->value);
	else
		return $this->value;
}
?>

Nevim, jak presne vypada original metody getValue

jasir
Člen | 746
+
0
-

insider napsal(a):

To nevypadá jako špatný nápad! Díky!

PetrP
Člen | 587
+
0
-

insider napsal(a):

Zdravim,

co prepsat nejak takhle

<?php
public function getValue($number = true){
	if($number)
		return str_replace(array(',','.'),array('',''),$this->value);
	else
		return $this->value;
}
?>

Nevim, jak presne vypada original metody getValue

Tím bych ale ze vstupu (string) 100.000,00 (100 tisíc) dostal (string) 10000000 (tedy 10 miliónů. ;])

jasir
Člen | 746
+
0
-

Nakonec jsem to vyřešil nějak takhle. Funguje to :-) Ale mám
takové tušení, že to nemusí být úplně ono :)

Třeba to někomu pomůže, tvorba vlastních Inputů tu moc řešená
zatím nebyla.

<?php
/**
* NumericTextInput.
*
* @package    Base
* @subpackage Controls
**/
class NumericTextInput extends TextInput
{

   protected $decimals = 2;
   protected $thousandsSeparator = ".";
   protected $decimalSeparator = ",";

   /**
   * Constructs Text Input and sets Rules.
   *
   * @param name $label
   * @param int $decimals
   * @param string $message
   * @param string $thousandSeparator
   * @param string $decimalSeparator
   * @param int $cols
   * @param int $maxLength
   * @return NumericTextInput
   */
   public function __construct  ($label = NULL, $decimals = 0, $message = NULL,
      $thousandSeparator = ".", $decimalSeparator = ",", $cols = NULL, $maxLength = NULL) {
      parent::__construct($label, $cols, $maxLength);

      $this->decimalSeparator = $decimalSeparator;
      $this->thousandsSeparator = $thousandSeparator;
      $this->decimals = $decimals;

      if ($message === NULL) {
         $message = "Číslo musí mít formát %s.";
      }
      $message = sprintf($message, $this->getSampleFormatText());

      $this->addRule(array($this, 'validateNumericText'), $message);
      $this->getControlPrototype()->class = 'right-aligned';

   }

   //---------------------------------------------------------------------------
   /**
   * Gets value.
   *
   * @param bool $transform Used for internal call from validator
   * @return mixed
   */
   public function getValue($transform = TRUE) {
      $value = parent::getValue();

      if ($transform === TRUE) {
         $value = str_replace(array($this->thousandsSeparator,$this->decimalSeparator), array('','.'), $value);
      }
      return $value;
   }

   //---------------------------------------------------------------------------

   /**
   * Creates HTML control.
   * Changes value to numeric format.
   */
   public function getControl() {
      $control = parent::getControl();
      $form = $this->getForm(FALSE);
      if ($form === FALSE || $form->isSubmitted() === FALSE) {
         $control->value
            = number_format($this->value, $this->decimals, $this->decimalSeparator, $this->thousandsSeparator);
      }
      return $control;
   }

   //---------------------------------------------------------------------------

   /**
   * Validates numeric format.
   * Allows format 100.000,00 (cents are optional) or
   * without thousands separators 1000000.
   *
   * @param mixed $item
   * @param mixed $arg
   */
   public function validateNumericText($control) {
      $value = $control->getValue(FALSE);

      if (preg_match(
         "/\\A[-]{0,1}[0-9]{1,3}+(?:\\{$this->thousandsSeparator}?[0-9]{3})*(?:\\"
         .$this->decimalSeparator
         ."[0-9]{"
         .$this->decimals
         ."})?\\z/", $value)
         ) {

         return TRUE;
      }
      return FALSE;
   }

   //---------------------------------------------------------------------------

   /**
   * Formats help string describing numeric format.
   * @returns string
   */
   protected function getSampleFormatText() {
      $s = "999{$this->thousandsSeparator}999{$this->thousandsSeparator}999";
      if ($this->decimals > 0) {
         $s .= $this->decimalSeparator . str_repeat('9', $this->decimals);
      }
      return $s;
   }

}
?>

Editoval jasir (22. 7. 2009 12:00)

kravčo
Člen | 721
+
0
-

PetrP napsal(a):

Tím bych ale ze vstupu (string) 100.000,00 (100 tisíc) dostal (string) 10000000 (tedy 10 miliónů. ;])

Toto už smeruješ do problémov s locale, totiž v našich končinách sa čísla píšu s desatinnou čiarkou a tisíce sa oddeľujú medzerou… Napríklad v Británii je to zase inak. Pokiaľ aplikácia nestojí len na jednom locale, číslo „100.000“ napísané slovákom/čechom a angličanom by malo byť chápané rôzne (sto resp. stotisíc). To, či je človek slovák alebo angličan samozrejme z prostredia PHP dá len ťažko zistiť ;) ale má asi zmysel uvažovať jazykové nastavenie…

PetrP
Člen | 587
+
0
-

kravco napsal(a):

Toto už smeruješ do problémov s locale, totiž v našich končinách sa čísla píšu s desatinnou čiarkou a tisíce sa oddeľujú medzerou… Napríklad v Británii je to zase inak. Pokiaľ aplikácia nestojí len na jednom locale, číslo „100.000“ napísané slovákom/čechom a angličanom by malo byť chápané rôzne (sto resp. stotisíc). To, či je človek slovák alebo angličan samozrejme z prostredia PHP dá len ťažko zistiť ;) ale má asi zmysel uvažovať jazykové nastavenie…

Reagoval jsem jenom na tuto konstrukci:

	return str_replace(array(',','.'),array('',''),$this->value);

která jak čárku tak tečku zahodí, nezávisle na čemkoli ;].

Celá problematika převodu (string) na (float) je hodně složitá a nebyla tématem mého příspěvku.