Architecture

Blocks and requests

Paradigm CMS separates UI composition (blocks) from operations (requests) to keep extensions modular and maintainable.

Block philosophy

Blocks are PHP view modules that render UI and can be composed into layouts. A block slug uses the pattern extension:block and can include -> to reference subdirectories.

The runtime resolves block slugs into files under extensions/<slug>/<version>/src/blocks/, loading either a direct PHP file or an index.php inside a folder.

Blocks can be requested dynamically via the block API. The block endpoint expects a block slug and optional props, then uses ParadigmPages->printBlock to render HTML.

Request philosophy

Requests are extension-scoped operations. A request action uses the pattern extension:request and maps directly to a file under extensions/<slug>/<version>/src/API/requests/.

The request router validates the extension name, selects the configured version (or the latest semantic version), and then securely resolves the request file path before executing it.

Action string to file path mapping

Action String File Path
extension:send_broadcast extension/{ver}/src/API/requests/send_broadcast.php
extension:broadcast->schedule extension/{ver}/src/API/requests/broadcast/schedule.php
extension:articles->create extension/{ver}/src/API/requests/articles/create.php
extension:admin->resources->delete extension/{ver}/src/API/requests/admin/resources/delete.php

API requests vs REST endpoints

Feature API Requests (API/requests/) REST Endpoints (API/REST/)
Invoked via ParadigmAPI.REQUEST() Native fetch()
File pattern $request = function($props) {...} Direct PHP script, echoes JSON
Input $props parameter (object) file_get_contents('php://input') or $_GET
Output return array(...) echo json_encode(array(...))
HTTP codes Handled by framework Set manually with http_response_code()
Route params N/A $routeParams global array

REST endpoint structure

REST endpoints live in src/API/REST/ and use HTTP method names as filenames:

extensions/<slug>/<version>/src/API/REST/
├── <resource>/
│   ├── get.php
│   ├── post.php
│   ├── put.php
│   ├── delete.php
│   └── <action>/
│       ├── get.php
│       └── post.php

URL to file path mapping

Method URL File Path
POST /API/core/extension/enable core/{ver}/src/API/REST/extension/enable/post.php
GET /API/core/setting core/{ver}/src/API/REST/setting/get.php
POST /API/event_registration/event event_registration/{ver}/src/API/REST/event/post.php

Route parameters

// URL: /API/extension/resource/action/param1/param2
// $routeParams[0] = 'param1'
// $routeParams[1] = 'param2'

global $routeParams;
$resourceId = isset($routeParams[0]) ? intval($routeParams[0]) : null;

Request body (REST)

$input = file_get_contents('php://input');
$data = json_decode($input, true);

REST response pattern

<?php

if (PAPI_is_user_logged_in() !== true) :
    http_response_code(401);
    echo json_encode(array(
        'success' => false,
        'errors' => array(
            array('message' => 'You must be logged in to access this resource.')
        )
    ));
    return;
endif;

// ... perform action ...

echo json_encode(array('success' => true, 'items' => $items));

Maintainability and interoperability

  • Blocks isolate view logic, so UI changes stay within each extension package.
  • Requests isolate operations, so data workflows stay scoped to the owning extension.
  • Versioned folders provide safe upgrades and quick rollbacks for new packages.
  • Consistent slug-to-file mapping makes it easy for AI tooling to locate and update code.

Quick reference

  • Block slug: core:documentation->indexextensions/core/<version>/src/blocks/documentation/index.php
  • Request action: core:update_extension_db_schemaextensions/core/<version>/src/API/requests/update_extension_db_schema.php
  • REST endpoint: POST /API/core/extension/enableextensions/core/<version>/src/API/REST/extension/enable/post.php