Laravel 11 і Vue.js: дизайн сторінки Vue, Datatable з Laravel API
Laravel 11 і Vue.js — це чудове поєднання для написання чистого та ефективного коду.
У цьому посібнику ми разом побачимо, як створити сторінку з Vue.js у Laravel 11 за допомогою Laravel API.
Щоб протестувати Vue.js, ми створимо кінцеву точку API для отримання дописів і відображення їх у таблиці.
Приблизний час читання: 8 хвилин
Приклад, описаний у цій статті, доступний за адресою Github.
Щоб прочитати та повністю зрозуміти цю статтю, рекомендуємо прочитати статтю Laravel 11 і Vue.js: встановлення, налаштування та приклади, а реалізацію коду можна знайти за адресою Репозиторій GitHUB.
Database
Спочатку нам потрібно створити шаблон, міграцію, контролер і шлях API. Як і передбачалося в статті Laravel 11 і Vue.js: встановлення, налаштування та приклади давайте керувати сутністю даних пошта.
З цією командою Ремісник давайте створимо модель пошта і міграція:
php artisan make:model Post -m
Змінимо файл міграції база даних/migrations/xxxx_create_posts_table.php наступним чином:
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->longText('content');
$table->timestamps();
});
}
Давайте відредагуємо модель app/Models/Post.php наступним чином:
class Post extends Model
{
protected $fillable = [
'title',
'content',
];
}
Давайте створимо контролер PostController, і зокрема ми вказуємо на Folder Api
php artisan make:controller Api/PostController
Наразі в контролері ми лише повертатимемо колекцію всіх публікацій.
app/Http/Controllers/Api/PostController.php :
class PostController extends Controller
{
public function index()
{
return Post::all();
}
}
Давайте тепер визначимо їх Routes
. Оскільки всі шляхи будуть для API
, ми додамо їх до файлу routes/api.php
. Таким чином Laravel автоматично додасть префікс api/
до маршрутів. Перед цим нам потрібно виконати команду Ремісник install:api
підготувати заявку Laravel дляAPI
.
php artisan install:api
Команда Ремісник як тільки його буде виконано, він змінить файл app.php
присутні під каталогом завантажувач. Перевірте файл 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();
Тепер ми можемо додати маршрут дописів у файл routes/api.php, щоб навести наш приклад:
use App\Http\Controllers\Api\PostController;
Route::get('posts', [PostController::class, 'index']);
Виклик API від Vue
Тепер ми можемо додати код JS до компонента Vue PostsIndex
які ми створили в статті Laravel 11 і Vue.js: встановлення, налаштування та приклади. Весь JS-код повинен бути розміщений у тегу <script>
.
По-перше, нам потрібно додати змінну даних, якій будуть присвоєні всі публікації. Давайте назвемо цю змінну posts
і ми призначаємо значення за замовчуванням порожній масив.
Відредагуйте файл ресурси/js/components/Posts/Index.vue:
<template>
// ...
</template>
<script>
export default {
data() {
return {
posts: []
}
}
}
</script>
Далі ми створимо метод fetchPosts
хто зробить запит на придбання e /api/posts
отримає всі дописи.
Коли запит успішний, метод .then
буде виконано. Він додасть усі публікації з відповіді API до змінної даних posts
.
Якщо щось піде не так, ми використовуємо .catch
за будь-які помилки. Поки що ми обмежимося використанням файлу console.log
.
Коли компоненти змонтовані (іншими словами, ініціалізовані), нам потрібно викликати fetchPosts()
. Дзвінок буде зроблено в mounted
. Ми використовуємо синтаксис this.fetchPosts()
, насправді він походить від того самого компонента.
Файл ресурси/js/components/Posts/Index.vue стає:
<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>
Три спостереження:
- Змінні ініціалізуються в
data()
- Ми додаємо спеціальні функції
methods:
- Якщо вам потрібно негайно викликати ці функції, вам потрібно викликати їх у
mounted()
Тепер перейдіть на домашню сторінку та відкрийте Inspect вашого браузера. На вкладці Мережа ви побачите, що запит API зроблено /api/posts
і показує всі дописи в частині відповіді.
Перегляд даних API в таблиці
Тепер нам потрібно показати всі ці пости в таблиці. Для цього ми будемо використовувати директиву Vue v-for
в елементі рядка tr
таблиці.
Замінюємо статичні дані в таблиці на v-for="post in posts"
:
ресурси/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>
Практично post in posts
означає, що він приймає змінну posts
на стороні компонента <script>
і виконує цикл, призначаючи кожен елемент змінній post
. Дорівнює foreach ($posts as $post)
PHP.
Тепер, після оновлення сторінки в браузері, ви повинні побачити таблицю з усіма повідомленнями, отриманими з API.
Vue.js Composition API 3
Vue.js версії 3 представив новий спосіб написання компонентів Vue під назвою API композиції. Починаючи з версії 3.2 Vue.js звільняється <script setup>
як новий спосіб написання компонентів Vue.
Давайте тепер подивимося, як поєднати модульні компоненти <script setup>
для написання багаторазового коду. Отже, давайте створимо складний компонент і викличемо його resources/js/composables/posts.js
.
ресурси/js/composables/posts.js :
import { ref } from 'vue'
export default function usePosts() {
const posts = ref([])
}
У цьому складному компоненті ми спочатку імпортуємо файл ref
. Ref
означає посилання, яке зробить змінні повідомлення адаптивними.
Отже, як і в будь-якому файлі, ми повинні експорт за замовчуванням . У нашому випадку ми будемо експортувати результат функції usePosts
, це useXyz()
це назва за замовчуванням, що починається з use
.
Далі нам потрібно додати асинхронний метод для отримання публікацій.
ресурси/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 }
}
І всі викриті стани повинні бути повернуті. Тепер маємо posts
e getPosts
.
Тепер нам потрібно змінити компонент Vue. Замість <script>
ми збираємося використовувати <script setup>
.
ресурси/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>
Як бачите, таким чином нам доведеться писати менше коду в самому компоненті Vue, а все інше знаходиться в composable, який можна повторно використовувати в інших компонентах. Іншими словами, composable є еквівалентом класу Service
in PHP/Laravel
і компонент Vue
використовувати метод Service
, як контролер Laravel
.
Використовуйте @ замість «../../»
Ми можемо покращити імпортування файлів замість повернення до каталогів із крапками на зразок ../../
ми можемо змінити це в знаку @
. Для цього нам потрібно додати новий псевдонім до конфігурації life.
screw.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',
},
},
});
Тепер ми можемо імпортувати так:
ресурси/js/components/Posts/Index.vue:
import usePosts from "@/composables/posts";
Якщо імпортувати файли таким чином, ваша IDE може не вказувати на аномалію, попередження. Але ми можемо легко вирішити це, створивши файл jsconfig.json
з кодом:
jsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["resources/js/*"]
}
},
"exclude": ["node_modules", "public"]
}
API ресурсів Eloquent для перетворення вмісту поля
Наразі наші дані в таблиці виглядають погано. Тому в цьому уроці ми використовуємо функцію Laravel під назвою Resources API
для перетворення даних, наприклад:
- зменшити деякі поля,
- правильне форматування дати e
- або ігнорувати деякі поля.
Спочатку створимо ресурс:
php artisan make:resource PostResource
Тому ми використовуємо Resource
щоб переконатися в цьому PostController
повернути всі дописи
app/Http/Controllers/Api/PostController.php :
class PostController extends Controller
{
public function index()
{
return PostResource::collection(Post::all());
}
}
Практично PostResource
це обгортка перед запитом Eloquent, яка перетворює кожне поле на те, що ми хочемо.
За замовчуванням він нічого не перетворює, але ми можемо зробити це, надавши наш масив.
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
це не просто змінює поля content
e created_at
. Він також додає a data
обгортка
Отже, на передній частині ми бачимо, що дані не відображаються.
Це пов'язано з data
обгортка, додана ресурсом API. Нам просто потрібно додати ці дані в composable, де ми призначаємо дані відповіді змінній posts.
ресурси/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 }
}
Письмо .data.data
це здається дивним, але це правильно: перше .data
надходить із відповіді JS API за замовчуванням, а другий .data
походить від оболонки ресурсу API Eloquent.
Тепер у нас знову є робоча таблиця зі зміненими значеннями. Вміст тепер має лише 50 символів і created_at
це рядок, а не екземпляр Carbon.
Пагінація із зовнішнім пакетом Vue
Завантаження всього списку даних у таблицю може бути неоптимальним, особливо якщо є сотні чи тисячі записів. Ось тут і з’являється пагінація.
Ми додамо розмітку сторінок до таблиці публікацій за допомогою пакета laravel-vue-pagination .
Спочатку встановлюємо пакет.
npm install laravel-vue-pagination
Далі, замість того, щоб просто викликати всі публікації в контролері, нам потрібно розбити їх на сторінки.
app/Http/Controllers/Api/PostController.php :
class PostController extends Controller
{
public function index()
{
return PostResource::collection(Post::paginate(10));
}
}
Далі в компоненті Vue PostIndex
в v-for
, нам потрібно змінити post
in posts.data
.
ресурси/js/components/Posts/Index.vue:
<template>
// ...
<tr v-for="post in posts.data">
// ...
</template>
Отже, наша змінна posts
у composable він має стати об’єктом замість масиву.
ресурси/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 }
}
Далі нам потрібно встановити параметр сторінки getPosts
. Також установіть цю сторінку як параметр URL-адреси. І вам не потрібно повертати результат даних. Ми повернемо повну відповідь API.
ресурси/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 }
}
Тепер нам потрібно додати посилання на сторінки під таблицею.
Для цього нам також потрібно імпортувати пакет у компонент PostIndex
Бачив.
Оскільки ми використовуємо Tailwind
для дизайну ми імпортуємо версію Tailwind
, але при необхідності є і версія Bootstrap
. Або ви можете використовувати невідтворений макет і стилізувати його самостійно.
ресурси/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>
Зараз у нас є робочий макет.
Приклад, описаний у цій статті, доступний на Github.
Пов'язані читання
Ercole Palmeri