На прошлом уроке вы сгенерировали и вывели наши товары на сайте.
Выглядит это пока простенько, поэтому довайте добавим нашим товарам картинки. То есть, напишем галерею товара.
Этот урок доступен бесплатно в рекламных целях, вдруг кто-то почитает и захочет оплатить доступ и к предыдущим?
У Vesp предусмотрена система работы с файлами, стандартная модель и таблица для них. Нам нужно будет только использовать эту систему для загрузки картинок товарам.
Я предлагаю создать новую модель ProductFile
, которая свяжет товары с файлами. Делаем composer db:create ProductFiles
и редактируем новую миграцию:
<?php
use Illuminate\Database\Schema\Blueprint;
use Vesp\Services\Migration;
final class ProductFiles extends Migration
{
public function up(): void
{
$this->schema->create(
'product_files',
function (Blueprint $table) {
// Связи с родительскими моделями
$table->foreignId('product_id')
->constrained('products')->cascadeOnDelete();
$table->foreignId('file_id')
->constrained('files')->cascadeOnDelete();
// Порядок вывода картинок в галерее
$table->unsignedInteger('rank')->default(0);
// Картинки можно будет отключать
$table->boolean('active')->default(true)->index();
// Назначаем первичный ключ из 2х колонок
$table->primary(['product_id', 'file_id']);
}
);
}
public function down(): void
{
$this->schema->drop('product_files');
}
}
Ну что, модели созданы, контроллеры отлажены, пора бы и вывести товары на нашем сайте.
Для удобной работы предлагаю использовать очень известную библиотеку Faker, которая позволит нам нагенерировать нужное количество товаров и категорий без лишних сложностей.
Устаналиваем fakerphp/faker
зависимостью для разработки, на рабочем окружении она не будет нужна:
composer require fakerphp/faker --dev
Затем создаём новый seed файл в core/db/seeds/Products.php
Возможно вы обратили внимание, что на прошлом уроке мы добавили в таблицы категорий и товаров строку поиска, но она не работает?
Это потому, что никто не написал сам функционал поиска в контроллерах. Мы просто расширили базовый ModelController
и больше ничего не меняли.
Помимо этого в категориях мы указали колонку для вывода общего количества товаров - и она тоже пуста, по той же причине.
Предлагаю сегодня доработать наши контроллеры и добавить недостающие функции.
Ну что, друзья! Начинается самое интересное - реальная разработка нашего магазина.
Сегодня напишем базовые миграции, модели и контроллеры админки. А потом создадим и страницы для работы со всем этим.
Перед начал разработки я провёл генеральную уборку кода пакета vesp/vesp и добавил новые команды в composer
, так что советую пересоздать проект заново, базу данных и настройки .env
можно не трогать, только обновить исходники.
По ходу дела я постараюсь прикинуть, сколько времени у меня уходит на те или иные операции.
Честно говоря, не думал, что только знакомство с системой затянется аж на 10 заметок. Мне-то оно всё давно привычно и понятно, а вот когда пытаешься внятно об этом рассказать...
В общем, сегодня мы заканчиваем вводную часть, и дальше будет настоящая работа по постройке магазина VespShop.
На нужно посмотреть, какие еще компоненты я написал для удобной работы на Vesp, и начнём с переключателя языка - vesp-change-locale
(по клику откроется GIFка)
Следующий важный компонент админки - модальные окна для создания и редактирования моделей.
Vesp-modal
расширяет компонент из BootstrapVue, принимает всего его параметры и добавляет отправку формы с данными.
Редактируемые данные передаются через v-model
, и если в них указан первичный ключ (по умолчанию поле id
, но можно настроить и другой), то форма отправится методом PATCH
(редактирование), а если нет, то PUT
(создание).
Содержимое формы вставляется в слот #form-fields
, а сами формы я предлагаю хранить отдельно от модальных окон, для удобства. Давайте разберём форму работы с пользователями.
Продолжаем наше знакомство с админкой Vesp.
Опираясь на свой опыт работы в MODX я постарался повторить примерную логику работы и здесь, для чего были написаны основные компоненты админки. Под капотом они используют BootstrapVue, но в будущем могут быть переписаны и на другую систему, при этом основной код страниц менять не придётся.
Для примера давайте разберёмся как именно работает раздел с пользователями
Сегодня мы начинаем знакомиться с админкой нашего приложения. Напоминаю, это точно такое же приложение, как и site
, только не в минимальной комплектации, а со всеми нужными модулями по умолчанию.
Работает оно с помощью компонентов Booststrap-Vue, и и к его документации мы будем обращаться очень часто. Для оформления внешнего вида мы подключаем стили из Bootstrap.
Первом делом мы видим форму авторизации, которая работает при помощи модуля @nuxtjs/auth. Все модули подключаются в frontend/src/admin/nuxt.config.js
, добавлением в Config.modules
или Config.buildModules
- это зависит от самого модуля и авторы пишут куда именно их нужно добавлять.
Этот модуль добавляет нам в приложение объект $auth
, к которому можно обращаться из любого компонента. Если посмотреть в основной шаблон админки admin/layouts/default.vue
, то вы увидите как именно выводится форма авторизации:
Как вы уже поняли по предыдущим заметкам, бэкенд и фронтенд Vesp не зависят друг от друга вообще никак. На прошлом занятии мы отправляли запросы в API из консоли через cURL и всё нормально работало.
Вместо консоли это могло быть и мобильное приложение, и любой веб-сайт. Я же предлагаю использовать веб-приложение, написанное на VueJS, с использованием NuxtJS - потому что они мне очень нравятся. И сейчас расскажу, почему.
Как мы уже выяснили в прошлой заметке, пользователь авторизовывается в контроллерах при каждем запросе с помощью токена JWT, который передаётся или в заголовках, или в GET параметре, или получается из куки.
Теперь давайте посмотрим, как происходит получение этого токена пользователем.
Согласно нашему route.php
запросы на авторизацию принимаются по адресу api/security/login
контроллером App\Controllers\Security\Login
, который расширяет Vesp\Controllers\Security\Login
:
<?php
// Это оригинальный контроллер из vesp/core
namespace Vesp\Controllers\Security;
use Psr\Http\Message\ResponseInterface;
use Vesp\Controllers\Controller;
use Vesp\Helpers\Jwt;
use Vesp\Models\User;
class Login extends Controller
{
// Контроллер работает с моделью User
protected $model = User::class;
// Обращаться можно только методом POST
public function post(): ResponseInterface
{
// для авторизации требуются username и password
$username = trim($this->getProperty('username', ''));
$password = trim($this->getProperty('password', ''));
// Выбираем пользователя по username
$user = (new $this->model())->newQuery()->where('username', $username)->first();
// Если есть такой, и указан верный пароль
if ($user && $user->verifyPassword($password)) {
// Проверяем его статус, и возвращаем токен, или ошибку
return !$user->active
? $this->failure('This user is not active', 403)
: $this->success(['token' => Jwt::makeToken($user->id)]);
}
// Авторизовать не удалось - возвращаем ошибку
return $this->failure('Wrong username or password');
}
}
Мы продолжаем знакомиться с внутренним устройством Vesp и сегодня пришла пора поговорить о системе прав доступа к данным.
После работы с MODX мне не хотелось изобретать что-то сложное, поэтому я придумал следующее:
scope
(область действия)users
позволяет любые запросы, то users/get
только получение данных, без put
, patch
и delete
.Таким образом каждой группе можно гибко давать разрешения на конкретные методы в контроллерах.
Когда подобной системы недостаточно, и нужно проверять, например, id
автора комментария при редактировании, то мы просто расширяем метод checkScope
у контроллера.
Давайте посмотрим, как он работает по-умолчанию.
На прошлых уроках мы работали с миграциями Phinx и моделями Eloquent - это всё сторонние библиотеки, а где же сам Vesp?
А он в контроллерах, которые были написаны под сильным впечатлением от процессоров MODX, и работают через Slim 4. Интересный факт - MODX 3 в своё время планировали завязать именно на Slim, но что-то пошло не так, и эти планы забросили.
Итак, все запросы от пользователей приходят в один-единственный коннектор www/api.php
. Он подключает всё нужное файлом core/bootstrap.php
(который можно использовать во всяких консольных скриптах) и создаёт экземпляр приложения Slim.
Дальше в это приложение грузятся наши маршруты из файла core/routes.php
, и теперь система знает, какой контроллер отвечает за конкретный запрос. Давайте разберёмся с маршрутами.