Skip to content

gmaxsoft/Symfony_Concert_Booking_System

Repository files navigation

Concert Booking System

Aplikacja do przeglądania koncertów, wybierania miejsc, składania rezerwacji z limitem czasu (hold 10 minut), symulacji płatności oraz pobierania biletu w PDF z kodem QR. Backend kładzie nacisk na spójność danych w transakcjach (blokady pesymistyczne w Doctrine) oraz asynchroniczne zwalnianie nieopłaconych rezerwacji przez Symfony Messenger.

Podgląd

Podgląd interfejsu — mapa miejsc jak w kinie i kroki rezerwacji

Struktura repozytorium

Katalog główny

Element Opis
backend/ API Symfony 7, API Platform, Doctrine ORM, Messenger, testy
frontend/ SPA Vue 3 (Vite) + Vuetify, proxy do API
docker-compose.yml MySQL 8 do lokalnego developmentu (port hosta domyślnie 3307)
.github/workflows/ CI na GitHub Actions (PHPUnit, Psalm, PHP CS Fixer, ESLint, build Vite)
preview.webp Zrzut ekranu interfejsu (mapa miejsc)
.env.docker.example Szablon zmiennych dla Dockera (skopiuj do .env w katalogu głównym)

Backend (backend/)

Ścieżka Zawartość
config/ Bundles, routing, packages/ (Doctrine, API Platform, Messenger, Nelmio CORS, itd.)
migrations/ Migracje schematu bazy
public/ Front kontrolera (index.php)
src/Command/ Komendy konsolowe (app:seed-demo)
src/Controller/ Akcje poza API Platform (np. PDF biletu, symulacja płatności)
src/Entity/ Encje Doctrine: Concert, Seat, Reservation
src/Enum/ SeatStatus, ReservationStatus
src/Message/ + src/MessageHandler/ Wiadomość ExpireReservationMessage i jej obsługa (Messenger)
src/Repository/ Repozytoria z metodami findAndLock (blokady pesymistyczne)
src/Service/ Logika domenowa m.in. BookingService (rezerwacja, płatność)
src/State/ Procesory API Platform (np. tworzenie rezerwacji)
tests/Integration/, tests/Unit/ Testy API (SQLite) i jednostkowe

Frontend (frontend/)

Ścieżka Zawartość
src/views/BookingView.vue Główny widok: wybór koncertu, mapa miejsc, kroki rezerwacji
src/api/client.js Klient HTTP (Axios)
src/router/ Vue Router
vite.config.js Proxy /api → backend w trybie dev

Blokady pesymistyczne

Pessimistic locking w tym projekcie to pesymistyczne blokady zapisu Doctrine: LockMode::PESSIMISTIC_WRITE. W MySQL (InnoDB) odpowiada to zwykle zapytaniu SELECT … FOR UPDATE — wiersz jest zablokowany do zakończenia bieżącej transakcji, więc inne sesje nie mogą w tym czasie zmodyfikować tego samego rekordu w sposób powodujący wyścig.

Dlaczego w transakcji: blokada ma sens tylko w obrębie aktywnej transakcji SQL. W kodzie operacje biznesowe są owinięte w EntityManager::wrapInTransaction(...), żeby odczyt z blokadą, walidacja i zapis (flush) działy jako jedna atomowa całość.

Gdzie w projekcie:

  1. Rezerwacja miejscaSeatRepository::findAndLock() ładuje wiersz Seat z PESSIMISTIC_WRITE. Dopiero po sprawdzeniu, że miejsce jest Available, tworzona jest rezerwacja Pending, a status miejsca zmieniany na Reserved. Dwie równoległe prośby o to samo miejsce są szeregowane przez bazę: druga transakcja czeka na pierwszą; po zwolnieniu blokady widzi już zaktualizowany stan i może zakończyć się błędem „miejsce niedostępne” zamiast nadpisać pierwszą rezerwację.

  2. Płatność — w BookingService::markPaid() rezerwacja jest ładowana przez EntityManager::find(..., LockMode::PESSIMISTIC_WRITE), aby nie kolidować z równoległą zmianą statusu (np. wygaśnięciem holdu).

  3. Wygaszenie rezerwacjiExpireReservationMessageHandler używa ReservationRepository::findAndLock(), żeby bezpiecznie sprawdzić status, ewentualnie zwolnić miejsce i ustawić rezerwację jako wygasłą bez wyścigu z inną operacją na tym samym wierszu.

To uzupełnia model „najpierw zablokuj wiersz, potem decyduj” zamiast polegania wyłącznie na optymistycznej wersji rekordu przy dużej konkurencji o te same miejsca.

Stack technologiczny

Backend

  • PHP 8.3+
  • Symfony 7.x
  • API Platform 4.x (REST, JSON i JSON-LD)
  • Doctrine ORM 3.x, MySQL 8
  • Symfony Messenger z transportem Doctrine (kolejka booking, opóźnione wiadomości DelayStamp)
  • Dompdf — generowanie PDF
  • endroid/qr-code — kod QR na bilecie
  • NelmioCorsBundle — CORS dla frontendu deweloperskiego

Frontend

  • Vue 3 (Composition API)
  • Vite
  • Vuetify 3
  • Vue Router 4
  • Axios (proxy /api → backend w trybie dev)
  • ESLint 9 (flat config) + eslint-plugin-vuenpm run lint w katalogu frontend/

Jakość kodu (backend)

  • PHPUnit + Symfony PHPUnit Bridge
  • Psalm
  • PHP CS Fixer (reguła @Symfony)
  • GitHub Actions — workflow .github/workflows/ci.yml uruchamia na push/PR do main/master joby backendu (Composer, PHPUnit, Psalm, CS Fixer dry-run) oraz frontendu (npm ci, ESLint, produkcyjny build Vite)

Przydatne skrypty Composer w backend/: composer psalm, composer cs-fix, composer test.

Testy (backend)

  • Integracja (API): tests/Integration/Api/ — Symfony WebTestCase, baza SQLite (var/test.db, tworzona przy testach), m.in. kolekcja koncertów, filtrowanie miejsc, pełny przepływ rezerwacja → płatność → PDF.
  • Jednostkowe: tests/Unit/ — m.in. BookingReservationProcessor z mockami.

Uruchomienie:

cd backend
composer test
# lub: php bin/phpunit

Do testów funkcjonalnych wymagany jest pakiet symfony/browser-kit (już w require-dev).

Infrastruktura

  • Docker: obraz mysql:8.0, baza concert_booking, użytkownik concert / hasło concert, port hosta 3306.

Uruchomienie projektu

Wymagania: PHP 8.3+, Composer, Node.js (npm), opcjonalnie Docker Desktop (MySQL) oraz Symfony CLI albo wbudowany serwer PHP.

1. Baza danych (MySQL)

Z katalogu głównego repozytorium:

W katalogu głównym projektu utwórz plik .env (obok docker-compose.yml, nie commituj — jest w .gitignore) na podstawie .env.docker.example i ustaw MYSQL_PASSWORD na to samo hasło, które podajesz w backend/.env w DATABASE_URL dla użytkownika concert. Domyślnie w compose jest concert, jeśli nie ustawisz .env.

Uwaga: hasło użytkownika concert w MySQL ustawia się tylko przy pierwszym starcie pustego wolumenu. Zmiana MYSQL_PASSWORD w .env nie zmienia hasła w już istniejącej bazie — wtedy albo docker compose down -v (kasuje dane w wolumenie) i ponownie up -d, albo ręcznie ALTER USER w MySQL.

docker compose up -d

Na Windows wymagany jest działający Docker Desktop (ikona w zasobniku = „Running”). Błąd w stylu dockerDesktopLinuxEngine: The system cannot find the file specified oznacza, że silnik Docker nie działa — uruchom Docker Desktop i spróbuj ponownie. Bez Dockera możesz zainstalować MySQL lokalnie i ustawić DATABASE_URL w backend/.env.

Domyślne mapowanie w docker-compose.yml to port hosta 3307 (wewnątrz kontenera nadal 3306), żeby nie kolidować z innym MySQL/MariaDB już nasłuchującym na 3306. W backend/.env ustaw DATABASE_URL z 127.0.0.1:3307 (i hasłem zgodnym z MYSQL_PASSWORD w compose, domyślnie concert).

Jeśli wolisz port 3306 dla Dockera, zatrzymaj lokalny serwis MySQL albo zmień mapowanie portów w docker-compose.yml i port w DATABASE_URL tak, aby się zgadzały.

Przy własnej instancji MySQL (bez Dockera) dostosuj DATABASE_URL w backend/.env lub backend/.env.local.

2. Backend

cd backend
composer install

Jeśli Composer zgłasza konflikt z rozszerzeniem redis w środowisku lokalnym, możesz tymczasowo użyć:

composer install --ignore-platform-req=ext-redis

Migracje i przykładowe dane (jeden koncert + siatka miejsc):

php bin/console doctrine:migrations:migrate --no-interaction
php bin/console app:seed-demo
# po zmianie siatki miejsc: php bin/console app:seed-demo --reset

Uruchom serwer HTTP (wybierz jedną z opcji):

# Symfony CLI (jeśli zainstalowane)
symfony server:start

# lub wbudowany serwer PHP (port 8000)
php -S 127.0.0.1:8000 -t public

API (dokumentacja): http://127.0.0.1:8000/api (ścieżka może się różnić w zależności od narzędzia).

3. Worker Messengera (wygaśnięcie rezerwacji)

Bez działającego konsumenta kolejki rezerwacje pending nie zostaną automatycznie zwolnione po 10 minutach. W osobnym terminalu:

cd backend
php bin/console messenger:consume async -vv

4. Frontend

cd frontend
npm install
npm run lint
npm run dev

Vite nasłuchuje zwykle na http://127.0.0.1:5173 i proxy przekazuje /api na backend. Domyślny cel proxy to http://127.0.0.1:8000 (plik frontend/.env.development, zmienna API_PROXY_TARGET). Najpierw uruchom backend (krok 2), potem npm run dev.

Błąd 502 na /api/... w przeglądarce (port 5173)

Oznacza to zwykle, że Vite nie może połączyć się z serwerem API (proxy zwraca Bad Gateway). Sprawdź: czy Symfony/PHP nasłuchuje na tym samym hoście i porcie co API_PROXY_TARGET, oraz czy w konsoli Vite nie ma komunikatu o błędzie proxy. Po uruchomieniu backendu odśwież stronę.

5. Typowy przepływ w UI

  1. Wybór koncertu i miejsca (mapa siedzeń).
  2. Podanie adresu e-mail i utworzenie rezerwacji (hold 10 min).
  3. Podsumowanie: przycisk symulacji płatności, potem otwarcie biletu PDF.

Uwaga: APP_SECRET w backend/.env powinien być ustawiony na unikalną wartość przed wdrożeniem produkcyjnym; w repozytorium nie commituj plików z sekretami produkcyjnymi (/.env.local).

About

Symfony Concert Booking System | Symfony 7 + Vue 3 + PHP 8 + Doctrine ORM + Vuetify 3

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors