Vlastní validační pravidla
- Croc
- Člen | 270
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)
- duke
- Člen | 650
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
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
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
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
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:
- od data v minulosti do aktuálního
- od aktuálního data do budoucnosti
- 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
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
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
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
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)
- Jan Mikeš
- Člen | 771
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
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
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)