Szablony smarty odc.5. Jak zrobić kartkę z kalendarza, czyli bardziej skomplikowane własności sekcji
Szablony Smarty Zostaw komentarzW jednym z realizowanych serwisów w którym wykorzystuję szablony Smarty. Przyszło mi wykonać kalendarium w formie kartki z kalendarza, przedstawiającej miesiąc z wyróżnionymi dniami w których są jakieś wydarzenia i odnośnikami do wpisów o tych wydarzeniach na stronie. Wiadomo, że do ‘narysowania’ takiej kartki z kalendarza nie ma nic lepszego niż tabelka, a do stworzenia tabelki najlepiej nadaje się pętla, czyli w przypadku Smarty sekcja.
Wczytanie daty
A teraz po kolei… Najpierw trzeba sobie przygotować dane, które przekażemy do skryptu. Wszystkie dane będę przetrzymywać w tablicy asocjacyjnej $dane[] w polach o odpowiednich nazwach. Jeśli w linku nie przekazano informacji o wybranym dniu i miesiącu trzeba przyjąć założenie, że chodzi o bieżący miesiąc bieżącego roku. Trzeba zwrócić uwagę na wykonane rzutowanie (int) zmiennej z tablicy $_GET wykonane ze względów bezpieczeństwa.
Zapytanie do bazy
Następnie trzeba wczytać z bazy, z odpowiedniej tabelki wszystkie wydarzenia z tego miesiąca. W tym celu trzeba ustalić zakres.
I skonstruować odpowiednie zapytanie
- $zapytanie = "SELECT ID,data
- FROM tabela_wydarzenia
- WHERE data BETWEEN '$od'
- AND '$do'
- ORDER BY data";
Jeśli jednego dnia jest więcej niż jedno wydarzenie można zapisać MIN(ID) zamiast ID a na końcu grupowanie GROUP BY data, żeby wczytać tylko jedno (pierwsze, czyli o najniższym ID) wydarzenie dnia.
Wczytanie rekordów
Kolejnym krokiem jest wczytanie rekordów z zapytania do tablicy $dane. Najpierw skonstruuję tablicę $wydarzenia, której indeksami będą dni miesiąca w których jest wydarzenie a wartościami wskaźnik ID wydarzenia. A potem tę tablicę wstawię do tablicy $dane['wydarzenia']
- $dane['wydarzenia'][$dzien]=$wydarzenie['ID'];
- }
- }
Przygotowanie dodatkowych informacji o wybranym miesiącu
Teraz trzeba jeszcze przygotować kilka informacji o miesiącu i wrzucić je do naszej tablicy, która powędruje do szablonu. Potrzebna nam informacja od którego dnia tygodnia zaczyna się miesiąc, na jakim dniu tygodnia się kończy, ile trwa oraz jak się nazywa:
- $znacznik =
- $dane['ilosc'] = $l_dni_w_miesiacu;
- 'Luty',
- 'Marzec',
- 'Kwiecień',
- 'Maj',
- 'Czerwiec',
- 'Lipiec',
- 'Sierpień',
- 'Wrzesień',
- 'Październik',
- 'Listopad',
- 'Grudzień');
- $dane['miesiac_nazwa'] = $miesiace[($dane['miesiac']-1)];
Przekazanie danych do szablonu Smarty
Teraz już można przekazać tablicę $dane do formularza i ją tam obsłużyć. W poprzednich artykułach pokazywałam jak przekazać dane do szablonu metodą assign można to zrobić w ten sposób, jednak ponieważ jest to dość rozbudowana tablica lepiej będzie użyć metody assign_by_ref, która operuje referencją przez co oszczędzamy nieco pamięci.
- <?
- $szablon->assign_by_ref('kalendarz', $dane);
- ?>
Nawigacja po kalendarzu
Pora pokazać co można z tak rozbudowaną tablicą zrobić w szablonie. Najpierw ustawimy sobie kilka wartości przydatnych do nawigowania po kalendarzu, czyli do przewijania miesięcy i lat. Nie chcemy przecież, aby po miesiącu nr 12 następował miesiąc nr 13, tylko żeby to był miesiąc nr 1 następnego roku. Załatwi nam to odpowiedni warunek, wewnątrz którego instrukcją {assign} ustawimy odpowiednie wartości kilku zmiennych.
Podobnie chcemy by przed miesiącem nr 1 następował miesiąc nr 12 poprzedniego roku. Dajemy więc warunek:
Teraz już możemy zacząć konstruować tabelkę. Pierwszy wiersz (poza znacznikiem początku tabeli table) będzie pokazywał rok i pozwalał go przewijać:
- <table>
- <tr>
- <td>
- <a href="index.php?
- miesiac={$kalendarz.miesiac}&rok={$kalendarz.rok-1}">
- «
- </a>
- </td>
- <td colspan="5">{$kalendarz.rok}</td>
- <td>
- <a href="index.php?
- miesiac={$kalendarz.miesiac}&rok={kalendarz.rok+1}">
- »
- </a>
- </td>
- </tr>
Następny wiersz tabeli będzie zawierał miesiąc i przyciski do przewijania:
- <tr>
- <td>
- <a href="index.php?miesiac={$prevM}&rok={$prevY}">
- «
- </a>
- </td>
- <td colspan="5">{$kalendarz.miesiac_nazwa}</td>
- <td>
- <a href="index.php?miesiac={$nextM}&rok={$nextY}">
- »
- </a>
- </td>
- </tr>
Po nim następuje prosty wiersz wypisujący siedem komórek z nazwami dni tygodnia. Uwaga! W naszym kalendarzu tydzień zaczyna się od niedzieli. Chcąc to zmienić trzeba odpowiednio poprzesuwać dane otrzymywane przy użyciu funkcji date()
- <tr>
- <th>Nd</th>
- <th>Pn</th>
- <th>Wt</th>
- <th>Śr</th>
- <th>Cz</th>
- <th>Pt</th>
- <th>Sb</th>
- </tr>
Puste komórki na początku miesiąca
Do tej pory było łatwo, teraz zacznie się zabawa. Otwieramy kolejny wiersz tabeli i na początek wypisujemy odpowiednią ilość pustych komórek, gdyż nasz miesiąc nie koniecznie zaczyna się od niedzieli. O tym ile ma być tych pustych komórek informuje nas zmienna $dane['poczatek']. Jeśli tydzień zaczyna się od czwartku. Ta zmienna ma wartość 4 (wartość zero jest dla niedzieli a 6 dla soboty). Oznacza to, że trzeba wstawić 4 puste komórki odpowiednio dla niedzieli, poniedziałku wtorku i środy. Jak to zrobić? Najlepiej pętlą:
- <tr>
- <td> </td>
- {/section}
Komórki z numerami dni miesiąca
Mamy już odpowiednią ilość pustych pól na początku pierwszego wiersza. Teraz wstawimy komórki zawierające numery dnia. Jednym słowem znów pętla. Nie zaczynamy nowego wiersza, gdyż musimy dokończyć ten, który rozpoczęliśmy pustymi komórkami.
W pętli wykorzystamy zmienną $smarty.section.dzien.iteration, która pokazuje bieżący index pętli (należy zwrócić uwagę na składnię tej zmiennej po ‘$smarty.section.’ następuje nazwa pętli ustawiona w atrybucie name znacznika {section}).
Upssss. Niedobrze, jeśli tak zrobimy wszystkie dni miesiąca będą w jednym rzędzie i tabela się rozjedzie.
Łamanie wierszy tabeli
Trzeba więc wstawić warunek, który będzie nam łamał wiersze co siedem pól. Ale to łamanie musi uwzględniać również puste pola na początku pierwszego wiersza. Jednym słowem łamanie musi nastąpić gdy suma zmiennych $kalendarz.poczatek oraz $smarty.section.dzien.iteration będzie podzielna przez siedem. Dlatego w pętli trzeba wprowadzić zmienną $total oraz warunek, który sprawdza, czy jest ona podzielna przez siedem.
Linki do wydarzeń
Świetnie. Teraz już wiersze łamią się jak należy. Cóż jednak z tego skoro w kalendarzu nie ma wydarzeń. Trzeba jeszcze dołączyć w pętli warunek, który będzie każdy dzień sprawdzał i w komórce zamiast numeru wstawiał link do wydarzenia.
- <td>
- <a href="index.php?wydarzenie={$ID}" >
- </a>
- </td>
- {/if}
- </tr><tr>
- {/if}
- {/section}
Oczywiście link umieszczony w znaczniku <a> może być inaczej skonstruowany. Wszystko zależy od architektury naszego serwisu i od tego jak ten link zamierzamy później wykorzystać.
Puste komórki na końcu miesiąca
Najtrudniejszy kawałek zadania za nami. Teraz wystarczy już tylko uzupełnić ostatni wiersz pustymi komórkami. Ile ma ich być? Wiemy którego dnia kończy się miesiąc, bo to sprawdziliśmy w skrypcie php. Jeśli miesiąc kończy się w poniedziałek. to zmienna $dane['koniec'] ma wartość 1 (2 dla wtorku, 3 dla środy itd). Oznacza to, że brakuje nam 6-$dane['koniec'] pustych komórek na końcu wiersza. Uruchamiamy więc ostatnią pętlę i zamykamy ostatni wiersz oraz tabelkę.
I już mamy pięknie działający kalendarz. Przykład można zobaczyć na stronie Polskiego Stowarzyszenia Cyklistów (jeszcze w budowie). Pozostaje jeszcze kwestia obsłużenia linku z kalendarza, ale to już wychodzi poza ramy tego artykułu.
Kilka uwag na koniec
Wszystkie dane, które były przesłane do skryptu smarty zapakowałam do jednej, dość rozbudowanej tablicy. Oczywiście można było je przesłać pojedynczo pod osobnymi zmiennymi i tez by działało. Zrobiłam tak z kilku powodów.
- Chciałam pokazać, że tak się da, i że odczyt danych z tablicy asocjacyjnej, która w dodatki może w sobie zawierać inne tablice jest możliwe i wcale nie takie trudne, szczegóły można znaleźć w polskiej dokumentacji
- Swoje programy piszę obiektowo i przekazywanie między obiektami tablic jest wygodniejsze niż przekazywanie kilkunastu osobnych zmiennych
- Często w swoich skryptach korzystam z obiektów, które są w podobny sposób obsługiwane i wygodnym wydaje mi się, jeśli kluczowe dane są w tych obiektach przetrzymywane pod zmienną o tej samej nazwie
.
Oczywiście jeśli chodzi o punkt drugi to można było do skryptu zamiast tablicy przekazać obiekt i go obsłużyć. Może kiedyś zacznę korzystać z takiej możliwości i o tym napiszę?
13 paź 2008 o 12:01
Witam,
mam problem. robie cos podobnego ale bez uzycia smartow.
moja tablica do wyswietlenie wyglada tak:
Array ( [frek] => Array ( [10-10-2008] => 0 [10-5-2008] => 1 [10-4-2008] => 0 ) )
problem mam z odpowiednim wysiwetleniem, tzn. chcialbym tak, ze jesli przy dacie [key] wartosc [value] jest rowna 0 to pole bylo koloru zielonego
natomiast jesli przy dacie jest 1 to pole powinno byc na czerwono.
nie wiem jak to poprawnie wyswietlic czy moglabys mi jakos pomoc. Oto moj kod:
…
margines = 0; // ile dni przed i za miesiącem
$r = ($_GET["r"])?number_format($_GET["r"],0):0; // różnica w miesiącach względem aktualnego
$znacznik = mktime(0, 0, 0, date(“m”)+$r, 1);
$iloscdni = date(“t”, $znacznik);
$miesiac = date(“m”, $znacznik);
$rok = date(“Y”, $znacznik);
$dni = array(“nd”,”pn”,”wt”,”śr”,”cz”,”pt”,”so”);
$miesiace = array(“Styczeń”, “Luty”, “Marzec”, “Kwiecień”, “Maj”, “Czerwiec”,
“Lipiec”, “Sierpień”, “Wrzesień”, “Październik”, “Listopad”, “Grudzień”);
echo “< | {$miesiace[$miesiac-1]} $rok | >“;
$od = date(“Y-m-d”, mktime(0, 0, 0, $miesiac, 1-$margines, $rok));
$do = date(“Y-m-d”, mktime(0, 0, 0, $miesiac, $iloscdni+$margines, $rok));
for($i=1-$margines; $i<=$iloscdni+$margines; $i++) {
$dzien = $dni[date("w", mktime(0, 0, 0, $miesiac, $i, $rok))];
$d = date(“d”, mktime(0, 0, 0, $miesiac, $i, $rok));
if ($i$iloscdni) $styl = “background:#eee;”;
else $styl=”;
$linia1 .= “$dzien”;
$linia2 .= “$d”;
if(array_search(‘0′,$uzyte['frek'])) $linia3 .=”YES”;
//if(array_key_exists(date(“n-j-Y”, mktime(0, 0, 0, $miesiac, $i, $rok)), $uzyte['frek']) and in_array(‘0′,$uzyte['frek'])) $linia3 .= “o”;
//if(array_key_exists(date(“n-j-Y”, mktime(0, 0, 0, $miesiac, $i, $rok)), $uzyte['frek']) and in_array(‘1′,$uzyte['frek'])) $linia3 .= “-”;
//if(array_key_exists(date(“n-j-Y”, mktime(0, 0, 0, $miesiac, $i, $rok)), $uzyte['frek']) and in_array(‘2′,$uzyte['frek'])) $linia3 .= “X”;
else $linia3 .= ‘x’;
//
}
echo “”;
echo “osoba$linia2″;
echo “”.$_POST['ewid'].”$linia3″;
echo “”;
14 paź 2008 o 12:48
A co właściwie robisz w tej linii:
if(array_search(’0′,$uzyte[’frek’])) $linia3 .=”YES”;
I co to za tablica $uzyte
14 paź 2008 o 22:16
tej linijki nie powinno jej tam byc, cos tylko sprawdzalem “if(array_search(’0′,$uzyte[’frek’])) $linia3 .=”YES”;”
Tablice $uzyte['frek'] tworze:
a) zapytanie do bazy danych
if(isset($_POST["pokaz"])) {
$pyt=mysql_query(“SELECT obec, data FROM frekwencja WHERE nr=’1234′”);}
while($dane = mysql_fetch_assoc($pyt)){
$daty = $dane['data'];
$uzyte['frek'][$daty] = $dane['obec'];
}
Chcialbym by to tak wygladalo
http://linologia.pl/img/help2.JPG
czyli $linia3 zmienia sie w zaleznosci od daty i wartoci.
$linia3 .= o
lub
$linia3 .= -
Jesli nadal cos jest nie tak, oczywiscie wyjasnie.
15 paź 2008 o 12:34
Czyli jaką ta tablica ma strukturę?
15 paź 2008 o 22:15
Array ( [frek] => Array ( [10-10-2008] => 0 [10-5-2008] => 1 [10-4-2008] => 0 ) )
16 paź 2008 o 12:51
Czy nie o to Ci chodzi?
if(isset($uzyte[’frek’][$data])){
//jest wydarzenie w bazie
if($uzyte[’frek’][$data] == 1){
//jest jedynka
}elseif($uzyte[’frek’][$data] == 0){
//jest zero
}
}else{
//nie ma wydarzenia w bazie
}
18 paź 2008 o 6:38
ok tylko jak to pożenić z ta petla ‘for’ ktora rysuje kalendarz
for($i=1-$margines; $i<=$iloscdni+$margines; $i++) {
$dzien = $dni[date("w", mktime(0, 0, 0, $miesiac, $i, $rok))];
$d = date(“d”, mktime(0, 0, 0, $miesiac, $i, $rok));
if ($i$iloscdni) $styl = “background:#eee;”;
else $styl=”;
$linia1 .= “$dzien”;
$linia2 .= “$d”;
if(isset($uzyte['frek'][$data])){
//jest wydarzenie w bazie
if($uzyte['frek'][$data] == 1){
$linia3 .= “b”;
}elseif($uzyte[’frek’][$data] == 0){
$linia3 .= “nb”;
}
}else{
$linia3 .= “x”;
}
} //koniec petli for
echo “”;
echo “Pracownik$linia2″;
echo “”.$_POST['ewid'].”$linia3″;
echo “”;
czy przed isset musze cos wykonac? skad wiadomosc ze isset?
pozdrawiam
19 paź 2008 o 11:26
No dobrze to pożeniłeś (tak na oko) czy przed isset coś wykonać? To zależy od ciebie. Na pewno w warunku zdecydujesz jak wyświetlić dany dzień. “Jeśli nie ma daty w bazie to w jakiś tam sposób, jeśli jest to w zależności od typu wybierzesz sposób wyświetlania.
Nie rozumiem pytania: “skad wiadomosc ze isset?”
19 paź 2008 o 12:38
to było glupie pytanie od isset – bo myslalem o czym innym. btw.
pozenilem to w taki sposob jak wyzej i dostalem to co do tej pory mialem.
Podaje zawartosc calego skryptu rusujacego:
<!– =0;i–) f.dzien.options[i]=null;
for (i=0;i –>
Nazwisko i Imię
Obecny
Urlop wypoczynkowy
Urlop bezpłatny
Zwolnienie lekarskie
Delegacja służbowa
Badnia okresowe
Data:
ktos
<? echo ”;
for ($i=2000;$i<=2090;$i++) {
echo “$i”;
}
echo ”;
for ($i=1;$i<=12;$i++) {
echo “$i”;
}
echo ”;
for ($i=1;$i<=date(“t”, mktime(0, 0, 0, date(‘m’), 1, date(‘Y’)));$i++) {
echo “$i”;
}
echo ”;
?>
<?
$data = “”.$_POST['miesiac'].’-’.$_POST['dzien'].’-’.$_POST['rok'].”";
//haslo i do bazy, polaczenie itd biore z innego skryptu
if ($baza) {
$wynik = mysql_select_db($nbaza,$baza);
if ($wynik and isset($_POST["zapisz"])) {
mysql_query (“INSERT INTO frekwencja (nr, obec, data) VALUES (‘$_POST[ewid]‘,’$_POST[obec]‘, ‘$data’)”);
echo ‘Dane zostały poprawnie dodane do bazy’;
}
if ($wynik and isset($_POST["kasuj"])) {
mysql_query(“DELETE FROM frekwencja WHERE nr LIKE ‘$_POST[ewid]‘ AND data LIKE ‘$data’”);
echo ‘Dane zostały usunięte’;
}
}
if(!isset($_POST["pokaz"])) {
$pyt=mysql_query(“SELECT obec, data FROM frekwencja WHERE nr=’96596′”);
//$zapyt=mysql_query(“SELECT data FROM frekwencja WHERE nr=’96596′”);
}
else {
$pyt=mysql_query(“SELECT obec, data FROM frekwencja WHERE nr=’$_POST[ewid]‘”);
//$zapyt=mysql_query(“SELECT data FROM frekwencja WHERE nr=’$_POST[ewid]‘”);
}
$uzyte = array();
$daty = array();
while($dane = mysql_fetch_assoc($pyt)){
$daty = $dane['data'];
$uzyte['frek'][$daty] = $dane['obec'];
}
echo ‘Poniższa tabela wskazuję dyspozycję pracownika w ciągu każdego miesiąca pracy.’;
$margines = 0; // ile dni przed i za miesiącem
$r = ($_GET["r"])?number_format($_GET["r"],0):0; // różnica w miesiącach względem aktualnego
$znacznik = mktime(0, 0, 0, date(“m”)+$r, 1);
$iloscdni = date(“t”, $znacznik);
$miesiac = date(“m”, $znacznik);
$rok = date(“Y”, $znacznik);
$dni = array(“nd”,”pn”,”wt”,”śr”,”cz”,”pt”,”so”);
$miesiace = array(“Styczeń”, “Luty”, “Marzec”, “Kwiecień”, “Maj”, “Czerwiec”,
“Lipiec”, “Sierpień”, “Wrzesień”, “Październik”, “Listopad”, “Grudzień”);
echo “< | {$miesiace[$miesiac-1]} $rok | >“;
$od = date(“Y-m-d”, mktime(0, 0, 0, $miesiac, 1-$margines, $rok));
$do = date(“Y-m-d”, mktime(0, 0, 0, $miesiac, $iloscdni+$margines, $rok));
for($i=1-$margines; $i<=$iloscdni+$margines; $i++) {
$dzien = $dni[date("w", mktime(0, 0, 0, $miesiac, $i, $rok))];
$d = date(“d”, mktime(0, 0, 0, $miesiac, $i, $rok));
if ($i$iloscdni) $styl = “background:#eee;”;
else $styl=”;
$linia1 .= “$dzien”;
$linia2 .= “$d”;
if(isset($uzyte['frek'][''.$miesiac.'-'.$i.'-'.$rok.''])) {//jest wydarzenie w bazie
if($uzyte['frek'][$daty] == 1){
$linia3 .= “b”;
}
elseif($uzyte['frek'][$daty] == 0){
$linia3 .= “nb”;
}
}else{
$linia3 .= “x”;}
}
echo “”;
echo “Pracownik$linia2″;
echo “”.$_POST['ewid'].”$linia3″;
echo “”;
Efekt mych dzialań:
http://linologia.pl/img/help3.JPG
a tak nie powinno rysowac, powinno byc tak:
http://linologia.pl/img/help2.JPG
Podałem Ci cała zawartość skryptu – moze sama go sprobujesz i cos wskurasz.
Pozdrawiam i dziekuje za dotychczasowa pomoc. Czekam na odp.
19 paź 2008 o 12:48
A nie powinno być tak:
$dddd = $miesiac.’-’.$i.’-’.$rok;
if(isset($uzyte[’frek’][$dddd])) {//jest wydarzenie w bazie
if($uzyte[’frek’][$dddd] == 1){
$linia3 .= “b”;
}
elseif($uzyte[’frek’][$dddd] == 0){
$linia3 .= “nb”;
}
}else{
$linia3 .= “x”;}
}
20 paź 2008 o 6:37
jest i działa. dziekuje BARDZO za cierpliwość i udzieloną pomoc.
20 paź 2008 o 10:13
Nie ma sprawy. Lubię czasem pogłówkować. Poza tym sama często otrzymuję pomoc w sieci, więc w pewnym sensie jestem zobowiązana
18 maj 2010 o 11:44
Kiedy coś nowego o smarty’m?
18 maj 2010 o 12:02
Prawdę mówiąc nie wiem, bo ostatnio nie używam. A jest jakieś zapotrzebowanie konkretne?