# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

shopPRO is a PHP e-commerce platform with an admin panel and customer-facing storefront. It uses Medoo ORM (`$mdb`), Redis caching, and a Domain-Driven Design architecture with Dependency Injection (migration from legacy architecture complete).

## PHP Version Constraint

**Production runs PHP < 8.0.** Do NOT use:
- `match` expressions (use ternary operators or if/else)
- Named arguments
- Union types (`int|string`)
- `str_contains()`, `str_starts_with()`, `str_ends_with()`
- Other PHP 8.0+ syntax

`composer.json` requires `>=7.4`.

## Commands

### Running Tests
```bash
# Full suite (recommended — PowerShell, auto-finds php)
./test.ps1

# Specific file
./test.ps1 tests/Unit/Domain/Product/ProductRepositoryTest.php

# Specific test method
./test.ps1 --filter testGetQuantityReturnsCorrectValue

# Alternative
composer test
```

PHPUnit 9.6 via `phpunit.phar`. Bootstrap: `tests/bootstrap.php`. Config: `phpunit.xml`.

Current suite: **750 tests, 2114 assertions**.

### Creating Updates
See `docs/UPDATE_INSTRUCTIONS.md` for the full procedure. Updates are ZIP packages in `updates/0.XX/`. Never include `*.md` files, `updates/changelog.php`, or root `.htaccess` in update ZIPs.

## Architecture

### Directory Structure
```
shopPRO/
├── autoload/                   # Autoloaded classes (core codebase)
│   ├── Domain/                 # Business logic repositories (\Domain\)
│   ├── Shared/                 # Shared utilities (\Shared\)
│   │   ├── Cache/              # CacheHandler, RedisConnection
│   │   ├── Email/              # Email (PHPMailer wrapper)
│   │   ├── Helpers/            # Helpers (formerly class.S.php)
│   │   ├── Html/               # Html utility
│   │   ├── Image/              # ImageManipulator
│   │   └── Tpl/                # Template engine
│   ├── api/                    # REST API layer (\api\)
│   │   ├── ApiRouter.php       # API router (\api\ApiRouter)
│   │   └── Controllers/        # API controllers (\api\Controllers\)
│   ├── admin/                  # Admin panel layer
│   │   ├── App.php             # Admin router (\admin\App)
│   │   ├── Controllers/        # DI controllers (\admin\Controllers\) — 28 controllers
│   │   ├── Support/            # TableListRequestFactory, Forms/FormRequestHandler, Forms/FormFieldRenderer
│   │   ├── Validation/         # FormValidator
│   │   └── ViewModels/         # Forms/ (FormEditViewModel, FormField, FormTab, FormAction, FormFieldType), Common/ (PaginatedTableViewModel)
│   └── front/                  # Frontend layer
│       ├── App.php             # Frontend router (\front\App)
│       ├── LayoutEngine.php    # Layout engine (\front\LayoutEngine)
│       ├── Controllers/        # DI controllers (\front\Controllers\) — 8 controllers
│       └── Views/              # Static views (\front\Views\) — 11 view classes
├── admin/                      # Admin panel
│   ├── templates/              # Admin view templates
│   └── layout/                 # Admin CSS/JS/icons
├── templates/                  # Frontend view templates
├── libraries/                  # Third-party libraries (Medoo, RedBeanPHP, PHPMailer)
├── tests/                      # PHPUnit tests
│   ├── bootstrap.php
│   ├── stubs/                  # Test stubs (CacheHandler, Helpers, ShopProduct)
│   └── Unit/
│       ├── Domain/             # Repository tests
│       ├── admin/Controllers/  # Controller tests
│       └── api/                # API tests
├── updates/                    # Update packages for clients
├── docs/                       # Technical documentation
├── config.php                  # Database/Redis config (not in repo)
├── index.php                   # Frontend entry point
├── ajax.php                    # Frontend AJAX handler
├── admin/index.php             # Admin entry point
├── admin/ajax.php              # Admin AJAX handler
├── cron.php                    # CRON jobs (Apilo sync)
└── api.php                     # REST API (ordersPRO + Ekomi)
```

### Autoloader

Custom autoloader in each entry point (not Composer autoload at runtime). Tries two filename conventions:
1. `autoload/{namespace}/class.{ClassName}.php` (legacy)
2. `autoload/{namespace}/{ClassName}.php` (PSR-4 style, fallback)

### Namespace Conventions (case-sensitive on Linux!)
- `\Domain\` → `autoload/Domain/` (uppercase D)
- `\admin\Controllers\` → `autoload/admin/Controllers/` (lowercase a)
- `\Shared\` → `autoload/Shared/`
- `\front\` → `autoload/front/`
- `\api\` → `autoload/api/`
- Do NOT use `\Admin\` (uppercase A) — the server directory is `admin/` (lowercase)
- `\shop\` namespace is **deleted** — all 12 legacy classes migrated to `\Domain\`, `autoload/shop/` directory removed

### Domain-Driven Architecture (migration complete)

All legacy directories (`admin/controls/`, `admin/factory/`, `admin/view/`, `front/controls/`, `front/view/`, `front/factory/`, `shop/`) have been deleted. All modules now use this pattern:

**Domain Layer** (`autoload/Domain/{Module}/`):
- `{Module}Repository.php` — data access, business logic, Redis caching
- Constructor DI with `$db` (Medoo instance)
- Methods serve both admin and frontend (shared Domain, no separate services)

**Domain Modules**: Article, Attribute, Banner, Basket, Cache, Category, Client, Coupon, Dashboard, Dictionaries, Integrations, Languages, Layouts, Newsletter, Order, Pages, PaymentMethod, Producer, Product, ProductSet, Promotion, Scontainers, Settings, ShopStatus, Transport, Update, User

**Admin Controllers** (`autoload/admin/Controllers/`):
- DI via constructor (repositories injected)
- Wired in `admin\App::getControllerFactories()`

**Frontend Controllers** (`autoload/front/Controllers/`):
- DI via constructor
- Wired in `front\App::getControllerFactories()`

**Frontend Views** (`autoload/front/Views/`):
- Static classes, no state, no DI — pure rendering

**API Controllers** (`autoload/api/Controllers/`):
- DI via constructor, stateless (no session)
- Wired in `api\ApiRouter::getControllerFactories()`
- Auth: `X-Api-Key` header vs `pp_settings.api_key`

### Key Classes
| Class | Purpose |
|-------|---------|
| `\admin\App` | Admin router — maps URL segments to controllers |
| `\front\App` | Frontend router — `route()`, `checkUrlParams()` |
| `\front\LayoutEngine` | Frontend layout engine — `show()`, tag replacement |
| `\Shared\Helpers\Helpers` | Utility methods (SEO, email, cache clearing) |
| `\Shared\Tpl\Tpl` | Template engine — `render()`, `set()` |
| `\Shared\Cache\CacheHandler` | Redis cache — `get()`, `set()`, `delete()`, `deletePattern()` |
| `\api\ApiRouter` | REST API router — auth, routing, response helpers |

### Database
- ORM: Medoo (`$mdb` global variable, injected via DI in new code)
- Table prefix: `pp_`
- Key tables: `pp_shop_products`, `pp_shop_orders`, `pp_shop_categories`, `pp_shop_clients`
- Full schema: `docs/DATABASE_STRUCTURE.md`

### Form Edit System
Universal form system for admin edit views. Docs: `docs/FORM_EDIT_SYSTEM.md`.
- **ViewModels** (`admin\ViewModels\Forms\`): `FormEditViewModel`, `FormField`, `FormTab`, `FormAction`, `FormFieldType`
- **Validation**: `admin\Validation\FormValidator`
- **Rendering**: `admin\Support\Forms\FormFieldRenderer`, `admin\Support\Forms\FormRequestHandler`
- **Template**: `admin/templates/components/form-edit.php`
- **Table lists**: `admin\Support\TableListRequestFactory` + `admin\ViewModels\Common\PaginatedTableViewModel`

### Caching
- Redis via `\Shared\Cache\CacheHandler` (singleton `RedisConnection`)
- Key pattern for products: `shop\product:{id}:{lang}:{permutation_hash}`
- Clear product cache: `\Shared\Helpers\Helpers::clear_product_cache($id)`
- Pattern delete: `CacheHandler::deletePattern("shop\\product:{$id}:*")`
- Default TTL: 86400 (24h)
- Data is serialized — requires `unserialize()` after `get()`
- Config: `config.php` (`$config['redis']`)

## Code Patterns

### New code should follow DI pattern
```php
// Repository with constructor DI
class ExampleRepository {
    private $db;
    public function __construct($db) {
        $this->db = $db;
    }
    public function find(int $id): ?array {
        return $this->db->get('pp_table', '*', ['id' => $id]);
    }
}

// Controller wiring (in admin\App or front\App)
$repo = new \Domain\Example\ExampleRepository($mdb);
$controller = new \admin\Controllers\ExampleController($repo);
```

### Medoo ORM pitfalls
- `$mdb->delete($table, $where)` takes **2 arguments**, NOT 3 — has caused bugs
- `$mdb->get()` returns `null` when no record, NOT `false`
- After `$mdb->insert()`, check `$mdb->id()` to confirm success

### File naming
- New classes: `ClassName.php` (no `class.` prefix)
- Legacy classes: `class.ClassName.php` (leave until migrated)

### Test conventions
- Extend `PHPUnit\Framework\TestCase`
- Mock Medoo: `$this->createMock(\medoo::class)`
- AAA pattern: Arrange, Act, Assert
- Tests mirror source structure: `tests/Unit/Domain/{Module}/{Class}Test.php`

## Workflow (AGENTS.md)

When user says **"KONIEC PRACY"**, execute in order:
1. Run tests
2. Update documentation if needed: `docs/DATABASE_STRUCTURE.md`, `docs/PROJECT_STRUCTURE.md`, `docs/FORM_EDIT_SYSTEM.md`, `docs/CHANGELOG.md`, `docs/TESTING.md`
3. SQL migrations (if DB changes): place in `migrations/{version}.sql` (e.g. `migrations/0.304.sql`). **NOT** in `updates/` — build script reads from `migrations/` automatically
4. Commit
5. Push

Before starting implementation, review current state of docs (see AGENTS.md for full list).

## Key Documentation
- `docs/MEMORY.md` — project memory: known issues, confirmed patterns, ORM pitfalls, caching conventions
- `docs/PROJECT_STRUCTURE.md` — current architecture, layers, cache, entry points, integrations
- `docs/DATABASE_STRUCTURE.md` — full database schema
- `docs/TESTING.md` — test suite guide and structure
- `docs/FORM_EDIT_SYSTEM.md` — form system architecture
- `docs/CHANGELOG.md` — version history
- `docs/API.md` — REST API documentation (ordersPRO)
- `docs/UPDATE_INSTRUCTIONS.md` — how to build client update packages
