Vlastní validační pravidla

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

Zdravím,
procházel jsem tady na foru různá témata na vlastní validaci, ale bohužel jsem nenašel řešení.

Mám připravenou vlastní validační funkce dle návodu , ale stále se mi ji nedaří rozchodit.

Mám tedy ve složce app/forms/MyValidation.php:

<?php

namespace App\Forms;

use Nette,
    Nette\Application\UI\Form,
    Nette\Forms\IControl;


class MyValidation
{

    const DATERANGE = 'MyValidation::validateDateRange';

    public static function validateDateRange(IControl $control)
    {
        $start_date = '1940-01-01';

        $end_date = date('Y-m-d');

        $pieces = explode(".", $control);
        $date_from_user = $pieces[2]."-".$pieces[1]."-".$pieces[0];

        // Convert to timestamp
        $start_ts = strtotime($start_date);
        $end_ts = strtotime($end_date);
        $user_ts = strtotime($date_from_user);

        // Check that user date is between start & end
        return (($user_ts >= $start_ts) && ($user_ts <= $end_ts));
    }
}

v UserFormFactory mám toto:

  $form->addDatePicker('birth_date', 'Datum narození:')
            ->setAttribute('class','date')
            ->addRule('MyValidation::DATERANGE', 'Datum musí být mezi 1.1.1940 a dneškem.');

v www/js/MyValidation.js mám:

Nette.validators.validateDateRange = function(elem, args, val) {

	var from = val.split(".");
	var date = new Date(from[2], from[1] - 1, from[0]);
	var inputDate = date.getTime();
	var date = new Date( 1940, 0, 1);
	var minDate = date.getTime();
	var date = new Date();
	var maxDate = date.getTime();

	if (inputDate > minDate && inputDate < maxDate){
		return true;
	}
	else{
		return false;
	}
};

Stále dostávám chybné hlášení:

Nette\InvalidArgumentException

Unknown validator 'MyValidation::DATERANGE' for control 'birth_date'.

Už opravdu nevím kde mi co chybí :(

Editoval Croc (17. 8. 2015 20:07)

newPOPE
Člen | 648
+
0
-

Skus takto

$form->addDatePicker('birth_date', 'Datum narození:')
          ->setAttribute('class','date')
          ->addRule(MyValidation::DATERANGE, 'Datum musí být mezi 1.1.1940 a dneškem.');
duke
Člen | 650
+
+1
-

Oprav konstantu na:

	const DATERANGE = 'App\Forms\MyValidation::validateDateRange';

A v javascriptu pak:

Nette.validators.AppFormsMyValidation_validateDateRange = function(elem, args, val) {
	// ...
}

A jak říká @newPOPE, použij pak tu konstantu. Na to si ji definuješ, abys ji mohl používat místo řetězce.

Editoval duke (17. 8. 2015 20:50)

Croc
Člen | 270
+
0
-

Díky moc za rady, uvedené věci pomohly :)

Mám tu ještě jeden problém. Nevím úplně přesně, jak se dostat k datu z daného pole. Pokud použiju

dump($control->getValue());

// Vrátí to:

Nette\Utils\DateTime
date => "1987-02-09 00:00:00.000000" (26)
timezone_type => 3
timezone => "Europe/Prague" (13)

Potřeboval bych se dostat k datu ve formátu Y-m-d.

Jedná se o datum z Nextras/DatePicker.

Jan Mikeš
Člen | 771
+
0
-

Je to trida Nette\Utils\DateTime, ktera mimo jine dedi vychozi php DateTime takze jednoduse

$control->getValue()->format("Y-m-d");

https://api.nette.org/…ateTime.html
http://php.net/…datetime.php

edit: nevim jak ten doplnek funguje a co vraci pri nevyplnenem poli, ale osobne bych to jeste testoval na DateTime:

$val = $control->getValue();
$date = $val instanceof \DateTime ? $val->format("Y-m-d") : NULL;

Zalezi proc potrebujes hodnotu Y-m-d a co s tim dale budes delat.

Editoval Lexi (17. 8. 2015 22:28)

Jan Mikeš
Člen | 771
+
0
-

Jeste jsem se podival poradne na tvuj priklad a toto je mozna lepsi reseni:

class MyValidation
{
    const DATERANGE = 'MyValidation::validateDateRange';

    public static function validateDateRange(IControl $control)
    {
        $startDate = new DateTime("1940-01-01");
        $endDate = new DateTime("now");
		$val = $control->getValue();
		return ($val instanceof DateTime && $val >= $startDate && $val <= $endDate);
    }
}

Je mnohem jednodussi pracovat s objekty DateTime nez to prevadet na timestamp

Croc
Člen | 270
+
0
-

Tu funkci jsem měl z původního webu a ještě mě nenapadlo ji předělat. Takže moc děkuju :)

Vypadá to, že vše teď funguje jak má.

Mám akorát ještě jeden dotaz. Je možné předat v rámci validace pole nebo více než jeden parametr? (jednalo by se o datum ve formátu 1940–01–01 a pak nějaký řetězec)
Něco jako:

        $form->addDatePicker('birth_date', 'Datum narození:')
            ->setAttribute('class','date')
            ->addRule(MyValidation::DATERANGE, 'Datum musí být mezi 1.1.1940 a dneškem.', $array(param1,param2,param3)); //s polem nebo:
  			->addRule(MyValidation::DATERANGE, 'Datum musí být mezi 1.1.1940 a dneškem.', param1,param2,param3); //více paramtetrů

Budu mít více scénářů pro různé formuláře:

  1. od data v minulosti do aktuálního
  2. od aktuálního data do budoucnosti
  3. Od – Do

Takže mi jde o to, jestli můžu využít jednu funkci pro všechny varianty. Nebo jestli budu muset mít pro každou variantu funkci zvlášť.

Editoval Croc (18. 8. 2015 10:27)

Jan Mikeš
Člen | 771
+
0
-

Muzes, btw pro inspiraci ja mam take sve InRange pravidlo pro datumy:

<?php

namespace App\Forms;

use Nette,
	Nette\Forms\IControl,
	Nette\Utils\Validators;

final class Rules extends Nette\Object
{
	const DATE_RANGE = "App\\Forms\\Rules::dateRange";

	public static function dateRange(IControl $control, array $range)
	{
		$dates = array_map("trim", explode("-", $control->getValue()));
		$start = \Nette\DateTime::from($dates[0]);
		$end = \Nette\DateTime::from($dates[1]);
		return (Validators::isInRange($start, $range) && Validators::isInRange($end, $range));
	}
}

V inputu je text ve formatu DD.MM.YYYY – DD.MM.YYYY a zvaliduje (s vyuzitim Nette\Utils\Validators) zda je v povolenem rozsahu. Vyuziti:

	$form->addText("date", "date.title")
		->addRule(Rules::DATE_RANGE, "errorMsg", [$startsAt, $endsAt]);

Druhy parametr tve validacni funkce dostava vse, co mu predas jako treti parametr v addRule() viz https://api.nette.org/…Control.html#…

duke
Člen | 650
+
0
-

Pro inspiraci, jak se s tím dá pracovat v JS, se můžeš podívat také do validátorů definovaných v netteForms.js.

Croc
Člen | 270
+
0
-

Moc děkuju všem za rady a inspiraci.

Nakonec jsem jako serverovou validaci vyrobil toto:

//Možnosti zadání vstupních hodnot:
//.....
->addRule(Rules::DATERANGE, 'Datum musí být mezi 1.1.1940 a dneškem.',['1940-01-01','']);
->addRule(Rules::DATERANGE, 'Datum musí být mezi dneškem a 1.1.2020.',['','2020-01-01']);
->addRule(Rules::DATERANGE, 'Datum musí být mezi 1.1.1940 a 1.1.2020.',['1940-01-01','2020-01-01']);
//Metoda:
    public static function validateDateRange(IControl $control, array $range)
    { // Pokud je hodnota v poli prázdná (''), DateTime doplní aktuální datum
        $range[0] = new Nette\Utils\DateTime($range[0]);
        $range[1] = new Nette\Utils\DateTime($range[1]);
        $date = $control->getValue();
        return (Validators::isInRange($date, $range));
    }

Co si o tom myslíte?

Na JS validaci zkusím kouknout navečer.

matopeto
Člen | 395
+
0
-

osobne by som ako rozsahy povolil i DateTime objekty, takze daco take:

//Metoda:
    public static function validateDateRange(IControl $control, array $range)
    { // Pokud je hodnota v poli prázdná (''), DateTime doplní aktuální datum
        $range[0] = $range[0] instanceof Nette\Utils\DateTime ? $range[0] : new Nette\Utils\DateTime($range[0]);
        $range[1] = $range[1] instanceof Nette\Utils\DateTime ? $range[1] : new Nette\Utils\DateTime($range[1]);
        $date = $control->getValue();
        return (Validators::isInRange($date, $range));
    }

Editoval matopeto (18. 8. 2015 13:09)

Croc
Člen | 270
+
0
-

Díky, zkusím.

Mám ještě otázku. Je možné s Nette\Utils\DateTime pracovat v JS? Potřebuju nyní udělat totožnou funkci v JS.

Jan Mikeš
Člen | 771
+
0
-

Myslim ze muzes vyuzit jako zaklad mou cast javascriptove validace pro daterange:

<script>
	Nette.validators.AppFormsRules_dateRange = function (elem, arg, value) {
		var values = value.split("-").map(function(val) {
			return moment(val.trim(), "DD.MM.YYYY HH:mm");
		});

		var minDate = moment(arg[0].date);
		var maxDate = moment(arg[1].date);

		for (var id = 0, len = values.length; id < len; id++) {
			var val = values[id];

			if (val.isBefore(minDate) || val.isAfter(maxDate)) {
				return false;
			}
		}

		return true;
	};
</script>

Vyuzivam moment.js ktery je vyborny na praci s daty, viz isBefore() a isAfter()

Editoval Lexi (18. 8. 2015 18:08)

Croc
Člen | 270
+
0
-

Díky za inspiraci. Nakonec jsem moment.js nepoužil a vyřešil jsem to takto:


<script>
	Nette.validators.AppFormsRules_validateDateRange = function(elem, arg, value) {

		var from = value.split(".");
		var inputDate = new Date(from[2], from[1] - 1, from[0]);

		if(arg[0])
			var minDate = new Date(arg[0]);
		else
			var minDate = new Date();

		if(arg[1])
			var maxDate = new Date(arg[1]);
		else
			var maxDate = new Date();

		if (inputDate > minDate && inputDate < maxDate){
			return true;
		}
		else{
			return false;
		}
	};
</script>

Editoval Croc (20. 8. 2015 9:57)

Croc
Člen | 270
+
0
-

Nakonec jsem moment.js použil, protože jsem potřeboval v některých případech k minDate přičíst 1 rok. A podobných případů budu mít asi více.

Kdyby někdo řešil něco podobného:

Definice:

->addRule(Rules::DATERANGE, 'Chybová zpráva',['1.1.1950','']; // bude validovat mezi 1.1.1950 a aktuálním datem
->addRule(Rules::DATERANGE, 'Chybová zpráva',['','1.1.2020']; // bude validovat mezi aktuálním datem a 1.1.2020
->addRule(Rules::DATERANGE, 'Chybová zpráva',['1.1.1950','1.1.2020']; // bude validovat mezi 1.1.1950 a 1.1.2020
->addRule(Rules::DATERANGE, 'Chybová zpráva',['1.1.1950','','param1']; // bude validovat mezi 1.1.1950 +1rok (v případě třetího parametru s hodnotou param1) a aktuálním datem

JS validace:

Nette.validators.AppFormsRules_validateDateRange = function(elem, arg, value) {

	var inputDate = moment(value, 'DD.MM.YYYY');

	if(arg[0]){//minDate
		var minDate = moment(arg[0], 'DD.MM.YYYY');
	}
	else
		var minDate = moment();

	if(arg[1]){//maxDate
		var maxDate = moment(arg[1], 'DD.MM.YYYY');
	}
	else
		var maxDate = moment();

	if(arg[2] == "param1"){//pokud je rovno, přičti jeden rok
		var minDate = moment(minDate).add(1, 'years'); //minDate + 1 rok
	}

	if (inputDate.isBefore(minDate) || inputDate.isAfter(maxDate)) {
		return false;
	}
	return true;
};

Server validace:

namespace App\Forms;

use Nette,
    Nette\Forms\IControl,
    Nette\Utils\Validators;


class Rules extends Nette\Object
{
    const DATERANGE = 'App\Forms\Rules::validateDateRange';

    public static function validateDateRange(IControl $control, array $range)
    {
        $range[0] = $range[0] instanceof Nette\Utils\DateTime ? $range[0] : new Nette\Utils\DateTime($range[0]);
        $range[1] = $range[1] instanceof Nette\Utils\DateTime ? $range[1] : new Nette\Utils\DateTime($range[1]);

        if(isset($range[2]) == 'param1')
            $range[0]->modify('+1 year');// minDay +1 year

        $date = $control->getValue();

        return (Validators::isInRange($date, $range));
    }
}

Ještě jednou moc děkuju všem za rady a připomínky :)

Editoval Croc (22. 8. 2015 16:59)