300 likes | 431 Views
Tworzenie Aplikacji Internetowych. 7. dr Wojciech M. Gańcza. Co z formatowaniem danych. Format poszczególnych kolumn można wymusić przy użyciu znacznika <col …> Można tak określić wszystko co można określić stylem, ale co na przykład z formatowaniem daty albo liczb
E N D
Tworzenie Aplikacji Internetowych 7 dr Wojciech M. Gańcza
Co z formatowaniem danych • Format poszczególnych kolumn można wymusić przy użyciu znacznika <col …> • Można tak określić wszystko co można określić stylem, ale co na przykład z formatowaniem daty albo liczb • Tu musimy uciec się do odpowiedniego formatowania danych
Formatowanie danych • Umożliwienie formatowania na poziomie tworzenia kodu tabeli – bardzo skomplikowałoby ten kod • Jakiekolwiek zmiany czy rozszerzenia – wymagałyby zmian w dość skomplikowanej klasie • Formatowanie na poziomie tworzenia źródła danych – ma dokładnie te same wady • A jakby tak wstawić coś pomiędzy…
Formatowanie danych … • Co można włożyć pomiędzy źródło danych a generowanie tabelki w html-u? • To coś musi mieć interfejs źródła danych, i ze źródła danych czerpać dane • Zauważmy, że nie musimy czytać wszystkich danych – możemy formatować dane ‘w locie’
Formatowanie danych … • Formatowanie dotyczy zazwyczaj pojedynczych kolumn, ale warto przygotować możliwość formatowania kilku kolumn pojedynczym obiektem • Zapamiętanie kolumn do przeformatowania warto przygotować we wspólnej dla wielu formaterów klasie bazowej • Definicję kolumn warto podać w jakiś wygodny sposób – na przykład w stringu z wybranym separatorem
Formater class FieldFormater { var$fields; var$source; function FieldFormater(&$source, $fieldList) { $this->fields = split($fieldList, ”|”); $this->source = &$source; } function eof() { return $this->source->eof(); }
Formater … function get() { $rec = $this->source->get(); foreach($this->fieldsas$field) { $rec[$field] = $this->format( $rec[$field]) } return$ret; } // Ta funkcja powinna być nadpisana function format($field) { return$field; } }
Formatowanie kwoty • Przygotowanie przykładowego formatera jest teraz dość proste • Zbudujmy formater który liczbę zapisze jako kwotę, wymuszając: • Dwa miejsca po przecinku • W części całkowitej – odstęp co trzy pozycje • Pisząc symbol waluty za wartością • Można przygotować też uniwersalny formater, ale na razie nie jest nam potrzebny.
Formatowanie kwoty class MoneyFormater extends Formater { function MoneyFormater(&$source, $fieldList) { $this->Formater(&$source, $fieldList) } function format($field) ... }
Metoda formatująca function format($field) { $integral = (int)($field); $fraction = round(100*( $field - $integral)); $frac = ","; if ($fraction < 10) { $frac = ',0' . $fraction; } else { $frac = ',' . $fraction; }
Metoda formatująca … $str = (string)($integral); $len = strlen($str); $ret = ""; while ($len > 3) { $ret = ' ' . substr($str, $len-3, 3) . $ret; $len -= 3; } $ret = substr($str, 0, $len) . $ret; return $ret . $frac . " zł"; }
Inny przykład • Całą metryczkę ksiązki można umieścić w jednym polu • Informacje o książkach zawierają: • Tytuł ksiązki • Autora • Wydawnictwo • Rodzaj ksiązki (podręcznik/atlas/ ćwiczenia) • Warto tylko różnie je sformatować tak by poszczególne pola się odróżniały
Inny przykład … • Niestety – tym razem nasz formater będzie mieszał pola, nie możemy więc skorzystać z bazowego formatera pól • Ale i tu warto przygotować klasę bazową • Musimy tylko zdecydować się na dwa separatory: • Oddzielający pola wynikowego rekordsetu ( na przykład ‘|’) • Oddzielające łączone pola (na przykład ‘+’)
MultiFormater class MultiFieldFormater { var$fields; var$source; function MultiFieldFormater(&$source, $fieldList) { $this->fields = split($fieldList, ”|”); for ($i=0; $i<count($this->fields); ++$i) { $this->fields[$i] = split( $this->fields[$i], ”+”); } $this->source = &$source; }
MultiFormater … function get() { $rec = $this->source->get(); $ret = array(); for ($i=0; $i<count($this->fields); ++$i) { $ret[] = $this->format( &$rec, $this->fields[$i]) } return$ret; } // Ta funkcja powinna być nadpisana function format(&$record, $fieldArr) { ... } }
MultiFormater … • Teraz trzeba zdecydować jak będą łączone poszczególne pola • Propozycja: Tytuł; autor; Wydawca; Typ • Jeśli występuje mniej pól – nie są formaty nie są wykorzystane. • Nie przewidujemy łączenia większej liczby pól, ale można założyć że formaty będą stosowane cyklicznie
Metoda formatująca … function format(&$record, $fieldArr) { $begins = array("","<i>","<b>","<u>"); $ends = array("; ","; </i>",";</b>", ";</u>"); $ret = ""; for ($i=0; $i<count($fieldArr); ++$i) { $ret .= $begins[$i] . $record[$fildArr[$i]] . $end[$i]; } return$ret; }
Poprawki • Warto zadbać o to by zamiast stałych znaczników formatujących – użyć odwołań do styli • Średnik po ostatnim polu nie jest potrzebny – ale nie wiemy ile pól będzie użytych • Czy takie rozszerzenie funkcjonalności niesie a sobą komplikację kodu? • Może, ale nie musi :-)
Metoda formatująca … function format(&$record, $fieldArr) { $ret = $record[$fildArr[0]] ; for ($i=1; $i<count($fieldArr); ++$i) { $ret .= ‘; <span class=„field‘ . $i . ‘">‘ . $record[$fildArr[$i]] . "</span>"; } return$ret; } • Większa funkcjonalność – a kod znacznie prostszy!
Inne zastosowania • Tak przygotowany formater można także używać do zmiany kolejności pól lub do wybierania pól które są potrzebne. • Ale zapomnieliśmy o jednej ważnej rzeczy – o tym jak używać formaterów. • Może dlatego, że wydaje się to oczywiste :-)
Przykład użycia • Mamy metodę getBooksWithPrice w klase DataAccess, która zwraca dane w postaci: • Tytuł • Przedmiot • Autor • Wydawca • Typ ksiązki • Cena • Jak sformatować dane ?
Przykład użycia formaterów $dbacc = new DataAccess( … ); $data0 = $dbacc->getBooksWithPrice(); $data1 = new MoneyFormater(&$data0, "5"); $data2 = new MultiFormater(&$data1, "1|0+2+3+4|5"); $out = new Output(); $grid = new GridRenderer(); $grid->render(&$out, &$data2); • Prawda, że proste?
Coś bardziej złożonego • Formatery mogą być użyte także do bardziej złożonych operacji • Jeśli konstrukcja bazy wymaga bardzo skomplikowanych operacji – dotarcie do potrzebnych danych wymagać może bardzo skomplikowanych zapytań • A nie wszystkie bazy sobie z takimi zapytaniami radzą
Lista książek • W naszej bazie w miarę prosto można zapytać o listę potrzebnych książek, oraz o listę książek które użytkownik posiada • Potrzebna książka może być na kilku listach do których użytkownik jest zapisany • Bez podzapytań sobie nie poradzimy • Ale MySQL nie radzi sobie (dobrze) z podzapytaniami
Działania na danych • Zamiast budować połączenia pomiędzy skomplikowanymi zapytaniami możemy użyć prostych zapytań i złożyć wyniki w PHP • Potrzebujemy operacji na źródłach danych • Będzie to źródło danych oparte na wielu (na przekład dwóch) źródłach danych:
Suma danych • W przypadku książek potrzebujemy wszystkie takie które mamy oraz wszystkie takie które są nam potrzebne • Potrzebujemy więc wszystkie rekordy które są w pierwszym rekordsecie uzupełnione o takie które są w drugim ale nie ma ich w pierwszym • Nie ma sensu czytać całych rekordsetów i operować na danch w pamięci
Suma danych… • Aby operacja miała sens – oba źródłowe rekordsety muszą mieć taki sam format. • Jeśli oba rekodrsety są posortowane i posiadają pole które wyróznia dane sprawa mocno się upraszcza … class RecordsetMerge { ...
RecordsetMerge var$key; var $order; var$first; var$second; var$firstRecord; var$secondRecord; function RecordsetMerge( &$firstRecordset, &$secondRecordset, $keyField, $compareForOrderField) { $this->key = $keyField; $this->order = $compareForOrderField; $this->first = &$firstRecordset; $this->second = &$secondRecordset; $this->firstRecord = ($this->first->eof() ? null : $this->first->get(); ); $this->secondRecord = ($this->second->eof() ? null : $this->second->get(); ); }
RecordsetMerge … function eof() { return $this->firstRecord !=null&& $this->secondRecord != null; } function get() { if (!$this->firstRecord) return$this->getSecondAndSkipSecond(); elseif (!$this->secondRecord) return$this->getFirstAndSkipFirst(); else if ($this->firstRecord[$this->key] == $this->secondRecord[$this->key]) return$this->getFirstAndSkipFirstSecond(); else if (str_compare_polish( $this->firstRecord[$this->order], $this->secondRecord[$this->order]) < 0) return$this->getFirstAndSkipFirst(); else return$this->getSecondAndSkipSecond(); }
W następnym odcinku • Co można zrobić po stronie klienta • Co warto zrobić po stronie klienta • Dostęp do dokumentu poprzez JavaScript • Przykład czegoś nieco bardziej skomplikowanego