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

vue.js tutorial configurazione laravel

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.

GET api laravel Vue
GET Api Laravel Vue

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.

table from api laravel vue
Tabelle da API Vue Laravel

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 postsgetPosts.

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 contentcreated_at. Aggiunge anche un data wrapper.

Quindi sul front-end possiamo vedere che i dati non vengono mostrati.

laravel vue api tabella vuota
laravel vue api tabella vuota

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.

laravel vue api con Resource
laravel vue api con Resource

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.

laravel vue api pagination
laravel vue api pagination

Letture Correlate

Ercole Palmeri

Autore