Blog / Servir un projet Laravel sur le Web, en Natif et sur Mobile avec Tauri
9 min - 14 Jan 2025
TL;DR: Comment afficher un projet Laravel simultanément sur le Web, son système d’exploitation et son appareil mobile à l’aide de Tauri.
Vous trouverez un projet Laravel d’exemple sur ce Github Repository. Découvrez-en plus sur Capsules, X ou Bluesky.
En préambule : NativePHP, un framework permettant de construire des applications natives avec PHP, n’est plus à présenter. Développé par Marcel Pociot et Simon Hamp, ce framework repose actuellement sur Electron. Cependant, une nouvelle approche basée sur Tauri est en cours d’implémentation.
Cet article explique comment servir en local un même projet sur trois supports différents, en utilisant le remplacement de module à chaud pour visualiser les modifications en temps réel. Le web est servi via la commande php artisan serve:web
. Le natif via la commande php artisan serve:desktop
. Le mobile via la commande php artisan serve:mobile
.
Dans l’intérêt du lecteur et pour limiter la longueur de cet article, certaines informations seront directement intégrées dans les commandes afin d’éviter des allers-retours inutiles.
La commande de base, php artisan serve:web
, se résume à ceci :
app/Console/Commands/ServeWeb.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Process;
use function Laravel\Prompts\intro;
use function Laravel\Prompts\note;
class ServeWeb extends Command
{
protected $signature = 'serve:web';
public function handle()
{
intro( 'Running Web Environment' );
$this->initViteServer();
$this->initPHPServer();
}
private function initViteServer() : void
{
note( "Starting Vite Development Server" );
Process::start( "npm run dev:vite:web" );
}
private function initPHPServer() : void
{
note( "Starting PHP Server" );
Process::forever()->tty()->run( "php artisan serve --port=50000" );
}
}
Plusieurs particularités sont à noter :
1. Un serveur de développement Vite est lancé avec la commande npm run dev:vite:web
au lieu de la commande classique npm run dev
. La commande php artisan serve
est utilisée avec le port 50000
.
2. Dans le carde de cet article, il est essentiel de séparer les différents serveurs Vite. Ces serveurs seront lancés simultanément et doivent, lorsqu’ils détectent une modification, notifier leur support de manière indépendante. Contrairement au serveur PHP, qui conserve le port 50000
. Les différents ports sont choisis arbitrairement.
Il est ensuite nécessaire de configurer le fichier package.json
pour y ajouter la commande npm run dev:vite:web
package.json
{
"private": true,
"type": "module",
"scripts": {
"build": "vite build",
"dev": "vite",
"dev:vite:web" : "vite --config vite.web.config.js"
},
"devDependencies": {
"autoprefixer": "^10.4.20",
"axios": "^1.7.4",
"concurrently": "^9.0.1",
"laravel-vite-plugin": "^1.0",
"postcss": "^8.4.47",
"tailwindcss": "^3.4.13",
"vite": "^6.0"
}
}
Et créer le fichier de configuration Vite spécifique à la commande web:serve
.
vite.web.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig( {
plugins : [
laravel( {
input : [ 'resources/css/app.css', 'resources/js/app.js' ],
refresh : true,
} ),
],
server : {
port : 50001
}
} );
Les ports 50000
et 50001
sont respectivement alloués au serveur PHP et au serveur Vite pour la commande web:serve
.
Voici le résultat obtenu lors de l’exécution de la commande php artisan web:serve
:
> php artisan web:serve
Running Web Environment
Starting Vite Development Server
Starting PHP Server
INFO Server running on [<http://127.0.0.1:50000>].
Press Ctrl+C to stop the server
2024-07-16 01:52:18 / ............................................... ~ 0.16ms
2024-07-16 01:52:18 /favicon.ico .................................. ~ 506.93ms
Pour l’instant, rien de nouveau.
L’étape suivante : créer une application native avec Tauri. Pour cela, il est nécessaire de mettre en place l’infrastructure Tauri. Mais qu’est-ce que Tauri.
Tauri est un framework pour construire de petits binaires rapides pour la majorité des plateformes natives et mobiles. Il a été conçu pour offrir une alternative plus légère à Electron. Tauri propose des solutions mobiles depuis la version 2.0.
Pour installer Tauri, rien de plus simple :
npm install --save-dev @tauri-apps/cli@2.2.2
Il faut ensuite ajouter le binaire aux scripts du fichier package.json
.
package.json
"scripts" : {
...
"tauri" : "tauri"
...
Pour ensuite initialiser Tauri à l’aide de la commande npm run tauri init
:
> npm run tauri init
tauri
tauri init
✔ What is your app name? · Tauri App
✔ What should the window title be? · Tauri
✔ Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri/tauri.conf.json" file that will be created? · ../public
✔ What is the url of your dev server? · <http://127.0.0.1:50000>
✔ What is your frontend dev command?
✔ What is your frontend build command?
Le dossier src-tauri
apparaît alors. Il est composé de plusieurs dossiers et fichiers :
> src-tauri
> capabilities
> icons
> src
.gitignore
build.rs
Cargo.tml
tauri.conf.json
Une fois Tauri installé, la génération d’une application native peut commencer. Pour cela, une nouvelle commande doit être créée : ServeDesktop
.
app/Console/Commands/ServeDesktop.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Facades\File;
use function Laravel\Prompts\intro;
use function Laravel\Prompts\note;
class ServeDesktop extends Command
{
protected $signature = 'serve:desktop';
public function handle()
{
intro( 'Running Desktop Environment' );
$this->initViteServer();
$this->initPHPServer();
$this->initTauriServer();
}
private function initTauriServer() : void
{
note( 'Starting Desktop App' );
if( ! File::exists( base_path( 'src-tauri/target' ) ) )
{
Process::path( 'src-tauri' )->forever()->tty()->run( "cargo build" );
}
Process::forever()->tty()->run( "npm run dev:tauri:desktop -- --port=50003" );
}
private function initViteServer() : void
{
note( "Starting Vite Development Server" );
Process::start( "npm run dev:vite:desktop" );
}
private function initPHPServer() : void
{
note( "Starting PHP Server" );
Process::tty()->start( "php artisan serve --port=50000" );
}
}
1. Trois processus seront lancés simultanément.
2. Les ports utilisés pour la commande desktop
sont 50000
, 50002
et 5003
3. La méthode initTauriServer
vérifie si le dossier src-tauri/target
existe. Si cela n’est pas le cas, cela indique que les dépendances liées à Tauri n’ont pas été installées.
4. Process::tty()
permet d’afficher les informations dans le terminal.
5. Process::forever()->run()
et Process::start()
exécutent une commande sans timeout. start
s’exécute en arrière-plan, tandis que run
reste le processus principal.
Le port du serveur PHP reste 50000
. Inutile de lancer plusieurs serveurs PHP : Si le serveur est déjà en cours d’exécution, un message d’erreur apparaîtra : Failed to listen on 127.0.0.1:50000 (reason: Address already in use)
. Pas d’inquiétude, cela signifie simplement que le serveur est toujours actif.
C’est au tour de la configuration Vite pour le pendant desktop
.
vite.desktop.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig( {
plugins : [
laravel( {
input : [ 'resources/css/app.css', 'resources/js/app.js' ],
refresh : true,
} ),
],
clearScreen : false,
server : {
port : 50002
}
} );
1. Les ports utilisés pour la commande ServeDesktop
sont : 50000
pour PHP, 50002
pour Vite et 50003
pour Tauri.
2. clearScreen : false
empêche Vite d’effacer l’écran avant d’afficher de nouvelles données.
Les commandes npm run dev:vite:desktop
et npm run dev:tauri:desktop
font leur apparition dans le fichier package.json
. La commande dev:tauri:desktop
représente simplement la commande tauri dev
.
package.json
...
"scripts": {
"dev:vite:web": "vite --config vite.web.config.js",
"dev:vite:desktop": "vite --config vite.desktop.config.js",
"dev:tauri:desktop": "tauri dev"
},
...
Le résultat :
> php artisan desktop:serve
Running Desktop Environment
Starting Desktop App
Updating crates.io index
Locking 471 packages to latest compatible versions
...
Compiling tauri-macros v2.0.4
Finished `dev` profile [unoptimized + debuginfo] target(s) in 36.49s
Starting Vite Development Server
Starting PHP Server
> dev:tauri:desktop
> tauri dev --port=50003
INFO Server running on [<http://127.0.0.1:50000>].
...
Info Watching /article/src-tauri for changes...
Compiling libc v0.2.169
Compiling core-foundation-sys v0.8.7
Compiling objc-sys v0.3.5
...
Compiling tauri-macros v2.0.4
Compiling app v0.1.0 (/article/src-tauri)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 23.44s
Running `target/debug/app`
2024-07-16 02:12:51 / ............................................... ~ 502.58ms
La dernière étape de cet article concerne l’application mobile. Une nouvelle commande est introduite : ServeMobile
.
app/Console/Commands/ServeMobile.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\File;
use function Laravel\Prompts\intro;
use function Laravel\Prompts\note;
use function Laravel\Prompts\warning;
class ServeMobile extends Command
{
protected $signature = 'serve:mobile {--android} {--ios}';
public function handle()
{
if( ! $this->option( 'ios' ) && ! $this->option( 'android' ) ) return warning( "A device option is needed : 'mobile:serve --android' or 'mobile:serve --ios'" );
intro( 'Running Mobile Environment' );
$this->initViteServer();
$this->initPHPServer();
$this->initTauriServer();
}
private function initTauriServer() : void
{
$device = $this->option( 'ios' ) ? 'ios' : 'android';
note( Str::headline( "Starting Mobile {$device} App" ) );
if( ! File::exists( base_path( 'src-tauri/target' ) ) )
{
Process::path( 'src-tauri' )->forever()->tty()->run( "cargo build" );
}
if( ! File::exists( base_path( "src-tauri/gen/{$device}" ) ) )
{
Process::forever()->tty()->run( "npm run tauri {$device} init" );
}
Process::forever()->tty()->run( "npm run dev:tauri:mobile:{$device} -- --port=50005" );
}
private function initViteServer() : void
{
note( "Starting Vite Development Server" );
Process::start( "npm run dev:vite:mobile" );
}
private function initPHPServer() : void
{
note( "Starting PHP Server" );
Process::tty()->start( "php artisan serve --port=50000" );
}
}
1. Un paramètre est désormais obligatoire, le choix entre --android
ou --ios
.
2. Un avertissement est affiché si le paramètre n’est pas spécifié.
3. Comme pour la gestion des dépendances manquantes de Tauri, il en va de même pour les dossiers spécifiques aux applications mobiles ios
et android
, via la commande npm run tauri {$device} init
..
4. Les ports 50000
, 50004
et 50005
sont utilisés respectivement pour le serveur PHP, le serveur Vite et le serveur Tauri.
Un nouveau fichier de configuration Vite doit être créé à la racine du projet.
vite.mobile.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig( {
plugins : [
laravel( {
input : [ 'resources/css/app.css', 'resources/js/app.js' ],
refresh : true,
} ),
],
clearScreen : false,
server : {
host : '0.0.0.0',
port : 50004,
strictPort : true,
hmr : {
protocol : 'ws',
host : "192.168.0.8",
port : 50005,
},
}
} );
1. On y retrouve les ports 50004
et 50005
.
2. L’hôte doit être spécifié pour permettre à Vite d’acheminer les fichiers indispensables au bon fonctionnement de la page web vers l’émulateur Android, dans ce cas précis. Une commande utile pour identifier cet hôte est : ipconfig getifaddr en0
.
Les différents scripts sont ensuite ajoutés au fichier package.json
.
package.json
...
"scripts": {
"dev:vite:web": "vite --config vite.web.config.js",
"dev:vite:desktop": "vite --config vite.desktop.config.js",
"dev:vite:mobile" : "vite --config vite.mobile.config.js",
"tauri": "tauri",
"dev:tauri:desktop": "tauri dev",
"dev:tauri:mobile:android" : "tauri android dev",
"dev:tauri:mobile:ios" : "tauri ios dev"
},
...
Et voici le résultat lorsque la commande est lancée :
> php artisan mobile:serve --android
Running Mobile Environment
Starting Mobile Android App
> tauri
> tauri android init
Generating Android Studio project...
Info "/article/src-tauri" relative to "/article/src-tauri/gen/android/app" is "../../../"
victory: Project generated successfully!
Make cool apps! 🌻 🐕 🎉
Starting Vite Development Server
Starting PHP Server
> dev:tauri:mobile:android
> tauri android dev --port=50005
INFO Server running on [<http://127.0.0.1>.:50000].
Press Ctrl+C to stop the server
Detected Android emulators:
[0] INFO | Storing crashdata in: /tmp/android/emu-crash-34.1.20.db, detection is enabled for process: 36048
[1] Medium_Phone_API_33
Enter an index for a emulator above.
Emulator: 1
...
...
Compiling rustls-webpki v0.102.3
Compiling tokio-rustls v0.26.0s]
Compiling hyper-rustls v0.27.1s]
Compiling reqwest v0.12.12 [29s]
Finished `dev` profile [unoptimized + debuginfo] target(s) in 14.54s
Info symlinking lib "/article/src-tauri/target/aarch64-linux-android/debug/libapp_lib.so" in jniLibs dir "/Users/mho/Work/Projects/Development/Web/Personal/serve/src-tauri/gen/android/app/src/main/jniLibs/arm64-v8a"
Info "/article/src-tauri/target/aarch64-linux-android/debug/libapp_lib.so" requires shared lib "liblog.so"
Info "/article/src-tauri/target/aarch64-linux-android/debug/libapp_lib.so" requires shared lib "libandroid.so"
Info "/article/src-tauri/target/aarch64-linux-android/debug/libapp_lib.so" requires shared lib "libdl.so"
Info "/article/src-tauri/target/aarch64-linux-android/debug/libapp_lib.so" requires shared lib "libm.so"
Info "/article/src-tauri/target/aarch64-linux-android/debug/libapp_lib.so" requires shared lib "libc.so"
Performing Streamed Install
Success
Starting: Intent { cmp=com.tauri.dev/.MainActivity }
2024-07-16 02:34:48 / ................................................ ~ 0.12ms
2024-07-16 02:34:49 /favicon.ico ..................................... ~ 0.06ms
Un choix d’émulateur sera proposé, ici Emulator : 1
, qui correspond à l’appareil virtuel Medium_Phone_API_33
créé à partir d’Android Studio. Ce lien explique comment créer un appareil virtuel Android.
Et lorsque les trois commandes sont exécutées simultanément :
Ravi d’avoir pu aider !