Tworzenie endpointów jest zadaniem powtarzalnym. Trzeba stworzyć odpowiednią encję, zaktualizować bazę danych oraz napisać nowe kontrolery, które pozwolą pobrać dane, stworzyć nowe lub wyedytować istniejące. Jednak dzięki API Platform jesteśmy w stanie część tych rzeczy zautomatyzować i zapomnieć o nich. Jak? Zapraszam do czytania.
Encje w Symfony
Zanim jednak automatycznie stworzymy nasze API musimy posiadać encje. W Symfony encjami nazywamy klasy, które reprezentują tabele z bazy danych. W ten sposób zamiast operować bezpośrednio na bazie danych, na kolumnach i relacjach mamy warstwę abstrakcji, która ukrywa bazę dając nam w zamian zwykłe programowanie obiektowe. Tym, żeby zmapować dane pomiędzy tymi dwoma elementami zajmują się biblioteki ORM - w naszym przypadku Doctrine.
Jak już wspomniałem w poprzednim poście tematem przewodnim tego i następnych wpisów będzie API do aplikacji do tworzenia i rozwiązywania quizów. Na te potrzeby stworzyłem 4 encje:
- User
- Quiz
- Question
- Answer
Same klasy wyglądają następująco:
Oprócz tego co widać zostały stworzone również gettery i settery, czyli funkcje które pozwalają pobierać i ustawiać wartości. W każdym przykładzie widzimy tradycyjną konfigurację dla encji w Symfony. Jest to zwykła klasa posiadająca prywatne pola oraz konfiguracja Doctrina dla wszystkich pola. Konfiguracja w tym przypadku to adnotacje czyli specjalne komentarze ustawiające pewne parametry np.: typ kolumny w bazie danych lub relacje do innych encji jak na przykład w Quiz do Questions. Zdaję sobie sprawę, iż za pierwszym razem może się to wydawać dziwne - sam się dziwiłem jak można konfigurować takie rzeczy przy pomocy komentarzy(prawie je usunąłęm bo myślałem, iż są zbędne).
Synchronizacja encji z bazą danych
Poprawna konfiguracja encji to połowa sukcesu ponieważ trzeba jeszcze zsynchronizować naszą konfigurację do tabel w bazie danych. Do tego celu polecam skorzystanie z DoctrineMigrations - pozwala tworzyć pliki migracji, które pokazują kolejne zmiany jakie były aplikowane na bazie danych. Dzięki tym plikom w razie problemów jesteśmy w stanie wrócić do poprzedniej wersji bazy cofając ostatnie zmiany. Aby móc z tego skorzystać musimy najpierw zainstalować odpowiednią paczkę przy pomocy composer require migrations. Następnie przy pomocy bin/console doctrine:migrations:diff porównywany jest aktualny stan bazy danych z konfiguracją w plikach encji. o ile występują jakieś różnice to zostanie wygenerowany plik o unikalnej nazwie zawierający polecenia, które przekształcą aktualną bazę danych do nowej wersji. W przypadku encji powyżej powstanie coś takiego:
Taka migracja jest zwykłą klasą PHP i składa się z dwóch funkcji up i down. Pierwsza z nich jest wywoływana kiedy aplikujemy zmiany do bazy danych natomiast druga gdy je cofamy. Oczywiście możemy rozbudować te funkcje o własne zapytania o ile tego potrzebujemy. Teraz żeby nałożyć te zmiany musimy skorzystać z polecenia bin/console doctrine:migrations:migrate
Warto jeszcze zauważyć, iż podczas migrowania bazy danych nie tworzymy od zera bazy tylko nakładamy ostanie zmiany. To jaka zmiana była ostatnia jest wiadome ponieważ nazwy(a dokładniej ta część gdzie jest data np.:20180819093401) wszystkich do tej pory wykonanych migracji są trzymane w specjalnej tabeli w bazie danych - migration_versions
Endpointy API Platform
Tyle wystarczy żeby baza danych miała odpowiednią strukturę. Ale możecie zapytać co z tym API? Aby je zobaczyć uruchomcie serwer przy pomocy bin/console server:run i wejść w przeglądarce na stronę 127.0.0.1:8000/api/docs. Zobaczymy tam Swaggera i wszystkie dostępne endpointy:
Jeśli zastanawiacie się skąd się to wzięło to zerknijcie z powrotem do części gdzie wkleiłem kod swoich encji. W adnotacji nad klasą zobaczycie taki wpis: @ApiResource() - to on powoduje, iż dla tej klasy zostaną wygenerowane enpointy. Dzięki tej jednej adnotacji w bezproblemowy sposób stworzyliśmy pierwsze endpointy, które możemy już wykorzystywać - w normalnej aplikacji, w Postmanie lub wykorzystując interfejs Swaggera. Czy jednak to koniec naszej pracy? No nie do końca. jeżeli byście weszli w szczegóły endpointów to zobaczycie, iż przykładowe zapytania zawierają czasami niepotrzebne pola czy też czasami nie będzie wszystkich enpointów które byśmy potrzebowali. Jak można to obejść? adekwatną konfiguracją ale to już następnym razem ;)