шаг 1 сделан

This commit is contained in:
Alisa
2026-06-03 23:12:10 +03:00
parent 891315780f
commit 6d7ef09c5b
21 changed files with 658 additions and 34 deletions
+14
View File
@@ -0,0 +1,14 @@
APP_NAME=RybStock
APP_ENV=local
APP_DEBUG=true
APP_URL=http://localhost
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=database_name
DB_USERNAME=database_user
DB_PASSWORD="database_password"
DB_CHARSET=utf8mb4
MAIL_FROM=orders@loveriba.ru
ADMIN_EMAIL=
+9
View File
@@ -0,0 +1,9 @@
.env
/storage/logs/*.log
/storage/cache/*
/storage/exports/*
/storage/uploads/*
!/storage/cache/.gitkeep
!/storage/exports/.gitkeep
!/storage/uploads/.gitkeep
!/storage/logs/.gitkeep
+36
View File
@@ -0,0 +1,36 @@
# Рыбсток
Интернет-магазин продуктов питания на чистом PHP.
## Техническая основа
- PHP 8.2+ или PHP 8.3+
- MariaDB/MySQL
- PDO
- HTML/CSS/JavaScript
- Собственная админка на PHP
## Стартовая структура
- `public/` - публичная часть сайта и входная точка.
- `app/` - основной PHP-код.
- `views/` - шаблоны страниц.
- `admin/` - будущая админка.
- `database/` - SQL-схемы и данные.
- `storage/` - загрузки, экспорты, кеш и логи.
- `docs/` - проектная документация.
## База данных
Данные подключения хранятся в `.env`.
Для безопасности `.env` добавлен в `.gitignore`; пример настроек лежит в `.env.example`.
## Запуск локально
1. Установить Open Server Panel, Laragon или другой локальный сервер с PHP и MariaDB.
2. Направить корень сайта на папку `public/`.
3. Проверить настройки базы в `.env`.
4. Открыть сайт в браузере.
Если сервер смотрит не в `public/`, нужно настроить виртуальный хост, чтобы публичной была именно эта папка.
+7
View File
@@ -0,0 +1,7 @@
<?php
declare(strict_types=1);
require_once dirname(__DIR__) . '/app/bootstrap.php';
echo 'RybStock admin panel will be created on the next steps.';
+36
View File
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace App\Core;
use PDO;
final class Database
{
private static ?PDO $pdo = null;
public static function pdo(): PDO
{
if (self::$pdo !== null) {
return self::$pdo;
}
$host = app_env('DB_HOST', 'localhost');
$port = app_env('DB_PORT', '3306');
$database = app_env('DB_DATABASE', '');
$charset = app_env('DB_CHARSET', 'utf8mb4');
$username = app_env('DB_USERNAME', '');
$password = app_env('DB_PASSWORD', '');
$dsn = "mysql:host={$host};port={$port};dbname={$database};charset={$charset}";
self::$pdo = new PDO($dsn, $username, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
return self::$pdo;
}
}
+52
View File
@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace App\Core;
final class Env
{
public static function load(string $path): void
{
if (!is_file($path)) {
return;
}
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($lines === false) {
return;
}
foreach ($lines as $line) {
$line = trim($line);
if ($line === '' || str_starts_with($line, '#')) {
continue;
}
[$key, $value] = array_pad(explode('=', $line, 2), 2, '');
$key = trim($key);
$value = self::normalizeValue(trim($value));
if ($key === '') {
continue;
}
$_ENV[$key] = $value;
putenv($key . '=' . $value);
}
}
private static function normalizeValue(string $value): string
{
if (
strlen($value) >= 2
&& (($value[0] === '"' && $value[-1] === '"') || ($value[0] === "'" && $value[-1] === "'"))
) {
return substr($value, 1, -1);
}
return $value;
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace App\Core;
final class View
{
public static function render(string $template, array $data = []): void
{
$templatePath = base_path('views/' . trim($template, '/') . '.php');
if (!is_file($templatePath)) {
http_response_code(500);
echo 'Template not found';
return;
}
extract($data, EXTR_SKIP);
require base_path('views/layouts/main.php');
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
spl_autoload_register(static function (string $class): void {
$prefix = 'App\\';
if (!str_starts_with($class, $prefix)) {
return;
}
$relativeClass = substr($class, strlen($prefix));
$path = __DIR__ . '/' . str_replace('\\', '/', $relativeClass) . '.php';
if (is_file($path)) {
require_once $path;
}
});
require_once __DIR__ . '/helpers.php';
App\Core\Env::load(dirname(__DIR__) . '/.env');
+28
View File
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
use App\Core\View;
function app_env(string $key, ?string $default = null): ?string
{
$value = $_ENV[$key] ?? getenv($key);
if ($value === false || $value === null) {
return $default;
}
return (string) $value;
}
function base_path(string $path = ''): string
{
$base = dirname(__DIR__);
return $path === '' ? $base : $base . '/' . ltrim($path, '/');
}
function view(string $template, array $data = []): void
{
View::render($template, $data);
}
+2
View File
@@ -0,0 +1,2 @@
-- RybStock database schema will be designed on step 2.
-- Database: alice_ro2
+1
View File
@@ -0,0 +1 @@
+53
View File
@@ -0,0 +1,53 @@
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
font-family: Arial, sans-serif;
color: #18201b;
background: #f7f8f4;
}
.page {
min-height: 100vh;
display: grid;
place-items: center;
padding: 24px;
}
.start-screen {
width: min(760px, 100%);
padding: 32px;
border: 1px solid #d9dfd3;
border-radius: 8px;
background: #ffffff;
}
.eyebrow {
margin: 0 0 8px;
font-size: 14px;
color: #687466;
}
h1 {
margin: 0 0 16px;
font-size: 40px;
line-height: 1.1;
}
p {
font-size: 18px;
line-height: 1.55;
}
.status-box {
display: flex;
gap: 8px;
align-items: center;
margin-top: 24px;
padding: 14px 16px;
border-radius: 8px;
background: #eef4ea;
}
+23
View File
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
use App\Core\Database;
require_once dirname(__DIR__) . '/app/bootstrap.php';
$dbStatus = 'not_checked';
try {
Database::pdo()->query('SELECT 1');
$dbStatus = 'connected';
} catch (Throwable $exception) {
$dbStatus = app_env('APP_DEBUG', 'false') === 'true'
? 'error: ' . $exception->getMessage()
: 'error';
}
view('pages/home', [
'title' => 'Рыбсток',
'dbStatus' => $dbStatus,
]);
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+21
View File
@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
/** @var string $templatePath */
/** @var string $title */
?>
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= htmlspecialchars($title ?? 'Рыбсток', ENT_QUOTES, 'UTF-8') ?></title>
<link rel="stylesheet" href="/assets/css/app.css">
</head>
<body>
<main class="page">
<?php require $templatePath; ?>
</main>
</body>
</html>
+16
View File
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
/** @var string $dbStatus */
?>
<section class="start-screen">
<p class="eyebrow">Старт проекта</p>
<h1>Рыбсток</h1>
<p>Чистый PHP-проект подготовлен. Следующий шаг - схема базы данных и таблицы для каталога, фасовок, заказов и админки.</p>
<div class="status-box">
<span>MariaDB:</span>
<strong><?= htmlspecialchars($dbStatus, ENT_QUOTES, 'UTF-8') ?></strong>
</div>
</section>
+31 -34
View File
@@ -356,7 +356,7 @@
## 5. Вопросы, которые стоит уточнить перед разработкой
1. На какой платформе быстрее запускать проект: Laravel/Filament, WordPress/WooCommerce с доработками, 1C-Bitrix, Django, Next.js + готовая админка или другая связка?
1. Какой локальный сервер и хостинг использовать для проекта на чистом PHP: Open Server Panel, Laragon, обычный хостинг, VPS или другой вариант?
2. Есть ли уже текущий сайт, каталог, база товаров, Excel/CSV с товарами и фотографиями?
3. Как сейчас хранятся товары: в Excel, 1С, МойСклад, вручную, в другой CMS?
4. Нужно ли интегрироваться с 1С, МойСкладом или другой учетной системой?
@@ -413,44 +413,36 @@
- Импорт товаров из Excel/CSV.
- Экспорт заказов в Excel.
## 7. Предлагаемый технический подход
## 7. Технический подход
Так как нужна быстрая сборка под ключ с логичной админкой, гибкими товарами и возможностью доработки программистом, оптимальные варианты:
Проект Рыбсток делаем на чистом PHP без фреймворка.
### Вариант A: Laravel + Filament
Базовая связка:
Подходит, если нужен индивидуальный проект с сильной админкой, гибкими фасовками, промокодами, заказами и прайсами.
- PHP 8.2+ или PHP 8.3+;
- MySQL или MariaDB;
- PDO для работы с базой;
- собственная админка на PHP;
- HTML, CSS и JavaScript для витрины;
- Яндекс.Карты для адресов и расчета доставки;
- email-отправка через SMTP;
- CSV/XLSX-выгрузка прайс-листа.
Плюсы:
Чистый PHP выбран потому, что проект должен быстро запускаться на обычном хостинге, легко передаваться программисту и не зависеть от тяжелых фреймворков. При этом код нужно писать структурно: отдельные папки для контроллеров, моделей/репозиториев, сервисов, шаблонов, админки, загрузок, экспортов и настроек.
- быстрая мощная админка;
- гибкая база данных;
- удобно дорабатывать;
- легко сделать Excel-выгрузку;
- хорошо подходит для кастомной логики доставки и фасовок.
Рекомендуемая структура проекта:
Минусы:
- больше разработки, чем на готовой CMS.
### Вариант B: WordPress + WooCommerce + доработки
Подходит, если важнее очень быстрый запуск на готовой базе.
Плюсы:
- быстро поднять витрину;
- много готовых модулей;
- понятная админка.
Минусы:
- сложная кастомная логика фасовок, доставки и промокодов может потребовать много доработок;
- админка может стать менее аккуратной при большом количестве плагинов.
### Рекомендация
Для проекта Рыбсток лучше выбрать Laravel + Filament, если цель - надежный сайт с гибкой логикой и понятной админкой, который можно быстро дорабатывать. Если нужно запуститься максимально срочно любой ценой, можно рассмотреть WooCommerce, но сложность фасовок и доставки делает индивидуальную разработку предпочтительнее.
- `public/` - входная точка сайта и публичные assets;
- `app/` - основной PHP-код;
- `app/Controllers/` - обработчики страниц;
- `app/Models/` - модели данных;
- `app/Repositories/` - запросы к базе;
- `app/Services/` - бизнес-логика: доставка, промокоды, корзина, письма, прайс;
- `views/` - шаблоны сайта;
- `admin/` - собственная админка;
- `storage/` - загрузки, экспорты, логи;
- `database/` - SQL-схема, миграции или seed-файлы;
- `docs/` - рабочие документы проекта.
## 8. Пошаговый план работ
@@ -631,4 +623,9 @@
## 10. Ближайший следующий шаг
Перед кодом нужно выбрать платформу и получить исходные данные по каталогу. Рекомендуемый следующий шаг: принять решение по технической связке и подготовить структуру базы данных/моделей для товаров, фасовок, заказов, промокодов и доставки.
Перед кодом нужно получить исходные данные по каталогу и подготовить структуру базы данных/моделей для товаров, фасовок, заказов, промокодов и доставки.
## 11. Статус выполнения
- Шаг 1 выполнен: выбрана техническая связка на чистом PHP, создан стартовый каркас проекта, подключение к MariaDB через `.env`, базовые папки и файл `ШАГ_1_ТЕХНИЧЕСКОЕ_РЕШЕНИЕ.md`.
- Следующий шаг: спроектировать базу данных, модели, связи и структуру собственной админки.
@@ -0,0 +1,280 @@
# Шаг 1. Техническое решение для сайта Рыбсток
## Решение
Проект Рыбсток делаем на чистом PHP без фреймворка.
Базовая связка:
- Backend: чистый PHP 8.2+ или PHP 8.3+
- База данных: MySQL или MariaDB
- Доступ к базе: PDO
- Frontend: HTML, CSS, JavaScript
- Админка: собственная админ-панель на PHP
- Шаблоны: обычные PHP-шаблоны
- Поиск на первом этапе: SQL-поиск с нормализацией русских слов и словарем синонимов
- Поиск на расширенном этапе: отдельный поисковый индекс или внешний движок, если понадобится
- Excel-выгрузка: CSV на первом этапе, XLSX через PHP-библиотеку или ручную генерацию позже
- Карты и адреса: Яндекс.Карты / Яндекс Геокодер / Яндекс Маршрутизация
- Email: SMTP-почта домена или внешний SMTP-сервис
## Почему чистый PHP подходит
Чистый PHP подходит, если важны:
- быстрый запуск на обычном хостинге;
- понятный код без тяжелого фреймворка;
- простая передача проекта другому программисту;
- полный контроль над логикой фасовок, корзины, доставки и админки;
- минимум зависимостей;
- возможность быстро править проект вручную.
Важно: чистый PHP нужно писать структурно. Даже без фреймворка проект должен иметь понятные слои: маршруты, контроллеры, модели/репозитории, сервисы, шаблоны и админку. Иначе большой каталог, фасовки, промокоды и доставка быстро станут трудными для поддержки.
## Что сейчас есть в папке проекта
В папке проекта создан стартовый каркас:
- `.git`
- `.vscode`
- `ПЛАН_ДЕЙСТВИЙ_РЫБСТОК.md`
- `ШАГ_1_ТЕХНИЧЕСКОЕ_РЕШЕНИЕ.md`
- `.env`
- `.env.example`
- `.gitignore`
- `README.md`
- `public/index.php`
- `public/assets/css/app.css`
- `app/bootstrap.php`
- `app/helpers.php`
- `app/Core/Env.php`
- `app/Core/Database.php`
- `app/Core/View.php`
- `views/layouts/main.php`
- `views/pages/home.php`
- `admin/index.php`
- `database/schema.sql`
- `storage/`
- `docs/`
В `.env` внесены данные подключения к базе MariaDB. Сам файл закрыт от git через `.gitignore`.
## Что нужно установить для разработки
На рабочем компьютере должны быть доступны:
- PHP 8.2 или новее, лучше PHP 8.3
- MySQL или MariaDB
- Веб-сервер: Apache или Nginx
- Git
Желательно:
- Open Server Panel, Laragon или другой локальный сервер для Windows
- phpMyAdmin или Adminer для работы с базой
- Composer только по желанию, если позже захотим подключить готовые библиотеки для SMTP, Excel или dotenv
Для чистого PHP Composer не обязателен на старте.
## Рекомендуемая структура проекта
```text
loveriba.ru/
app/
Config/
Controllers/
Core/
Models/
Repositories/
Services/
Validators/
admin/
controllers/
views/
public/
assets/
css/
js/
images/
index.php
storage/
uploads/
exports/
logs/
views/
layouts/
pages/
product/
catalog/
cart/
checkout/
emails/
database/
migrations/
seeders/
schema.sql
docs/
.env.example
README.md
```
## Основные модули
- `Products` - товары
- `ProductVariants` - фасовки и цены
- `Categories` - категории и подкатегории
- `Cart` - корзина
- `Checkout` - оформление заказа
- `Orders` - заказы
- `Promocodes` - промокоды и ограничения
- `Delivery` - расчет доставки
- `CustomerAccess` - вход по email-коду и история заказов
- `CompareList` - список сравнения до 10 товаров
- `Search` - поиск и голосовой ввод
- `Reviews` - отзывы и рейтинг
- `PriceExport` - прайс-лист
- `AdminSettings` - настройки сайта, писем, доставки, счетчиков
## Базовая структура базы данных
На втором шаге нужно спроектировать таблицы:
- `admins` - администраторы
- `categories`
- `products`
- `product_variants`
- `orders`
- `order_items`
- `promocodes`
- `promocode_targets`
- `customer_login_codes`
- `reviews`
- `site_settings`
- `popular_products`
- `promo_blocks`
- `delivery_calculations`
- `product_images`
- `product_search_terms`
## Важные технические решения
### Фасовки
Фасовка должна быть отдельной таблицей, а не просто текстовым полем товара. Это важно, потому что у каждой фасовки своя цена, шаг количества, единица измерения и отображение в корзине.
Пример:
- товар: горбуша неразделанная;
- фасовка: пласт 10 кг;
- базовая единица: кг;
- цена за кг: 600 руб.;
- цена фасовки: 6000 руб.;
- шаг количества: 10 кг.
### Корзина
Корзина должна хранить не просто товар, а конкретную фасовку товара.
Пример позиции:
- `product_id`
- `variant_id`
- название товара
- название фасовки
- количество
- шаг количества
- цена за единицу
- цена за фасовку
- итог
### Заказы
Заказ должен сохранять снимок данных на момент оформления:
- название товара;
- выбранная фасовка;
- цена;
- количество;
- итог;
- расчет доставки;
- примененный промокод.
Это нужно, чтобы изменение цены в каталоге не ломало старые заказы.
### Доставка
Расчет доставки делаем отдельным PHP-сервисом `DeliveryCalculator`.
Он принимает:
- тип получения;
- адрес;
- сумму заказа;
- расстояние от МКАД;
- признак подъема.
И возвращает:
- стоимость доставки;
- порог бесплатной доставки;
- текстовое объяснение для клиента;
- технические данные для заказа.
### Админка
Админка должна быть собственной, без лишних плагинов.
Минимальные разделы:
- товары;
- категории;
- фасовки;
- заказы;
- промокоды;
- акции;
- популярные товары;
- отзывы;
- настройки доставки;
- настройки писем;
- прайс-лист;
- страницы и тексты сайта.
### Email вместо SMS
Все коды входа и письма отправляются только на email. SMS не используются.
### Прайс-лист
На первом этапе можно сделать выгрузку CSV, который открывается в Excel. Если нужен именно `.xlsx`, подключим отдельную библиотеку или сделаем генератор XLSX.
Прайс должен собираться из опубликованных товаров и опубликованных фасовок. Каждая фасовка должна быть отдельной строкой, чтобы покупатель видел все цены.
## Второй шаг
Следующий шаг: спроектировать базу данных и модели проекта под чистый PHP.
Нужно подготовить:
1. Список таблиц.
2. Поля каждой таблицы.
3. Связи между таблицами.
4. Правила расчета цены фасовки.
5. Правила расчета доставки.
6. Структуру собственной админки.
7. Первичный SQL-файл `database/schema.sql`.
После второго шага можно будет создавать файловую структуру проекта и начинать код.
Файловая структура уже создана на шаге 1, поэтому на шаге 2 можно сразу перейти к SQL-схеме, таблицам и первичным PHP-моделям.
## Что нужно уточнить перед кодом
Минимально важные вопросы:
1. Есть ли файл с товарами, ценами и фасовками: Excel, CSV или другая база?
2. Есть ли фотографии товаров и склада?
3. Какой email использовать для получения заказов?
4. Есть ли логотип Рыбсток?
5. Где будет размещаться сайт: обычный хостинг, VPS, Timeweb, Beget, Reg.ru, другой вариант?
6. Нужно ли сразу делать импорт из Excel или на старте можно заносить товары через админку?
7. Какой локальный сервер удобнее использовать: Open Server Panel, Laragon или другой?