Начинаем разработку VespShop
Ну что, друзья! Начинается самое интересное - реальная разработка нашего магазина.
Сегодня напишем базовые миграции, модели и контроллеры админки. А потом создадим и страницы для работы со всем этим.
Перед начал разработки я провёл генеральную уборку кода пакета vesp/vesp и добавил новые команды в composer, так что советую пересоздать проект заново, базу данных и настройки .env можно не трогать, только обновить исходники.
По ходу дела я постараюсь прикинуть, сколько времени у меня уходит на те или иные операции.
Переходим в новый проект и запускаем composer db:create Products - это создаст новую миграцию в файле core/db/migrations/меткавремени-products.php.
Мы не ограничены количеством миграций, поэтому сегодня будет минимальный набор колонок в таблицах, а дальше будем добавлять новые по необходимости:
<?php
use Illuminate\Database\Schema\Blueprint;
use Vesp\Services\Migration;
final class Products extends Migration
{
public function up(): void
{
// Таблица категорий товаров
$this->schema->create(
'categories',
function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description')->nullable();
$table->boolean('active')->default(true)->index();
$table->timestamps();
}
);
// Таблица товаров
$this->schema->create(
'products',
function (Blueprint $table) {
$table->id();
// Связь товаров с категорией
$table->foreignId('category_id')
// Запрет удаления категории, если в ней есть хотя-бы 1 товар
->constrained('categories')->restrictOnDelete();
$table->string('title');
$table->text('description')->nullable();
// Артикул товара должен быть уникальным
$table->string('sku')->unique();
// Цену храним в колонке с цифрами после запятой
$table->unsignedDecimal('price')->nullable();
$table->boolean('active')->default(true)->index();
$table->timestamps();
}
);
}
public function down(): void
{
// При откате миграции удаляем таблицы в обратном порядке
$this->schema->drop('products');
$this->schema->drop('categories');
}
}
Дальше делаем composer db:migrate и создаём наши модели в core/src/Models.
core/src/Models/Category.php
<?php
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* @property int $id
* @property string $title
* @property ?string $description
* @property bool $active
* @property Carbon $created_at
* @property Carbon $updated_at
*
* @property-read Product[] $products
*/
class Category extends Model
{
protected $guarded = ['id', 'created_at', 'updated_at'];
protected $casts = ['active' => 'boolean'];
// Каждая категория может иметь много товаров
public function products(): HasMany
{
return $this->hasMany(Product::class);
}
}
Как видите - ничего особенного, просто прописываем всё то, что уже написали в миграциях.
core/src/Models/Product.php
<?php
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* @property int $id
* @property int $category_id
* @property string $title
* @property ?string $description
* @property string $sku
* @property float $price
* @property bool $active
* @property Carbon $created_at
* @property Carbon $updated_at
*
* @property-read Category $category
*/
class Product extends Model
{
protected $guarded = ['id', 'created_at', 'updated_at'];
protected $casts = [
'active' => 'boolean',
'price' => 'float',
];
// Каждый товар принадлежит одной категории
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
}
Мне кажется, тут вопросов никаких - всё предельно просто. Раз у нас есть модели, можно написать для них и контроллеры.
core/src/Contollers/Admin/Categories.php
<?php
namespace App\Controllers\Admin;
use App\Models\Category;
use Vesp\Controllers\ModelController;
class Categories extends ModelController
{
protected $scope = 'products';
protected $model = Category::class;
}
Совершенно минимальная конфигурация - только требование разрешения products и указание модели.
core/src/Contollers/Admin/Products.php
<?php
namespace App\Controllers\Admin;
use App\Models\Product;
use Vesp\Controllers\ModelController;
class Products extends ModelController
{
protected $scope = 'products';
protected $model = Product::class;
}
Здесь ровно то же самое.
core/routes.php
Добавляем 2 новых адреса в группу /admin:
$group->group(
'/admin',
static function (RouteCollectorProxy $group) {
// Это было по-умолчанию
$group->any('/users[/{id}]', App\Controllers\Admin\Users::class);
$group->any('/user-roles[/{id}]', App\Controllers\Admin\UserRoles::class);
// А вот это мы добавляем
$group->any('/categories[/{id}]', App\Controllers\Admin\Categories::class);
$group->any('/products[/{id}]', App\Controllers\Admin\Products::class);
}
);
Собственно, с бэкендом мы уже закончили. У меня на создание миграций, моделей и контроллеров ушло примерно 15 минут.
Права доступа
Оба контроллера требуют одно разрешение - products, поэтому нам нужно добавить его группе администраторов.
Это можно сделать как вручную через админку, отредактировав группу Admin, так и через сиды.
Во втором случае меняем core/db/seeds/UserRoles.php и добавляем разрешение на 12й строке:
$roles = [
'Administrator' => [
'scope' => ['profile', 'users', 'products'], // Вот здесь
],
'User' => [
'scope' => ['profile'],
],
];
После этого можно сделать composer db:seed-one UserRoles для обновления только групп юзеров.
Фронтенд
Переходим на фронтенд и создаём страницу frontend/src/admin/pages/categories.vue:
<template>
<div>
<vesp-table
:url="url"
:header-actions="headerActions"
:table-actions="tableActions"
:fields="fields"
:filters="filters"
:sort="sort"
:dir="dir"
:row-class="rowClass"
/>
<nuxt-child />
</div>
</template>
<script>
// Адрес нашего контроллера
export const url = 'admin/categories'
export default {
name: 'CategoriesPage',
validate({app}) {
// Проверка разрешения
return app.$hasScope('products')
},
data() {
return {
url,
filters: {
query: '',
},
sort: 'id',
dir: 'asc',
}
},
head() {
return {
title: [this.$t('models.category.title_many'), this.$t('project')].join(' / '),
}
},
computed: {
headerActions() {
return [{route: 'categories-create', icon: 'plus', title: this.$t('actions.create')}]
},
tableActions() {
return [
{route: 'categories-edit-id', icon: 'edit', title: this.$t('actions.edit')},
{function: 'onDelete', icon: 'times', title: this.$t('actions.delete'), variant: 'danger'},
]
},
fields() {
return [
{key: 'id', label: this.$t('components.table.columns.id'), sortable: true},
{key: 'title', label: this.$t('models.category.title'), sortable: true},
// Эта колонка будет выводить количество товаров категории
{key: 'products_count', label: this.$t('models.category.products'), sortable: true},
{
key: 'created_at',
label: this.$t('components.table.columns.created_at'),
formatter: this.$options.filters.datetime,
sortable: true,
},
]
},
},
methods: {
rowClass(item) {
return item && !item.active ? 'text-muted' : ''
},
},
}
</script>
Ничего необычного, всё примерно как на странице users.vue. Таким эе образом создаём и страницу products.vue - можно вообще скопировать предыдущую и переименовать внутри все слова categories и category на products и product соответственно. Я лично так и делаю.
Финальный штрих - добавляем новые страницы в frontend/src/admin/plugins/menu.js:
export default [
// Новые записи ставим в начало
{
name: 'products',
title: 'models.product.title_many',
scope: 'products',
},
{
name: 'categories',
title: 'models.category.title_many',
scope: 'products',
},
// Старые, понятно, тоже оставляем как было
{
name: 'users',
title: 'models.user.title_many',
scope: 'users',
children: [
{
name: 'users-roles',
title: 'models.user_role.title_many',
scope: 'users',
},
],
},
]
Запускаем composer node:dev и заходим в админку как admin.
Всё работает, только нет записей в лексиконах.
Лексиконы
Идём в frontend/admin/src/lexicons и меняем все 3 словаря. Я привожу здесь только ru.js в сокращённом виде:
export default {
project: 'Vesp',
// ...
models: {
user: {
// ...
},
user_role: {
// ...
},
category: {
title_one: 'Категория',
title_many: 'Категории',
title: 'Название',
description: 'Описание',
products: 'Товары',
active: 'Включено',
},
product: {
title_one: 'Товар',
title_many: 'Товары',
title: 'Название',
description: 'Описание',
sku: 'Артикул',
price: 'Цена',
category: 'Категория',
active: 'Включено',
},
},
// ...
}
В других словарях нужно добавить точно такие же записи, только на соответствующем языке. Для PhpStorm можно установить плагин Translation, это очень ускоряет переводы.
При каждом редактировании словарей страница разработки обновляется и в итоге всё становится вот так:
Формы редактирования
Дальше идём в frontend/src/admin/components/forms и создаём 2 формы для редактирования категорий и товаров.
forms/category.vue
<template>
<div>
<b-form-group :label="$t('models.category.title')">
<b-form-input v-model.trim="record.title" required autofocus />
</b-form-group>
<b-form-group :label="$t('models.category.description')">
<b-form-textarea v-model.trim="record.description" rows="5" />
</b-form-group>
<b-form-group>
<b-form-checkbox v-model="record.active">
{{ $t('models.category.active') }}
</b-form-checkbox>
</b-form-group>
</div>
</template>
<script>
// Никакой особой логики
export default {
// Имя компонента
name: 'FormCategory',
// Один-единственный принимаемый параметр через v-model
props: {
value: {
type: Object,
required: true,
},
},
// И вычисляемый параметр для изменения
computed: {
record: {
get() {
return this.value
},
set(newValue) {
this.$emit('input', newValue)
},
},
},
}
</script>
Логику работы форм мы уже разбирали в уроке про модальные окна.
forms/product.vue
Почти такая же форма, за одним исключением - здесь добавляется обязательный выбор категории товара. Публикую только часть с template:
<template>
<div>
<b-form-group :label="$t('models.product.title')">
<b-form-input v-model.trim="record.title" required autofocus />
</b-form-group>
<b-form-group :label="$t('models.product.description')">
<b-form-textarea v-model.trim="record.description" rows="5" />
</b-form-group>
<!--Артикул и цену располагаем в одну строку-->
<b-row>
<b-col md="6">
<b-form-group :label="$t('models.product.sku')">
<b-form-input v-model.trim="record.sku" required />
</b-form-group>
</b-col>
<b-col md="6">
<b-form-group :label="$t('models.product.price')">
<b-form-input v-model.trim="record.price" />
</b-form-group>
</b-col>
</b-row>
<!--А вот и выбор категории-->
<b-form-group :label="$t('models.product.category')">
<vesp-input-combo-box v-model="record.category_id" url="admin/categories" required />
</b-form-group>
<b-form-group>
<b-form-checkbox v-model.trim="record.active">
{{ $t('models.product.active') }}
</b-form-checkbox>
</b-form-group>
</div>
</template>
Везде используем одни и те же значения для лексиконов.
Формы есть, осталось создать страницы для работы с моделями.
Создание и редактирование моделей
Напоминаю, что вся работа с конкретными моделями находится во вложенных маршрутах по отношению к их таблицам. Поэтому создаём новые страницы для товаров и категорий по следующим адресам:
- pages/categories/create.vue - создание категории
- pages/categories/edit/_id.vue - редактирование категории
- pages/products/create.vue - создание товара
- pages/products/edit/_id.vue - редактирование товара
Приведу здесь только страницы товаров, у категорий всё ровно так же - можно смело копипастить с небольшими изменениями в наборе полей и строках лексикона.
pages/products/create.vue
<template>
<vesp-modal v-model="record" :url="url" :title="$t('models.product.title_one')">
<template #form-fields>
<form-product v-model="record" />
</template>
</vesp-modal>
</template>
<script>
// импорт адреса контроллера из таблицы
import {url} from '../products'
// Импорт формы
import FormProduct from '../../components/forms/product'
export {url}
export default {
name: 'ProductCreatePage',
components: {FormProduct},
data() {
return {
url,
record: {
// Значения по-умолчанию
title: '',
description: '',
sku: '',
price: null,
category_id: null,
active: true,
},
}
},
}
</script>
pages/products/edit/_id.vue
У страниц редактирования обычно вообще ничего не меняется.
Всё нужное просто импортируется из страницы создания модели и добавляется предварительная загрузка модели из API.
<script>
import Create, {url} from '../create'
export default {
name: 'ProductEditPage',
extends: Create,
validate({params}) {
return /^\d+$/.test(params.id)
},
async asyncData({app, params, error}) {
try {
const {data: record} = await app.$axios.get(url + '/' + params.id)
return {record}
} catch (e) {
error({statusCode: e.statusCode, message: e.data})
}
},
}
</script>
Всё готово
Действительно, спустя примерно час создания файлов и копирования их туда-сюда, у нас всё готово для проверки работы.
Итого, спустя примерно 1.5 - 2 часа у вас должно появиться 2 связанных друг сдругом модели товаров и их категорий.
Как видите, нет ничего сложного, всё максимально автоматизировано и предназначего для очень быстрой работы.
Наш новый проект я опубликовал на Github, чтобы вы могли посмотреть, что именно у меня получается, вот все изменения за сегодня.
На следующем уроке будем расширять наши новые модели и страницы.
0
👍
👎
❤️
🔥
😮
😢
😀
😡
480
17.06.2022, 13:25:32
38 комментариев
NightRider
18.06.2022, 23:15:06
Странное дело... чтобы отобразились хоть какие-то изменения после редактирования vue файлов на домене vesp-shop.test, мне приходится давать команду composer node:generate
При этом если я копирую адрес http://192.168.0.175:4000/admin/ из результатов команды composer node:dev и работаю с ним, то там вижу всегда актуальную информацию. Но там проблема кросс-доменных запросов, потому что ajax запросы улетают на http://vesp-shop.test/api/ Не понятна эта магия)
19.06.2022, 07:31:13
Проект локально находится на vesp-shop.test, PHP крутится только там, больше нигде - все запросы в API идут туда.
А вот фронт у нас есть в 2х видах:
- режим разработки, который открывается на локальном IP после команды composer node:dev
- режим готового приложения, которое собирается в статичные файлы после composer node:generate
Режим разработки нужен, чтобы сразу видеть все изменения, а статические файлы - для выгрузки на хостинг. Или, в нашем случае, на локальный vesp-shop.test
Так при разработке всегда используем режим dev, а generate - если хотим видеть наш проект на локальном домене после разработки, без запуска node.
NightRider
19.06.2022, 12:43:23
Спасибо за разъяснение, этот момент понял. Тогда остается проблема еще в том, что когда я захожу на адрес http://192.168.0.175:4000/admin/products, то в консоли пишется ошибка:
Access to XMLHttpRequest at 'http://vesp-shop.test/api/admin/products?page=1&limit=20&sort=id&dir=asc&query=' from origin 'http://192.168.0.175:4000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Причем на странице категории такой ошибки нет.
19.06.2022, 13:00:13
А если напрямую открыть http://vesp-shop.test/api/admin/products?page=1&limit=20&sort=id&dir=asc&query= - ошибки нет, случайно?
Очень похоже, что просто контроллер не работает, и возвращает неправильный JSON ответ.
NightRider
19.06.2022, 13:22:35
Да, правы вы! Там ошибка, но это по моей невнимательности она была допущена. Исправил, все заработало! Спасибо!
19.06.2022, 13:30:40
На здоровье!
Игорь
19.06.2022, 02:52:56
Пересоздал проект заново, набираю в терминале composer db:create Productsи получаю ошибку
19.06.2022, 07:26:02
А ты как пересоздал? Надо же все файлы заново загрузить, включая composer.json - там есть новые команды.
Игорь
19.06.2022, 17:03:25
Я так понимаю, что надо было просто скачать пакет vesp/vesp и перезаписать все файлы в директории VespShop. Я же "пересоздал проект заново" выполнив composer create-project vesp/vesp ./VespShop
19.06.2022, 17:17:01
Эта команда вернёт ошибку, если директория не пустая.
Скорее всего так и было, просто ты не обратил внимания =)
Игорь
19.06.2022, 17:33:47
Наверное так и было бы, но я умудрился выполнить composer create-project vesp/vesp ./VespShop уже находясь в директории VespShop и там чудесным образом создалась новая директория VespShop со всем содержимым =) А можно как-то обновлять пакет vesp/ через composer?
19.06.2022, 17:56:21
Да, конечно, composer update и composer node:update - но это обновит только PHP и JS зависимости, которые в core/vendor и frontend/node_modules.
Всё остальное - это уже твои файлы, они не обновляются, и меняются только тобой. Что с ними хочешь, то и делай.
Я просто по ходу курса обучения сам вижу свои косяки и правлю, поэтому сейчас пришлось файлы обновить. А в реальной работе обновляются только зависимости.
Игорь
20.06.2022, 20:45:33
22.06.2022, 09:47:46
Забыл ответить на комментарий, исправляюсь.
Загрузка фото точно будет, текстовый редактор ты можешь попробовать подключить и сам. Со списком товаров ничего особенного делать не планирую.
На данный момент я хочу провести вас через полный цикл разработки, заканчивая запуском проекта на хостинге. А дорабатывать его потом можно сколько угодно.
Игорь
22.06.2022, 15:57:53
Спасибо! Посоветуешь текстовый редактор? Не факт что получится, но обязательно попробую подключить самостоятельно.
22.06.2022, 16:08:29
Я обычно не пользуюсь RTE редакторами, потому что они пишут всякое непонятное что в HTML.
Но можно вот здесь почитать - https://www.tiny.cloud/blog/best-vue-rich-text-editors/
21.06.2022, 01:51:12
Почти все заработало, но под конец понеслось что-то не понятное:
categories/create.vue
Импортирую ранее созданную форму в forms/category.vue
и у меня выдает в этом месте ошибку:
export default {
name: 'CategoryCreatePage',
components: {FormCategory}, Ошибка тут
}
// Конкретно тут в конце, пишет что компонент зарегистрирован (оно и логично ведь я задал ему это имя в форме), но не используется в vue/no-unused-components.
Я специально попробовал добавить форму импортировать и добавить components: {FormCategory} продукта и странно он почему-то ошибку не выдает.
Попробовал удалить FormCategory и просто импортированный FormProduct, ошибок в редакторе естественно не получаю, в админке пытаюсь добавить категорию и выдает следующее:
_SQLSTATE[42S22]: Column not found: 1054 Unknown column 'updated_at' in 'field list' (SQL: insert into old_categories (title, description, active, updated_at, created_at) values (qweq, qwe, true, 2022-06-20 19:44:19, 2022-06-20 19:44:19))_
21.06.2022, 07:53:54
Если ты что-то импортировал, то нужно это использовать внутри template - иначе ESLint будет ругаться на бесполезный импорт. То есть, у тебя там обязятельно должен быть вызван <form-category v-model="record" />
В комментарии можно прикладывать картинки, кстати говоря - это вроде твой скриншот из телеграма
А насчёт ошибки SQL что-то странное. Проверь, есть ли у тебя в БД у категорий такая колонка вообще. Если нет, то ты забыл указать $table->timestamps(); в миграции. Тогда нужно всё указать, и сделать
21.06.2022, 12:19:13
Спасибо, теперь я понял по поводу импорта :)
А по поводу ошибки sql - timestamps(); прописан, в таблице также есть записи.
Но при попытке запустить composer db:rollback получаю следующее:
21.06.2022, 12:34:09
Тебе там пишут warning, что в в миграции одновременно методы change и up/down - так нельзя. Удали change(), мы его не используем.
На 2м скриншоте наоброт видно, что колонок с датами у тебя нет. Разберись с миграциями, в репозитории можно свериться с правильным файлом.
04.07.2022, 02:30:11
Почему-то, у меня не создались таблицы app_categories и app_products, и файл ***_products.php верный и команду выполнил composer db:migrate, а таблицы не создаются.
Василий, подскажи, на каком этапе создаются таблицы?
04.07.2022, 09:55:05
Надо всё внимательно проверять, что-то не так сделал. Таблицы создаются сразу при запуске миграции.
Если их нет, у тебя должна быть какая-то ошибка при запуске.
05.07.2022, 04:59:02
Устанавливал, все по инструкции, ошибок никаких не было, после браузер показал ошибку, когда перешел в раздел Категории:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'VespShop.app_categories' doesn't exist (SQL: select count(*) as aggregate from `old_categories`)
Сейчас после запуска команды: composer db:create Products
composer db:rollback
05.07.2022, 05:34:46
Что-то странное у тебя произошло: миграция есть, и вроде как выполнена, но таблицы при этом отсутствуют. Как-будто вручную были удалены после выполнения миграции.
Нужно это привести в порядок, самый простой способ - удалить запись Products из old_migrations и запустить composer db:migrate
Ну или вообще удалить все таблицы и сделать composer db:migrate && composer db:seed
05.07.2022, 14:06:29
Василий, спасибо большое, за ценный комментарий, еще больше стал понимать, как оно все устроено!
Первым способом восстановил таблицы.
05.07.2022, 14:40:29
На здоровье!
Вася
19.10.2022, 23:36:20
Что это за ошибка никто не в курсе? И как от нее избавится?
20.10.2022, 11:49:26
Рассказывал в одной из предыдущих заметок, раздел ESLint и Prettier.
Вася
19.10.2022, 23:51:06
И как на странице товаров вывести название категории я к сожалению не разобрался и не смог
В проекте на github просто такая строка {key: 'category.title', label: this.$t('models.product.category')},
Но если ее вставить на этом этапе то ни чего не будет
как вытащить название категории по id в таблицу пока не понятно )
Следующий урок там ответ
03.05.2023, 10:47:18
Все получилось до пункта "Создание и редактирование моделей". Т.е. в админке создались вкладки "Категории" и "Товары" и они добавились в меню.
Но после того как создал директории и файлы:
пошли ошибки типа:
Старался делать как написано, где был не уверен - брал файлы с Github, , но что-то видимо сделал не так.(
03.05.2023, 16:14:44
Ошибки пишут, чтобы их читать и исправлять.
Модуль с относительным путём не был найден. Проверяй путь к этому компоненту .
05.05.2023, 08:48:16
У меня в директории
вообще нет компонента
. И в исходниках его не было, странно неужели я что-то по курсу пропустил? В репозитории Vesp-shop - он есть. Сейчас создам у себя и с ним попробую.
05.05.2023, 09:14:53
Ну так на этом уроке ничего про компонент inputs/alias.vue и не сказано. Ты откуда узнал, что его нужно использовать в формах?
В конце указана ссылка на полный коммит с данными на момент урока. Там про этот компонент ничего не сказано, он появится позже.
Если ты забежал вперёд и копируешь итоговый код после окончания курса - сам себе злобный буратина. Копируй уж тогда всё полностью.
05.05.2023, 09:40:11
Не совсем так, да и не особо важно теперь, когда все в итоге получилось). Я выше писал, что половина этого урока у меня получилась, и появились вкладки Категории и Товаров, а также они отобразились в меню. Но вот когда создал эти директории и страницы:
у меня пошли ошибки, и ты мне написал выше проверить путь к этому компоненту.
Я проверил и компонент у себя не обнаружил. Затем его добавил и все заработало))) Теперь формы появились и создаются категории и товары. Прикольно)
05.05.2023, 10:07:53
Еще раз, в этом уроке нет ничего про компонент alias, он появится позже. Откуда ты его взял? Как его импорт оказался на твоих созданных страницах?
Только если ты скопировал код из репозитория на момент завершения курса. Так делать не нужно - я оставляю ссылки на финальный код урока в конце самого урока.
Смотреть нужно именно его.
05.05.2023, 11:17:27
Я про этот компонент ничего не знал и заранее ничего не копировал конечно. Но я выполнял урок и дошел до ошибки. В моем первом комменте я описал ошибку и приложил скрин.
В ответ на этот мой комментарий ты ответил:
После твоего комментария я и стал проверять путь к компоненту и обнаружил, что компонента у меня и нет. Я решил, что вся проблема в этом и добавил компонент.
И только после того, как я добавил этот компонент - я смог выполнить этот урок до конца. Т.е. исчезли ошибки, добавились формы и как на твоей гифке - я могу создавать товары и категории.
05.05.2023, 11:57:16
Просто покажи скриншот, где в этом уроке что-то написано про компонент input-alias.
Прямо вот ткни меня носом, где я здесь написал тебе его включить в свою форму, чтобы ты получил ошибку.
Ты видишь на моей картинке где-то <input-alias ...? Нет? Правильно, потому что в этом уроке про него нет ни слова.
Ты просто скопировал более поздний код из репозитория и теперь тратишь моё время зря.
05.05.2023, 12:49:32
Блин, вот оказывается из-за чего! Извини Василий, не было злого умысла!)
Я видно уже припарился и когда дошел до этого Публикую только часть с template, понял, что вдруг что не так сделаю из-за того, что Почти такая же форма (значит почти - да не совсем) и тут уже полез в твой Github и оттуда дернул новые файлы (ну чтобы наверняка - без почти), и поэтому ошибка и вылезла
bezumkin.ru
Личный сайт Василия Наумкина
Прямой эфир
Василий Наумкин
01.07.2024, 11:56:41
Да, верно, именно так.
А в контроллере, скорее всего, ловить данные методом post.
Василий Наумкин
26.06.2024, 09:38:15
О, точно, вылезает если не залогинен.
Спасибо, исправил!
Василий Наумкин
09.04.2024, 04:45:01
> Ошибка 500
Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи.
...
russel gal
09.03.2024, 20:17:18
> А этот стоило написать хотя бы затем, чтобы получить комментарий от юзера, который ничего не писал...
Александр Наумов
27.01.2024, 03:06:18
Василий, спасибо!
Извини, тупанул.