Listing 1: Fragment szablonu index-klienci.szb
35
Rysunek 23: Moduł zarządzania klientami, widok standardowy
Rysunek 24: Moduł zarządzania klientami: widok edycji
36
5. Implementacja
W rozdziale tym umieszczono kody zródłowe najważniejszych funkcji systemu. Przedstawiono
zapytania SQL kierowane do relacyjnej bazy danych oraz zapytania MDX, tworzÄ…ce tabele w
Reporting Services.
5.1 Logowanie użytkownika
Panel logowania przedstawiono na rysunku 25.
Rysunek 25: Panel logowania
Kod zródłowy umieszczony na listingach w tym podrozdziale przedstawia funkcję logowania.
Pod kodem opisano szczegółowo czynności, których celem jest stworzenie algorytmu logowania i
autentykacji użytkowników.
public function index()
{
$this->strona->setPath( 'Login', 'login/' );
//Sprawdzenie, czy użytkownik jest zalogowany
if( $this->auth->clearance() )
{
$this->tpl->setSzablon( array( 'main'=>'login-zalogowany.szb' ) );
}
else
{
$this->tpl->setSzablon( array( 'main'=>'login-panel.szb' ) );
//Sprawdzenie, czy dane logowania zostały wysłane
if( isset( $_POST['logon-send'] ) )
{
//Próba zalogowania
if( !$this->auth->login( $_POST['login'], $this->hash( $_POST['passwd'] ), $_POST['auto'] ) )
{
$this->tpl->setZmienna( array( 'LOGIN'=>$_POST['login'] ) );
}
else
{
//Udana próba kieruje na stronę główną
header( 'Location: '.PATH );
}
}
}
$this->generate();
}
Listing 2: Główna strona logowania
Fragment przedstawiony na listingu 2 tworzy główną stronę modułu logowania. Jeśli
użytkownik jest zalogowany, zostaje pobrany odpowiedni szablon z informacją o tym, jeśli nie,
zostaje pobrany szablon panelu logowania. Dane pobierane sÄ… z tablicy $_POST, przechowujÄ…cej
37
żądania typu post wysłane do serwera i przekazywane do metody login obiektu auth (jej kod
przedstawiono poniżej, na listingu 3.). Udane logowanie kieruje użytkownika na główną stronę.
public function login( $login, $passwd, $auto=null )
{
//zapytanie sql
$sql = "SELECT userID AS id FROM ".USER."
WHERE userLogin='$login' AND userPasswd='$passwd';";
$this->db->query( $sql );
//Sprawdzenie, czy zwróciło wynik
if( $this->db->numrows() > 0 )
{
$row = $this->db->fetch();
//Zapisanie informacji do sesji
$this->session->setSession( array( 'logged'=>1, 'userName'=>$login, 'userid'=>intval( $row['id'] ) ) );
//zapisanie do cookie, jeśli użytkownik sobie tego życzy
if( isset( $auto ) )
{
setcookie( $this->cookie, $login."||".$passwd, time()+31104000, "/" );
}
//Informacja o sukcesie
return true;
}
else
{
//Zapisanie informacji do sesji o Anonimowym użytkowniku
$this->session->setSession( array( 'logged'=>0, 'userName'=>'Anonim','userid'=>0 ) );
//Informacja o porażce
return false;
}
}
Listing 3: Metoda login
Na początku tworzone jest zapytanie SQL, które od razu porównuje login i hasło użytkownika
z danymi przechowywanymi w bazie danych. Zwrot wiersza z bazy oznacza sukces, logujÄ…cy siÄ™
użytkownik istnieje i podał poprawne hasło.
Następnie w sesji zapisywane są odpowiednie informacje, takie jak ID użytkownika i jego
login, które to informacje są pózniej używane w systemie bez potrzeby odwoływania się za każdym
razem do bazy danych.
Jeśli użytkownik sobie tego zażyczył podczas wypełniania formularza logowania, w cookie
zapisywane sÄ… informacje potrzebne do jego automatycznego zalogowania (funkcja Autologowania),
gdy sesja straci ważność.
5.2 Operacje na danych testowych, na przykładzie modułu produktów
Fragment panelu zawierajÄ…cego operacje na produktach przedstawiono na rysunku 26.
Rysunek 26: Operacje na produktach
38
Zaprezentowany w tym punkcie kod przedstawia operacje na produktach, jako przykład
operacji na danych testowych. Przedstawione fragmenty kodu zródłowego są odpowiednio opisane.
//Pobieranie listy produktów
$array = $this->objDao->getProductsList();
if( is_array( $array ) )
{
foreach( $array as $key => $row )
{
//Wypełnienie kolejnych wierszy danymi producentami
$this->tpl->setBlok( 'products_list', array( 'ID'=>$row['id'], 'NAME'=>$row['name'], 'CENA'=>$row['price'] ) );
//Pobranie listy kategorii
$catArray = $this->objDao->getCategoriesList();
if( is_array( $catArray ) )
{
foreach( $catArray as $catKey => $catRow )
{
//Pobranie listy podkategorii
$subCatArray = $this->objDao->getCategoriesList( $catRow['id'] );
if( is_array( $subCatArray ) )
{
//Uzupełnienie szablonu listami kategorii, i podkategorii
$this->tpl->setBlok( 'catgroup2', array( 'NAME1'=>$catRow['name'] ) );
foreach( $subCatArray as $subCatKey => $subCatRow )
{
$this->tpl->setBlok( 'cat_list2', array( 'NAME1'=>substr( $catRow['name'],0,5 ),
'NAME2'=>$subCatRow['name'],
'ID2'=>$subCatRow['id'],
'CAT_SELECT'=>( $row['catid'] == $subCatRow['id'] ) ? ' selected="selected" ' : '' ) )
;
}
}
}
}
//Pobranie listy producentów
$manArray = $this->objDao->getProducerList();
if( is_array( $manArray ) )
{
foreach( $manArray as $manKey => $manRow )
{
//uzupełnienie szablonu o listę producentów
$this->tpl->setBlok( 'producer_list2', array( 'MAN_NAME'=>$manRow['name'],
'MAN_ID'=>$manRow['id'],
'MAN_SELECT'=>( $row['manid'] == $manRow['id'] ) ? ' selected="selected" ' : '' ) );
}
}
}
}
Listing 4: Wyświetlanie listy producentów
Przedstawiony na listingu 4 fragment kodu odpowiada za tworzenie listy produktów wraz z
opcjami służącymi do działania na nich. Za pomocą pętli foreach, każdy z produktów otrzymuje listę
kategorii z domyślnie zaznaczoną tą, w której dany produkt się znajduje tak samo rozwiązana jest
lista producentów, gdzie każdy produkt otrzymuje listę z domyślnie wybranym producentem
przypisanym do danego produktu.
Formularz odpowiedzialny za dodawanie nowego produktu nie otrzymuje domyślnych
wyborów w tych listach.
39
if( isset( $_POST['save'] ) )
{
$this->objDao->saveProduct( $_POST['produkt'], $_POST['cat'], $_POST['producent'], $_POST['price'] );
}
elseif( isset( $_POST['edit'] ) )
{
$this->objDao->editProduct( $_POST['id'], $_POST['produkt'], $_POST['price'], $_POST['cat'], $_POST['producent'] );
}
elseif( isset( $_POST['del'] ) )
{
$this->objDao->delProduct( $_POST['id'] );
}
Listing 5: Wybór procedur sterowania danymi
Kod przedstawiony na listingu 5 odpowiada za wywoływanie odpowiednich procedur
sterowania danymi. Zależnie od przyciśniętego przycisku przesyła dane do odpowiedniej metody
obiektu operującej na danych dla tego modułu. Informacja o przyciśniętych przyciskach jest
przekazywana w tablicy $_POST, gdzie nazwa elementu jest nazwÄ… przycisku.
//Zapisywanie produktu do bazy danych
public function saveProduct( $name, $cat, $prod, $price )
{
if( !empty( $name ) && !empty( $price ) && !empty( $cat ) && !empty( $prod ) )
{
$sql = "INSERT INTO ".PROD."
(products_name, categories_id, manufacturers_id, products_price)
VALUES('$name','$cat','$prod','$price');";
$this->db->query( $sql );
return true;
}
else
{
return false;
}
}
Listing 6: Metoda saveProduct
Kod przedstawiony tutaj (listing 6), to metoda dodajÄ…ca produkt do bazy danych. Najpierw
sprawdzane są przekazane zmienne, czy przypadkiem nie są puste. Jeśli do metody nie przekazano
pustych danych, zostaje stworzone zapytanie SQL, a następnie wykonane.
public function editProduct( $id, $name, $price, $catid, $manid )
{
if( !empty( $id ) && !empty( $name ) && !empty( $price ) && !empty( $catid ) && !empty( $manid ) )
{
$sql = "UPDATE ".PROD."
SET products_name='$name', products_price='$price', categories_id='$catid', manufacturers_id='$manid'
WHERE products_id='$id';";
$this->db->query( $sql );
return true;
}
else
{
return false;
}
}
Listing 7: Metoda editProduct
Metoda przedstawiona na listingu 7 odpowiada za edycjÄ™ danych produktu. Podobnie, jak przy
dodawaniu, po sprawdzeniu danych, tworzone jest zapytanie, które zostaje następnie wykonane.
Podobnie do obu powyższych metod wygląda metoda odpowiedzialna za usuwanie produktu z bazy
danych. Różnicą jest tylko zapytanie, oraz fakt, że metoda wymaga tylko podania id. Zapytanie to
przedstawiono na listingu 8.
40
$sql = "DELETE FROM ".PROD."
WHERE products_id='$id';";
Listing 8: Zapytanie SQL usuwajÄ…ce produkt z bazy danych.
5.3 Generacja testowych danych zakupów
Analiza musi opierać się na danych. Dane poddane analizie w niniejszym systemie są
generowane sztucznie tak, by symulować miesięczne natężenie ruchu - zakupów, jego wzrosty i
spadki, w sklepie internetowym. Oczywiście, użytkownik powinien sam ustalić w których
miesiącach ruch powinien wzrastać, a w których maleć, jednak aby dane nie wyglądały zbyt
sztucznie, zastosowano pewien stopień losowości, dlatego też ustawienie współczynnika wzrostu na
1, wcale nie musi oznaczać wzrostu ilości transakcji o 10% w stosunku do średniej ilości transakcji z
poprzednich miesięcy, tylko mniej. W ten sposób można wygenerować na przykład zwiększony ruch
w okresie świąt Bożego Narodzenia, w okresie komunii, zakończenia i rozpoczęcia roku szkolnego.
Poniższy kod zródłowy zawiera funkcje i metody, które generują zestaw danych testowych.
public function index()
{
$this->tpl->setSzablon( array( 'main'=>'index-generator.szb' ) );
if( isset( $_POST['step2'] ) )
{
$this->strona->setPath('Krok 2', 'generator/');
$this->step2();
}
elseif( isset( $_POST['step3'] ) )
{
$this->strona->setPath( 'Krok 3', 'generator/' );
$this->step3();
}
elseif( isset( $_POST['generate'] ) )
{
$this->strona->setpath( 'Generacja', 'generator/' );
$this->step4();
}
elseif( isset( $_POST['clean'] ) )
{
$this->objDao->cleanOrders();
header( 'Location: ./' );
}
else
{
$this->strona->setPath( 'Krok 1', 'generator/' );
if( $this->objDao->ordersExists() )
{
$this->tpl->setSzablon( array( 'step'=>'generator-pelne.szb' ) );
}
else
{
$this->tpl->setSzablon( array( 'step'=>'generator-pierwszy.szb' ) );
}
$this->tpl->closeSzablon( 'STEPS' );
}
$this->generate();
}
Listing 9: Główna strona generacji danych
Metoda przedstawiona na listingu 9 steruje wyborem etapu generacji poprzez przyciskane
przez użytkownika przyciski (tablica $_POST, gdzie odpowiednie elementy odpowiadają
przyciskom). Dodatkowo wyświetla pierwszy ekran, którego funkcjonalność zależy od tego, czy
41
dane są już wygenerowane, czy nie. Jeśli są, zostaje wyświetlony szablon generator-pelne.szb ,
zawierający informację o tym, ze baza jest pełna, oraz możliwość wykasowania zawartości bazy
(przycisk clean ). Wtedy też, jak również w przypadku, gdy baza od początku nie zawiera danych,
zostaje wyświetlony szablon generator-pierwszy.szb , zawierający ekran pierwszego kroku
generacji, na którym użytkownik określa liczbę miesięcy, dla których zostaną wygenerowane dane
(liczone do miesiąca obecnego), oraz Współczynnik Zatłoczenia, będący procentem z iloczynu
produktów i klientów.
$customers = $this->objDao->getClientsNumber()
$products = $this->objDao->getProductsNumber()
$transactions = ceil( $customers['quant'] * $products['quant'] * $_POST['wt'] / 100 )
Listing 10: Ustalanie ilości transakcji
Na początek (listing 10) określana jest ilość produktów i klientów, a następnie wyznaczana
ilość transakcji. Ilość ta odnosi się tylko do pierwszego miesiąca, dla których dane mają zostać
wygenerowane, w kolejnych miesiącach, ilość ta będzie rosła, lub malała, częściowo zależna od
współczynników wzrostu ustawianych dla każdego z miesięcy.
$year =date('Y') - floor( $_POST['month'] / 12 );
$month = date( 'm' ) - ( $_POST['month'] % 12 );
if( $month <= 0 )
{
$year--;
$month += 12;
}
$startTime = strtotime( $year.'-'.$month.'-01' );
Listing 11: Określenie daty początkowej
Następnie (listing 11) określana jest data początkowa, od której generowana będzie zawartość
bazy danych. Z formularza kroku pierwszego generacji, pobierana jest wybrana ilość miesięcy.
Służy ona do wyliczenia daty początkowej, która następnie zamieniana jest do unikowego znacznika
czasu, naturalnej postaci daty w języku php.
42
$yearGen = $year;
$monthGen = $month;
for( $i=0; $i <= $_POST['month']; $i++ )
{
( $i > 0 ) ? $monthGen++ : $monthGen ;
if( $monthGen > 12 )
{
$yearGen++;
$monthGen -= 12;
}
if( $yearBlok != $yearGen )
{
$this->tpl->setBlok( 'rok', array( 'ROK'=>$yearGen ) );
$yearBlok = $yearGen;
}
$this->tpl->setBlok( 'months', array( ) );
if( $i == 0 )
{
for($j=1; $j < $month; $this->tpl->setBlok( 'not_month', array( ) ), $j++ );
}
$this->tpl->setBlok( 'month', array( 'ID'=>$i ) );
}
$this->tpl->setBlok( 'months', array( ) );
for($j=12; $j > $monthGen; $this->tpl->setBlok( 'not_month', array( ) ), $j-- );
$this->tpl->closeSzablon( 'STEPS' );
Listing 12: Przygotowanie siatki miesięcy i lat
Ostatnim etapem kroku drugiego, jest przygotowanie ekranu, na którym użytkownik będzie
mógł ustawić współczynnik wzrostu dla konkretnych miesięcy (listing 12). Kolejne elementy są
ułożone na siatce miesięcy i lat, aktywowana dla nich zawartość szablonu, zawierająca pole wyboru
współczynnika znajduje się w bloku month , natomiast not_month służy do aktywowania pustego
pola, pomagającego nadać siatce estetyczny wygląd. Przykładowo, jeśli generacja ma nastąpić od
miesiąca marca, przed polem wyboru współczynnika wzrostu dla marca, zostaną wstawione dwa
puste pola.
Krok trzeci określa ostateczną ilość transakcji w danym miesiącu.
$yearGen = date('Y',$_POST['startDate'])
$monthGen = date('m',$_POST['startDate'])
Listing 13: Określenie pierwszego miesiąca i roku
Najpierw (listing 13) pobierany jest uniksowy znacznik czasu, określający datę pierwszego
miesiÄ…ca.
Następnie, w pętli foreach, obejmującej kod z listingów 14 i 15, dla każdego miesiąca
obliczana jest ilość transakcji, zmieniająca się pod wpływem współczynnika transakcji i wartości
losowej. W pętli tej znajdują się również instrukcje pilnujące poprawności numerycznej zmiennych
określających rok i miesiąc.
$time = strtotime( $yearGen.'-'.$monthGen.'-'.date( 't', strtotime( $yearGen.'-'.$monthGen.'-01' ) ) ) + 86400;
Listing 14: Obliczenie uniksowego znacznika czasu dla pierwszego dnia następnego miesiąca
Dla każdego miesiąca określane są najpierw ramy czasowe, czyli znacznik czasowy
pierwszego dnia danego miesiąca i znacznik pierwszego dnia następnego miesiąca, przy czym
43
przekazywany dalej jest tylko ten ostatni. System dalej korzysta ze znacznika końcowego
poprzedniego miesiąca, lub ze znacznika określającego pierwszy miesiąc, który jest przesyłany
osobno. Parametr t funkcji date, określa ilość dni danego miesiąca, która to ilość służy potem jako
numer ostatniego dnia miesiÄ…ca. DodajÄ…c do tak wygenerowanego znacznika daty 86400,
otrzymujemy znacznik daty dla pierwszego dnia następnego miesiąca. Liczba ta, to długość dnia w
sekundach.
if( $monthGen == date( 'm', $_POST['startDate'] ) && $yearGen == date( 'Y', $_POST['startDate'] ) )
{
for($j=1; $j < $monthGen; $this->tpl->setBlok( 'not_month', array() ), $j++ );
$this->tpl->setBlok( 'month', array( 'TRANS'=>$trans, 'TIME'=>$time ) );
}
else
{
$tendence = ( rand( 79, 121 ) / 100 ) + ( $wx / 10 );
$monthsTrans = ceil( ( $trans / $i ) * $tendence );
$this->tpl->setBlok( 'month', array( 'TRANS'=>$monthsTrans, 'TIME'=>$time ) );
$trans += $monthsTrans;
}
Listing 15: Obliczanie ilości transakcji dla danego miesiąca
Dla pierwszego miesiąca ilość transakcji została określona ręcznie w poprzednich krokach.
Dlatego też tutaj zostaje przekazana bez zmian. Natomiast ilość dla pozostałych miesięcy jest już
zmieniana częściowo pod wpływem współczynnika wzrostu ustawianego dla każdego miesiąca w
kroku czwartym, a częściowo losowo, również średnia arytmetyczna transakcji z poprzednich
miesięcy wpływa na ilość transakcji w danym miesiącu.
Krok czwarty generacji, to już właściwa generacja.
$startDate = $_POST['startDate']
$clients = $this->objDao->getClientsID()
$products = $this->objDao->getProductsList()
$quantClients = count( $clients )
$quantProducts = count( $products )
Listing 16: Przygotowanie danych do generacji
Najpierw zostają pobrane danych, tak jak powyżej. Data początkowa jest przesyłana z
poprzedniego ekranu, z kroku trzeciego. Natomiast z bazy danych pobierana jest lista klientów do
tablicy clients i produktów do tablicy products. Ponieważ na tej ostatniej będą dokonywane operacje
przy okazji każdej transakcji będzie tworzona kopia tej tablicy o nazwie tmpProducts.
Po tym, w pętli foreach, dla każdego z miesięcy zostają wygenerowane transakcje.
$endDate = $_POST['time'][$key]
Listing 17: Pobranie unikowego znacznika czasu dla pierwszego dnia następnego miesiąca.
Najpierw zostaje pobrany znacznik czasu. Jak napisano powyżej, znacznik ten zamyka
przedział czasowy dla danego miesiąca. Otwiera go znacznik pobrany dla poprzedniego miesiąca,
lub początkowy, jeśli jest to pierwszy miesiąc, dla którego zostają wygenerowane transakcje, w
ilości określonej w poprzednim kroku.
$shoppingDate = rand($startDate,$endDate)
$monthsTransactions[$key][$shoppingDate]['client'] = $clients[rand( 0, ( $quantClients - 1 ) )]
Listing 18: Określenie dnia i godziny transakcji zawartej w danym miesiącu.
Najpierw z przedziału czasowego zostaje wylosowany znacznik czasu dla danej transakcji.
44
$transactionW = rand( 0, 100 )
Listing 19: Określenie ilości różnych produktów
Następnie zostaje określona ilość różnych produktów, elementów zamówienia, w danym
zamówieniu. Wylosowana liczba powyżej 90 oznacza trzy różne produkty, pomiędzy 90 a 60 dwa, a
w pozostałych przypadkach 1. W taki sam sposób określana jest ilość danego produktu w.
$tmpProductID = rand( 0, ( $quantProducts - ( 1 + $i2 ) ) );
$monthsTransactions[$key][$shoppingDate]['trans'][$i2]['productsNumber'] = $productQuant;
$monthsTransactions[$key][$shoppingDate]['trans'][$i2]['product'] = $tmpProducts[$tmpProductID];
$monthsTransactions[$key][$shoppingDate]['value'] += ( $productQuant *
$monthsTransactions[$key][$shoppingDate]['trans'][$i2]['product']['price'] );
Listing 20: Określanie zamawianych pozycji
Dla każdego elementu zamówienia, określony jest konkretny produkt, obliczana jest również
cena danej ilości danego produktu.
$lastElement = array_pop( $tmpProducts );
f( $monthsTransactions[$key][$shoppingDate]['trans'][$i2]['product']['id'] != $lastElement['id'] )
{
$tmpProducts[$tmpProductID] = $lastElement;
}
Listing 21: Eliminacja wybranych już do danego zamówienia produktów
Fragment z listingu 21 ma za zadanie wyeliminować z tymczasowej tablicy produktów ten,
który został już w danej transakcji wykorzystany. Jeśli transakcja obejmuje więcej niż jedną pozycję,
kolejne produkty będą wybierane z pomniejszonej już puli.
ksort( $monthsTransactions[$key] );
$startDate = $endDate;
Listing 22: Sortowanie tablicy transakcji
Po wygenerowaniu danych da danego miesiÄ…ca, tablica transakcji zostaje posortowana rosnÄ…co
według znacznika czasu. Dzięki temu dane zapisane do bazy danych są czytelniejsze.
foreach( $monthsTransactions as $key => $month )
{
foreach( $month as $shoppingDate => $shoppingContent )
{
$transactionsQuantity++;
$id = $this->objDao->addOrder( $shoppingDate, $shoppingContent['client'],$shoppingContent['value'] );
foreach( $shoppingContent['trans'] as $orderDetail )
{
$transDetQuantity++;
$prodDetQuant += $orderDetail['productsNumber'];
$this->objDao->addOrderDetail( $id, $orderDetail['product'], $orderDetail['productsNumber'] );
}
}
}
Listing 23: Zapisywanie transakcji do bazy danych
Na koniec w złożonej pętli foreach, transakcje są zapisywane do bazy danych poprzez
odpowiednie metody obiektu objDao. Dodanie nowego zamówienia zwraca id, które potrzebne jest
przy dodaniu pozycji tego zamówienia.
45
public function addOrder( $date, array $customer, $value )
{
$date = date( "Y-m-d H:i:s", $date );
$did = $this->getDeliveryID( $customer['id'] );
$sql = "INSERT INTO ".ORDER."
(date_purchased, customers_id, value, delivery_id)
VALUES('$date','".$customer['id']."','$value','$did');";
$this->db->query( $sql );
return $this->db->lastid();
}
Listing 24: Metoda addOrder
Przed dodaniem zamówienia do bazy danych, uniskowy znacznik czasu jest konwertowany na
datę o takim formacie, jaki jest przechowywany przez pole DateTime bazy danych MS SQL. Dzięki
temu datę tę można wykorzystać bez problemów w SQL Server Analysis Services. Następnie
tworzone jest zapytanie SQL wstawiajÄ…ce dane do bazy.
public function addOrderDetail( $orderId, array $product, $quantity )
{
$sql = "INSERT INTO ".ORD_DET."
(orders_id, products_id, products_name, products_price, final_price, products_quantity )
VALUES('$orderId','".$product['id']."','".$product['name']."','".$product['price']."','
".( $product['price'] * $quantity )."','$quantity');";
$this->db->query( $sql );
}
Listing 25: Metoda addOrderDetail
Podobnie do metody dodajÄ…cej transakcje, wyglÄ…da metoda addOrderDetail (listing 25)
dodajÄ…ca kolejne pozycje transakcji do bazy danych. Dane sÄ… przekazywane w parametrach,
następnie tworzone jest zapytanie SQL, którego wykonanie dodaje dane do bazy danych.
5.4 Konstrukcja kostki OLAP
Do stworzenia kostki OLAP użyto narzędzia SQL Server Bussiness Inteligence Development
Studio, firmy Microsoft. Diagram tabeli faktów i tabel wymiarów zaprezentowano w rozdziale 4.2
podając jednocześnie, co będzie zanalizowane.
Rysunek 27: Dimmension Usage: Połączenia pomiędzy wymiarami i miarami
Na rysunku 27 zaprezentowano siatkę grup miar (Measure Groups) i Wymiarów
(Dimmensions). W polach, na przecięciu się kolumn (miar) i wierszy (wymiarów), jest informacja,
w jaki sposób dany wymiar łączy się z daną grupą miar. Dla połączeń bezpośrednich, znajduje się
tam nazwa kolumny tabeli, dla połączeń pośrednich, znajduje się nazwa tabeli pośredniczącej.
Dzięki temu, można mieć pewność, ze miary będą zagregowane według wymiarów w odpowiedni
46
sposób, co umożliwi poprawną ich analizę. Poszczególne grupy miar to Fakty sprzedaży,
zawierająca miary wartości sprzedaży oraz ilości sprzedanych produktów, Ilość zamówień
zawierająca ze względu na jej charakter wyliczeniowy tylko ilość zamówień oraz Ilość produktów
zawierająca podobnie jak Ilość zamówień, tylko miarę określającą ilość różnych produktów.
W bazie danych OLAP zastosowano również szereg nazwanych kalkulacji, które poprawiają
czytelność analizowanych danych.
CASE
WHEN DATEPART(month, date_purchased) = 1 THEN 'Styczeń'
WHEN DATEPART(month, date_purchased) = 2 THEN 'Luty'
WHEN DATEPART(month, date_purchased) = 3 THEN 'Marzec'
WHEN DATEPART(month, date_purchased) = 4 THEN 'Kwiecień'
WHEN DATEPART(month, date_purchased) = 5 THEN 'Maj'
WHEN DATEPART(month, date_purchased) = 6 THEN 'Czerwiec'
WHEN DATEPART(month, date_purchased) = 7 THEN 'Lipiec'
WHEN DATEPART(month, date_purchased) = 8 THEN 'Sierpień'
WHEN DATEPART(month, date_purchased) = 9 THEN 'Wrzesień'
WHEN DATEPART(month, date_purchased) = 10 THEN 'Pazdziernik'
WHEN DATEPART(month, date_purchased) = 11 THEN 'Listopad'
WHEN DATEPART(month, date_purchased) = 12 THEN 'Grudzień'
ELSE 'Nieznany'
END
Listing 26: Nazwana kalkulacja NazwaMiesiac
Kod z listingu 26, to nazwana kalkulacja NazwaMiesiac . Zależnie od miesiąca przyjmuje
odpowiednią dla niego nazwę. W kostce tej znajdują się również inne kalkulacje, jednak ze względu
na to, że większość operuje na dacie, zostały one pominięte w tym rozdziale.
5.5 Raporty
SQL Server Reporting Services operuje na plikach .rdl, zbudowanych za pomocą języka xml.
Jednak do projektowania służy podobnie, jak przy wielowymiarowej bazie danych typu OLAP, SQL
Server Business Inteligence Development Studio.
SELECT NON EMPTY { [Measures].[Wartość sprzedaży], [Measures].[Ilość zamówień] }
ON COLUMNS, NON EMPTY { ([Zamówienia].[Rok kalendarzowy].[Miesiac].ALLMEMBERS
) } DIMENSION PROPERTIES MEMBER_CAPTION, MEMBER_UNIQUE_NAME ON ROWS FROM
[Artificial Shop] CELL PROPERTIES VALUE, BACK_COLOR, FORE_COLOR,
FORMATTED_VALUE, FORMAT_STRING, FONT_NAME, FONT_SIZE, FONT_FLAGS
Listing 27: zapytanie MDX określające dataset raportu czas-sprzedaz.rdl
Zapytanie MDX z listingu 27 pobiera z bazy danych OLAP informacje o Wartości sprzedaży
i Ilości zamówień , dla hierarchii Rok kalendarzowy wymiaru Zamówienia do DataSetu
danego raportu. Zapytanie to pochodzi z raportu czas-sprzedaz.rdl .
47
String
All
true
rok
Listing 28: Definicja parametru raportu
Powyższy fragment kodu xml, definiuje parametr raportu. Typ parametru to String, a domyślna
wartość All .
Equal
=iif(Parameters!rok.Value="All",Fields!Rok.Value,Parameters!rok.Value)
=Fields!Rok.Value
Listing 29: Definicja filtrów datasetu
Ten fragment odpowiada za filtrację danych w DataSecie ze względu na wartość parametru.
Jeśli parametr rok ma wartość All , co jest jego wartością domyślną, pole Rok przyjmuje
wartość standardową, natomiast, jeśli parametr ten przyjmie inną wartość, zostanie ona przypisana
polu Rok , ograniczając w ten sposób raport tylko dodanego roku.
48
5.6 System szablonów
Jednym z istotniejszych, ze względu na wykonanie aplikacji, elementów systemu jest system
szablonów. System ten, operując na szablonach elementów stron zapisanych w pliku tekstowym,
otrzymuje z aplikacji informację co i w jaki sposób w szablonie umieścić, oddzielając tym samym
warstwÄ™ logicznÄ… od warstwy prezentacji.
$this->tpl = Template::getInstance();
Listing 30: Utworzenie obiektu systemu szablonów.
By można było korzystać w danym module aplikacji z systemu szablonów, najpierw trzeba
utworzyć odpowiedni obiekt, tak jak na listingu 30.
public static function getInstance( $tpl = "" )
{
if( self::$instance == false )
{
self::$instance = new Template( $tpl );
}
return self::$instance;
}
Listing 31: Metoda getInstance
Dzięki metodzie getInstance przedstawionej na listingu 31, na jednym szablonie można
jednocześnie pracować w różnych miejscach systemu. Metoda zwraca referencję do już istniejącego
obiektu, a jeśli taki nie istnieje, tworzy go.
$this->tpl->setSzablon( array( 'main'=>'index-generator.szb' ) );
Listing 32: Definicja szablonu
PosiadajÄ…c utworzony obiekt szablonu, definiuje siÄ™ szablon, wykorzystany w systemie.
public function setSzablon( array $array )
{
foreach( $array as $handle => $file )
{
if( file_exists( $this->path.$file ) )
{
if( array_key_exists( $handle,$this->files ) )
{
$this->error( '
'.__METHOD__.': Podana zmienna plikowa już istnieje: '.$handle );
}
$this->files[$handle] = $file;
}
else
{
$this->error( '
'.__METHOD__.': Nie ma takiego pliku: '.$file );
}
}
}
Listing 33: Metoda setSzablon
Metoda setSzablon sprawdza najpierw, czy dany plik istnieje. Jeśli tak, zostaje zapisany do
tablicy przechowującej pliki szablonów, na których aktualnie operuje system. Kolejne szablony
można wywołać w dowolnym momencie, dzięki czemu można je w sobie zagnieżdżać. Po
zdefiniowaniu szablonu, treści przekazywane z systemu trafiają tylko do tego szablonu, dopóki nie
zostanie on zamknięty, lub nie zostanie zdefiniowany następny szablon.
49
$this->tpl->setZmienna( array( 'TRANSACTIONS'=>$transactions, 'STARTDATE'=>$startTime ) );
Listing 34: Przypisanie wartości zmiennym szablonowym
Aby uzupełnić szablon o treści, stosuje się trzy konstrukcje, jedną z nich jest metoda
setZmienna, zamieniająca zdefiniowane w szablonie zmienne na treści przekazane z systemu.
public function setZmienna( array $array )
{
$handle = $this->lastHandle();
$this->replace[$handle] = ( !is_array( $this->replace[$handle] ) ) ? array() : $this->replace[$handle];
foreach( $array as $variable => $value )
{
if( strtoupper( $variable ) == $variable )
{
if( !array_key_exists( $variable, $this->replace[$handle] ) )
{
$this->replace[$handle][$variable] = $value;
}
else
{
$this->error( '
'.__METHOD__.': Podana zmienna już istnieje: '.$variable );
}
}
else
{
$this->error( '
'.__METHOD__.': Proszę podać nazwę zmiennej używając dużych liter!'.$variable );
}
}
}
Listing 35: Metoda setZmienna
Do metody można przekazać wartości dla kilku zmiennych jednocześnie. Każda z nich jest
zapisywana do tablicy zmiennych i ich wartości aktualnego szablonu, chyba, że dana zmienna
została już ustawiona. System zwraca wtedy błąd, jako, że wartość jednej zmiennej może być
ustawiona tylko raz. Zdefiniowana w szablonie zmienna zawarta jest w nawiasie klamrowym w
formie: {NAZWA_ZMIENNEJ}.
$this->tpl->setBlok( 'month', array( 'ID'=>$i ) );
Listing 36: Tworzenie bloku
Drugą konstrukcją jest metoda setBlok, dzięki której można tworzyć różnego rodzaju listy, lub
zablokować wyświetlanie całego fragmentu szablonu.
public function setBlok( $handle_blok, array $array )
{
$handle = $this->lastHandle();
$i = count( $this->blocks[$handle] );
$this->blocks[$handle][$i]['name'] = $handle_blok;
$this->blocks[$handle][$i]['vars'] = $array;
}
Listing 37: Metoda setBlok
Ponieważ jeden blok może, a by utworzyć listę, powinien być wywoływany, wielokrotnie, nie
sprawdza się, czy dany blok został już zdefiniowany. Do tablicy bloków dla danego szablonu
dopisywane sÄ… po prostu kolejne wiersze bloku, oraz jego zmiennych. konstrukcji bloku w szablonie
przedstawiona jest na listingu 38.
Zawartość bloku {ZMIENNA_BLOKU}Listing 38: Konstrukcja bloku w szablonie
50
Bloki mogą być zagnieżdżane w sobie, jak również mogą zawierać zmienne.
$this->tpl->closeSzablon( 'STEPS' );
Listing 39: Zamknięcie szablonu i przypisanie go do zmiennej innego szablonu.
Trzecią konstrukcją jest closeSzablon. Zamyka ona aktualnie używany szablon, po czym
wygenerowaną treść przypisuje do zmiennej szablony wywołanego poprzednio.
public function closeSzablon( $variable )
{
$this->readFile();
$this->compileBlok();
$this->parseSzablon();
$this->cleanSzablon();
$this->unsetHandle();
$this->setZmienna( array( $variable => $this->file ) );
}
Listing 40: Metoda closeSzablon
Szablon jest najpierw wczytywany. Następnie są kompilowane bloki, po czym w zmienne
zdefiniowane w szablonie zostają wstawione przypisane wartości. Metoda cleanSzablon czyści
otrzymaną treść z niewykorzystanych konstrukcji szablonowych, a metoda unsetHandle usuwa
wszystkie przekazane dla tego szablonu treści. Na koniec zostaje on przypisany do zmiennej
poprzedniego szablonu.
$this->tpl->printTemplate();
Listing 41: Zakończenie pracy z szablonem
Szablon można zamknąć również metodą printTemplate, jedyną różnicą pomiędzy metodą
printTemplate a closeSzablon jest to, że printTemplate nie przypisuje zawartości szablonu do innego
szablonu, a wyświetla jego zawartość na ekranie.
//Szukamy wszystkich bloków w pliku
preg_match_all( '//', $this->file, $blocks );
if( count($blocks['1']) > 0 )
{
//WyciÄ…gamy kod bloku
foreach( $blocks['1'] as $key => $blockName )
{
if( preg_match( '/(.*)/s', $this->file, $blockFile ) )
{
$blockFiles[$blockName]['file'] = $blockFile;
//Znajdujemy wszystkie podbloki
preg_match_all( '//s', $blockFile['0'], $dependers );
foreach( $dependers['1'] as $childrenKey => $childrenName )
{
if( $childrenName == $blockName )
{
$supremeBlocks[$childrenName] = 1;
}
else
{
$childrenBlocks[$childrenName] = $blockName;
}
}
}
}
Listing 42: Wyszukiwanie bloków w szablonie i ustalanie ich hierarchii
51
Podczas kompilacji bloku, najpierw znajdowane sÄ… w nim wszystkie zdefiniowane bloki, oraz
określane są istniejące podbloki.
//odwracamy tablicę zależności bloków i podbloków, w tablicy supremeBlock zostają tylko bloki główne.
if( is_array( $childrenBlocks ) )
{
foreach( $childrenBlocks as $childrenBlock => $parentBlock )
{
$dependerBlock[$parentBlock][$childrenBlock] = 1;
$blockFiles[$parentBlock]['file']['1'] = str_replace( $blockFiles[$childrenBlock]['file']['0'], '{'.$childrenBlock.'}',
$blockFiles[$parentBlock]['file']['1'] );
unset( $supremeBlocks[$childrenBlock] );
}
}
Listing 43: Określenie bloków głównych
Następnie, określane jest, które bloki są blokami głównymi, a we wszystkich blokach
zawierających podbloki treść tych podbloków zamieniana jest na odpowiednią zmienną.
//uzupełniamy odpowiednie podbloki zmiennymi i zapisujemy ich kolejność
if( is_array( $this->blocks[$handle]) )
{
foreach( $this->blocks[$handle] as $row )
{
$blocksHandles[] = $row['name'];
$tmpString = $blockFiles[$row['name']]['file']['1'];
foreach( $row['vars'] as $variable => $value )
{
$tmpString = str_replace( '{'.$variable.'}', $value, $tmpString );
}
$filledBlock[$row['name']][] = $tmpString;
}
}
Listing 44: Uzupełnianie bloków danymi
Kolejną czynnością, jaka następuje, jest wstawienie kolejnych wierszy tabeli przechowującej
dane przesłane dla bloków danego szablonu w odpowiednie bloki.
//Å‚Ä…czymy bloki w jeden ciÄ…g
while( $blocksHandles )
{
$currentBlock = array_pop( $blocksHandles );
$tmpString = array_pop( $filledBlock[$currentBlock] );
if( is_array( $dependerBlock[$currentBlock] ) )
{
foreach( $dependerBlock[$currentBlock] as $children => $set )
{
$tmpString = str_replace( '{'.$children.'}', $composeBlock[$children], $tmpString );
unset( $composeBlock[$children] );
}
}
$composeBlock[$currentBlock] = $tmpString.$composeBlock[$currentBlock];
}
Listing 45: Aączenie podbloków z blokami, i łączenie ich w listy
Następnie bloki są łączone ze sobą wedle kolejności ich wywoływania przez system.
Zachowana jest przy tym ich kolejność oraz hierarchia z szablonu.
52
//Wpisanie bloków głównych do pliku
foreach( $supremeBlocks as $parent => $children )
{
if( !is_array( $childrenBlocks[$parent] ) )
{
$this->file = str_replace( $blockFiles[$parent]['file']['0'],
preg_replace( '/\{[A-Z0-9_]+\}/', '', $composeBlock[$parent] ),
$this->file );
}
}
Listing 46: Połączenie bloków z szablonem
Ostatnim krokiem kompilacji bloków jest wpisanie głównych bloków uzupełnionych o
odpowiednie treści w ich miejsce w szablonie.
//Podstawia zmienne w szablon
private function parseSzablon()
{
$handle = $this->lastHandle();
if( is_array( $this->replace[$handle] ) )
{
foreach( $this->replace[$handle] as $variable => $value )
{
$this->file = str_replace( '{'.$variable.'}', $value, $this->file );
}
}
}
Listing 47: Metoda parseSzablon
Gdy bloki zostały już skompilowane i naniesione na szablon, zostają uzupełnione zmienne
zdefiniowane w szablonie.
//Czyści szablon ze zmiennych
private function cleanSzablon()
{
$this->file = preg_replace( '/\{[A-Z0-9_]+\}/', '', $this->file );
}
Listing 48: Metoda cleanSzablon
Na koniec szablon zostaje oczyszczony z niewykorzystanych zmiennych. Metoda ta nie czyści
z konstrukcji blokowych, ponieważ te, są automatycznie czyszczone w metodzie kompilującej bloki.
Listing 49: Fragment szablonu header.szb
Przedstawiony na listingu 49 fragment szablonu header.szb odpowiada za wyświetlanie ścieżki
w aplikacji. Ścieżka ta, oprócz pierwszego elementu, jest różna na każdym kolejnym ekranie
aplikacji. Szablon wczytywany w listingu 32 służy jako ogólny szkielet modułu generacji, w nim
zagnieżdżane są szablony odpowiedzialne za wygląd każdego z kroków generacji. Umieszczane są
one w miejscu zmiennej {STEPS} (listing 39).
53
6. Testy
6.1 Test działania systemu
Cel testu: Sprawdzenie działania systemu na serwerze WWW, miejscu, które jest jego
środowiskiem pracy.
Oczekiwany rezultat: Prawidłowe wyświetlenie strony logowania.
Przebieg testu: Skopiowano folder z plikami systemu do folderu C:/www/, gdzie
przechowywane są treści www obsługiwane przez serwer apache. Sprawdzono działanie systemu i
nie stwierdzono problemów. Ekran logowania wyświetla się poprawnie, co przedstawiono na
rysunku 28.
Rysunek 28: Ekran logowania systemu
6.2 Test trybów logowania użytkownika
Cel testu: Poprawne zalogowanie do systemu z opcją zapamiętaj, jak i bez tej opcji.
Oczekiwany rezultat: Wyświetlenie strony powitalnej. W trybie bez opcji zapamiętania w
przeglądarce nie powinno być cookie nazwanego autologowanie. W trybie z zaznaczoną opcją,
cookie takie powinno wystąpić.
Przebieg testu: W pierwszej kolejności przetestowano logowanie bez zaznaczonej opcji
autologowania. W celu zalogowania skorzystano z użytkownika o loginie admin (rys.29).
Rysunek 29: Logowanie do systemu bez opcji Zapamiętania
54
Jak widać na powyższym obrazku, opcja Zapamiętaj nie została zaznaczona. Logowanie
przebiegło pomyślnie i wyświetlił się ekran powitalny przedstawiony na rysunku 30.
Rysunek 30: Ekran powitalny
W celu zweryfikowania trybu logowania otwarto spis ciasteczek zapisanych przez system w
przeglądarce. Jak widać na poniższym rysunku, zostało zapisane tylko ciasteczko sesji
przedstawione na rysunku 31.
Rysunek 31: Spis ciasteczek logowania bez opcji Zapamiętaj
By przeprowadzić kolejny krok testu, wylogowano się z systemu. W ekranie logowanie
wprowadzono te same dane, co poprzednim razem, zaznaczając tym razem opcję Zapamiętaj,
przedstawionÄ… na rysunku 32.
Rysunek 32: Logowanie z opcją Zapamiętaj
Logowanie zakończyło się sukcesem i wyświetlił się ekran powitalny (rys. 23). W celu
zweryfikowania trybu, wyświetlono spis plików cookie zapisanych przez system w przeglądarce
przedstawiony na rysunku 33.
Rysunek 33: Spis plików cookie z ciasteczkiem autologin
Przeprowadzony test spełnił oczekiwania i zakończył się sukcesem.
55
6.3 Test funkcji manipulacji danymi
Cel testu: Sprawdzenie panelu manipulacji danymi Produktów.
Oczekiwany rezultat: Pojawienie się nowego produktu w spisie produktów, po czym nastąpi
jego edycja, a następnie usunięcie.
Przebieg testu: Po zalogowaniu się do systemu wybrano z menu głównego pozycję produkty.
Na Ekranie pojawił się panel manipulacji danymi produktów przedstawiony na rysunku 34.
Rysunek 34: Panel manipulacji danych produktów
W pierwszym wierszu, służącym do dodawania nowych produktów do bazy danych
wprowadzono następujące dane:
- Nazwa: wpisano GeForce 2 MX
- Kategoria: wybrano Karty graficzne
- Producent: wybrano nVivdia
- Cena: wpisano 99
56
Rysunek 35: Dodawanie nowego produktu
Po wciśnięciu przycisku Zapisz! , nowy produkt pojawia się w bazie danych (rys.36).
Rysunek 36: Nowy produkt w bazie danych
Tym samym pojawia się również w spisie produktów (rys.37).
Rysunek 37: Nowy produkt w spisie treści
Jak widać, pierwszy etap testu przebiegł pomyślnie, kolejnym etapem, będzie zmiana
wprowadzonego produktu. Zmienione zostaną następujące pola:
- Nazwa: GeForce 2 MX 200
- Cena: 89.99
Rysunek 38: Edycja produktów
57
Po zmianie wymienionych pól na powyższe wartości, wciśnięto przycisk Edytuj . Dokonane
w formularzu zostały wprowadzone do bazy danych (rys.39).
Rysunek 39: Zmienione dane w bazie danych
Wprowadzone zmiany pojawiły się również w spisie produktów, w związku z czym, ten etap
testu również zakończył się powodzeniem. Kolejną czynnością w teście jest usunięcie produktu.
W tym celu naciśnięto przycisk Usuń . Na ekranie pokazał się komunikat przedstawiony na
rysunku 40.
Rysunek 40: Komunikat ostrzegający przed przypadkowym usunięciem.
Po potwierdzeniu, produkt zostaje usunięty z bazy danych i tym samym nie pojawi się więcej
w spisie produktów.
6.4 Test generacji danych do analizy
Cel testu: Sprawdzenie panelu generacji danych do analizy.
Oczekiwany rezultat: Wygenerowanie danych transakcyjnych służących do analizy.
Przebieg testu: By wygenerować dane do analizy, po zalogowaniu użytkownik wybiera opcję
generacja z menu głównego systemu. Ponieważ dane zostały już wygenerowane, otrzymano
komunikat przedstawiony na rysunku 39.
58
Rysunek 41: Komunikat o istniejÄ…cych danych transakcyjnych
Poprzez naciśnięcie przycisku Tak! usunięto wygenerowane uprzednio dane transakcyjne i
komunikat został zastąpiony przez ekran pierwszego kroku generacji danych do analizy (rys.42).
Wypełniono następujące pola:
- Ilość miesięcy: zmieniono domyślną wartość 24 na 26
- Współczynnik zatłoczenia: pozostawiono domyślną wartość domyślną 60
Rysunek 42: Krok pierwszy generacji danych transakcyjnych
Po przejściu do następnego ekranu (rys.43), ustawiono wartości współczynniki wzrostu w
następujący sposób:
- Styczeń: -1
- Luty: 0
- Marzec: 0
- Kwiecień: 1
- Maj: 2
- Czerwiec: -1
- Lipiec: 0
- Sierpień: 0
- Wrzesień: 1
59
- Pazdziernik: 0
- Listopad: 1
- Grudzień 2
Rysunek 43: Krok drugi generacji danych transakcyjnych
Na podstawie system wylicza dla każdego miesiąca ilość transakcji, które będzie generować
raport przedstawiony na rysunku 44.
Rysunek 44: Krok trzeci generacji danych transakcyjnych
Następnie został naciśnięty przycisk Generuj zakupy . O powodzeniu informuje nas
komunikat systemowy przedstawiony na rysunku 45.
60
Rysunek 45: Komunikat o wygenerowanych danych
6.5 Test raportowania
Cel testu: Przetestowanie opcji przeglądania raportów.
Oczekiwany rezultat: Poprawne wyświetlenie testu, zgodnego z wygenerowanymi w
rozdziale 6.4 danymi.
Przebieg testu: Po zalogowaniu się do systemu, wybrano z menu głównego opcję Raporty. Z
listy raportów wybrano raport: Roczne zestawienie sprzedaży (rys.46).
Rysunek 46: Wybór raportu
Następnie ustawiono parametry dla raportu. Spośród zboru zawierającego lata 2005, 2006,
2007 i wartość domyślną All wybrano rok 2006.
61
Rysunek 47: Ustawianie parametrów raportu
Po ustawieniu parametrów (rys.47), wciśnięto przycisk Zobacz raport:& , który otwiera
ekran z wygenerowanym raportem. Raport zawiera ilość zamówień miesięcznych, oraz ich wartość.
Wykres przedstawiony na rysunku 48 obrazuje zmieniającą się ilość zamówień.
62
Rysunek 48: Raport rocznego zestawienia sprzedaży
63
7. Podsumowanie
System stworzony w pracy dyplomowej stanowi realizacje pomostu pomiędzy językiem php,
który jest popularnym narzędziem przy tworzeniu internetowych aplikacji typu e-commerce, a
technologią OLAP, która jest jednym z wydajniejszych i wygodniejszych rozwiązań przeznaczonym
dla analizy biznesowej.
System zaprojektowano i wykonano w oparciu o model obiektowy. Dzięki modelowi
obiektowemu w oparciu o który zaprojektowano i wykonano system, jak również wykorzystaniu idei
systemu szablonów, i innych standardów programowania obiektowego, po odrzuceniu modułów
odpowiedzialnych za generację danych i ją wspomagających, jest możliwe zintegrowanie go z
istniejącym już sklepem internetowym.
Stworzono jasne i przejrzyste raporty obrazujące zachodzącą w systemie sprzedaż i
pomagające w podejmowaniu biznesowych decyzji. Dzięki czytelnie skonstruowanym tabelom,
prostym wykresom oraz parametryzacji, raporty te stanowią doskonałe zródło informacji o
procesach zachodzących w sprzedaży jak również zmieniających się trendach.
Zaprojektowano wygodny, ergonomiczny i przejrzysty interfejs użytkownika. Dzięki swojej
prostocie, podkreśleniu istotnych elementów, a także zastosowaniu ścieżki, użytkownik zawsze wie,
w którym miejscu systemu się znajduje i nie ma możliwości zagubienia.
Prezentowane przez system możliwości, są zaledwie ułamkiem tego, co można uzyskać
stosując obie technologie, jednak w sposób wyrazny je sygnalizują. Z powodu ograniczeń
czasowych zrezygnowano z bezpośredniego połączenia systemu z bazą danych typu OLAP. W
zamian za to skorzystano z usługi SQL Server Reporting Services, dostępnej razem z SQL Server
2005, który wymaga serwera IIS instalowanego na systemach Microsoft Windows.
64
8.Bibliografia
1. Gorawski Marcin Ocena efektywności architektur OLAP ( http://www.e-
informatyka.pl/article/show/430 )
2. Reed Jacobson Microsoft SQL Server 2005 Analysis Services krok po kroku
3. Dr inż. Damian Dudek wykłady na temat Projektowanie Baz Danych
4. Dr inż. Paweł Myszkowski wykłady na temat Analityczne Bazy Danych
5. Dokumentacja PHP ( http://www.php.net/manual/ )
6. Microsoft Corporation Books Online Microsoft SQL Server 2005
7. Andi Gutmans What s new in PHP 5? (http://devzone.zend.com/node/view/id/1714 )
8. osCommerce ( http://www.oscommerce.com )
9. JShop E-Commerce ( http://www.jshop.co.uk/ )
10. Eclipse ( http://www.eclipse.org/ )
11. Sybase Power Designer 11 Evaluation ( http://www.sybase.com/detail?id=1033602 )
65
Wyszukiwarka
Podobne podstrony:
przykladowa praca dyplomowa dla grupyBezpieczeństwo systemňw komputerowych praca dyplomowa7176525 Praca Dyplomowa Kontrukcje DrewnianeJak napisac prace dyplomowa (praca dyplomowa, praca magisterska) (2)Budowa systemu ekspertowego (Praca dyplomowa)Praca Dyplomowa Przegląd włókien naturalnychKreatywna praca dyplomowa Jak stworzyc fascynujacy tekstAudyt wewnętrzny i kontrola finansowa (praca dyplomowa)praca dyplomowaPraca Dyplomowa Sklep Internetowy(1)praca dyplomowa, magisterskie, licencjat,Zalecenia praca dyplomowa (1)cukrzyca praca dyplomowa Pawłapraca dyplomowa serwer internetowy na linuxiewięcej podobnych podstron