Laravel 11 e Vue.js: progettazione pagina Vue, Datatable con Laravel API

Laravel 11 e Vue.js è una combinazione ottimale per scrivere codice pulito ed efficiente.
In questo tutorial vediamo insieme come creare una pagine con Vue.js in laravel 11 utilizzando laravel API.
Per provare Vue.js creeremo un endpoint API per ottenere i post e li visualizzeremo in una tabella.
Tempo di lettura stimato: 8 minuti
L’esempio descritto in questo articolo è disponibile su Github.
Per leggere e comprendere al pieno questo articolo si consiglia la lettura dell’articolo Laravel 11 e Vue.js: installazione, configurazione ed esempi, e implementazione del codice che puoi trovare su repository GitHUB.
Database
Innanzitutto, dobbiamo creare un modello, una migrazione, un controller e un percorso API. Come anticipato nell’articolo Laravel 11 e Vue.js: installazione, configurazione ed esempi andiamo a gestire l’entità di dato Post.
Con questo comando Artisan andiamo a creare il modello Post e il file di migration:
php artisan make:model Post -m
Modifichiamo il file di Migration database/migrations/xxxx_create_posts_table.php come segue:
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->longText('content');
$table->timestamps();
});
}
Modifichiamo il file Model app/Models/Post.php come segue:
class Post extends Model
{
protected $fillable = [
'title',
'content',
];
}
Creiamo il controller PostController, e in particolare specifichiamo il Folder Api
php artisan make:controller Api/PostController
Nel controller per ora restituiremo solo una raccolta di tutti i post.
app/Http/Controllers/Api/PostController.php :
class PostController extends Controller
{
public function index()
{
return Post::all();
}
}
Andiamo ora a definire le Routes
. Poiché tutti i percorsi saranno per API
, li aggiungeremo al file routes/api.php
. In questo modo Laravel aggiungerà automaticamente un prefisso api/
alle rotte. Prima di ciò, dobbiamo eseguire il comando Artisan install:api
per preparare l’applicazione Laravel per l’API
.
php artisan install:api
Il comando Artisan appena eseguito, andrà a modificare il file app.php
presente sotto la directory bootstrap. Verifica il file bootstrap/app.php
:
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->statefulApi();
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();
Adesso possiamo aggiungere la rotta posts nel file routes/api.php, per fare il nostro esempio:
use App\Http\Controllers\Api\PostController;
Route::get('posts', [PostController::class, 'index']);
Chiamata API da Vue
Ora possiamo aggiungere il codice JS al componente Vue PostsIndex
che abbiamo creato nell’articolo Laravel 11 e Vue.js: installazione, configurazione ed esempi. Tutto il codice JS deve essere inserito nel tag <script>
.
Innanzitutto, dobbiamo aggiungere una variabile di dati a cui verranno assegnati tutti i post. Chiamiamo quella variabile posts
e assegniamo un valore predefinito di array vuoto.
Modifica il file resources/js/components/Posts/Index.vue:
<template>
// ...
</template>
<script>
export default {
data() {
return {
posts: []
}
}
}
</script>
Successivamente, creeremo un metodo fetchPosts
che effettuerà una richiesta di acquisizione e /api/posts
recupererà tutti i post.
Quando la richiesta ha esito positivo, il metodo .then
verrà eseguito. Aggiungerà tutti i post dalla risposta API alla variabile dati posts
.
Se qualcosa va storto, utilizziamo .catch
per avere eventuali errori. Per ora ci limiteremo all’uso del file console.log
.
Quando i componenti vengono montati (in altre parole, inizializzati), dobbiamo chiamare fetchPosts()
. La chiamata verrà fatta nel mounted
. Usiamo la sintassi di this.fetchPosts()
, infatti proviene dallo stesso componente.
Il file resources/js/components/Posts/Index.vue diventa:
<template>
// ...
</template>
<script>
export default {
data() {
return {
posts: []
}
},
mounted() {
this.fetchPosts()
},
methods: {
fetchPosts() {
axios.get('/api/posts')
.then(response => this.posts = response.data)
.catch(error => console.log(error))
}
}
}
</script>
Tre osservazioni:
- Le variabili si inizializzano in
data()
- Aggiungiamo funzioni custom in
methods:
- Se è necessario richiamare immediatamente tali funzioni, è necessario richiamarle nel
mounted()
Ora visita l’Home Page e apri l’Inspect del browser. Nella scheda Network vedrai che la richiesta API è stata effettuata /api/posts
, e mostra tutti i post nella parte Risposta.

Visualizzazione dei dati API nella tabella
Ora dobbiamo mostrare tutti questi post nella tabella. Per questo utilizzeremo la direttiva Vue v-for
nell’elemento riga tr
della tabella.
Sostituiamo i dati statici presenti in tabella, con v-for="post in posts"
:
resources/js/components/Posts/Index.vue :
<template>
<div class="overflow-hidden overflow-x-auto p-6 bg-white border-gray-200">
<div class="min-w-full align-middle">
<table class="min-w-full divide-y divide-gray-200 border">
<thead>
<tr>
<th class="px-6 py-3 bg-gray-50 text-left">
<span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">ID</span>
</th>
<th class="px-6 py-3 bg-gray-50 text-left">
<span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Title</span>
</th>
<th class="px-6 py-3 bg-gray-50 text-left">
<span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Content</span>
</th>
<th class="px-6 py-3 bg-gray-50 text-left">
<span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Created at</span>
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200 divide-solid">
<tr v-for="post in posts">
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ post.id }}
</td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ post.title }}
</td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ post.content }}
</td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ post.created_at }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
// ...
</script>
Praticamente post in posts
significa che prende la variabile posts
dalla parte del componente <script>
, ed esegue un ciclo, assegnando ogni elemento alla variabile post
. Equivale al foreach ($posts as $post)
del PHP.
Ora, dopo un refresh della pagina sul browser, dovresti vedere la tabella con tutti i post recuperati dall’API.

Composition API di Vue.js 3
Vue.js versione 3 ha introdotto un nuovo modo di scrivere i componenti Vue chiamato Composition API. Dalla versione 3.2 Vue.js viene rilasciato <script setup>
come un nuovo modo di scrivere componenti Vue.
Adiamo ora a vedere come combinare i componenti componibili <script setup>
per scrivere codice riutilizzabile. Creiamo quindi un componente componibile, e lo chiameremo resources/js/composables/posts.js
.
risorse/js/composables/posts.js :
import { ref } from 'vue'
export default function usePosts() {
const posts = ref([])
}
In questo componente componibile importiamo prima il file ref
. Ref
significa riferimento che renderà reattivi i post variabili.
Quindi, come in ogni file, dobbiamo esportare default . Nel nostro caso esporteremo il risultato della funzione usePosts
. Questo useXyz()
è il nome della convenzione di denominazione predefinita che inizia con use
.
Successivamente, dobbiamo aggiungere un metodo asincrono per ottenere i post.
risorse/js/composables/posts.js :
import { ref } from 'vue'
export default function usePosts() {
const posts = ref([])
const getPosts = async () => {
axios.get('/api/posts')
.then(response => {
posts.value = response.data;
})
}
return { posts, getPosts }
}
E tutti gli stati esposti devono essere restituiti. Ora abbiamo posts
e getPosts
.
Ora dobbiamo cambiare il componente Vue. Invece di <script>
andremo ad usare <script setup>
.
resources/js/components/Posts/Index.vue :
<template>
// ...
</template>
<script setup>
import { onMounted } from "vue";
import usePosts from "../../composables/posts";
const { posts, getPosts } = usePosts()
onMounted(() => {
getPosts()
})
</script>
Come puoi vedere, in questo modo dobbiamo scrivere meno codice nel componente Vue stesso, e tutto il resto sta nel componibile che può essere riutilizzato in altri componenti. In altre parole, componibile è un equivalente della classe Service
in PHP/Laravel
e il componente Vue
utilizza il metodo Service
, come un controller Laravel
.
Usare @ invece che “../../”
Possiamo migliorare l’importazione di file invece di tornare indietro nelle directory con punti come ../../
possiamo cambiarlo nel segno @
. Per fare ciò, dobbiamo aggiungere un nuovo alias alla configurazione di vite.
vite.config.js:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [
laravel({
input: [
'resources/css/app.css',
'resources/js/app.js',
],
refresh: true,
}),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
],
resolve: {
alias: {
vue: 'vue/dist/vue.esm-bundler.js',
'@': '/resources/js',
},
},
});
Ora possiamo importare in questo modo:
resources/js/components/Posts/Index.vue:
import usePosts from "@/composables/posts";
Importando file in questo modo il tuo IDE potrebbe noindicare un’anomalia, un warning. Ma possiamo risolverlo facilmente creando un file jsconfig.json
con il codice:
jsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["resources/js/*"]
}
},
"exclude": ["node_modules", "public"]
}
Resources API di Eloquent per trasformare il contenuto dei campi
Per ora, i nostri dati nella tabella non sembrano belli. Quindi, in questa lezione utilizziamo una funzionalità di Laravel chiamata Resources API
per trasformare i dati, ad esempio:
- ridurre alcuni campi,
- formattazione corretta della data e
- oppure ignorare alcuni campi.
Per prima cosa creiamo la risorsa:
php artisan make:resource PostResource
Utilizziamo quindi la Resource
per fare in modo che PostController
restituisca tutti i post
app/Http/Controllers/Api/PostController.php :
class PostController extends Controller
{
public function index()
{
return PostResource::collection(Post::all());
}
}
Praticamente PostResource
è un wrapper a monte della la query Eloquent che trasformerà ogni campo in quello che desideriamo.
Per impostazione predefinita, non trasforma nulla ma possiamo farlo fornendo il nostro array.
app/Http/Resources/PostResource.php :
class PostResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'content' => substr($this->content, 0, 50) . '...',
'created_at' => $this->created_at->toDateString()
];
}
}
Ma Resource
non modifica solo i campi content
e created_at
. Aggiunge anche un data
wrapper.
Quindi sul front-end possiamo vedere che i dati non vengono mostrati.

Ciò è dovuto al data
wrapper aggiunto dalla risorsa API. Dobbiamo solo aggiungere questi dati nel componibile dove assegniamo i dati di risposta alla variabile posts.
resources/js/composables/posts.js :
import { ref } from 'vue'
export default function usePosts() {
const posts = ref([])
const getPosts = async () => {
axios.get('/api/posts')
.then(response => {
posts.value = response.data.data;
})
}
return { posts, getPosts }
}
La scrittura .data.data
sembra strano ma è corretto: il primo .data
proviene dalla risposta API JS predefinita e il secondo .data
proviene dal wrapper di risorse API Eloquent.
Ora abbiamo di nuovo una tabella di lavoro con valori modificati. Il contenuto ora ha solo 50 caratteri e created_at
è una stringa anziché un’istanza Carbon.

Paginazione con pacchetto Vue esterno
Caricare l’intero elenco di dati in una tabella potrebbe non essere ottimale, soprattutto se ci sono centinaia o migliaia di record. È qui che entra in gioco l’impaginazione.
Aggiungeremo l’impaginazione alla tabella dei post utilizzando il pacchetto laravel-vue-pagination .
Per prima cosa installiamo il pacchetto.
npm install laravel-vue-pagination
Successivamente, invece di richiamare semplicemente tutti i post nel Controller, dobbiamo impaginarli.
app/Http/Controllers/Api/PostController.php :
class PostController extends Controller
{
public function index()
{
return PostResource::collection(Post::paginate(10));
}
}
Successivamente, nel componente Vue PostIndex
, in v-for
, dobbiamo modificare i post
in posts.data
.
resources/js/components/Posts/Index.vue:
<template>
// ...
<tr v-for="post in posts.data">
// ...
</template>
Quindi la nostra variabile posts
nel componibile deve diventare un oggetto anziché un array.
resources/js/composables/posts.js:
import { ref } from 'vue'
export default function usePosts() {
const posts = ref({})
const getPosts = async () => {
axios.get('/api/posts')
.then(response => {
posts.value = response.data.data;
})
}
return { posts, getPosts }
}
Successivamente, dobbiamo impostare un parametro di pagina su getPosts
. Inoltre, imposta quella pagina come parametro per l’URL. E non è necessario restituire il risultato dei dati. Restituiremo la risposta API completa.
resources/js/composables/posts.js:
import { ref } from 'vue'
export default function usePosts() {
const posts = ref({})
const getPosts = async (page = 1) => {
axios.get('/api/posts?page=' + page)
.then(response => {
posts.value = response.data;
})
}
return { posts, getPosts }
}
Ora dobbiamo aggiungere i collegamenti di impaginazione sotto la tabella.
Per fare ciò, dobbiamo anche importare il pacchetto nel componente PostIndex
Vue.
Poiché utilizziamo Tailwind
per la progettazione, importeremo la versione Tailwind
, ma se necessario esiste anche una versione Bootstrap
. Oppure puoi utilizzare un’impaginazione senza rendering e modellarla tu stesso.
resources/js/components/Posts/Index.vue:
<template>
<div class="overflow-hidden overflow-x-auto p-6 bg-white border-gray-200">
<div class="min-w-full align-middle">
<table class="min-w-full divide-y divide-gray-200 border">
// ...
</table>
<TailwindPagination :data="posts" @pagination-change-page="getPosts" class="mt-4" />
</div>
</div>
</template>
<script setup>
import { onMounted } from "vue";
import { TailwindPagination } from 'laravel-vue-pagination';
import usePosts from "@/composables/posts";
const { posts, getPosts } = usePosts()
onMounted(() => {
getPosts()
})
</script>
Ora abbiamo un’impaginazione funzionante.
L’esempio fin qui descritto in questo articolo è disponibile su Github.

Letture Correlate
Ercole Palmeri