Utilizzo di Laravel Scout per abilitare la ricerca full-text

Pubblicato: 2023-05-08

Il framework Laravel è diventato una risorsa di riferimento per gli sviluppatori che creano servizi web.

Come strumento open source, Laravel offre una miriade di funzionalità pronte all'uso che consentono agli sviluppatori di creare applicazioni robuste e funzionali.

Tra le sue offerte c'è Laravel Scout, una libreria per la gestione degli indici di ricerca per la tua applicazione. La sua flessibilità consente agli sviluppatori di mettere a punto le configurazioni e scegliere tra i driver Algolia, Meilisearch, MySQL o Postgres per archiviare gli indici.

Qui, esploreremo questo strumento in modo approfondito, insegnandoti come aggiungere il supporto per la ricerca full-text a un'applicazione Laravel tramite il driver. Modellerai un'applicazione Laravel demo per memorizzare il nome dei treni mockup e quindi utilizzerai Laravel Scout per aggiungere una ricerca all'applicazione.

Prerequisiti

Per seguire, dovresti avere:

  • Il compilatore PHP installato sul tuo computer. Questo tutorial utilizza PHP versione 8.1.
  • Il motore Docker o Docker Desktop installato sul tuo computer
  • Un account cloud Algolia, che puoi creare gratuitamente
Vuoi rendere la tua app più user-friendly? Prova ad aggiungere un supporto per la ricerca full-text! Ecco come fare con Laravel Scout ️ Click to Tweet

Come installare Scout in un progetto Laravel

Per utilizzare Scout, devi prima creare un'applicazione Laravel in cui intendi aggiungere la funzionalità di ricerca. Lo script Laravel-Scout Bash contiene i comandi per generare un'applicazione Laravel all'interno di un contenitore Docker. L'utilizzo di Docker significa che non è necessario installare software di supporto aggiuntivo, come un database MySQL.

Lo script Laravel-scout utilizza il linguaggio di scripting Bash, quindi è necessario eseguirlo in un ambiente Linux. Se stai usando Windows, assicurati di configurare il sottosistema Windows per Linux (WSL).

Se usi WSL, esegui il comando seguente nel tuo terminale per impostare la tua distribuzione Linux preferita.

 wsl -s ubuntu

Successivamente, vai alla posizione sul tuo computer in cui desideri posizionare il progetto. Lo script Laravel-Scout genererà qui una directory di progetto. Nell'esempio seguente, lo script Laravel-Scout creerebbe una directory all'interno della directory desktop .

 cd /desktop

Esegui il comando seguente per eseguire lo script Laravel-Scout. Impalcherà un'applicazione Dockerizzata con il codice boilerplate necessario.

 curl -s https://laravel.build/laravel-scout-app | bash

Dopo l'esecuzione, cambia la tua directory usando cd laravel-scout-app . Quindi, esegui il comando sail-up all'interno della cartella del progetto per avviare i container Docker per la tua applicazione.

Nota: su molte distribuzioni Linux, potrebbe essere necessario eseguire il comando seguente con il comando sudo per avviare privilegi elevati.

 ./vendor/bin/sail up

Potresti riscontrare un errore:

Errore che indica che la porta è allocata
Errore che indica che la porta è allocata.

Per risolvere questo problema, utilizza la variabile APP_PORT per specificare una porta all'interno del comando sail up :

 APP_PORT=3001 ./vendor/bin/sail up

Successivamente, esegui il comando seguente per eseguire l'applicazione tramite Artisan sul server PHP.

 php artisan serve
Servire l'applicazione Laravel con Artisan
Servire l'applicazione Laravel con Artisan

Dal browser Web, accedere all'applicazione in esecuzione all'indirizzo http://127.0.0.1:8000. L'applicazione visualizzerà la pagina di benvenuto di Laravel nel percorso predefinito.

Pagina di benvenuto dell'applicazione Laravel
Pagina di benvenuto dell'applicazione Laravel

Come aggiungere Laravel Scout all'applicazione

Nel tuo terminale, inserisci il comando per abilitare il gestore di pacchetti Composer PHP per aggiungere Laravel Scout al progetto.

 composer require laravel/scout

Successivamente, pubblica il file di configurazione di Scout utilizzando il comando vendor:publish. Il comando pubblicherà il file di configurazione scout.php nella directory di configurazione dell'applicazione.

 php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"

Ora modifica il file boilerplate .env in modo che contenga un valore booleano SCOUT_QUEUE .

Il valore SCOUT_QUEUE consentirà a Scout di mettere in coda le operazioni, fornendo tempi di risposta migliori. Senza di esso, i conducenti di scout come Meilisearch non rifletteranno immediatamente i nuovi record.

 SCOUT_QUEUE=true

Inoltre, modifica la variabile DB_HOST nel file .env in modo che punti al tuo localhost per utilizzare il database MySQL all'interno dei contenitori Docker.

 DB_HOST=127.0.0.1

Come contrassegnare un modello e configurare l'indice

Scout non abilita i modelli di dati ricercabili per impostazione predefinita. Devi contrassegnare esplicitamente un modello come ricercabile utilizzando il suo tratto Laravel\Scout\Searchable .

Inizierai creando un modello di dati per un'applicazione Train demo e contrassegnandolo come ricercabile.

Come creare un modello

Per l'applicazione Train , ti consigliamo di memorizzare i nomi segnaposto di ogni treno disponibile.

Esegui il comando Artisan di seguito per generare la migrazione e denominala create_trains_table .

 php artisan make:migration create_trains_table
Esecuzione di una migrazione denominata create_trains_table
Esecuzione di una migrazione denominata create_trains_table

La migrazione verrà generata in un file il cui nome combina il nome specificato e il timestamp corrente.

Apri il file di migrazione che si trova nella directory database/migrations/ .

Per aggiungere una colonna title, aggiungi il seguente codice dopo la colonna id() nella riga 17. Il codice aggiungerà una colonna title.

 $table->string('title');

Per applicare la migrazione, eseguire il comando seguente.

 php artisan migrate
Applicazione della migrazione Artisan
Applicazione della migrazione Artisan

Dopo aver eseguito le migrazioni del database, crea un file denominato Train.php nella directory app/Models/ .

Come aggiungere il tratto LaravelScoutSearchable

Contrassegna il modello Train per la ricerca aggiungendo il tratto Laravel\Scout\Searchable al modello, come mostrato di seguito.

 <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Laravel\Scout\Searchable; class Train extends Model { use Searchable; public $fillable = ['title'];

Inoltre, è necessario configurare gli indici di ricerca sovrascrivendo il metodo searchable . Il comportamento predefinito di Scout manterrebbe il modello in modo che corrisponda al nome della tabella del modello.

Quindi, aggiungi il seguente codice al file Train.php sotto il codice del blocco precedente.

 /** * Retrieve the index name for the model. * * @return string */ public function searchableAs() { return 'trains_index'; } }

Come usare Algolia con Scout

Per la prima ricerca full-text con Laravel Scout, utilizzerai il driver Algolia. Algolia è una piattaforma software as a service (SaaS) utilizzata per effettuare ricerche in grandi quantità di dati. Fornisce una dashboard web per gli sviluppatori per gestire i loro indici di ricerca e una solida API a cui puoi accedere tramite un kit di sviluppo software (SDK) nel tuo linguaggio di programmazione preferito.

All'interno dell'applicazione Laravel, utilizzerai il pacchetto client Algolia per PHP.

Come impostare l'Algolia

Innanzitutto, devi installare il pacchetto client di ricerca Algolia PHP per la tua applicazione.

Esegui il comando seguente.

 composer require algolia/algoliasearch-client-php

Successivamente, devi impostare l'ID applicazione e le credenziali della chiave API segreta da Algolia nel file .env .

Utilizzando il tuo browser web, accedi alla dashboard di Algolia per ottenere l'ID applicazione e le credenziali della chiave API segreta.

Fai clic su Impostazioni nella parte inferiore della barra laterale sinistra per accedere alla pagina Impostazioni .

Successivamente, fai clic su Chiavi API nella sezione Team e accesso della pagina Impostazioni per visualizzare le chiavi per il tuo account Algolia.

Navigazione alla pagina delle chiavi API su Algolia Cloud
Pagina delle chiavi API su Algolia Cloud

Nella pagina Chiavi API, prendi nota dei valori ID applicazione e Chiave API amministratore . Utilizzerai queste credenziali per autenticare la connessione tra l'applicazione Laravel e Algolia.

Visualizzazione dell'ID applicazione e delle chiavi API di amministrazione dalla pagina Chiavi API Algolia
ID applicazione e chiavi API di amministrazione

Aggiungi il codice seguente al tuo file .env utilizzando il tuo editor di codice e sostituisci i segnaposto con i corrispondenti segreti dell'API Algolia.

 ALGOLIA_APP_ID=APPLICATION_ID ALGOLIA_SECRET=ADMIN_API_KEY

Inoltre, sostituisci la variabile SCOUT_DRIVER con il codice seguente per modificare il valore da meilisearch ad algolia . La modifica di questo valore istruirà Scout a utilizzare il driver Algolia.

 SCOUT_DRIVER=algolia

Come creare i controller dell'applicazione

All'interno della directory app/Http/Controllers/ , crea un file TrainSearchController.php per archiviare un controller per l'applicazione. Il controller elencherà e aggiungerà i dati al modello Train .

Aggiungi il seguente blocco di codice nel file TrainSearchController.php per creare il controller.

 <?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(); } }

Come creare i percorsi delle applicazioni

In questo passaggio, creerai i percorsi per elencare e aggiungere nuovi treni al database.

Apri il tuo file route/web.php e sostituisci il codice esistente con il blocco sottostante.

 <?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');

Il codice precedente definisce due percorsi nell'applicazione. La richiesta GET per il percorso /trains-lists elenca tutti i dati dei treni memorizzati. La richiesta POST per il percorso /create-item crea nuovi dati del treno.

Come creare le viste dell'applicazione

Crea un file all'interno della directory resources/views/ e chiamalo Train-search.blade.php . Il file visualizzerà l'interfaccia utente per la funzionalità di ricerca.

Aggiungi il contenuto del blocco di codice sottostante nel file Train-search.blade.php per creare una singola pagina per la funzionalità di ricerca.

 <!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>

Il codice HTML sopra contiene un elemento del modulo con un campo di input e un pulsante per digitare il titolo di un treno prima di salvarlo nel database. Il codice ha anche una tabella HTML che mostra i dettagli id ​​, title , created_at e updated_at di una voce del treno all'interno del database.

Come utilizzare la ricerca in Algolia

Per visualizzare la pagina, vai a http://127.0.0.1:8000/trains-lists dal tuo browser web.

Visualizzazione dei dati del modello di treno visualizzati all'interno della pagina degli elenchi dei treni
Formare i dati del modello

Il database è attualmente vuoto, quindi è necessario inserire il titolo di un treno dimostrativo nel campo di immissione e fare clic su Crea nuovo treno per salvarlo.

Inserimento di un nuovo ingresso treno
Inserimento di un nuovo ingresso treno

Per utilizzare la funzione di ricerca, digita una parola chiave da qualsiasi titolo di treno salvato nel campo di immissione Inserisci titolo per la ricerca e fai clic su Cerca .

Come mostrato nell'immagine sottostante, verranno visualizzate solo le voci di ricerca che contengono la parola chiave nei loro titoli.

Utilizzando la funzione di ricerca per trovare una voce del treno
Utilizzando la funzione di ricerca per trovare una voce del treno

Meilisearch con Laravel Scout

Meilisearch è un motore di ricerca open source incentrato su velocità, prestazioni e una migliore esperienza degli sviluppatori. Condivide diverse caratteristiche con Algolia, utilizzando gli stessi algoritmi, strutture dati e ricerca, ma con un linguaggio di programmazione diverso.

Gli sviluppatori possono creare e ospitare autonomamente un'istanza Meilisearch all'interno della loro infrastruttura locale o cloud. Meilisearch ha anche un'offerta cloud beta simile ad Algolia per gli sviluppatori che desiderano utilizzare il prodotto senza gestirne l'infrastruttura.

Nel tutorial, hai già un'istanza locale di Meilisearch in esecuzione all'interno dei tuoi contenitori Docker. Ora estenderai la funzionalità Laravel Scout per utilizzare l'istanza Meilisearch.

Per aggiungere Meilisearch all'applicazione Laravel, esegui il comando seguente nel terminale del tuo progetto.

 composer require meilisearch/meilisearch-php

Successivamente, è necessario modificare le variabili Meilisearch all'interno del file .env per configurarlo.

Sostituire le variabili SCOUT_DRIVER , MEILISEARCH_HOST e MEILISEARCH_KEY nel file .env con quelle seguenti.

 SCOUT_DRIVER=meilisearch MEILISEARCH_HOST=http://127.0.0.1:7700 MEILISEARCH_KEY=LockKey

La chiave SCOUT_DRIVER specifica il driver che Scout deve utilizzare, mentre MEILISEARCH_HOST rappresenta il dominio in cui è in esecuzione l'istanza di Meilisearch. Sebbene non sia richiesto durante lo sviluppo, si consiglia di aggiungere MEILISEARCH_KEY in produzione.

Nota: commentare l'ID e il segreto Algolia quando si utilizza Meilisearch come driver preferito.

Dopo aver completato le configurazioni .env , è necessario indicizzare i record preesistenti utilizzando il comando Artisan di seguito.

 php artisan scout:import "App\Models\Train"

Laravel Scout con motore di database

Il motore di database di Scout potrebbe essere più adatto per le applicazioni che utilizzano database più piccoli o gestiscono carichi di lavoro meno intensivi. Attualmente, il motore di database supporta PostgreSQL e MySQL.

Questo motore utilizza clausole "where-like" e indici full-text rispetto al database esistente, consentendogli di trovare i risultati di ricerca più pertinenti. Non è necessario indicizzare i record quando si utilizza il motore del database.

Per utilizzare il motore di database, è necessario impostare la variabile SCOUT_DRIVER .env sul database.

Apri il file .env all'interno dell'applicazione Laravel e modifica il valore della variabile SCOUT_DRIVER .

 SCOUT_DRIVER = database

Dopo aver modificato il driver nel database, Scout passerà all'utilizzo del motore del database per la ricerca full-text.

Motore di raccolta con Laravel Scout

Oltre al motore di database, Scout offre anche un motore di raccolta. Questo motore utilizza clausole "where" e filtri di raccolta per estrarre i risultati di ricerca più pertinenti.

A differenza del motore di database, il motore di raccolta supporta tutti i database relazionali supportati anche da Laravel.

Puoi utilizzare il motore di raccolta impostando la variabile di ambiente SCOUT_DRIVER su collection o specificando manualmente il driver di raccolta nel file di configurazione di Scout.

 SCOUT_DRIVER = collection

Esploratore con Elasticsearch

Con la forza delle query Elasticsearch, Explorer è un moderno driver Elasticsearch per Laravel Scout. Offre un driver Scout compatibile e vantaggi come l'archiviazione, la ricerca e l'analisi di enormi quantità di dati in tempo reale. Elasticsearch con Laravel offre risultati in millisecondi.

Per utilizzare il driver Elasticsearch Explorer nella tua applicazione Laravel, dovrai configurare il file boilerplate docker-compose.yml generato dallo script Laravel-Scout. Aggiungerai le configurazioni aggiuntive per Elasticsearch e riavvierai i contenitori.

Apri il tuo file docker-compose.yml e sostituisci il suo contenuto con quanto segue.

 # 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

Quindi, esegui il comando seguente per eseguire il pull della nuova immagine Elasticsearch che hai aggiunto al file docker-compose.yml .

 docker-compose up

Quindi, esegui il comando Composer di seguito per installare Explorer nel progetto.

 composer require jeroen-g/explorer

È inoltre necessario creare un file di configurazione per il driver Explorer.

Esegui il comando Artisan di seguito per generare un file explorer.config per l'archiviazione delle configurazioni.

 php artisan vendor:publish --tag=explorer.config

Il file di configurazione generato sopra sarà disponibile nella directory /config .

Nel tuo file config/explorer.php , puoi fare riferimento al tuo modello usando la chiave indexes .

 'indexes' => [ \App\Models\Train::class ],

Modificare il valore della variabile SCOUT_DRIVER all'interno del file .env in elastic per configurare Scout in modo che utilizzi il driver Explorer.

 SCOUT_DRIVER = elastic

A questo punto, utilizzerai Explorer all'interno del modello Train implementando l'interfaccia Explorer e sovrascrivendo il metodo mappableAs() .

Apri il file Train.php all'interno della directory App > Models e sostituisci il codice esistente con il codice sottostante.

 <?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, ]; } }

Con il codice che hai aggiunto sopra, ora puoi utilizzare Explorer per cercare testo all'interno del modello Train .

Laravel + Scout = integrazione della ricerca full-text veloce, robusta e pulita. Crea un'app demo e provala con questa guida ️ Click to Tweet

Riepilogo

Per gli sviluppatori PHP, Laravel e i componenti aggiuntivi come Scout semplificano l'integrazione di funzionalità di ricerca full-text veloci e robuste. Con il motore di database, il motore di raccolta e le funzionalità di Meilisearch ed Elasticsearch, puoi interagire con il database della tua app e implementare meccanismi di ricerca avanzati in pochi millisecondi.

La gestione e l'aggiornamento senza problemi del tuo database significa che i tuoi utenti ricevono un'esperienza ottimale mentre il tuo codice rimane pulito ed efficiente.

Con le nostre soluzioni di hosting di applicazioni e database, Kinsta è il vostro sportello unico per tutte le vostre moderne esigenze di sviluppo di Laravel. I primi $ 20 li offriamo noi.