Projekt logiczny
Projektowanie tabel, kluczy, kluczy obcych, powiązań między tabelami, indeksów, etc. w oparciu o zdefiniowany diagram ERD
Słowniki danych
categories
id – not null, unique, autoinkrementacja, liczba całkowita nieujemna
root_id – domyślnie null, liczba całkowita
lft – domyślnie null, liczba całkowita
rgt – domyślnie null, liczba całkowita
level – domyślnie null, liczba całkowita
created_at – not null, data+czas
updated_at – not null, data+czas
category_data
owner_id – not null, liczba całkowita nieujemna, domyślnie 0
lang – not null, ciąg znaków mniejszy od 3, domyślnie ciąg pusty, utf8
revision_id – not null, liczba całkowita nieujemna, domyślnie 0
name – ciąg znaków mniejszy od 33, domyślnie null, utf8
created_at – not null, data+czas
updated_at – not null, data+czas
items
id – not null, unique, autoinkrementacja, liczba całkowita nieujemna
category_id – domyślnie null, liczba całkowita nieujemna
created_at – not null, data+czas
updated_at – not null, data+czas
item_data
owner_id – not null, liczba całkowita nieujemna, domyślnie 0
lang – not null, ciąg znaków mniejszy od 3, domyślnie ciąg pusty, utf8
revision_id – not null, liczba całkowita nieujemna, domyślnie 0
name – ciąg znaków mniejszy od 33, domyślnie null, utf8
created_at – not null, data+czas
updated_at – not null, data+czas
properties
id – not null, unique, autoinkrementacja, liczba całkowita nieujemna
property_group_id – domyślnie null, całkowita liczba nieujemna
created_at datetime – not null, data+czas
updated_at datetime – not null, data+czas
property_data
owner_id – not null, liczba całkowita nieujemna, domyślnie 0
lang – not null, ciąg znaków mniejszy od 3, domyślnie ciąg pusty, utf8
revision_id – not null, liczba całkowita nieujemna, domyślnie 0
name – ciąg znaków mniejszy od 33, domyślnie null, utf8
created_at – not null, data+czas
updated_at – not null, data+czas
property_groups
id – not null, unique, autoinkrementacja, liczba całkowita nieujemna
category_id – domyślnie null, całkowita liczba nieujemna
created_at – not null, data+czas
updated_at – not null, data+czas
property_group_data
owner_id – not null, liczba całkowita nieujemna, domyślnie 0
lang – not null, ciąg znaków mniejszy od 3, domyślnie ciąg pusty, utf8
revision_id – not null, liczba całkowita nieujemna, domyślnie 0
name – ciąg znaków mniejszy od 33, domyślnie null, utf8
created_at – not null, data+czas
updated_at – not null, data+czas
property_values
id – not null, unique, autoinkrementacja, liczba całkowita nieujemna
property_id – domyślnie null, całkowita liczba nieujemna
item_id – domyślnie null, całkowita liczba nieujemna
created_at – not null, data+czas
updated_at – not null, data+czas
property_value_data
owner_id – not null, liczba całkowita nieujemna, domyślnie 0
lang – not null, ciąg znaków mniejszy od 3, domyślnie ciąg pusty, utf8
revision_id – not null, liczba całkowita nieujemna, domyślnie 0
value – ciąg znaków mniejszy od 33, domyślnie null, utf8
created_at – not null, data+czas
updated_at – not null, data+czas
revisions
id – not null, unique, autoinkrementacja, liczba całkowita nieujemna
user_id – domyślnie null, liczba całkowita nieujemna
rev_no – domyślnie null, liczba całkowita nieujemna
is_head – domyślnie null, liczba całkowita
created_at – not null, data+czas
updated_at – not null, data+czas
revision_ratings
revision_id – not null, liczba całkowita nieujemna, domyślnie 0
user_id – not null, liczba całkowita nieujemna, domyślnie 0
rating – domyślnie null, liczba całkowita
created_at – not null, data+czas
updated_at – not null, data+czas
users
id – not null, unique, autoinkrementacja, liczba całkowita nieujemna
fb_id – domyślnie null, liczba całkowita nieujemna
username – ciąg znaków mniejszy od 33, domyślnie null, utf8
password_salt – ciąg znaków mniejszy od 33, domyślnie null, utf8
password_hash – ciąg znaków mniejszy od 33, domyślnie null, utf8
created_at – not null, data+czas
updated_at – not null, data+czas
Analiza zależności funkcyjnych i normalizacja tabel
Wszystkie tabeli w bazie danych spełniają warunku pierwszej postaci normalnej 1NF, gdyż każda z nich:
opisuje jeden obiekt,
wartości atrybutów są elementarne,
nie zawiera kolekcji,
posiada klucz główny,
posiada wiersze, których kolejność jest dowolna
Relacja jest w drugiej postaci normalnej wtedy i tylko wtedy gdy jest w pierwszej postaci normalnej i każda kolumna zależy funkcyjnie od całego klucza głównego (a nie np. od części klucza).
W pierwszej kolejności można sprawdzić, czy każda kolumna zależy od klucza prostego (całego klucza dla danej tabeli). Ten warunek spełniają tabele:
categories, items, properties, property_groups, property_values, revisions, users
Kolumny w tabelach posiadających klucze złożone (owner_id, lang, revision_id) zależą funkcyjnie od całego klucza głównego. Na przykład każda z kolumn w tabeli category_data zależy od id kategorii (owner_id), języka (lang) oraz wersji (revision_id). Do tabel spełniających ten warunek należą:
category_data, item_data, property_data, property_group_data, property_value_data
Tabela revision_ratings również jest w drugiej postaci normalnej.
Baza danych jest w trzeciej postaci normalnej 3NF, gdy jest w 2NF oraz wszystkie niekluczowe pola zależą tylko od atrybutów kluczowych (nie ma przechodnich zależności).
W szczególności dla tabel *_data, atrybut name lub value znajdujące się w każdej z nich, tyczy się danego typu (np. kategorii) i zależy tylko od klucza głównego. W tabeli categories atrybuty opisują położnie kategorii w jednym z drzew (podgrup) kategorii i zależą również tylko od klucza, jakim jest id kategorii. Tabele items, properies, property_groups, property_values mają podobną strukturę, posiadają atrybuty będące kluczami głównymi i obcymi. Są to pomocnicze tabele do tworzenia w bazie danych relacji wiele do wielu. Tabela revisions posiada niekluczowe atrybuty: rev_no, is_head oraz user_id (FK), które w kolejności oznaczają numer wersji, oznaczenie, czy dana wersja jest aktualna oraz id użytkownika, który dokonał zmian w stosunku do poprzedniej wersji. Wszystkie te kolumny zależą tylko od klucza głównego. Tabela users posiada dane charakterystyczne dla użytkownika o podanym id. W tabeli ratings atrybut rating zależy w całości od klucza głównego, jakim jest numer wersji i id użytkownika.
Stworzona baza danych jest w trzeciej postaci normalnej 3NF.
Projektowanie operacji na danych
Serwis WikiItems używa biblioteki Doctrine do komunikacji z bazą danych. Z tego powodu jako twórcy strony internetowej nie jesteśmy zmuszeni do tworzenia zapytań SQL, gdyż są one generowane z kodu PHP. W projekcie zastosowano mapowanie obiektowo-relacyjne. Przykładowe zapytania do bazy danych są przedstawione i opisane poniżej:
Kod PHP:
query = Doctrine_Query::create()
→from('Model_Item i')
→leftJoin('i.Category c')
→where('i.category_id = ? OR (c.root_id = ? AND i.category_id BETWEEN ? AND ?)', array(
category→getId(),
category→getRootId(),
category→getLeftId(),
category→getRightId()
));
Wygenerowane zapytanie:
SELECT i.id, i.category_id, i.created_at, i.updated_at, c.id, c.root_id, c.lft, c.rgt, c.level, c.created_at, c.updated_at FROM items i LEFT JOIN categories c ON i.category_id = c.id WHERE (i.category_id = ? OR (c.root_id = ? AND i.category_id BETWEEN ? AND ?))
Zapytanie odnajduje wszystkie produkty, które należą do wybranej kategorii, lub podkategorii tej kategorii.
Kod PHP:
query = Doctrine_Query::create()
→from('Model_PropertyGroup pg')
→leftJoin('pg.Category c')
→where('c.root_id = ?', root→getId())
→andWhere('c.id BETWEEN ? AND ?', array(
root→getLeftId(),
root→getRightId()
))
→andWhere('c.level < ? OR c.id = ?', array(
category→getLevel(),
category→getId()
));
Wygenerowane zapytanie:
SELECT p.id, p.category_id, p.created_at, p.updated_at, c.id, c.root_id, c.lft, c.rgt, c.level, c.created_at, c.updated_at FROM property_groups p LEFT JOIN categories c ON p.category_id = c.id WHERE (c.root_id = ? AND c.id BETWEEN ? AND ? AND (c.level < ? OR c.id = ?))
Odnajduje PropertyGroups, czyli grupy właściwości/atrybutów produktów należących do wybranej kategorii. Do grupy właściwości może należeć kilka konkretnych właściwości, którym można przypisywać wartości w tabeli property_value_data. Przykładowo grupą może być rozmiar, atrybuty należące do grupy to szerokość i wysokość, natomiast ich wartości mogą być wyrażone w calach.
Wersja Alpha: http://wikiitems.org