docs: map existing codebase
- stack.md (68 lines) - PHP/MySQL/Apache stack, vendored libraries - architecture.md (131 lines) - Custom MVC CMS, dual-layer (front/admin) - structure.md (170 lines) - Directory layout and conventions - conventions.md (98 lines) - PHP snake_case, SCSS $c/$f prefixes, jQuery patterns - testing.md (49 lines) - No automated tests detected - integrations.md (111 lines) - Google Maps, PHPMailer, Pixieset, Facebook - concerns.md (150 lines) - Critical security issues: hardcoded creds, MD5, unserialize - db_schema.md (260 lines) - ~32 tables with pp_ prefix, inferred from source - tech_changelog.md (9 lines) - Initial log entry Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
318
.paul/codebase/db_schema.md
Normal file
318
.paul/codebase/db_schema.md
Normal file
@@ -0,0 +1,318 @@
|
||||
# Database Schema
|
||||
|
||||
**Updated:** 2026-05-05 | **Total tables:** ~32 | **Engine:** MySQL InnoDB | **Prefix:** `pp_`
|
||||
|
||||
Schema inferred from Medoo ORM queries in PHP source files. No migration files found — schema managed manually.
|
||||
|
||||
---
|
||||
|
||||
## Content — Articles
|
||||
|
||||
**`pp_articles`** — Main article/content records
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `hash` | VARCHAR | Unique identifier for external reference |
|
||||
| `status` | TINYINT | Published/draft/archived |
|
||||
| `date_add` | DATETIME | Creation timestamp |
|
||||
| `views` | INT | View counter |
|
||||
| `password` | VARCHAR | Optional password protection |
|
||||
| `pixieset` | VARCHAR | Pixieset gallery identifier |
|
||||
|
||||
**`pp_articles_langs`** — Language-specific article content
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `article_id` | INT | FK → `pp_articles.id` |
|
||||
| `lang_id` | INT | FK → `pp_langs.id` |
|
||||
| `title` | VARCHAR | Article title |
|
||||
| `content` | LONGTEXT | Rich text content |
|
||||
| `seo_link` | VARCHAR | URL slug |
|
||||
| `noindex` | TINYINT | SEO noindex flag |
|
||||
|
||||
**`pp_articles_images`** — Article gallery images
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `article_id` | INT | FK → `pp_articles.id` |
|
||||
| `src` | VARCHAR | Image filename/path |
|
||||
| `o` | INT | Sort order |
|
||||
| `favorite` | TINYINT | Flagged as favorite |
|
||||
| `to_delete` | TINYINT | Soft-delete flag |
|
||||
|
||||
**`pp_articles_files`** — Downloadable file attachments
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `article_id` | INT | FK → `pp_articles.id` |
|
||||
| `src` | VARCHAR | File path |
|
||||
| `name` | VARCHAR | Display name |
|
||||
| `to_delete` | TINYINT | Soft-delete flag |
|
||||
|
||||
**`pp_articles_pages`** — Article-to-Page assignments
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `article_id` | INT | FK → `pp_articles.id` |
|
||||
| `page_id` | INT | FK → `pp_pages.id` |
|
||||
| `o` | INT | Sort order on page |
|
||||
| `status` | TINYINT | Visibility on this page |
|
||||
|
||||
**`pp_articles_tags`** — Many-to-many: Articles ↔ Tags
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `article_id` | INT | FK → `pp_articles.id` |
|
||||
| `tag_id` | INT | FK → `pp_tags.id` |
|
||||
|
||||
**`pp_articles_additional_params`** — Custom field definitions per article
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `article_id` | INT | FK → `pp_articles.id` |
|
||||
| `name` | VARCHAR | Field name |
|
||||
| `language` | VARCHAR | Language code |
|
||||
| `status` | TINYINT | Active/inactive |
|
||||
|
||||
**`pp_articles_additional_values`** — Custom field values
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `param_id` | INT | FK → `pp_articles_additional_params.id` |
|
||||
| `article_id` | INT | FK → `pp_articles.id` |
|
||||
| `value` | TEXT | Field value |
|
||||
| `language_id` | INT | FK → `pp_langs.id` |
|
||||
|
||||
---
|
||||
|
||||
## Content — Pages & Layouts
|
||||
|
||||
**`pp_pages`** — Website page hierarchy
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `menu_id` | INT | FK → `pp_menus.id` |
|
||||
| `parent_id` | INT | Self-referencing for nested pages |
|
||||
| `page_type` | VARCHAR | Type identifier |
|
||||
| `sort_type` | VARCHAR | Article sort order |
|
||||
| `status` | TINYINT | Published/hidden |
|
||||
| `start` | TINYINT | Homepage flag |
|
||||
| `o` | INT | Sort order in navigation |
|
||||
| `cache` | TINYINT | Page-level cache toggle |
|
||||
|
||||
**`pp_pages_langs`** — Page language translations
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `page_id` | INT | FK → `pp_pages.id` |
|
||||
| `lang_id` | INT | FK → `pp_langs.id` |
|
||||
| `title` | VARCHAR | Page title |
|
||||
| `seo_link` | VARCHAR | URL slug |
|
||||
|
||||
**`pp_layouts`** — Template layout containers
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `layout_id` | INT | Parent layout reference |
|
||||
| `name` | VARCHAR | Layout name |
|
||||
| `html` | LONGTEXT | Desktop HTML with `[PLACEHOLDER:id]` tags |
|
||||
| `m_html` | LONGTEXT | Mobile HTML |
|
||||
| `css` | TEXT | Inline CSS |
|
||||
| `m_css` | TEXT | Mobile inline CSS |
|
||||
| `js` | TEXT | Inline JS |
|
||||
| `m_js` | TEXT | Mobile inline JS |
|
||||
| `status` | TINYINT | Active/inactive |
|
||||
|
||||
**`pp_layouts_pages`** — Layout-to-page assignments
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `layout_id` | INT | FK → `pp_layouts.id` |
|
||||
| `page_id` | INT | FK → `pp_pages.id` |
|
||||
|
||||
---
|
||||
|
||||
## Content — Static Containers & Banners
|
||||
|
||||
**`pp_scontainers`** — Reusable static content blocks
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `status` | TINYINT | Active/inactive |
|
||||
|
||||
**`pp_scontainers_langs`** — Static container translations
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `scontainer_id` | INT | FK → `pp_scontainers.id` |
|
||||
| `lang_id` | INT | FK → `pp_langs.id` |
|
||||
| `html` | LONGTEXT | Content HTML |
|
||||
|
||||
**`pp_banners`** — Promotional banners
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `name` | VARCHAR | Banner name |
|
||||
| `status` | TINYINT | Active/inactive |
|
||||
|
||||
**`pp_banners_langs`** — Banner language versions
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `id_banner` | INT | FK → `pp_banners.id` |
|
||||
| `id_lang` | INT | FK → `pp_langs.id` |
|
||||
| `title` | VARCHAR | Banner title |
|
||||
| `content` | TEXT | Banner content/HTML |
|
||||
|
||||
---
|
||||
|
||||
## Navigation & Taxonomy
|
||||
|
||||
**`pp_menus`** — Navigation menu definitions
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `name` | VARCHAR | Menu name |
|
||||
|
||||
**`pp_tags`** — Article tags/categories
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `name` | VARCHAR | Tag name |
|
||||
|
||||
**`pp_authors`** — Article author profiles
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
|
||||
**`pp_authors_langs`** — Author translations
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `author_id` | INT | FK → `pp_authors.id` |
|
||||
| `lang_id` | INT | FK → `pp_langs.id` |
|
||||
|
||||
---
|
||||
|
||||
## Users & Access Control
|
||||
|
||||
**`pp_users`** — Admin panel users
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `login` | VARCHAR | Username |
|
||||
| `password` | VARCHAR | MD5 hash (security concern — see concerns.md) |
|
||||
| `status` | TINYINT | Active/inactive |
|
||||
| `active_to` | DATE | Account expiry date |
|
||||
| `admin` | TINYINT | Super-admin flag |
|
||||
| `error_logged_count` | INT | Failed login counter |
|
||||
|
||||
**`pp_users_privileges`** — User permission assignments
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id_user` | INT | FK → `pp_users.id` |
|
||||
| `name` | VARCHAR | Privilege name (string key) |
|
||||
|
||||
---
|
||||
|
||||
## Multi-language
|
||||
|
||||
**`pp_langs`** — Available language configurations
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `start` | TINYINT | Default language flag |
|
||||
| `domain` | VARCHAR | Domain for this language |
|
||||
| `main_domain` | VARCHAR | Primary domain |
|
||||
| `o` | INT | Sort order |
|
||||
| `status` | TINYINT | Active/inactive |
|
||||
|
||||
**`pp_langs_translations`** — UI string translations
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| Dynamic columns per language ID | VARCHAR | Translation strings |
|
||||
|
||||
---
|
||||
|
||||
## Newsletter
|
||||
|
||||
**`pp_newsletter`** — Email subscribers
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `email` | VARCHAR | Subscriber email |
|
||||
| `status` | TINYINT | Active/unsubscribed |
|
||||
|
||||
**`pp_newsletter_send`** — Sent campaign log
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `email_template_id` | INT | FK → `pp_newsletter_templates.id` |
|
||||
|
||||
**`pp_newsletter_templates`** — Email campaign templates
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
| `name` | VARCHAR | Template name |
|
||||
| `subject` | VARCHAR | Email subject |
|
||||
| `html` | LONGTEXT | Email HTML body |
|
||||
|
||||
---
|
||||
|
||||
## Contacts & Locations
|
||||
|
||||
**`pp_contacts_maps`** — Location/contact point entries
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INT | PK, AUTO_INCREMENT |
|
||||
|
||||
**`pp_contacts_maps_products`** — Product associations for locations
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `map_id` | INT | FK → `pp_contacts_maps.id` |
|
||||
| `product_id` | INT | Product reference |
|
||||
|
||||
**`pp_contacts_maps_provinces`** — Province associations for locations
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `map_id` | INT | FK → `pp_contacts_maps.id` |
|
||||
| `province_id` | INT | Province reference |
|
||||
|
||||
**`pp_contact_emails`** — Contact form submissions
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `email` | VARCHAR | Sender email |
|
||||
| `phone` | VARCHAR | Sender phone |
|
||||
| `title` | VARCHAR | Subject |
|
||||
| `mail` | TEXT | Message body |
|
||||
| `add_date` | DATETIME | Submission timestamp |
|
||||
|
||||
---
|
||||
|
||||
## SEO & Settings
|
||||
|
||||
**`pp_seo_additional`** — Per-article SEO metadata
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `article_id` | INT | FK → `pp_articles.id` |
|
||||
| `lang_id` | INT | FK → `pp_langs.id` |
|
||||
|
||||
**`pp_settings`** — Global CMS configuration (key-value store)
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `param` | VARCHAR | Setting key (e.g., `google_map_key`, `email_host`) |
|
||||
| `value` | TEXT | Setting value |
|
||||
|
||||
---
|
||||
|
||||
## Schema Characteristics
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Engine | MySQL InnoDB |
|
||||
| Table prefix | `pp_` |
|
||||
| Soft deletes | `to_delete` column on `pp_articles_images`, `pp_articles_files` |
|
||||
| Multi-language | All content tables have `_langs` counterpart |
|
||||
| Migrations | None — schema managed manually |
|
||||
| Timestamps | `date_add` on articles; not standardized across all tables |
|
||||
|
||||
## Key Relationships
|
||||
|
||||
- **1:N** — `pp_articles` → `pp_articles_images`, `pp_articles_files`, `pp_articles_langs`
|
||||
- **N:N** — `pp_articles` ↔ `pp_tags` (via `pp_articles_tags`)
|
||||
- **N:N** — `pp_articles` ↔ `pp_pages` (via `pp_articles_pages`)
|
||||
- **Hierarchical** — `pp_pages.parent_id` self-reference for nested page tree
|
||||
- **Language join** — all content tables join `pp_langs` on `lang_id` for translation
|
||||
Reference in New Issue
Block a user