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->index→extensions/core/<version>/src/blocks/documentation/index.php - Request action:
core:update_extension_db_schema→extensions/core/<version>/src/API/requests/update_extension_db_schema.php - REST endpoint:
POST /API/core/extension/enable→extensions/core/<version>/src/API/REST/extension/enable/post.php