Zadávání data
Školení, která pořádám
Pro ukládání data a času v databázi doporučuji typ datetime. Jak datum zadávat v HTML formuláři? Možnosti vidím v zásadě tři:
- Pomocí běžného textového pole
- Výhoda tohoto přístupu spočívá v největší rychlosti zadávání, nevýhoda v nečastějších překlepech nezkušených uživatelů.
- Pomocí kalendáře
- Kalendář se obvykle otevírá do nového okna a slouží typicky jako doplněk textového pole. Je nejroztomilejší, ale také je s ním nejvíc práce.
- Pomocí skupiny výběrových polí
- U „klikačů“ je tento přístup skoro tak oblíbený jako kalendář a přitom se vejde přímo do formuláře a není s ním tolik práce.
Textové pole
Osobně mi nejvíc vyhovuje přímé zadávání data, ale pokud uživatele nemáte pod kontrolou (např. na veřejně přístupných stránkách), tak se chybnému formátu nevyhnete. Někdo datum zadá jako m/d
, někdo jako d,m
, ať formát popíšete sebenázorněji. Slušností je v tomto případě umožnit datum zadat v národním formátu, u nás tedy d.m.rrrr
. Pro určení formátu data je v MySQL vyhrazena konfigurační direktiva date_format, která je ale bohužel zatím bez účinku, a jediný přijímaný formát je yyyy-mm-dd
. Mezi těmito formáty lze převádět např. funkcí preg_replace nebo explode.
<?php
preg_replace('~^([0-9]+)-0?([0-9]+)-0?([0-9]+)~', '\\3.\\2.\\1', $row["date"]); // z databáze
preg_replace('~^([0-9]+)\\.([0-9]+)\\.([0-9]+)~', '\\3-\\2-\\1', $_POST["date"]); // do databáze
?>
Využito je tolerance MySQL, která datum dovoluje zadat i ve formátu yyyy-m-d
.
Kalendář
Kalendář se obvykle otvírá do nového okna, ale pokud nějak vyřešíme přepínání měsíců a roků, je možné ho nechat i přímo ve formuláři, může tam ale překážet. Okno s kalendářem pro zadání data do pole date
můžeme otevřít odkazem <a href="calendar.php" onclick="return !window.open(this.href + '?transfer=date', '', 'width=300, height=200');">
, přenos hodnoty zpět do formuláře zajistí:
<?php
echo "<a href='calendar.php?print=$date' onclick=\"if (opener && opener.document.getElementById('$_GET[transfer]')) { opener.document.getElementById('$_GET[transfer]').value = '$date'; window.close(); }\">";
?>
Výběrová pole
Pro zadávání data mi tento způsob připadá neprakticky pomalý, navíc neumožňuje celé datum zkopírovat ze schránky, řada lidí ho má ale přesto ráda.
<?php $date = ($_POST["update"] ? $_POST["date"] : getdate(strtotime($row["date"]))); ?>
<select name="date[mday]"><?php echo optionlist(range(1, 31), $date["mday"], "not_vals"); ?></select>.
<select name="date[mon]"><?php echo optionlist(range(1, 12), $date["mon"], "not_vals"); ?></select>.
<input name="date[year]" value="<?php echo $date["year"]; ?>" size="4" maxlength="4">
<select name="date[hours]"><?php echo optionlist(range(0, 23), $date["hours"], "not_vals"); ?></select>:
<select name="date[minutes]"><?php echo optionlist(array_merge(array("00", "01", "02", "03", "04", "05", "06", "07", "08", "09"), range(10, 59)), $date["minutes"], "not_vals"); ?></select>
Viz optionlist
.
Funkce getdate se pro naše potřeby ideálně hodí, protože najednou vrátí všechny části data. Formát potřebný pro uložení do databáze potom získáme pomocí $_POST["date"]["year"] . "-" . $_POST["date"]["mon"] . "-" . $_POST["date"]["mday"] . " " . $_POST["date"]["hours"] . ":" . $_POST["date"]["minutes"]
.
Kontrola data
MySQL při ukládání kontroluje pouze rozsahy jednotlivých částí data. Pokud chceme zkontrolovat, zda je v daném měsíci zadaný den, můžeme to udělat v PHP nebo už na straně klienta:
<script type="text/javascript">
function date_check(year, month, day) {
var date = new Date(year, month - 1, day);
return (date.getFullYear() == year && date.getMonth() == month - 1 && date.getDate() == day);
}
</script>
Diskuse
Rick:
Nechápu proč tak složitě
Jan Brašna:
Jakube, input je ideální pro pošuky (nic ve zlém... :D) jako jsi ty. Normální uživatel absolutně netuší, co s tím. Proto mně osobně přijde nejlepší input s formátem, ale navěsit na něj rovnou rozbalovací kalendář. Pak si můžeš zadávat datum textově, ale zbytek světa si to může naklikat.
Provedením to však zdaleka ideální není. Při každém kliknutí se přenáší mezi serverem a klientem 5kB dat a to na stránce žádný další aktivní prvek není. Jinak by bylo ASP.NET schopno klidně tahat i 100kB a více....
Spud:
co je 5kB? nic ... ale samo by se to dalo resit jen javascriptem. jen jsem mel na mysli zobrazeni kalendare byz vyskakovani novyho okna.
vd:
A co kdyz ti tam prijde 1000 uzivatelu, 10 000 uzivatelu nebo 100 000 uzivatelu. Spocitej si kolik ti pak bude generovat jen ten kalendar. Kolig GiB si platis na hostingu?
T3RMiX:
Já tohle vyřešil v poslední aplikaci tak že v nastavení uživatele je možné si zvolit input+kalendář, select - at si každý vybere :)
Ta ukázka je moc pěkná, hned se na to jdu podivat :)
Filip:
OMG proc tak slozite pres preg_replace. Je videt zes nakazen regularni nemoci (navyk pouzivat regularni vyrazy absolutne vsude bez premysleni) :D
nevypada tohle prehlednejsi(a dvakrat kratsi:) ?
date('d.m.Y',strtotime($row['date']));
RATMex B:
Funkcia strtotime() robí konverziu dosť netransparentne (jedine, že by si si naštudoval zdrojové kódy) a navyše prevádzať formát dátumu na Unix timestamp je krok späť. Okrem toho tento prevod má za následok, že dátumy mimo rozsah 1970-01-01 00:00:00 - 2038-19-01 03:14:08 vracajú timestamp -1, čo je zbytočné obmedzenie. Navrch, tento prevod je dosť pomalý.
Čo sa týka prehľadnosti, tak použitie 2 funkcí miesto jednej (bez komplexných argumentov) je implicitne zložitejší zápis.
Regulárny výraz, ktorý je použitý v článku je natoľko jednoduchý, že je na prvý pohľad jasné, čo predstavuje a v postate ho zbehlé oko úplne odignoruje.
Filip:
To ze to vraci -1 ale nijak nesouvisi s tim co jsem psal.
O kontrole validity data nebyla rec a nedela to ani ten preg!
S tou pomalosti to nebude tak horke. Uzke hrdlo je a jeste dlouho bude na discich.
O slozitosti/jednoduchosti regularniho vyraz tez nebyla rec
RATMex B:
Veď o validite som ani ja nič nepísal. Ale OK, máš právdu, že tá mínus jednotka nesúvisí s tým, čo si písal, nemôžem poprieť, že je ten zápis kratší.
Pokiaľ je pre Teba kratší zápis lepší aj napriek tomu, že nefunguje správne a na drvivú časť možných vstupov vráti nekorentný výstup, tak ho kľudne používaj. My ostatní sa radšej uspokojíme so skriptami, ktoré budú fungovať bez chýb a nie iba na oko.
uzivatel:
no toto obmedzenie plati iba pre 32bit systemy. a nepocitam ze v roku 32bity budu este exitovat :)
ale inak povedat, ktore riesenie je rychlejsie si netrufam. regularny vyraz robi len cisla, co by mohlo byt rychle, ale zase dve casove funckie tiez budu robit iba cisla. takze nech si uzivatel vyberie, ktore mu viac sekne
Kalendář mi připadá mírně nepraktický. Protože jak se v kalendáříku bude vybírat rok a měsíc? Buďto šipkama doleva/doprava (to je pakárna), nebo selectama. Ale pokud může být větší rozsah roků (datum narození nebo katalog antikvariátu), tak input pro roky a select pro měsíce. Pak zadáváme tři čísla a pro každé máme jiný ovládací prvek.
Nejpraktičtější se mi proto zdají tři comboboxy, jenže ty v HTML nejsou a museli by se nejednoduše skriptovat.
Hever:
Používám selecty a se zadáváním problémy nemám (tabulátorem najedu např. na volbu roku a naťukám 2006 a mám to tam).
Selecty mi přijdou nejintuitivnější ... co se znám, tak mě nebaví studovat v jakémžeto formátu to mám do toho inputu napsat, natož vyklikávat si to složitě v nějakém kalednáři. U selectu to jasně vidím a nemusím studovat okolní nápovědy.
Ještě teda připomínka ... když jsou v selectu na výběr měsíce, tak bych upřednostnil místo "january,february..." nebo "1,2,3.." nějakou kombinaci - "1-january, 2-february..." přece jen člověk mluvící jinou řečí než který si vybral autor formuláře s tím může mít problém. A samotné čísla se mohou zaměnit s označením dne v měsíci.
mila:
Nedávno jsem něco podobného řešil, a nakonec jsem došel k tomu, že stačí select - "dnes", "tento týden", "tento měsíc"
Samozřejmě to je něco jiného, jen je dobré se zamyslet, zda po uživateli opravdu potřebuji přesné datum.
Diskuse je zrušena z důvodu spamu.