Architecture

Database schema

Extension data models are declared in JSON files. The core schema updater reads these definitions and automatically creates database tables.

File location and naming

Schema files live in src/config/objects/ inside each extension. Use singular names:

  • article.json — not articles.json
  • campaign.json — not campaigns.json
  • recipient.json — not recipients.json

Schema format (infoKeys / metaKeys)

Every schema uses two key arrays: infoKeys for database columns and metaKeys for flexible schemaless data stored in a separate meta table.

{
    "infoKeys": [
        {
            "slug": "id",
            "type": "int(11)",
            "permissions": {
                "read": true,
                "update": ["PAPI_is_super_admin"]
            }
        },
        {
            "slug": "organization_ID",
            "type": "int(11)",
            "index": true,
            "permissions": {
                "read": true,
                "update": ["PAPI_is_super_admin"]
            }
        },
        {
            "slug": "label",
            "type": "varchar(250)",
            "permissions": {
                "read": true,
                "update": ["PAPI_is_super_admin"]
            }
        },
        {
            "slug": "slug",
            "type": "varchar(150)",
            "index": true,
            "permissions": {
                "read": true,
                "update": ["PAPI_is_super_admin"]
            }
        },
        {
            "slug": "status",
            "type": "varchar(50)",
            "permissions": {
                "read": true,
                "update": ["PAPI_is_super_admin"]
            }
        }
    ],
    "metaKeys": [],
    "createForm": {
        "fields": [
            {
                "type": "text",
                "label": "Name",
                "slug": "label",
                "placeholder": "Enter name...",
                "value": "",
                "required": true
            }
        ]
    },
    "updateForm": false,
    "permissions": {
        "create": ["PAPI_is_super_admin"],
        "read": true,
        "update": ["PAPI_is_super_admin"],
        "delete": ["PAPI_is_super_admin"]
    }
}

SQL field types

SQL Type Use For
int(11) IDs, foreign keys, counts, integer values
varchar(N) Short strings (names, slugs, statuses). N = max length
text Medium text, JSON data, descriptions
longtext Large content (HTML, rich text, large JSON)
date Date only (YYYY-MM-DD)
datetime Date and time
time Time only (HH:MM:SS)
json Structured JSON data (typically in metaKeys)
decimal(10,2) Currency, precise numbers
tinyint(1) Boolean flags

Common field patterns

Every object should have:

  • idint(11), always first.
  • statusvarchar(50), lifecycle tracking (active, draft, archived, deleted).

If organization-scoped:

  • organization_IDint(11), indexed.

If user-owned:

  • user_IDint(11), indexed.

Other common fields:

  • label / namevarchar(250), human-readable name.
  • slugvarchar(150), indexed, URL-friendly identifier.
  • descriptiontext, longer description.
  • configtext, JSON configuration (auto-decoded by PAPI helpers).
  • sort_orderint(11), for manual ordering.
  • created_at / updated_atdatetime.

Foreign keys to other objects use the <parent_object>_ID pattern with int(11) and "index": true.

Permissions

Permissions control access at the field and object level:

  • true — public access, no restrictions.
  • ["PAPI_is_super_admin"] — restricted to super admins.
  • An array of permission function names for custom access control.

Object-level permissions use the same format in the top-level permissions key with create, read, update, and delete operations.

MetaKeys

MetaKeys store flexible, schemaless data in a separate _meta table. Use them for data that does not need direct SQL queries.

"metaKeys": [
    {
        "slug": "settings",
        "type": "json",
        "permissions": {
            "read": true,
            "update": ["PAPI_is_super_admin"]
        }
    }
]

Use "slug": "*" to allow arbitrary meta keys:

"metaKeys": [
    {
        "slug": "*",
        "type": "text",
        "permissions": {
            "read": true,
            "update": ["PAPI_is_super_admin"]
        }
    }
]

Table naming

Tables are auto-created as {DB_PREFIX}_{extension_slug}_{object_slug}. Meta tables use {DB_PREFIX}_{extension_slug}_{object_slug}_meta.

Access data via PAPI_get_object('{ext}:{object}', ...) and PAPI_get_objects('{ext}:{object}', ...). See PHP conventions for full query syntax.