AUDYT 1 — SERWIS / UŻYTKOWNIK
Serwis
Mamy jedną platformę, której głównym zadaniem jest tworzenie i prowadzenie turniejów dartowych.
Dodatkowo istnieje Szybka Gra:
- nie wymaga logowania,
- nie tworzy uczestnictwa w turnieju,
- nie zapisuje historii na koncie,
- korzysta tylko z mechanizmu licznika meczu.
Użytkownik
Konto jest potrzebne do:
- utworzenia turnieju,
- bycia Adminem turnieju,
- bycia Coadminem,
- opcjonalnego powiązania uczestnika turnieju z profilem użytkownika.
Samo uczestnictwo w turnieju nie wymaga konta.
Uczestnik ≠ użytkownik
To jest chyba najważniejsze ustalenie:
USER │ │ opcjonalne user_id ↓ PARTICIPANT → TURNIEJ
Każdy zawodnik w turnieju jest participant.
participant.user_id może być:
- NULL — zawodnik lokalny,
- ID użytkownika — zawodnik powiązany z kontem.
W jednym turnieju oba rodzaje mogą występować jednocześnie. Nie mamy już „turnieju anonimowego” i „turnieju użytkowników”.
Powiązanie z kontem
Jeżeli uczestnik zostaje powiązany z user_id, system pobiera z konta aktualną nazwę/nick i klub i zapisuje je w participant.
Od tej chwili są to dane turniejowe. Późniejsza zmiana klubu/nazwy na profilu nie zmienia historii turnieju.
Admin może:
- zmienić user_id,
- podpiąć konto później,
- odpiąć konto,
- poprawić nazwę,
- poprawić klub.
Bez sztucznych blokad.
Statystyki użytkownika
Nie zapisujemy statystyk na koncie.
Profil tylko odczytuje:
user_id ↓ participants ↓ matches ↓ legs ↓ visits
Dlatego poprawienie błędnego user_id automatycznie powoduje, że cała historia tego uczestnictwa zaczyna być widoczna na właściwym profilu. Nie trzeba przenosić żadnych statystyk.
Uprawnienia
Mamy tylko:
1 Admin + 0…n Coadminów
Admin = twórca/właściciel turnieju i pełna konfiguracja.
Coadmini = bieżąca obsługa, przede wszystkim prowadzenie i korekta wyników, bez zmiany podstawowych założeń turnieju.
Nie tworzymy roli Redaktora.
Wynik audytu #1
Na razie widzę jedną rzecz do zaznaczenia do późniejszego sprawdzenia, ale nie jako błąd:
co dokładnie zawiera profil użytkownika — nazwa/nick, klub itd. Nie projektowaliśmy jeszcze tabeli profilu.
I zgodnie z umową teraz jej nie projektujemy. 😁
Poza tym na tym poziomie nie widzę sprzeczności ani zbędnego mechanizmu.
Jeśli Ty też nie widzisz dziury, stawiamy:
AUDYT 1: ✅
AUDYT 2 — TURNIEJ
Odtwarzam stan, który mamy po wszystkich wczorajszych cięciach.
Sam rekord tournaments miał być bardzo lekki. Na pewno mamy:
tournaments
id
admin_id
name
Do tego konfiguracja gry, którą wczoraj potwierdziliśmy:
start_score default 501
double_in default 0
double_out default 1
round_limit default 15
default_first_to
default_first_to jest tylko wartością startową dla generatora. Po utworzeniu meczu jego właściwe first_to siedzi już w matches i admin może je indywidualnie zmienić.
Czas turnieju
Tu świadomie zrezygnowaliśmy z ręcznego systemowego start_time/end_time.
Rzeczywisty start:
moment rozpoczęcia pierwszego meczu.
Rzeczywisty koniec:
zakończenie finału.
Datę/godzinę podawaną przez organizatora traktowaliśmy jako informację organizacyjną/publikacyjną, a nie element sterujący silnikiem.
Status
Nie zapisujemy osobnego status.
Czy turniej:
oczekuje / trwa / zakończony
można ustalić na podstawie jego przebiegu.
Coadmini
Nie upychamy ich w tournaments, bo może być ich wielu. Mamy relację w osobnej tabeli, roboczo:
tournament_coadmins
id
tournament_id
user_id
Czego świadomie NIE trzymamy w tournaments
Nie wrzucamy tutaj:
liczby grup, wielkości grup, liczby zawodników, rozmiaru drabinki, LL, BYE, liczby awansujących, rozstawienia itd.
Albo wynikają z utworzonej struktury, albo należą do konfiguracji konkretnej części turnieju.
I tutaj podczas audytu widzę jedną rzecz oznaczoną ⚠️, ale jej teraz nie rozwiązuję:
Wczoraj ustaliliśmy guaranteed_places i fill_bracket, ale jeszcze nie zamknęliśmy ostatecznie gdzie te dwie wartości mieszkają. Przez chwilę pojawiło się round_robin_settings, ale nie zatwierdziliśmy finalnej tabeli.
To zapisuję na listę braków, zamiast teraz kombinować.
Poza tym sam tournaments wygląda spójnie.
Twoja kolej na szukanie głupoty. 😄
jeżeli pamietamy, że jest to X01, to jest ok
Tak. To ważne doprecyzowanie: silnik gry na tym etapie projektujemy jako X01, nie jako uniwersalny silnik wszystkich odmian darta.
Czyli start_score nie oznacza „rodzaju gry”, tylko wartość startową X01:
301 / 501 / 701 / 1001 / ...
a double_in, double_out i round_limit są regułami tego X01.
Nie dodajemy więc żadnego game_type = x01, skoro system obecnie jest systemem X01. Gdyby kiedyś pojawił się Cricket czy coś podobnego, to będzie rozszerzenie architektury, a nie problem, który mamy rozwiązywać dzisiaj.
Czyli:
AUDYT 2 — TURNIEJ: ✅
default firs to zostaje ale ustawiamy też osobno dla różnych faz turneju
AUDYT 3 — participants
Tutaj rzeczywiście powinno pójść szybko.
Finalnie uczestnik jest rekordem zawodnika w konkretnym turnieju, a nie globalnym zawodnikiem serwisu.
Mamy:
participants
id
tournament_id
tournament_no
user_id NULL
name
club NULL
seeded
id — techniczny identyfikator rekordu.
tournament_id — uczestnik należy do konkretnego turnieju.
tournament_no — numer zawodnika w obrębie turnieju. Wczoraj zastanawialiśmy się, czy jest potrzebny i zostawiliśmy go, bo upraszcza późniejsze odwołania i pracę systemu.
user_id — opcjonalne powiązanie z kontem. Może być NULL, można je później podpiąć, zmienić albo odpiąć.
name — nazwa używana w tym turnieju. Przy podaniu user_id zaczytywana z aktualnego profilu, ale potem pozostaje własnością rekordu participant i admin może ją poprawić.
club — identycznie. Kopia aktualnej wartości w momencie dodawania/powiązania, żeby późniejsza zmiana klubu użytkownika nie zmieniała historii turnieju.
seeded — 0/1. Potrzebne podczas losowania grup. Po losowaniu zostawiamy jako informację historyczną, mimo że nie musimy jej publicznie pokazywać.
Nie ma tutaj:
group_id — bo relację mamy osobno,
position — wynika z wyników grupy,
points, legs_won, legs_lost — wyliczamy z meczów,
status, eliminated, qualified — wynika z przebiegu turnieju,
żadnych statystyk — wszystkie wynikają z matches → legs → visits.
Jedna rzecz do sprawdzenia w audycie
tournament_no.
Wczoraj świadomie go zostawiliśmy, ale teraz warto zastosować nasz test redundancji:
Czy ten numer ma własne znaczenie, czy jest tylko drugim ID uczestnika?
Jeżeli ma być widocznym numerem zawodnika w ramach turnieju (#1, #2, #173) i używać go będziemy w interfejsie, wyszukiwaniu czy przy ręcznym przypisywaniu — zostaje.
Jeżeli nigdy użytkownik go nie zobaczy i wszystko będzie działało na participants.id — wtedy jest kandydatem do wywalenia.
To jedyny element tej tabeli, przy którym podczas audytu postawiłbym ?.
Reszta wygląda mi bardzo czysto.
uczestnik, jeśli będzie oglądał statystyki, to tournament_id ma z poprzedniej sekcji - to przydałoby się tylko wtedy gdyby statystyki gracza czytały tylko i wyłącznie tą sekcje chyba czy nie tak?
Tak — jeśli mówisz o tournament_no, to dokładnie tak.
participant.id już jednoznacznie identyfikuje rekord zawodnika, a participant.tournament_id mówi, do którego turnieju ten rekord należy. Statystyki gracza i tak idą dalej przez jego participant.id → matches → legs → visits, więc dodatkowy numer zawodnika w obrębie turnieju niczego nam tam nie daje.
tournament_no miałby sens właściwie tylko wtedy, gdybyśmy chcieli użytkownikowi pokazywać jakiś stały numer typu „Zawodnik #37” albo używać go jako osobnego identyfikatora przy imporcie/obsłudze. Tego nie planujemy.
Więc po audycie wywaliłbym go.
Zostaje:
participants
id
tournament_id
user_id NULL
name
club NULL
seeded
Natomiast tournament_id zdecydowanie zostaje — to podstawowa relacja mówiąca, że ten konkretny rekord uczestnika należy do tego konkretnego turnieju. Bez niej musielibyśmy ustalać jego turniej okrężnie przez grupy albo mecze.
Czyli znaleźliśmy pierwszą dzisiejszą „głupotę”: tournament_no → kosz. 😄
więc tniemy
✂️ tournament_no wylatuje.
Finalnie:
participants
id
tournament_id
user_id NULL
name
club NULL
seeded
I wszystko, czego potrzebujemy później, wychodzi z id + relacji.
AUDYT 3 — PARTICIPANTS: ✅
Pierwsza redukcja audytu zaliczona. 😄
Następne: AUDYT 4 — grupy + przypisanie zawodników do grup. Tu będzie ciekawiej, bo zahaczymy też o pozostawione wczoraj guaranteed_places / fill_bracket.
czy bez slot_no będziemy potrafić przypisać zawodnikowi odpowiedni mecz w szachownicy, żeby wiedział, że nie może iść na piwo? a guaranted_places... dotyczy drabinki nie grupy moim zdaniem tym chyba powinniśmy sterować z poziomu zakładania turnieju i tak też mamy musimy dodać tylko fill bracket yes/no ale jako stała w bazie chyba nam nie potrzebna
Tak — i chyba właśnie znaleźliśmy bardzo czyste rozwiązanie obu tematów.
slot_no zostaje. Bez niego nadal dałoby się kombinować np. kolejnością id, ale byłoby to niepotrzebnie kruche. slot_no ma konkretne znaczenie sportowe: określa miejsce zawodnika w szachownicy i pozwala wygenerować stałą kolejność meczów grupowych. Dzięki temu system wie, że np. zawodnik ze slotu 1 gra mecze lokalne 1, 3, 5 i rzeczywiście może ocenić, kiedy wolno mu iść na piwo. 😄 To nie jest redundancja.
Natomiast z guaranteed_places i fill_bracket zgadzam się z Tobą prawie całkowicie: to są parametry generatora przejścia do drabinki, a nie cechy grup.
Co więcej, możemy ich w ogóle nie przechowywać na stałe.
Admin podczas zakładania turnieju ustala np.:
awansują miejsca 1–2
dopełniaj drabinkę: TAK
A kiedy grupy zostaną utworzone i znamy strukturę dalszej fazy, system od razu może przygotować sloty wejściowe drabinki:
slot X ← 1. miejsce Grupy 1
slot Y ← 1. miejsce Grupy 2
...
slot Z ← 2. miejsce Grupy 1
...
Jeżeli trzeba dopełniać, przygotowuje również odpowiednie wejścia z kolejnego miejsca. Jeśli fill_bracket = NIE, zostawia odpowiednie sloty puste jako przyszłe BYE.
Od tego momentu pierwotne:
guaranteed_places = 2
fill_bracket = yes
nie są już potrzebne, bo ich rezultat został zapisany w strukturze drabinki.
To dokładnie odpowiada naszej zasadzie:
konfiguracja służy do wygenerowania struktury; potem źródłem prawdy jest wygenerowana struktura.
Czyli audyt grup daje:
groups
id
tournament_id
group_no
group_participants
id
group_id
participant_id
slot_no
plus group_tiebreak, jeśli faktycznie wystąpi złoty leg.
A żadnego round_robin_settings nie potrzebujemy.
To cięcie mi się podoba. ✂️😄
Tak. match_no tniemy. Jeśli id ma być zwykłym czytelnym numerem 1, 2, 3..., nie potrzebujemy drugiego numeru robiącego dokładnie to samo.
A z walkowerem chyba właśnie wskazałeś lepszy kierunek: nie powinniśmy tworzyć osobnego sposobu wybierania zwycięzcy tylko dla walkoweru.
W normalnym meczu silnik robi mniej więcej:
wynik ostatniego lega
↓
ustalony zwycięzca meczu
↓
promuj zawodnika przez match_links
Przy walkowerze admin klika np.:
WALKOWER
system pyta:
Kto wygrywa walkowerem?
[ Kowalski ] [ Nowak ]
Po wyborze robimy dokładnie tę samą operację końcową, co po normalnym zakończeniu meczu:
Kowalski = zwycięzca
↓
match_links
↓
Kowalski trafia dalej
Różnica jest tylko taka, że nie powstały żadne legs ani visits, a mecz zostaje oznaczony:
is_walkover = 1
i jego wynik sportowy w legach pozostaje 0:0.
Czy potrzebujemy przez to winner_id w matches? Chyba niekoniecznie. Jeśli normalnie zwycięzca wynika z legów, to przy walkowerze możemy potraktować wybór admina jako wykonanie promocji przez silnik, a informację historyczną „kto wygrał walkowerem” odczytać z tego, który z dwóch uczestników został przepuszczony dalej.
Ale tu zapaliłbym małą lampkę audytową ⚠️: musimy później sprawdzić, czy rzeczywiście zawsze da się jednoznacznie odtworzyć zwycięzcę z match_links, szczególnie dla ostatniego meczu drabinki, który nie ma już dokąd promować zawodnika.
Jeśli nie — wtedy winner_id okaże się jednak potrzebne dla każdego meczu i będzie najczystszym rozwiązaniem. Nie rozstrzygałbym tego na siłę, dopóki nie przejdziemy za chwilę przez match_links.
Natomiast jedno już pewne:
match_no → ✂️ kosz.
Walkower nie potrzebuje żadnego numeru ani specjalnego wyniku. is_walkover wystarcza jako informacja „jak zakończono mecz”.
Przy okazji audytu match_links rozstrzygniemy, gdzie faktycznie mieszka zwycięzca meczu. To będzie właściwe miejsce na tę decyzję.
tak zróbmy - jesli tego tam nie ma, dodamy tutaj (prawdopodobnie to drugie)
Tak. Zostawiamy więc na liście audytowej:
⚠️ matches.winner_id — decyzja po audycie match_links.
Podejrzewam tak jak Ty, że finalnie go dodamy, bo finał od razu pokazuje problem z odczytywaniem zwycięzcy z dalszego przepływu. Ale nie uprzedzamy audytu. 😄
Na tę chwilę matches:
id
tournament_id
group_id NULL
bracket_id NULL
player1_id NULL
player2_id NULL
official_score_1
official_score_2
is_walkover
started_at
finished_at
first_to
match_no → ✂️
I możemy przejść do AUDYTU 6 — brackets + match_links + wejścia do drabinki.
Tu się okaże, czy nasza wczorajsza „piękna i uniwersalna” drabinka rzeczywiście przeżyje kontakt z audytem. 😂
AUDYT 6 — brackets + match_links + wejścia do drabinki
Najpierw sama drabinka. Po wszystkich cięciach została nam właściwie naga:
brackets
id
tournament_id
name
I to jest nadal sensowne. name pozwala odróżnić np. Drabinka główna, Losers, Lucky Losers, ale nie próbujemy kodować typu, rozmiaru ani statusu. To wszystko wynika z meczów i połączeń.
Teraz match_links:
match_links
id
from_match_id
result winner / loser
to_match_id
to_slot 1 / 2
To nadal wydaje mi się bardzo dobre. Jeden rekord mówi po prostu:
zwycięzca/przegrany z meczu X trafia do slotu 1 albo 2 w meczu Y.
Działa dla Single Elimination, Double Elimination, LL, meczu o III miejsce i wszelkich późniejszych przejść.
I teraz ważna rzecz z winner_id.
match_links nie przechowuje zwycięzcy konkretnego meczu. Ono tylko opisuje, co zrobić z przyszłym zwycięzcą lub przegranym.
Czyli przy finale:
Mecz 128
→ brak to_match_id
nie mamy żadnego miejsca, z którego później odczytamy, kto wygrał. Przy walkowerze tym bardziej.
Więc audyt właśnie rozstrzyga:
matches.winner_id jednak jest potrzebne.
I dobrze, bo wtedy silnik meczu ma jedno wspólne wyjście niezależnie od sposobu zakończenia:
winner_id = Kowalski
Normalny mecz → silnik ustala go z wyniku.
Walkower → admin wskazuje zwycięzcę.
Mecz zakończony administracyjnie → również wskazujemy zwycięzcę.
A match_links tylko bierze winner_id albo drugiego zawodnika jako loser i przesyła dalej.
Czyli do matches wraca:
winner_id NULL
i tym razem nie jest redundancją. Jest oficjalnym rozstrzygnięciem meczu.
Wejścia do pierwszej rundy
Mieliśmy roboczo coś w rodzaju:
bracket_entries
id
match_id
slot
group_id
group_place
participant_id
I tutaj zaczynam mieć podejrzenie, że możemy to zrobić jeszcze prościej.
Bo slot wejściowy może dostać zawodnika:
- bezpośrednio z participant_id,
- z konkretnego miejsca grupy.
Czyli jeden rekord mówi, skąd ma się wziąć zawodnik do slotu pierwszego meczu.
Na razie nie widzę tu zbędnego pola poza tym, że group_id/group_place i participant_id są alternatywne. Jeden zestaw jest wypełniony, drugi NULL.
To jest akceptowalne.
BYE
Nie potrzebuje żadnej tabeli ani specjalnego zawodnika. Brak bracket_entry dla danego slotu oznacza pusty slot. Jeśli drugi slot jest obsadzony, zawodnik przechodzi dalej automatycznie.
Czyli z audytu 6 mamy jedną konkretną zmianę:
matches.winner_id → DODAJEMY.
Reszta drabinki na razie przechodzi czysto.
Jeśli Ty nie widzisz dziury w bracket_entries, możemy ten audyt uznać za zaliczony.
ok - winner wchodzi do poprzedniego - tu mam wątpliwości, ale chyba nic nie zmieniamy jak nam jeden babol wyskoczy ostatecznie to nie katastrofa
Dokładnie. winner_id dopisujemy do matches, czyli poprzedniej sekcji, a match_links zostaje bez zmian.
I zgadzam się z podejściem: nie próbujmy teraz osiągnąć matematycznej doskonałości modelu. Po to robimy audyt przed implementacją, żeby wyłapać większość rzeczy. Jeśli przy budowie Fabrika wyjdzie jedno pole do dodania albo przesunięcia, to jest normalna korekta, a nie katastrofa.
Czyli:
AUDYT 6 — DRABINKI / MATCH_LINKS: ✅
↳ korekta: matches.winner_id dodane.
Następne są legs. A tam mamy aż pięć pól, więc jeśli zdołamy coś namieszać, będzie to już pewien talent. 😂
AUDYT 7 — legs
Po wczorajszym goleniu zostało nam:
legs
id
match_id
leg_no
starter_id
winner_id
I sprawdzamy:
id — oczywiście.
match_id — musi być, leg należy do konkretnego meczu.
leg_no — potrzebny do kolejności legów w meczu oraz historii/edycji.
starter_id — potrzebny. Tablet musi wiedzieć, kto rozpoczyna lega, a przy korektach musimy zachować faktycznego startera.
winner_id — potrzebny, bo nie każdy leg musi zakończyć się zejściem do zera. Mamy limit 15 rund i możliwość rozstrzygnięcia jedną lotką do środka.
Nie mamy:
- czasu rozpoczęcia/zakończenia lega — ✂️ wywaliliśmy,
- wyniku lega,
- liczby lotek,
- średnich,
- checkoutu,
- 180 itd. — wszystko wynika z visits, o ile odpowiednie dane istnieją.
I tutaj naprawdę... nie widzę czego ruszyć.
legs
id
match_id
leg_no
starter_id
winner_id
AUDYT 7: ✅
A teraz AUDYT 8 — visits, czyli miejsce, które wczoraj miało być trudne, a okazało się śmiesznie proste.
Zobaczymy, czy dzisiaj się zemści. 😂
AUDYT 8 — visits
Mamy:
visits
id
leg_id
player_id
visit_no
score
dart_1 NULL
dart_2 NULL
dart_3 NULL
id — techniczne ID wizyty.
leg_id — mówi, do którego lega należy kolejka.
player_id — kto rzucał. Potrzebne, bo w jednym legu mamy wizyty obu zawodników.
visit_no — numer kolejki/wizyty danego zawodnika. Potrzebny do zachowania właściwej historii, szybkiej edycji wcześniejszego rzutu i przeliczenia wszystkiego po korekcie.
score — wynik całej wizyty. To jest podstawowa informacja niezależnie od użytego licznika.
I opcjonalnie:
dart_1
dart_2
dart_3
Klasyczny licznik:
score = 100
dart_1 = NULL
dart_2 = NULL
dart_3 = NULL
Fanaberyjny:
score = 100
dart_1 = T20
dart_2 = S20
dart_3 = S20
Dzięki temu oba interfejsy korzystają z dokładnie tej samej tabeli, a reszta systemu może zawsze opierać się na score.
Tu tylko jedna rzecz wymaga audytowego spojrzenia: przy checkoutach zawodnik może wykorzystać 1 albo 2 lotki, więc:
dart_1 = D20
dart_2 = NULL
dart_3 = NULL
jest całkowicie prawidłowym rekordem. I to pasuje do naszego wcześniejszego pomysłu, żeby klasyczny licznik przy zakończeniu lega tylko zapytał popupem:
Ile lotek wykorzystano do zakończenia? 1 / 2 / 3
Ale uwaga — klasyczny licznik nadal nie zna konkretnych dart_1/2/3. Zna tylko liczbę wykorzystanych lotek.
I właśnie tutaj chyba znaleźliśmy jedną brakującą informację:
darts_used
Bez niej przy klasycznym liczniku wiemy, że ostatnia wizyta miała np. score = 40, ale nie wiemy, czy zawodnik skończył pierwszą, drugą czy trzecią lotką. A chcieliśmy tę statystykę zachować.
Czyli potencjalnie:
visits
id
leg_id
player_id
visit_no
score
darts_used default 3
dart_1 NULL
dart_2 NULL
dart_3 NULL
Przy zwykłej wizycie 3. Przy checkoutcie 1/2/3.
Ale nie dopisuję jeszcze. 😄 Pamiętam, że popup 1 lotka / 2 lotki był decyzją interfejsową, nad którą chwilę się wahaliśmy. Jeśli faktycznie chcemy tę statystykę — darts_used ma sens. Jeśli nie — możemy wyrzucić cały pomysł i visits przechodzi audyt bez zmian.
tak - to musimy dodać, mimo że mi się to nie podoba ale dla nich to z ajkiegoś powodu ważne
Tak — darts_used dodajemy.
Czyli finalnie:
visits
id
leg_id
player_id
visit_no
score
darts_used default 3
dart_1 NULL
dart_2 NULL
dart_3 NULL
Przy zwykłej kolejce zawsze 3. Przy kończącym checkoutcie klasyczny interfejs pyta popupem 1 / 2 / 3, a „fanaberyjny” licznik może to ustalić automatycznie z zapisanych lotek.
To nam pozwoli później policzyć dokładną liczbę lotek w legu i statystyki typu 15-dart leg / 18-dart leg / średnia 3-dartowa.
Czyli AUDYT 8 — VISITS: ✅, z jedną poprawką: darts_used.
I nadal bez zemsty — tylko jedno pole doszło. 😄
I zgoda — profil użytkownika domknijmy teraz. Nie ma sensu zostawiać jednej samotnej dziury przed przejściem do grafik/przepływów.
Ja zacząłbym naprawdę minimalistycznie. Joomla już zapewnia nam konto techniczne: user_id, login/mail, hasło itd. My potrzebujemy tylko profilu dartowego, np.:
user_profiles
id
user_id
display_name
club
I teraz trzeba sprawdzić, czy czegoś rzeczywiście potrzebujemy więcej.
display_name — zdecydowanie tak. To z niego przy podaniu ID pobieramy nazwę zawodnika do participant.name.
club — tak samo, trafia początkowo do participant.club.
Natomiast imię, nazwisko, płeć, data urodzenia, miejscowość, telefon itd. — na ten moment nie widzę żadnego zastosowania w silniku. Jeśli kiedyś będzie potrzebne do rankingu, kategorii junior/senior czy zapisów na konkretny turniej, wtedy dołożymy.
Jest za to jedna rzecz, którą warto rozstrzygnąć od razu: czy display_name musi być unikalne?
Moim zdaniem nie. To nie jest login. Dwóch Jan Kowalski albo dwóch Kiler może istnieć w serwisie. Jedyną jednoznaczną identyfikacją pozostaje user_id.
Czyli być może cały nasz brakujący „profil zawodnika” to naprawdę tylko:
user_id
display_name
club
a id tabeli profilowej może być wręcz zbędne, jeśli user_id będzie kluczem rekordu.
I wtedy możemy powiedzieć, że profil też mamy z głowy, a potem faktycznie lecimy po grafikach/kreatorze i sprawdzamy cały przepływ od strony użytkownika. 😄
no właśnie zauwazyłem że mamy user id i userprofile id nic tu więcej nie trzeba - jakby ktoś kiedyś chciał, to się doda. jak user będzie się chciał w jakiś sposób opisać - damy mu w joomli możliwość jakiegoś description
Dokładnie. I nawet user_profiles.id bym wyciął. ✂️
Skoro profil jest relacją 1:1 z użytkownikiem Joomla, to user_id może być jednocześnie jego identyfikatorem:
user_profiles
user_id
display_name
club
Nie istnieje sytuacja, w której jeden user_id potrzebuje dwóch profili dartowych, więc dodatkowe id niczego nam nie daje.
Cała reszta należy do Joomla: e-mail, dane konta, ewentualny opis użytkownika/avatar czy cokolwiek kiedyś zechcemy udostępnić. Nie kopiujemy tego do naszego systemu turniejowego.
A participants pobiera przy powiązaniu tylko:
display_name → participant.name
club → participant.club
i dalej żyje własnym życiem jako historyczny rekord turnieju.
Czyli brak z początku audytu:
⚠️ profil użytkownika → rozwiązany.
I chyba możemy oficjalnie powiedzieć, że audyt struktury danych mamy skończony.
Teraz grafiki. I tam już nie pytamy „jakie pole?”, tylko puszczamy człowieka przez cały system i patrzymy, czy gdzieś trafia na ścianę. 😄
