Tworzenie Aplikacji Internetowych
dr Wojciech M. Gańcza
6
Dostęp do danych
Dane możemy zapisywać lub
odczytywać poleceniami SQL
Niestety różne serwery baz danych
mają różne wymagania co do zapytań
Baza może ulegać zmianom
Nie będziemy ciągle modyfikowali
wszystkich miejsc w których
korzystamy z danych
Warto umieścić zapytania w jednym
miejscu.
Konstrukcja bazy
Bazę można przygotować w
dowolnym narzędziu do edycji danych
Można ją odzyskiwać z backupu
Ale co jeśli zmieni się struktura?
Lepiej przygotować program który
wypełni bazę danymi!
Najlepiej jeśli będzie to funkcja w tej
samej klasie do konstrukcja bazy
DatabaseConstructor
class DatabaseConstructor
{
var $database;
var $dbcreator;
var $creator;
var $config;
function DatabaseConstructor()
{
$this->database = new DatabaseAccess();
$this->dbcreator = new DBDatabaseCreator(&$this->database);
$this->config = new DatabaseConfiguration();
$this->creator = new DBTableCreator(&$this->database);
}
Na początek przygotujemy sobie
warsztat pracy
Przydatne informacje
Warto pamiętać stan bazy danych
Oczywiście najprościej – w samej
bazie danych
Dodajmy tablicę w której będzie
przechowywany numer wersji bazy
oraz opis wersji (informacje o tym
jakie dane znajdują się w bazie)
Tak więc funkcja konstruująca bazę
powinna zawierać:
Tworzymy bazę
$this->dbcreator->createDatabase(
$this->config->database_name);
$this->creator->startTable("Version");
$this->creator->addField("VerVersion",
"name");
$this->creator->addField("VerState",
"name");
$this->creator->createTable();
this->insert("insert into Version
(VerVersion, VerState) values ('UUK
0.1.0', 'Empty');");
Status bazy
Przed jakimikolwiek operacjami na
bazie – warto sprawdzić jej stan:
function state()
{
$data = $this->database->query(
"select VerState from Version;");
if ($data->eof())
return "Nonexisting";
else
{
$data = $data->get();
return $data[0];
}
}
Insert
Dodając rekordy warto wiedzieć jaki
identyfikator baza nadała nowemu
wpisowi
Można to sprawdzić zadając
odpowiednie zapytanie
Warto takie zapytanie dodać do
metody która wstawia dane do bazy
Napiszmy metodę ‘insert’:
Insert …
function insert($query)
{
$this->database->query($query);
$set = $this->database->query(
"select LAST_INSERT_ID();");
$set = $set->get();
return $set[0];
}
Funkcja wykonuje zapytanie i zwraca nadany
identyfikator
Dodawanie danych
Do dodawania danych warto
przygotować sobie funkcje
function addSubject($subject)
{
$this->insert("insert into
Subjects (SubjectsName)
values ('$subject');" );
}
Tworzymy tabelę i dane
Tabele i dane testowe powinny być
tworzone w jednej klasie
Warto przygotować różne metody do
Tworzenia pustej bazy – zawierającej
same struktury danych
Wypełniania danymi testowymi pustej
bazy (warto sprawdzać status )
Wypełniania danymi niezbędnymi do
pracy z pustą bazą
Wypełnianie danymi
W relacyjnej bazie danych dane mogą
od siebie zależeć
Jeśli chcemy umieścić referencje – to
musimy ją znać
Najprościej zapamiętać identyfikatory
w tablicach
I używać ich gdy są potrzebne przy
wywoływaniu funkcji dodających
elementy do kolejnych tabel
Wypełnianie danymi …
$subjects = array();
$subjects["angielski"] =
$this->addSubject('Język
angielski');
$subjects["biologia"] =
$this->addSubject('Biologia');
$subjects["chemia"] =
$this->addSubject('Chemia');
$subjects["fizyka"] =
$this->addSubject('Fizyka');
Wypełnianie danymi …
$books = array();
$books[0] = $this->addBook(
'Język polski. Między tekstami.
Część 1. Podręcznik. Początki.
Średniowiecze (echa
współczesne)',
$types["podrecznik"],
$publishers["gwo"] );
$this->addBookSubject($books[0],
$subjects["polski"]);
$this->addBookAuthor($books[0],
$authors["mackiewicz"]);
Wypełnianie danymi …
Metody wypełniające danym mogą
być dość długie (paręset linii)
Nie muszą być eleganckie – są pisane
na potrzeby testowania programu
A jeśli o testowaniu mowa – to warto
przetestować czy wszystkie dane
poprawnie dodały się do bazy
To ważne – bo inne testy będą
zakładały odpowiednią zawartość
bazy
Test danych testowych
function test_000100_TestingTestdatabase(&$tester)
{
$tester->message("Testing test database");
$constructor = new DatabaseConstructor();
$constructor->dropDatabase();
$constructor->createDatabase();
$constructor->fillWithTestData();
$tester->test("Testing count of records in Authors
table", countRowsOfTable(&$database, "Authors"),
80);
$tester->test("Testing count of records in Books
table", countRowsOfTable(&$database, "Books"), 88);
$tester->test("Testing count of records in BooksAuthors
relation table", countRowsOfTable(&$database,
"BooksAuthors"), 217);
I już wiemy czy wszystkie dane się dodały
Dostęp do danych
Dostęp do danych zorganizujemy w
postaci pojedynczej klasy
Metody będą zwracały źródła danych
W metodach będziemy się odwoływali
do bazy budując odpowiednie
zapytania
Centralizacja wszystkich zapytań
bardzo ułatwia późniejsze
modyfikacje struktury bazy (nie
mamy oporów przed zmianami)
Dostęp do danych …
Dostęp rozpoczniemy od odczytu listy
książek potrzebnych wybranemu
użytkownikowi.
Jako parametr – musimy podać
użytkownika – pozostałe parametry są
w bazie
Dostęp do danych nie jest prosty –
przy bazie zdefiniowanej tak jak to
pokazywaliśmy – musimy utworzyć
zapytanie ze złączeniami
Lista książek
function getBooksNeededByUser($userId)
{
return $this->database->query(
"select distinct
Books.BooksID, TypesName,
BchSubjects, BokTitle,
BchAuthors, PublishersName
from
UsersGrups inner join
BooksGrups on
UsersGrups.GrupsID=BooksGrups.GrupsID
inner join
Books on BooksGrups.BooksID=Books.BooksID
left outer join
BooksCache on Books.BooksID=BookId
left outer join
Publishers on PublishersID = PublisherID
left outer join
Types on TypesID = TypeID
where UsersID=$userId
order by BokTitle;");
}
Lista książek …
Wyświetlenie takiej listy jest
skomplikowane a nie mamy jeszcze
informacji czy książka jest w
posiadaniu użytkownika oraz czy jest
przez niego sprzedawana
Ale wszystko po kolei…
Najpierw test!
Test jest bardzo prosty – wynik
działania tej metody dla wybranego
parametry – porównujemy ze
wzorcem
Test listy książek
Możemy utworzyć nową funkcję
testową lub dodać nowy test do
istniejącego testu bazy testowej
$tester->testRecordset(
"Testing all books in database",
$dataAccess->getBooksNeededByUser(1),
341760974);
Możemy test wykonać także dla
innych użytkowników
Panel - Widok danych
Panel naszego programu będzie
zawierał listę książek
Mogą to być różne listy
Lista wszystkich książek (posiadanych i
potrzebnych)
Lista książek które trzeba kupić
Listę książek które można sprzedać
Panel powinien wyświetlać dane z
podanego źródła danych
Lista książek
Wystarcz podać obiektowi klasy
GridRenderer źródło danych i mamy
gotową stronę
$output = new Output();
$page = new PageFrame();
$panel = new BookPanel(
/* definicja tabeli */,
/* dane – na przykład userId */);
$page->setPanel(&$panel);
$page->render(&$output);
BookPanel
class BookPanel
{
function render(&$output)
{
$db = new DatabaseAccess();
$data = new DataSource(&$db);
$grid = new GridBuilder("
Tytuł:LB3|
Autor:CN3:60|
Wydawnictwo:CN1:160|
Przedmiot:LN1:250");
$grid->renderGrid(&$output,
$data->getBooksNeededByUser(1));
}
}
Operacje na źródłach danych
Dane ze źródła są mapowane do pola
tabeli w stosunku 1:1
A tymczasem często chcemy umieścić
kilka informacji w jednym polu tabeli
– wyróżniając poszczególne części
różnym krojem pisma
Czy można osiągnąć taki efekt bez
modyfikacji klasy tworzącej kod
tabeli?
Operacje na źródłach danych
Użyjemy „dekoratora” – klasy która
ma taki sam interface jak oryginalne
źródło danych i przeformatowuje
dane ze źródła podanego jako
parametr
Przygotujmy taki dekorator który
umieści wszystkie informacje na
temat ksiązki w jednej kolumnie
Pozostałe kolumny będą nam
potrzebne do określenia statusu
ksiązki
Składanie kolumn
class Concatenator
{
var $src;
function Concatenator(&$src)
{
$this->src = $src;
}
function eof()
{
return $src->eof();
}
Składanie kolumn …
function get()
{
$arr = $src->get();
$res = array();
$res[] = „<b>” . $arr[0] .
„</b><i>” . $arr[1] .
„</i><u>” . $arr[2] .
„</u>” . $arr[3];
for ($i=4; $i<count($arr); ++$i)
{
$res[] = $arr[i];
};
}
Składanie kolumn…
Dobrze jest przygotować sobie kasę
przeformatowująca dane w bardziej
elastycznej postaci
Przekażemy informacje o tym które
kolumny powinny być łączone by
utworzyć kolumnę wynikową
Jeśli określimy tak informacje o
wszystkich kolumnach – to możemy
je także przetasowywać
Formatowanie
Dobrze sobie przygotować klasy
bazowe do formatowania
Klasy takie mogą wstępnie
interpretować przekazane informacje
dotyczące kolumn
Wszystkie operacje będą miały taki
sam interfejs
Klasy formatujące będą wtedy bardzo
proste
Ale o tym – za tydzień.
W następnym odcinku
Co zrobić gdy zapytania są zbyt
skomplikowane.
Formatowanie danych ze źródła „w
locie”
Operacje na źródłach danych