\section{Úpravy databázové vrstvy}
\label{sec:upravydatabazove}

\secpreview{Tato část přináší podrobnější návrhy a diskuzi k~úpravám databázové
  vrstvy, jak vyplývá z~oddílu~\ref{sec:databazova2} a~\ref{sec:navrhshrnuti}.}

V~části~\ref{sec:navrhshrnuti} bylo řečeno, že \ientryb{databázová
  vrstva}{databázová vrstva} netrpí většími nedostatky, avšak použití nějaké
obecné knihovny pro přístup k~databázi může vlastnosti této vrstvy zlepšit.


\subsection{Nová databázová knihovna}
\label{sec:nova}

Použitím nové databázové knihovny získá \tts{} především ve standardizaci
databázového kódu a využití některých nových vlastností databázových strojů
zcela bez námahy vývojářů.

Požadavkům systému budou vyhovovat obě v~současné době rozšířené knihovny, jež
jsou užívány v~řadě projektů --- \cl{ADOdb}~\cite{phpADODB} a
\cl{PEAR::DB}~\cite{phpPEARDB}.

Použitím takovéto knihovny bude dosaženo těchto přínosů:

\begin{enumerate}
  
\item \emph{Standardizace databázového kódu.}
  
  Jedná se o značně rozšířené knihovny, jež čerpají inspiraci z~řady dalších
  podobných projektů (například JDBC~\cite{jdbcspec}).

  Vývojáři je umí používat, jsou rozsáhle testovány a poskytují velmi dobré
  odstínění od konkrétního databázového stroje.
  
\item \emph{Odpadne nutnost udržovat databázový kód.}
  
  V~současnosti by podpora transakčního zpracování či generátorů v~nových
  verzích MySQL musela být komunitou vývojářů \tts{}u implementována, pokud
  bude zájem tyto služby využívat.  Použitím dostupné knihovny tuto vlastnost
  bude možné ihned využít.
  
\item \emph{Podpora více databází.}
  
\item \emph{Alternativní přístup k~datům.}
  
  Budou podporovány alternativní způsoby přístupu k~databázím (Embedded SQL a
  jiné).
  
\item \emph{Parametrizovatelné SQL dotazy.}
  
  Parametrizovatelné SQL dotazy znamenají automatickou konverzi datových typů a
  rychlejší vykonávání příkazů.

\item \emph{Ošetřování chyb pomocí výjimek.}

\end{enumerate}

Knihovna \cl{ADOdb} navíc oproti běžným databázovým abstrakcím nabízí:

\begin{itemize}
  
\item Snadnější přenositelnost mezi databázemi --- vlastní datové typy pro
  čísla, řetězce a časové typy, včetně jejich transparentní vnitřní konverze
  mezi PHP datovými typy na databázové typy.
  
\item Podporu specifických vlastností daných databázových strojů (což v~\tts{}u
  nebude využito --- tam je hlavní přenositelnost).
  
\item Množství alternativních způsobů, jak zpracovávat výsledky dotazů:

  \begin{enumerate}
  \item Své vlastní metody pro přístup k~datům, podobné těm
    z~JDBC~\cite{jdbcspec}.
  \item \cl{PEAR::DB} rozhraní
  \item Iterační rozhraní (\nientrya{iterator} design pattern,
    viz~\cite{iteratorwiki}), jež je možné využít například v~PHP verze 5.
  \end{enumerate}
  
\item Podporu relace (session) na úrovni databáze (opět není třeba používat
  v~\tts{}u).

\end{itemize}


\subsection[Speciální požadavky]{Speciální požadavky na databázovou vrstvu}
\label{sec:specialni}

V~části~\ref{sec:databazova2} jsou jmenovány dvě speciální vlastnosti
databázové vrstvy, implementované ve dvou metodách (převzato
z~části~\ref{sec:databazova2}):

\begin{enumerate}
  
\item \cl{Tutos\_DB::nextID()}, jež poskytuje nové volné ID, které může být
  použito pro nově vkládaný objekt.

\item \cl{Result::getObject()} --- tato funkce vybere z~databáze objekt na
  základě jeho jedinečného ID.

\end{enumerate}

Implementace těchto dvou funkčností je samozřejmě možná i s~použitím nové
knihovny.

\cl{Tutos\_DB::nextID()} je implementován pomocí generátorů.  Tam, kde nejsou
generátory implementovány (MySQL) je použito speciální tabulky s~jedním
sloupcem typu celé číslo a chování generátoru je emulováno v~kódu.

\cl{Result::getObject()} je implementován jako \nientrya{factory method} design
pattern, viz~\cite{fmwiki}.  Tento design pattern slouží k~tvorbě objektů
různého typu se stejným chováním, ale různou implementací tohoto chování
(tj.~objektů odvozených od stejné základové třídy), kdy typ vznikajícího
objektu je určen nějakými vstupními parametry.  Při tvorbě objektu na základě
jeho jednoznačného identifikátoru (vstupní informace) je postupováno takto:

\begin{enumerate}
  
\item Alternativní krok.  \emph{Načtení objektu z~vyrovnávací paměti.}  Během
  zpracování požadavku si \tts{} udržuje již jednou načtené objekty ve
  vyrovnávací paměti a programátor si může vyžádat získání objektu právě z~této
  paměti.  Pokud objekt není nalezen, je načten z~databáze, jak uvádí další
  postup.
  
\item \emph{Zjištění typu objektu.}  \tts{} si udržuje databázovou tabulku, kde
  mapuje identifikátory objektů na jejich typy.  Dotazem do této tabulky se
  získá typ objektu (representován jako celé číslo na úrovni databáze a jako
  konstanta na úrovni aplikace).
  
\item \emph{Vytvoření objektu příslušného typu.} (Vytvoření probíhá v~rozsáhlém
  příkazu \inlinecode{switch}.)
  
\item \emph{Volání metody \inlinecode{read()} na objektu.}  Tímto voláním jsou
  do objektu načtena data z~databáze.  Jde o přetíženou metodu zděděnou od
  třídy \cl{Tutos\_Base}, viz~\ref{sec:tutosbase}.

\end{enumerate}

Jak je vidět, za těmito operacemi se neskrývá žádné tajemství a obě tyto metody
lze bez potíží implementovat s~libovolnou knihovnou.

Zůstává pouze otázka, zda tyto metody ponechat ve třídách, v~nichž nyní jsou
(rozuměj: v~obdobných třídách nové knihovny), nebo je začlenit do jiného
jmenného prostoru.

\begin{itemize}
  
\item \cl{Tutos\_DB::nextID()} by měla být ze svého charakteru dostupná vždy a
  měla by probíhat mimo transakční kontext (aby byla rychlá,
  viz~\cite{firebird}) s~tím, že musí být zajištěna atomicita inkrementu čísla
  v~databázi.  Pokud databázový stroj podporuje generátory, je/může být toto
  zajištěno.
  
  Proto je její začlenění ve třídě reprezentující databázové spojení v~pořádku
  (jakmile je dostupné databázové spojení, mohu začít bez ohledu na cokoli
  generovat nové identifikátory).
  
\item \cl{Result::getObject()} je implementován jako dotaz do databáze.  Měl by
  probíhat v~rámci aktuálního transakčního kontextu, který je řízen aplikačním
  programátorem (pouze on ví, jaké jsou jeho úmysly s~načítaným objektem).
  
  Proč by mělo být nutné vytvářet objekt třídy \cl{Result}, když mým jediným
  cílem je volat jeho metodu \inlinecode{getObject()}?
  
  Nazíráno z~tohoto hlediska je začlenění do \cl{Result} nevhodné. I tato
  metoda by měla být dostupná v~okamžiku, kdy je dostupné spojení na databázi.

\end{itemize}

Závěrem lze shrnout: Buď tyto metody implementovat jako globální funkce, nebo
je vnořit do jmenného prostoru třídy představující spojení na
databázi\footnote{V~samotném \tts{}u je ve skutečnosti \cl{Result::getObject()}
  implementován tak, že volá globální funkci \inlinecode{getObject()}, které je
  předáno spojení na databázi, které si interně objekt třídy \cl{Result}
  udržuje.}.

Nejspíše je vhodné modifikovat kód databázové knihovny co možná nej\-méně,
takže bude žádoucí definovat tyto metody jako globální funkce.


\subsection{Dopady na systém}
\label{sec:dbdopady}

Začlenění nové knihovny do systému si vyžádá tyto změny v~implementaci:

\begin{itemize}
  
\item Přepsání všech databázových metod zděděných od \cl{Tutos\_Base},
  viz~\ref{sec:tutosbase}.
  
  To v~řadě případů bude znamenat pouze změnu v~deklaracích proměnných,
  v~některých (zejména u metod \inlinecode{Tutos\_Base::delete()},\\
  \inlinecode{Tutos\_Base::readHistory()}, či
  \inlinecode{Tutos\_Base::deleteHistory()})
  
  Avšak u metod jako \inlinecode{Tutos\_Base::read()} a
  \inlinecode{Tutos\_Base::save()}, jež jsou ve většině tříd přetěžovány, to
  bude znamenat pozměnit většinu kódu.  Jak je patrno
  z~obrázku~\ref{fig:businesslayer}, tříd k~přepsání je přes 30.
  
\item Změnu všech stránek, které k~databázové vrstvě přistupují přímo (systém
  má otevřenou architekturu, \ref{sec:zpusob}).  V~sekci~\ref{sec:prezentacni}
  je uvedeno, že typicky se k~jednomu objektu tvoří 4 stránky.
  
  Stránky zobrazující (1)~seznam objektů a (2)~vyhledání objektu v~databázi
  patří do této kategorie.  To je přibližně 60 stránek k~přepsání
  (2$\times$30).
  
\item Drobné úpravy v~inicializačním kódu.
  
\item Implementace dvou specifických funkčností \tts{}u za využití nové
  knihovny.

\end{itemize}

Je vidět, že by to znamenalo množství práce.  Avšak zejména při použití
knihovny \cl{ADOdb} a jejího iteračního rozhraní by se mohl kód značně
zpřehlednit a zjednodušit.  Pokud by \tts{} dále využil vlastnosti této
knihovny (definice časových typů) mohlo by se dostavit užitek i v~jiných
oblastech (přenositelnost dat mezi databázemi\footnote{\tts{} například
  podporuje funkci \emph{zrcadlení databáze}, kdy jsou data z~jedné databáze
  přenesena do druhé.  Lze tak řešit například zálohování.}).


\subsection{Shrnutí}
\label{sec:databazeshrnuti}

Databázová vrstva tak, jak je navržena, implementována a používána nyní,
neobsahuje žádnou zásadní chybu, spíše drobné nedostatky a nutnost udržovat kód
aktuální.

Její změna by přinesla zejména (1)~standardizaci de--facto (tj.~snazší použití
pro vývojáře zvyklé na jiné projekty) a (2)~odpadla by nutnost udržovat
databázové třídy.  Nepříjemnou skutečností by však byla nutnost přepsat značnou
část kódu.
