Codice: instalator webowy dla aplikacji opartej o Laravel

Dośc długo zastanawiałem się czemu mógłbym poświęcić swój pierwszy, faktyczny wpis poświęcony rozwojowi Codice i problemom jakie musiałem rozwiązać. Daruję sobie rozpoczynanie od struktury katalogów, tworzenia kontrolerów i podobnych zadań — zaczniemy od początku, ale z perspektywy użytkownika.

Dość dużo mówi się o tym, jak zmienił się rozwój aplikacji webowych w ostatnich latach. Kiedyś (czy to patrząc na rok, czy na doświadczenie konkretnego programisty) prawdpodobnie wszyscy zaczynaliśmy, w wypadku PHP, od kilku plików na krzyż, w ekstremalnych przypadkach wrzucając jakąś rozpakowaną bibliotekę do osobnego folderu. Dzisiaj rozwój projektów, zwłaszcza na początku, przypomina budowę z klocków Lego. Zaczynamy od zainstalowania całego środowiska programistycznego — serwera (lub programu do obsługi środowisk zwirtualizowanych), Composera do obsługi zależności PHP, menedżera zależności dla JavaScript popularnego akurat w danym tygodniu (pun intended ;) ) i dopiero ostatecznie przystępujemy do pisania faktycznego kodu.

Ze stawianiem już gotowej aplikacji jest całkiem podobnie. Pobieramy kod, a następnie zdefiniowane w projekcie zależności. Potem jest już z górki — kompilujemy assety dla frontendu (w końcu to tylko kwestia odpalenia task runnera), wypełniamy plik konfiguracyjny (lub ustawiamy zmienne środowiskowe), uruchamiamy migracje dla bazy danych, jeśli nam zależy to wrzucamy do cache co się da — na koniec tworzymy konto użytkownika i voilà. Proste, prawda?

A no właśnie nie bardzo. Wszystkie z opisanych rozwiązań to ogromny krok naprzód i ułatwienie dla programistów. Co więcej, przekładające się bezpośrednio na jakość ostatecznego produktu. Nie przerzucajmy jednak całego procesu ustawiania środowiska na użytkownika. Mamy dostępnych tyle świetnych technik i narzędzi, a tymczasem proces instalacji przeciętnego skryptu PHP się uwstecznił. W wypadku Codice postanowiłem połączyć zdobycze naszych czasów z prostotą instalacji dawnych skryptów.

Czynności do wykonania możemy podzielić na trzy grupy:

  • wykorzystanie zewnętrznych narzędzi — node.js wraz z Yarnem zarządzającym zależnościami dla tej technologii i gulpem odpowiedzialnym za przygotowanie zasobów dla client-side, ale także Composer dla zależności PHP
  • wypełnienie plików konfiguracyjnych
  • uruchomienie procesów oskryptowanych w PHP — na przykład migracji dla struktury bazy danych

Pierwszy krok zrealizowałem dzięki udostępnieniu do pobrania wstępnie zbudowanej paczki plików. W archiwum znajdują się produkcyjne zależności PHP, a także skompilowane assety (nie ma za to node_modules i innych plików wykorzystywanych wyłącznie przy developmencie — szanujmy łącza i czas użytkowników).

Resztą zajmuje się już instalator, będący de facto zwyczajnym kontrolerem Laravela. Aplikację zainstalowaną od tej jeszcze niezainstalowanej postanowiłem rozróżniać sprawdzajac obecność pliku .env. Ponieważ jednak jest on tworzony na pewnym etapie instalacji, potrzebny był dodatkowy sposób oznaczenia obecnie trwającej instalacji (tak, aby po kroku wypełniającym .env nie wyrzuciło nas z instalatora) — jest to po prostu plik tymczasowy tworzony po wejściu do instalatora i usuwany na jej ostatnim kroku. Cały proces wygląda następująco:

public function __construct()
{
    if (file_exists(base_path('.env')) && !file_exists(storage_path('app/.install-pending'))) {
        $this->denyInstallation = true;
        return Redirect::route('index')->send();
    }

    // ...
}

Kod znajduje się w konstruktorze więc automatycznie jest wykonywany dla każdego z kroków (akcji) instalatora. Warto zwrócić uwagę na użycie metody send(), jest ona wymagana w wypadku próby obsługi żądań HTTP z poziomu konstruktora.

Kolejnym etapem jest sprawdzenie wymagań, tj. obecności rozszerzeń PHP i zapisywalności określonych katalogów. Nie ma tu żadnej filozofii także przejdę dalej, do wypełniania pliku .env. Z pozoru proste zadanie, ot umieszczenie określonego stringa w pliku tekstowym. Jest jednak pewna pułapka, która swego czasu spędziła mi sen z powiek na ładnych parę godzin…

Problem pojawia się gdy w instalatorze wykorzystujemy jakiekolwiek sesje bądź ciasteczka (w moim wypadku było to ustawienie języka instalacji). Zarówno sesje jak i ciasteczka obsługiwane przez Laravela są szyfrowane przy użyciu klucza, tzw. APP_KEY. Jest on indywidualny dla każdej aplikacji, a więc przechowywany właśnie w pliku .env. Ten jednak nie istnieje na samym początku instalacji. Co wtedy robi Laravel? Spójrzmy na plik config/app.php:

'key' => env('APP_KEY', 'SomeRandomStringSomeRandomString'),

W wypadku gdy zmienna nie istnieje, klucz jest ustawiany na wartość domyślną, podaną w drugim parametrze funkcji. To jest właśnie klucz szyfrujący ciastka i sesje na poczatku instalacji… aż do momentu gdy, wypełniając .env, wstawimy do niego nowy klucz, który własnie wygenerowaliśmy. Skutek można sobie łatwo wyobrazić - dane w nich zapisane stają się niemożliwe do odczytania. Mając tę wiedzę spokojnie możecie wymyślić kilka różnych obejść problemu — ja zdecydowałem się doczepić aktualnie wybrany język do adresu, na który przekierowuję po wypełnieniu pliku konfiguracyjnego. W kolejnym kroku mogę go z tego adresu odczytać i ponownie zapisać do sesji.

W ten sposób doszliśmy do kolejnej kwestii, która na pierwszy rzut oka może wydawać się problematyczna — uruchomienie migracji z poziomu skryptu PHP. Domyślnie jest to bowiem komenda artisana, a dokumentacja zdaje się nie wspominać o alternatywnych drogach. Również wymóg użycia exec() dla zwykłej instalacji wydaje się przesadny, wszkaże wiele hostingów nadal blokuje tę i podobne funkcje. Tym razem jednak rozwiązanie jest wyjątkowo proste, a z pomocą przychodzi nam Artisan::call(), który wykonuje kod danej komendy bez jakiegokolwiek używania warstwy linii komend.

Artisan::call('migrate', ['--force' => true]);

Pamiętajmy o użyciu opcji --force, jest ona wymagana aby migracje mogły być uruchomione w środowisku produkcyjnym.

W wypadku Codice to już praktycznie koniec, pozostało tylko utworzenie konta dla użytkownika i dodanie mu notatki powitalnej. W swoich aplikacjach możecie oczywiście wykonać wszystkie inne czynności związane z instalacją, tak aby jak najbardziej ułatwić życie Waszych użytkowników końcowych. Naprawdę nie ma potrzeby wymagać od nich znajomości typowo programistycznych narzędzi ;) Ponadto, stosując przedstawione tutaj rozwiązanie nie blokujemy żadnej z dotychczasowych dróg instalacji. Nic nie stoi na przeszkodzie aby pobrać źródła (lub sklonować repozytorium) i wszystkie czynności wykonać ręcznie.

Na koniec, wszystkim zainteresowanym dokładnym działaniem instalatora polecam zacząć przeglądanie kodu od tego kontrolera. Aby zamieszać jeszcze troszkę, dodam że Codice obsługuje też instalację z poziomu CLI, dla osób które chciałyby postawić całą instalację bez opuszczania terminala bądź szukają wygodnej drogi na zautomatyzowanie procesu. Odpowiedzialny za to kod znajdziecie w tym miejscu.


Powyższy wpis przedstawia część doświadczeń zdobytych przy tworzeniu projektu Codice. Po więcej szczegółów zapraszam do pierwszego wpisu oraz na codice.eu. Poza planowaną od dawna serią jest to też realizacja wymogów konkursu "Daj Się Poznać". Więcej o nim możecie przeczytać w tym wpisie. Po nowe materiały poświęcone tworzeniu projektu Codice zapraszam w każdy wtorek. Ponadto więcej wpisów, poświęconych temu projektowi lub ogólnej tematyce bloga przeczytacie co sobotę.

Komentarze wyłączone

Możliwość komentowania na blogu została wyłączona. Zapraszam do kontaktu na Twitterze, Facebooku lub poprzez formularz, o ten tutaj. Do usłyszenia!