seedproject-web/api/core/core.md
Carlos Arias 1559ce017d chore: scaffold SeedProject base (Phase 1)
Clean-room copy of the reusable engines from comiida, with all
instance data, secrets, dependencies, and build output excluded:
- app/         Astro theme skeleton (no comiida blog posts; hero image -> placeholder)
- api/         SeedProject PHP framework (no vendor/.env/config.php)
- content-pipeline/  engine only (scripts/admin/prompts; empty runtime state)
- astroagent.config.json + app/.astroagent/skills

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SYHWLHihq3v9nxNwoPCKSn
2026-07-04 22:53:10 +00:00

3.7 KiB

Core Framework — core/

Framework internals. Rarely modified. Contains the request lifecycle engine.

Files

File Purpose
Bootstrap.php Bootstraps environment, loads config, starts routing
Controller.php Base controller class — all UI controllers extend this (via AppController)
View.php View renderer — handles render(), PartialView(), asset injection
AltoRouter.php URL router used for complex custom routes

Request Lifecycle

Request
  → public/index.php
  → config.php          (loads .env, defines constants)
  → core/Bootstrap.php  (environment setup, session, autoload)
  → AltoRouter / auto-routing
  → ControllerClass::method($param)
  → $this->view->render(...)
  → Response (HTML or JSON)

Auto-routing maps /controller/method/param directly to ControllerClass::method($param).


Controller (core/Controller.php)

Base class. Provides:

  • $this->view — View instance
  • $this->JavaScript[] — array of JS paths to inject
  • $this->Styles[] — array of CSS paths to inject
  • redirect($url) — HTTP redirect helper

Do not modify this file. App-wide customisation belongs in app/Controllers/AppController.php.


AppController (app/Controllers/AppController.php)

Sits between core/Controller and all UI page controllers. UI controllers extend AppController, not Controller directly.

Current responsibilities:

  • Injects $UserProfile (logged-in user's full profile via User::profile()) into every view automatically.

use statement scoping rule:

  • Namespaces needed on every page → AppController
  • Namespaces needed by one controller → that controller only
  • Namespaces needed by one method → inline as fully-qualified class name
// app/Controllers/AppController.php
use App\Components\User;

class AppController extends Controller {
    function __construct() {
        parent::__construct();
        if (!empty($_SESSION['login']['userid'])) {
            $this->view->UserProfile = User::profile($_SESSION['login']['userid']);
        }
    }
}

View (core/View.php)

Properties:

  • Any property set on $this->view becomes a variable in the rendered template.

Methods:

  • render($viewPath, $wrapper = '', $useWelcome = true) — render a view
  • PartialView($partialName) — load and return a partial as a string

Render signatures used in practice:

$this->view->render(__CLASS__ . '/' . __FUNCTION__);          // default admin wrapper
$this->view->render(__CLASS__ . '/' . __FUNCTION__, 'login'); // login wrapper
$this->view->render('clients/index', true, 'site');           // legacy style

Routing

Auto-Routing (default)

URL Maps to
/clients Clients::index()
/clients/show/123 Clients::show(123)
/products/edit/456 Products::edit(456)

AltoRouter (complex routes)

Define in public/routes.php or api/routes.php:

// Named route with typed parameter
$router->map('GET', '/users/[i:id]', 'Users#show', 'user_show');

// Multiple parameters
$router->map('GET', '/blog/[i:year]/[i:month]/[*:slug]', 'Blog#show', 'blog_post');

// POST
$router->map('POST', '/api/users/create', 'UsersAPI#create', 'api_user_create');

Match type tokens:

  • [i:id] — integer
  • [a:action] — alphanumeric (A-Z, a-z, 0-9, -)
  • [h:key] — hex
  • [*:trailing] — catch-all (no slashes)
  • [**:path] — catch-all including slashes
// Generate URL from named route
echo $router->generate('user_show', ['id' => 5]); // → /users/5