Piccolo tutorial per creare una nuova applicazione Laravel senza l’uso di Docker o Sail.
Sommario
Installare composer
Composer è uno strumento per la gestione delle dipendenze in PHP. Permette di dichiarare le librerie da cui dipende il tuo progetto e le gestirà per te (installazione o aggiornamento che sia).
Per esempio se utilizzi Faker per popolare di dati di prova il database della tua applicazione, compser ti solleva dal compito di installarea posteriori Faker se non c’è o di aggiornarlo se hai una versione vecchia.
Nella pagina di download c’è lo script PHP da linea di comando per installare Composer. Ovviamente dobbiamo già avere installato una versione di PHP a bordo. Io ho ancora PHP 7.4.16 (cli), mentre attualmente PHP è già alla versione 8.
Dal mio ultimo progetto PHP è passato un po’ di tempo ho questo messaggio:
$ composer create-project laravel/laravel example-app Warning from https://packagist.org: Support for Composer 1 is deprecated and some packages will not be available. You should upgrade to Composer 2. See https://blog.packagist.com/deprecating-composer-1-support/
Quindi procedo all’upgrade. Tra l’altro gli improvements raggiunti con l’ultima versione sono davvero notevoli:
Per cui si procede all’update.
$ composer self-update
Un paio di inghippi:
[RuntimeException]
SHA384 is not supported by your openssl extension, could not verify the phar file integrity
Occorre allora rimuovere le versioni precedenti e installare l’ultima versione di Composer:
Rimozione della versione installata e sostituzione con la nuova:
$ sudo rm -f /usr/local/bin/composer $ sudo curl -s https://getcomposer.org/installer | php $ sudo mv composer.phar /usr/local/bin/composer
Creare una nuova applicazione Laravel
Utilizzando Composer possiamo creare una nuova applicazione Laravel:
$ composer create-project laravel/laravel logisticmapper
Creating a "laravel/laravel" project at "./logisticmapper"
Installing laravel/laravel (v8.6.5)
- Downloading laravel/laravel (v8.6.5)
- Installing laravel/laravel (v8.6.5): Extracting archive
Created project in /home/marcob/IdeaProjects/PHP/camon/logisticmapper
> @php -r "file_exists('.env') || copy('.env.example', '.env');"
Loading composer repositories with package information
Updating dependencies
Lock file operations: 111 installs, 0 updates, 0 removals
- Locking asm89/stack-cors (v2.0.3)
- Locking brick/math (0.9.3)
- Locking dflydev/dot-access-data (v3.0.1)
...
- Locking webmozart/assert (1.10.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 111 installs, 0 updates, 0 removals
- Downloading doctrine/inflector (2.0.4)
- Downloading symfony/polyfill-ctype (v1.23.0)
- Downloading symfony/polyfill-php80 (v1.23.1)
...
- Downloading phpunit/phpunit (9.5.10)
- Installing doctrine/inflector (2.0.4): Extracting archive
- Installing doctrine/lexer (1.2.1): Extracting archive
- Installing symfony/polyfill-ctype (v1.23.0): Extracting archive
...
- Installing phpunit/phpunit (9.5.10): Extracting archive
70 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: facade/ignition
Discovered Package: fruitcake/laravel-cors
...
Discovered Package: nunomaduro/collision
Package manifest generated successfully.
78 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> @php artisan vendor:publish --tag=laravel-assets --ansi
No publishable resources for tag [laravel-assets].
Publishing complete.
> @php artisan key:generate --ansi
Application key set successfully.
$ cd logisticmapper
$ ls -l
totale 388
drwxrwxr-x 12 marcob marcob 4096 nov 8 15:21 ./
drwxrwxr-x 3 marcob marcob 4096 nov 8 15:21 ../
drwxrwxr-x 7 marcob marcob 4096 ott 26 17:20 app/
-rwxr-xr-x 1 marcob marcob 1686 ott 26 17:20 artisan*
drwxrwxr-x 3 marcob marcob 4096 ott 26 17:20 bootstrap/
-rw-rw-r-- 1 marcob marcob 1737 ott 26 17:20 composer.json
-rw-rw-r-- 1 marcob marcob 291109 nov 8 15:21 composer.lock
drwxrwxr-x 2 marcob marcob 4096 ott 26 17:20 config/
drwxrwxr-x 5 marcob marcob 4096 ott 26 17:20 database/
-rw-rw-r-- 1 marcob marcob 258 ott 26 17:20 .editorconfig
-rw-rw-r-- 1 marcob marcob 950 nov 8 15:22 .env
-rw-rw-r-- 1 marcob marcob 899 ott 26 17:20 .env.example
-rw-rw-r-- 1 marcob marcob 111 ott 26 17:20 .gitattributes
-rw-rw-r-- 1 marcob marcob 207 ott 26 17:20 .gitignore
-rw-rw-r-- 1 marcob marcob 473 ott 26 17:20 package.json
-rw-rw-r-- 1 marcob marcob 1202 ott 26 17:20 phpunit.xml
drwxrwxr-x 2 marcob marcob 4096 ott 26 17:20 public/
-rw-rw-r-- 1 marcob marcob 3999 ott 26 17:20 README.md
drwxrwxr-x 6 marcob marcob 4096 ott 26 17:20 resources/
drwxrwxr-x 2 marcob marcob 4096 ott 26 17:20 routes/
-rw-rw-r-- 1 marcob marcob 563 ott 26 17:20 server.php
drwxrwxr-x 5 marcob marcob 4096 ott 26 17:20 storage/
-rw-rw-r-- 1 marcob marcob 194 ott 26 17:20 .styleci.yml
drwxrwxr-x 4 marcob marcob 4096 ott 26 17:20 tests/
drwxrwxr-x 44 marcob marcob 4096 nov 8 15:22 vendor/
-rw-rw-r-- 1 marcob marcob 559 ott 26 17:20 webpack.mix.js
$ php artisan serve
Starting Laravel development server: http://127.0.0.1:8000
[Mon Nov 8 15:27:19 2021] PHP 7.4.16 Development Server (http://127.0.0.1:8000) started
Puntando il browser sull’url visualizzato si ottiene

Artisan è l’interfaccia a riga di comando inclusa in Laravel. Artisan esiste alla radice della tua applicazione come script artisan e fornisce una serie di comandi utili che possono aiutarti mentre crei la tua applicazione. Per visualizzare un elenco di tutti i comandi Artisan disponibili, puoi utilizzare il comando list:
$ php artisan list
Laravel Framework 6.18.8
Usage:
command [options] [arguments]
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
--env[=ENV] The environment the command should run under
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Available commands:
clear-compiled Remove the compiled class file
down Put the application into maintenance mode
env Display the current framework environment
help Displays help for a command
inspire Display an inspiring quote
...
E via molte altre righe. È uno script molto ricco di comandi, consente di fare molte cose.
Il comando serve, come visto sopra, permette di utilizzare l’applicazione da http://127.0.0.1:8000.
Possiamo anche creare un VirtualHost Apache attraverso il quale l’applicazione risponda invece ad un indirizzo fittizio del tipo http://example-app.local/.
I VirtualHost sono definiti nella mia installazione di Apache sotto /etc/apache2/sites-available/:
<VirtualHost *:80>
DocumentRoot /var/www/html/example-app/public/
ServerName example-app.local
Options Indexes FollowSymLinks
<Directory /var/www/html/example-app//public/>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error_app.log
CustomLog ${APACHE_LOG_DIR}/access_app.log combined
</VirtualHost>
Abbiamo anche specializzato la produzione dei log di accesso/errore di Apache per la nostra applicazione.
Apache va riavviato per rileggere i file di configurazione:
$ sudo service apache2 restart
Dobbiamo anche definire nel DNS, o nel file host locale del computer in cui stiamo sviluppando, una nuova entry per risolvere il nome host in un indirizzo IP; nel secondo caso:
127.0.0.1 example-app.local
Definizione del model
Il modello è una rappresentazione dello spazio dei dati nell’applicazione, che deve regolarne le dipendenze relazionali e le operazioni su di essi (CRUD).
Per implementare un modello abbiamo bisogno dell’ORM (Object Relationship Mapper) che permette di mappare precisamente il modello ad oggetti PHP con lo strato business (dati) realizzato con un DBMS.
L’ORM di Laravel si chama Eloquent ed è molto potente. Per creare una tabella dobbiamo innanzitutto fornire a Laravel le coordinate del database. Creiamo innanzituto il database:
mysql> create database logisticmapper; Query OK, 1 row affected (0,02 sec) mysql> create user 'logmap_user'@'localhost' identified by 'l0gm4pus3r'; Query OK, 0 rows affected (0,03 sec) mysql> GRANT ALL ON logisticmapper.* TO 'logmap_user'@'localhost'; Query OK, 0 rows affected (0,03 sec)
Dopodiché mettiamo i parametri nel file .env
... DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=logsticmapper DB_USERNAME=logmap_user DB_PASSWORD=l0gm4pus3r ...
Creazione di una tabella
Dopo possiamo cominciare a creare le tabelle
$ php artisan make:migration create_client_table Created Migration: 2021_11_08_145944_create_client_table
Ora definamo la tabella attraverso Eloquent: il comando precedente è una “migrazione”: significa una operazione di definizione di un oggetto Database e del simultaneo collegamento con lo spazio delle calsse PHP. Editando il file 2021_11_08_145944_create_client_table vediamo al suo interno una classe già predisposta da Laravel che noi dobbiamo implementare aggiungendo i campi, i vincoli e le relazioni che ci servono:
class CreateClientTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('client', function (Blueprint $table) {
$table->id()->autoIncrement();
$table->string('name');
$table->string('website')->nullable();
$table->string('email')->unique();
$table->string('mainPhoneNo');
$table->float('latitude')->nullable();
$table->float('longitude')->nullable();
$table->timestamps();
$table->softDeletes();
$table->index(['name']);
});
}
...
Le calssi elle migrazioni hanno due metodi standard: up() e down(); il primo crea la tabella, il secondo la elimina. Viene invocato uno o l’altro a seconda del comando di migrazione.
Ora, se facciamo girare la migrazione si otterrà l’effetto di creare una tabella nel database:
$ php artisan migrate Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (281.55ms) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (587.28ms) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (465.73ms) Migrating: 2019_12_14_000001_create_personal_access_tokens_table Migrated: 2019_12_14_000001_create_personal_access_tokens_table (718.36ms) Migrating: 2021_11_08_145944_create_client_table Migrated: 2021_11_08_145944_create_client_table (257.00ms)
Posso vedere la tabella nel db:
$ mysql -u logmap_user -p logisticmapper mysql> desc client; +-------------+-----------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+-----------------+------+-----+---------+----------------+ | id | bigint unsigned | NO | PRI | NULL | auto_increment | | name | varchar(255) | NO | MUL | NULL | | | website | varchar(255) | YES | | NULL | | | email | varchar(255) | NO | UNI | NULL | | | mainPhoneNo | varchar(255) | NO | | NULL | | | latitude | double(8,2) | YES | | NULL | | | longitude | double(8,2) | YES | | NULL | | | created_at | timestamp | YES | | NULL | | | updated_at | timestamp | YES | | NULL | | | deleted_at | timestamp | YES | | NULL | | +-------------+-----------------+------+-----+---------+----------------+ 10 rows in set (0,01 sec)
Per eliminarla, si torna indietro col comando rollback:
$ php artisan migrate:rollback
e verrà lanciato il metodo down()
Creazione di una tabella con una relazione
Una seconda tabella potrà avere un vincolo relazionale con la precedente; supponiamo di avere bisogno di una tabella di veicoli che fanno parte del parco automezzi del Cliente:
$ php artisan make:migration create_vehicle_table
Created Migration: 2021_11_10_154349_create_vehicle_table
Implementiamo i campi della tabella:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateVehicleTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('vehicle', function (Blueprint $table) {
$table->id();
$table->string('model');
$table->string('licensePlate');
$table->integer('year');
$table->bigInteger('client_id')->unsigned();
$table->foreign('client_id')->references('id')->on('client');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('vehicle');
}
}
Nota bene: nella definizione dell’identificativo del record, la convenzione con Eloquent è che se dichiariamo un campo id, per Eloquent sarà implicitamente:
- integer
- not null
- auto incrementing
cioè verrò dichiara auto_increment nella definizione in DDL.
È comunque consentito di derogare dalla convenzione quando si vuole (esempio, una chiave che non si chiama id, che non è autoincrement e che non è un numero intero).
In particolare, il metodo $table->foreign() consente di creare la relazione di chiave esterna. lanciamo la mirgrazione:
$ php artisan migrate Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (179.97ms) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (177.75ms) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (156.99ms) Migrating: 2019_12_14_000001_create_personal_access_tokens_table Migrated: 2019_12_14_000001_create_personal_access_tokens_table (359.57ms) Migrating: 2021_11_08_145944_create_client_table Migrated: 2021_11_08_145944_create_client_table (792.39ms) Migrating: 2021_11_10_154349_create_vehicle_table Migrated: 2021_11_10_154349_create_vehicle_table (748.10ms)
Attenzione!
Nella nuova versione di Laravel, le chiavi primarie sono definite come binginteger unsigned. Qualunque campo che debba riferirsi a questi campi va dichiarato allo stesso modo, biginteger unsigned. Vedi ad esempio l’attributo vehicle.client_id che ospita la chiave esterna di client.id.
Se guardiamo nel database vedremo:
mysql> desc vehicle; +--------------+-----------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------------+-----------------+------+-----+---------+----------------+ | id | bigint unsigned | NO | PRI | NULL | auto_increment | | model | varchar(255) | NO | | NULL | | | licensePlate | varchar(255) | NO | | NULL | | | year | int | NO | | NULL | | | client_id | bigint unsigned | NO | MUL | NULL | | | created_at | timestamp | YES | | NULL | | | updated_at | timestamp | YES | | NULL | | +--------------+-----------------+------+-----+---------+----------------+
E alla fine la creazione del modello
Artisan creera la classe collegandola alla tabella del database:
$ php artisan make:model Client --migration Model created successfully. Created Migration: 2021_11_10_164311_create_clients_table
La nuova classe la troviamo in app/Models/Client.php:
All’inizio è veramente neat:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Client extends Model
{
use HasFactory;
}

Commenti recenti