Używanie Laravel Scout do włączenia wyszukiwania pełnotekstowego
Opublikowany: 2023-05-08Framework Laravel stał się podstawowym zasobem dla programistów tworzących usługi internetowe.
Jako narzędzie typu open source, Laravel oferuje mnóstwo gotowych funkcji, które umożliwiają programistom tworzenie solidnych i funkcjonalnych aplikacji.
Wśród jego ofert znajduje się Laravel Scout, biblioteka do zarządzania indeksami wyszukiwania dla Twojej aplikacji. Jego elastyczność pozwala programistom na precyzyjne dostrojenie konfiguracji i wybór spośród sterowników Algolia, Meilisearch, MySQL lub Postgres do przechowywania indeksów.
Tutaj dogłębnie zbadamy to narzędzie, ucząc Cię, jak dodać obsługę wyszukiwania pełnotekstowego do aplikacji Laravel za pośrednictwem sterownika. Zamodelujesz demonstracyjną aplikację Laravel do przechowywania nazw makiet pociągów, a następnie użyjesz Laravel Scout, aby dodać wyszukiwanie do aplikacji.
Wymagania wstępne
Aby kontynuować, powinieneś mieć:
- Kompilator PHP zainstalowany na twoim komputerze. W tym samouczku użyto PHP w wersji 8.1.
- Silnik Docker lub Docker Desktop zainstalowany na komputerze
- Konto w chmurze Algolia, które możesz założyć bezpłatnie
Jak zainstalować Scouta w projekcie Laravel
Aby korzystać ze Scouta, musisz najpierw stworzyć aplikację Laravel, w której zamierzasz dodać funkcję wyszukiwania. Skrypt Laravel-Scout Bash zawiera polecenia do generowania aplikacji Laravel w kontenerze Docker. Korzystanie z Dockera oznacza, że nie musisz instalować dodatkowego oprogramowania pomocniczego, takiego jak baza danych MySQL.
Skrypt Laravel-scout używa języka skryptowego Bash, więc musisz go uruchomić w środowisku Linux. Jeśli korzystasz z systemu Windows, upewnij się, że skonfigurowano podsystem Windows dla systemu Linux (WSL).
Jeśli używasz WSL, wykonaj następujące polecenie w swoim terminalu, aby ustawić preferowaną dystrybucję Linuksa.
wsl -s ubuntu
Następnie przejdź do lokalizacji na komputerze, w której chcesz umieścić projekt. Skrypt Laravel-Scout wygeneruje tutaj katalog projektu. W poniższym przykładzie skrypt Laravel-Scout utworzyłby katalog w katalogu na pulpicie .
cd /desktop
Uruchom poniższe polecenie, aby wykonać skrypt Laravel-Scout. Stworzy rusztowanie aplikacji Dockerized z niezbędnym kodem standardowym.
curl -s https://laravel.build/laravel-scout-app | bash
Po wykonaniu zmień katalog za pomocą cd laravel-scout-app
. Następnie uruchom polecenie sail-up
w folderze projektu, aby uruchomić kontenery Docker dla swojej aplikacji.
Uwaga: w wielu dystrybucjach Linuksa może być konieczne uruchomienie poniższego polecenia z poleceniem sudo
, aby zainicjować podwyższone uprawnienia.
./vendor/bin/sail up
Możesz napotkać błąd:
Aby rozwiązać ten problem, użyj zmiennej APP_PORT
, aby określić port w poleceniu sail up
:
APP_PORT=3001 ./vendor/bin/sail up
Następnie wykonaj poniższe polecenie, aby uruchomić aplikację przez Artisan na serwerze PHP.
php artisan serve
W przeglądarce internetowej przejdź do uruchomionej aplikacji pod adresem http://127.0.0.1:8000. Aplikacja wyświetli stronę powitalną Laravel na trasie domyślnej.
Jak dodać Laravel Scout do aplikacji
W swoim terminalu wprowadź polecenie, aby umożliwić menedżerowi pakietów Composer PHP dodanie Laravel Scout do projektu.
composer require laravel/scout
Następnie opublikuj plik konfiguracyjny Scouta za pomocą polecenia vendor:publish. Polecenie opublikuje plik konfiguracyjny scout.php
w katalogu konfiguracyjnym Twojej aplikacji.
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
Teraz zmodyfikuj plik wzorcowy .env , aby zawierał wartość logiczną SCOUT_QUEUE
.
Wartość SCOUT_QUEUE
pozwoli Scoutowi na kolejkowanie operacji, zapewniając lepsze czasy odpowiedzi. Bez tego sterowniki Scout, takie jak Meilisearch, nie będą od razu odzwierciedlać nowych rekordów.
SCOUT_QUEUE=true
Zmodyfikuj również zmienną DB_HOST
w pliku .env , aby wskazywała hosta lokalnego, aby korzystać z bazy danych MySQL w kontenerach platformy Docker.
DB_HOST=127.0.0.1
Jak oznaczyć model i skonfigurować indeks
Scout domyślnie nie włącza przeszukiwalnych modeli danych. Musisz jawnie oznaczyć model jako możliwy do przeszukiwania, używając jego cechy Laravel\Scout\Searchable
.
Zaczniesz od utworzenia modelu danych dla demonstracyjnej aplikacji Train
i oznaczenia go jako możliwego do przeszukiwania.
Jak stworzyć model
W przypadku aplikacji Train
będziesz chciał przechowywać zastępcze nazwy każdego dostępnego pociągu.
Wykonaj poniższe polecenie Artisan, aby wygenerować migrację i nadaj jej nazwę create_trains_table
.
php artisan make:migration create_trains_table
Migracja zostanie wygenerowana w pliku, którego nazwa jest połączeniem podanej nazwy i bieżącego znacznika czasu.
Otwórz plik migracji znajdujący się w katalogu database/migrations/ .
Aby dodać kolumnę tytułu, dodaj następujący kod po kolumnie id()
w linii 17. Kod doda kolumnę tytułu.
$table->string('title');
Aby zastosować migrację, wykonaj poniższe polecenie.
php artisan migrate
Po uruchomieniu migracji bazy danych utwórz plik o nazwie Train.php w katalogu app/Models/ .
Jak dodać cechę LaravelScoutSearchable
Zaznacz model Train
do wyszukiwania, dodając do modelu cechę Laravel\Scout\Searchable
, jak pokazano poniżej.
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Laravel\Scout\Searchable; class Train extends Model { use Searchable; public $fillable = ['title'];
Ponadto należy skonfigurować indeksy wyszukiwania, zastępując metodę searchable
. Domyślne zachowanie Scouta utrzymywałoby model, aby pasował do nazwy tabeli modelu.
Dodaj więc poniższy kod do pliku Train.php pod kodem z poprzedniego bloku.
/** * Retrieve the index name for the model. * * @return string */ public function searchableAs() { return 'trains_index'; } }
Jak używać Algolii ze Scoutem
Do pierwszego wyszukiwania pełnotekstowego za pomocą Laravel Scout użyjesz sterownika Algolia. Algolia to platforma oprogramowania jako usługi (SaaS) służąca do przeszukiwania dużych ilości danych. Zapewnia internetowy pulpit nawigacyjny dla programistów do zarządzania indeksami wyszukiwania oraz solidny interfejs API, do którego można uzyskać dostęp za pośrednictwem zestawu programistycznego (SDK) w preferowanym języku programowania.
W aplikacji Laravel będziesz korzystać z pakietu klienta Algolia dla PHP.
Jak skonfigurować Algolię
Najpierw musisz zainstalować pakiet klienta wyszukiwania Algolia PHP dla swojej aplikacji.
Wykonaj poniższe polecenie.
composer require algolia/algoliasearch-client-php
Następnie musisz ustawić poświadczenia Application ID i Secret API Key z Algolii w pliku .env .
Korzystając z przeglądarki internetowej, przejdź do pulpitu nawigacyjnego Algolia, aby uzyskać dane uwierzytelniające Identyfikator aplikacji i Tajny klucz API.
Kliknij Ustawienia u dołu paska bocznego po lewej stronie, aby przejść do strony Ustawienia .
Następnie kliknij Klucze API w sekcji Zespół i dostęp na stronie Ustawienia, aby wyświetlić klucze do swojego konta Algolia.
Na stronie Klucze interfejsu API zanotuj wartości identyfikatora aplikacji i klucza interfejsu API administratora . Tych poświadczeń użyjesz do uwierzytelnienia połączenia między aplikacją Laravel a Algolią.
Dodaj poniższy kod do pliku .env za pomocą edytora kodu i zastąp symbole zastępcze odpowiednimi kluczami tajnymi API Algolia.
ALGOLIA_APP_ID=APPLICATION_ID ALGOLIA_SECRET=ADMIN_API_KEY
Zastąp także zmienną SCOUT_DRIVER
poniższym kodem, aby zmienić wartość z meilisearch
na algolia
. Zmiana tej wartości spowoduje, że Scout użyje sterownika Algolia.
SCOUT_DRIVER=algolia
Jak utworzyć kontrolery aplikacji
W katalogu app/Http/Controllers/ utwórz plik TrainSearchController.php do przechowywania kontrolera aplikacji. Kontroler wyświetli listę i doda dane do modelu Train
.
Dodaj następujący blok kodu do pliku TrainSearchController.php , aby zbudować kontroler.
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests; use App\Models\Train; class TrainSearchController extends Controller { /** * Get the index name for the model. * * @return string */ public function index(Request $request) { if($request->has('titlesearch')){ $trains = Train::search($request->titlesearch) ->paginate(6); }else{ $trains = Train::paginate(6); } return view('Train-search',compact('trains')); } /** * Get the index name for the model. * * @return string */ public function create(Request $request) { $this->validate($request,['title'=>'required']); $trains = Train::create($request->all()); return back(); } }
Jak utworzyć trasy aplikacji
W tym kroku utworzysz trasy do wyświetlania i dodawania nowych pociągów do bazy danych.
Otwórz plik tras/web.php i zastąp istniejący kod blokiem poniżej.
<?php use Illuminate\Support\Facades\Route; use App\Http\Controllers\TrainSearchController; Route::get('/', function () { return view('welcome'); }); Route::get('trains-lists', [TrainSearchController::class, 'index']) -> name ('trains-lists'); Route::post('create-item', [TrainSearchController::class, 'create']) -> name ('create-item');
Powyższy kod definiuje dwie trasy w aplikacji. Żądanie GET
dla trasy /trains-lists
zawiera listę wszystkich przechowywanych danych pociągów. Żądanie POST
dla trasy /create-item
tworzy nowe dane pociągu.
Jak utworzyć widoki aplikacji
Utwórz plik w katalogu resources/views/ i nadaj mu nazwę Train-search.blade.php . Plik wyświetli interfejs użytkownika dla funkcji wyszukiwania.
Dodaj zawartość poniższego bloku kodu do pliku Train-search.blade.php , aby utworzyć pojedynczą stronę dla funkcji wyszukiwania.
<!DOCTYPE html> <html> <head> <title>Laravel - Laravel Scout Algolia Search Example</title> <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> </head> <body> <div class="container"> <h2 class="text-bold">Laravel Full-Text Search Using Scout </h2><br/> <form method="POST" action="{{ route('create-item') }}" autocomplete="off"> @if(count($errors)) <div class="alert alert-danger"> <strong>Whoops!</strong> There is an error with your input. <br/> <ul> @foreach($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <input type="hidden" name="_token" value="{{ csrf_token() }}"> <div class="row"> <div class="col-md-6"> <div class="form-group {{ $errors->has('title') ? 'has-error' : '' }}"> <input type="text" name="title" class="form-control" placeholder="Enter Title" value="{{ old('title') }}"> <span class="text-danger">{{ $errors->first('title') }}</span> </div> </div> <div class="col-md-6"> <div class="form-group"> <button class="btn btn-primary">Create New Train</button> </div> </div> </div> </form> <div class="panel panel-primary"> <div class="panel-heading">Train Management</div> <div class="panel-body"> <form method="GET" action="{{ route('trains-lists') }}"> <div class="row"> <div class="col-md-6"> <div class="form-group"> <input type="text" name="titlesearch" class="form-control" placeholder="Enter Title For Search" value="{{ old('titlesearch') }}"> </div> </div> <div class="col-md-6"> <div class="form-group"> <button class="btn btn-primary">Search</button> </div> </div> </div> </form> <table class="table"> <thead> <th>Id</th> <th>Train Title</th> <th>Creation Date</th> <th>Updated Date</th> </thead> <tbody> @if($trains->count()) @foreach($trains as $key => $item) <tr> <td>{{ ++$key }}</td> <td>{{ $item->title }}</td> <td>{{ $item->created_at }}</td> <td>{{ $item->updated_at }}</td> </tr> @endforeach @else <tr> <td colspan="4">No train data available</td> </tr> @endif </tbody> </table> {{ $trains->links() }} </div> </div> </div> </body> </html>
Powyższy kod HTML zawiera element formularza z polem wprowadzania danych oraz przyciskiem do wpisania tytułu pociągu przed zapisaniem go do bazy danych. Kod zawiera również tabelę HTML wyświetlającą szczegóły id , title , created_at i updated_at wpisu pociągu w bazie danych.
Jak korzystać z wyszukiwarki Algolia
Aby wyświetlić tę stronę, przejdź do http://127.0.0.1:8000/trains-lists w przeglądarce internetowej.
Baza danych jest obecnie pusta, dlatego należy wpisać tytuł pociągu demonstracyjnego w polu wejściowym i kliknąć Utwórz nowy pociąg , aby go zapisać.
Aby skorzystać z funkcji wyszukiwania, wpisz słowo kluczowe z dowolnego zapisanego tytułu pociągu w polu wejściowym Wprowadź tytuł do wyszukania i kliknij przycisk Szukaj .
Jak pokazano na poniższym obrazku, zostaną wyświetlone tylko wpisy wyszukiwania zawierające słowo kluczowe w tytule.
Meilisearch z Laravel Scout
Meilisearch to wyszukiwarka typu open source, która koncentruje się na szybkości, wydajności i ulepszonym doświadczeniu programistów. Ma kilka wspólnych funkcji z Algolią, wykorzystuje te same algorytmy, struktury danych i badania — ale z innym językiem programowania.
Deweloperzy mogą tworzyć i samodzielnie hostować instancje Meilisearch w swojej infrastrukturze lokalnej lub w chmurze. Meilisearch oferuje również chmurę w wersji beta, podobną do Algolii, dla programistów, którzy chcą korzystać z produktu bez zarządzania jego infrastrukturą.
W samouczku masz już lokalną instancję Meilisearch działającą w kontenerach Dockera. Rozszerzysz teraz funkcjonalność Laravel Scout, aby korzystać z instancji Meilisearch.
Aby dodać Meilisearch do aplikacji Laravel, uruchom poniższe polecenie w swoim terminalu projektu.
composer require meilisearch/meilisearch-php
Następnie musisz zmodyfikować zmienne Meilisearch w pliku .env , aby go skonfigurować.
Zastąp zmienne SCOUT_DRIVER
, MEILISEARCH_HOST
i MEILISEARCH_KEY
w pliku .env poniższymi.
SCOUT_DRIVER=meilisearch MEILISEARCH_HOST=http://127.0.0.1:7700 MEILISEARCH_KEY=LockKey
Klucz SCOUT_DRIVER
określa sterownik, którego powinien używać Scout, podczas gdy MEILISEARCH_HOST
reprezentuje domenę, w której działa Twoja instancja Meilisearch. Chociaż nie jest to wymagane podczas programowania, zalecane jest dodanie MEILISEARCH_KEY
w środowisku produkcyjnym.
Uwaga: jeśli używasz Meilisearch jako preferowanego sterownika, zakomentuj identyfikator Algolia i klucz tajny.
Po zakończeniu konfiguracji .env należy zindeksować istniejące wcześniej rekordy za pomocą poniższego polecenia Artisan.
php artisan scout:import "App\Models\Train"
Laravel Scout z silnikiem bazy danych
Silnik bazy danych Scout może być najbardziej odpowiedni dla aplikacji, które używają mniejszych baz danych lub zarządzają mniej intensywnymi obciążeniami. Obecnie silnik bazy danych obsługuje PostgreSQL i MySQL.
Ten silnik wykorzystuje klauzule „where-like” i indeksy pełnotekstowe w stosunku do Twojej istniejącej bazy danych, umożliwiając znalezienie najtrafniejszych wyników wyszukiwania. Nie musisz indeksować swoich rekordów podczas korzystania z silnika bazy danych.
Aby użyć silnika bazy danych, musisz ustawić zmienną SCOUT_DRIVER
.env na bazę danych.
Otwórz plik .env w aplikacji Laravel i zmień wartość zmiennej SCOUT_DRIVER
.
SCOUT_DRIVER = database
Po zmianie sterownika na bazę danych Scout przełączy się na korzystanie z silnika bazy danych do wyszukiwania pełnotekstowego.
Silnik kolekcjonerski z Laravel Scout
Oprócz silnika bazy danych, Scout oferuje również silnik kolekcji. Ten silnik wykorzystuje klauzule „gdzie” i filtrowanie kolekcji w celu wyodrębnienia najtrafniejszych wyników wyszukiwania.
W przeciwieństwie do silnika bazy danych, silnik kolekcji obsługuje wszystkie relacyjne bazy danych, które obsługuje również Laravel.
Możesz użyć mechanizmu zbierania, ustawiając zmienną środowiskową SCOUT_DRIVER
na collection
lub ręcznie określając sterownik zbierania w pliku konfiguracyjnym Scout.
SCOUT_DRIVER = collection
Eksplorator z Elasticsearch
Dzięki sile zapytań Elasticsearch Explorer jest nowoczesnym sterownikiem Elasticsearch dla Laravel Scout. Oferuje kompatybilny sterownik Scout i korzyści, takie jak przechowywanie, wyszukiwanie i analizowanie ogromnych ilości danych w czasie rzeczywistym. Elasticsearch z Laravel oferuje wyniki w milisekundach.
Aby użyć sterownika Elasticsearch Explorer w aplikacji Laravel, musisz skonfigurować plik docker-compose.yml , który został wygenerowany przez skrypt Laravel-Scout. Dodasz dodatkowe konfiguracje dla Elasticsearch i ponownie uruchomisz kontenery.
Otwórz plik docker-compose.yml i zastąp jego zawartość następującą.
# For more information: https://laravel.com/docs/sail version: '3' services: laravel.test: build: context: ./vendor/laravel/sail/runtimes/8.1 dockerfile: Dockerfile args: WWWGROUP: '${WWWGROUP}' image: sail-8.1/app extra_hosts: - 'host.docker.internal:host-gateway' ports: - '${APP_PORT:-80}:80' - '${VITE_PORT:-5173}:${VITE_PORT:-5173}' environment: WWWUSER: '${WWWUSER}' LARAVEL_SAIL: 1 XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}' XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' volumes: - '.:/var/www/html' networks: - sail depends_on: - mysql - redis - meilisearch - mailhog - selenium - pgsql - elasticsearch mysql: image: 'mysql/mysql-server:8.0' ports: - '${FORWARD_DB_PORT:-3306}:3306' environment: MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' MYSQL_ROOT_HOST: "%" MYSQL_DATABASE: '${DB_DATABASE}' MYSQL_USER: '${DB_USERNAME}' MYSQL_PASSWORD: '${DB_PASSWORD}' MYSQL_ALLOW_EMPTY_PASSWORD: 1 volumes: - 'sail-mysql:/var/lib/mysql' - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh' networks: - sail healthcheck: test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"] retries: 3 timeout: 5s elasticsearch: image: 'elasticsearch:7.13.4' environment: - discovery.type=single-node ports: - '9200:9200' - '9300:9300' volumes: - 'sailelasticsearch:/usr/share/elasticsearch/data' networks: - sail kibana: image: 'kibana:7.13.4' environment: - elasticsearch.hosts=http://elasticsearch:9200 ports: - '5601:5601' networks: - sail depends_on: - elasticsearch redis: image: 'redis:alpine' ports: - '${FORWARD_REDIS_PORT:-6379}:6379' volumes: - 'sail-redis:/data' networks: - sail healthcheck: test: ["CMD", "redis-cli", "ping"] retries: 3 timeout: 5s pgsql: image: 'postgres:13' ports: - '${FORWARD_DB_PORT:-5432}:5432' environment: PGPASSWORD: '${DB_PASSWORD:-secret}' POSTGRES_DB: '${DB_DATABASE}' POSTGRES_USER: '${DB_USERNAME}' POSTGRES_PASSWORD: '${DB_PASSWORD:-secret}' volumes: - 'sailpgsql:/var/lib/postgresql/data' networks: - sail healthcheck: test: ["CMD", "pg_isready", "-q", "-d", "${DB_DATABASE}", "-U", "${DB_USERNAME}"] retries: 3 timeout: 5s meilisearch: image: 'getmeili/meilisearch:latest' ports: - '${FORWARD_MEILISEARCH_PORT:-7700}:7700' volumes: - 'sail-meilisearch:/meili_data' networks: - sail healthcheck: test: ["CMD", "wget", "--no-verbose", "--spider", "http://localhost:7700/health"] retries: 3 timeout: 5s mailhog: image: 'mailhog/mailhog:latest' ports: - '${FORWARD_MAILHOG_PORT:-1025}:1025' - '${FORWARD_MAILHOG_DASHBOARD_PORT:-8025}:8025' networks: - sail selenium: image: 'selenium/standalone-chrome' extra_hosts: - 'host.docker.internal:host-gateway' volumes: - '/dev/shm:/dev/shm' networks: - sail networks: sail: driver: bridge volumes: sail-mysql: driver: local sail-redis: driver: local sail-meilisearch: driver: local sailpgsql: driver: local sailelasticsearch: driver: local
Następnie uruchom poniższe polecenie, aby pobrać nowy obraz Elasticsearch dodany do pliku docker-compose.yml .
docker-compose up
Następnie wykonaj poniższe polecenie Composer, aby zainstalować Eksploratora w projekcie.
composer require jeroen-g/explorer
Należy również utworzyć plik konfiguracyjny dla sterownika Eksploratora.
Wykonaj poniższe polecenie Artisan, aby wygenerować plik explorer.config do przechowywania konfiguracji.
php artisan vendor:publish --tag=explorer.config
Wygenerowany powyżej plik konfiguracyjny będzie dostępny w katalogu /config .
W pliku config/explorer.php możesz odwołać się do swojego modelu za pomocą klucza indexes
.
'indexes' => [ \App\Models\Train::class ],
Zmień wartość zmiennej SCOUT_DRIVER
w pliku .env na elastic
, aby skonfigurować Scout do używania sterownika Explorer.
SCOUT_DRIVER = elastic
W tym momencie będziesz używać Eksploratora w modelu Train
, implementując interfejs Eksploratora i przesłaniając metodę mappableAs()
.
Otwórz plik Train.php w katalogu App > Models i zastąp istniejący kod poniższym kodem.
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use JeroenG\Explorer\Application\Explored; use Laravel\Scout\Searchable; class Train extends Model implements Explored { use HasFactory; use Searchable; protected $fillable = ['title']; public function mappableAs(): array { return [ 'id'=>$this->Id, 'title' => $this->title, ]; } }
Dzięki kodowi, który dodałeś powyżej, możesz teraz używać Eksploratora do wyszukiwania tekstu w modelu Train
.
Streszczenie
Dla programistów PHP Laravel i dodatki, takie jak Scout, ułatwiają integrację szybkiej, niezawodnej funkcji wyszukiwania pełnotekstowego. Dzięki Database Engine, Collection Engine oraz możliwościom Meilisearch i Elasticsearch możesz wchodzić w interakcje z bazą danych swojej aplikacji i wdrażać zaawansowane mechanizmy wyszukiwania w ciągu zaledwie milisekund.
Bezproblemowe zarządzanie bazą danych i jej aktualizowanie oznacza, że użytkownicy otrzymują optymalne doświadczenie, a Twój kod pozostaje czysty i wydajny.
Dzięki naszym rozwiązaniom w zakresie hostingu aplikacji i baz danych, Kinsta jest Twoim punktem kompleksowej obsługi dla wszystkich Twoich nowoczesnych potrzeb rozwojowych Laravel. Pierwsze 20 $ jest na nasz koszt.