System + Business Directory Cheat Sheet

Canonical reference for routing, tenants, storage, and how each homepage section is populated. Use this page to stay consistent while building new routes.

Updated: 2026-06-04T18:13:24-05:00

Quick Diagnostics

These values should be provided by the route (tenant, DB file, row counts). If they are blank, wire them up in the route stub.

Tenant
systmzro
BD SQLite File
/home1/sys9/public_html/database/tenants/systmzro/business-directory.sqlite
Exists: Yes
Row Counts
bd_categories: 0
bd_listings: 0
db_categories:
db_listings:

Cheat Sheet

Use the filter to quickly find the section you need.

0) Mental model: how a request becomes a rendered page

HTTP Request → /core/router.php → loads /core/assets/config/base-controller.php → dispatches to a route PHP file → route loads plugin bootstrap(s) → route builds data → route renders Twig via $twig->render().

1) Core paths, tenants, and storage rules

Canonical constants (from constants.php)
  • BASE_PATH = project root (trailing slash)
  • CORE_PATH = BASE_PATH . 'core/'
  • PLUGIN_PATH = BASE_PATH . 'plugins/'
  • DATA_PATH = CORE_PATH . 'assets/data/' (flat files)
  • DATABASE_TENANTS_PATH = CORE_PATH . 'assets/database/tenants/' (SQLite DBs)
Global SQLite rule

All plugin SQLite DBs live in: /core/assets/database/tenants/<tenant>/ (no plugin subfolders).

Important

If a plugin uses a different SQLite location than the global rule, you will keep seeing “minor” mismatches: wrong categories, wrong listing counts, “missing” data, etc.

2) System bootstrap (Twig, themes, menus)

  • /core/assets/config/base-controller.php initializes Twig and adds namespaces like @core and @theme.
  • Active theme is derived from tenant data under Data::$ROOT/settings/active-theme.json.
  • Menus are loaded either via MenuStore or JSON fallback (e.g., main_menu.json).
PHP compatibility note

If any environment still runs PHP 7.x, check for usage of array_is_list() (PHP 8.1+).

3) Router: request dispatch contract

Routing order (high-level)
  1. Registry direct routes
  2. Public shortcuts owned by plugin groups
  3. Groups dispatch + group bootstrap require
  4. Slug/content lookup
  5. Fallback to /core/routes/<prefix>.php or index
  6. 404
Role gating

meta[route]['role'] can enforce early 403 gates.

4) Auth + tenant + affiliates (users subsystem)

  • Users canonical storage: /core/assets/database/tenants/<tenant>/users.sqlite
  • Profiles may still be JSON in /core/assets/data/tenants/<tenant>/profiles/
  • Affiliate intercept occurs pre-routing based on reserved prefixes and custom URLs.

5) Business Directory plugin bootstrap

Bootstrap contract
  • Idempotent (safe to require multiple times)
  • No output, no headers, no session changes
  • Defines tenant + DB helpers and ensures schema
Canonical BD tables (intended)
  • bd_categories — parent/child, featured flags, icons, slugs
  • bd_listings — extracted columns + full payload_json
High-impact rule

If homepage departments are built from one categories table, but listing.category_id points to a different categories table, department listing counts will be zero. Ensure the homepage uses the same categories “universe” as listings.

6) Critical inconsistencies to resolve

  • SQLite location conflict: global rule uses /core/assets/database/tenants/<tenant>/, but BD may be using a DATA_PATH location in some implementations.
  • Plugin root inconsistency: standardize on PLUGIN_PATH for plugins (not CORE_PATH . 'plugins/').
  • Auth tenant path duplication: long-term refactor to share tenant path helpers across subsystems.

7) “When I change something” checklist

  • When a route changes: update RouteRegistry, confirm shortcut ownership, confirm role gating.
  • When tenant changes: confirm data is under DATA_PATH and SQLite under DATABASE_TENANTS_PATH.
  • When categories change: confirm JSON → SQLite sync, and confirm listings reference the same category IDs.
  • When listing payload changes: confirm extracted columns remain accurate (name, slug, category_id, city, state, status, plan_slug).

8) Business Directory homepage data map

This section is the operational “what pulls what” map. Keep it updated as routes evolve.

A) Featured This Week (right side in hero)
  • Source: listings query with featured_only=1
  • DB: bd_listings (or your active listings table)
  • Fields used: id, name, logo (payload), city, state, category_name
B) Search the directory (grid)
  • Source: listings query with filters (q/category/state/city)
  • DB: listings table
  • Behavior note: your Twig form currently posts to /business-directory-search-results, not the same page
C) Browse by department (category cards)
  • Source: categories query (featured parents) + departmentStats rollups
  • DB: categories table + listings table aggregation
  • Critical contract: category IDs used in this section must match the IDs referenced by listing.category_id
D) Casting / Events / News blocks
  • Source: JSON files (tenant-aware)
  • Location: assets/json/<tenant>/casting.json etc (or whichever resolved path your bd_file() selects)
E) Packages (plans)
  • Source: plans JSON (tenant-aware)
  • Filtered: is_active then sorted by sort_order
If “Listings” stays at 0 here, check exactly three things first
  1. Which categories table is used for departments (bd_categories vs db_categories)
  2. Which listings table is used for counts (bd_listings vs legacy table)
  3. Whether listing.category_id values exist in the chosen categories table (ID-space match)