User Docs Developer Docs | |

Conventions

Naming

  • Table names: snake_case, plural (e.g. posts, widget_instances, author_profiles)
  • Model class names: Singular PascalCase (e.g. PostModel, WidgetInstanceModel)
  • Primary key: always idINT UNSIGNED AUTO_INCREMENT
  • Foreign keys: {related_table_singular}_id (e.g. post_id, user_id, widget_id)
  • Pivot tables: {table1}_to_{table2} in alphabetical order where possible (e.g. posts_to_categories, tags_to_posts)
  • Plugin tables: prefixed with a short plugin code + underscore (e.g. pvd_articles, ds_products, vt_events)

Timestamps

All core tables have:

created_at DATETIME NULL,
updated_at DATETIME NULL

CI4's Model timestamps feature manages these automatically when $useTimestamps = true.

Soft Deletes

Content tables (posts, pages, comments) use soft delete via:

deleted_at DATETIME NULL

CI4's SoftDeleteTrait (or the $useSoftDeletes = true Model flag) filters these rows from standard queries.

Status Enums

Content status (posts, pages)

ValueMeaning
draftNot visible publicly
publishedLive and visible
scheduledPublish at scheduled_at datetime

Comment status

ValueMeaning
approvedVisible on post page
pendingAwaiting moderation
spamFlagged as spam, hidden
trashSoft-deleted

Extension status (themes, plugins, widgets)

ValueMeaning
activeLoaded and in use
inactiveInstalled but disabled
unavailableDirectory missing from filesystem

Boolean Columns

Stored as TINYINT(1). Use 1 for true, 0 for false. CI4 Model casting should be configured for boolean columns:

protected $casts = [
    'is_premium'       => 'boolean',
    'share_on_publish' => 'boolean',
    'is_active'        => 'boolean',
];

Plugin Table Prefixes

Plugin developers must prefix their tables to avoid conflicts with core tables and other plugins. Use 2–4 character prefix derived from the plugin name:

PluginPrefixExample table
PvDocspvd_pvd_articles, pvd_versions
DStoreds_ds_products, ds_orders
VenueTixvt_vt_events, vt_tickets

CI4 Query Builder Conventions

Pubvana models extend CodeIgniterModel. Direct DB access in plugins should go through a Model class, not raw db() calls, to maintain soft delete consistency and timestamp management:

// Correct — goes through Model with soft delete filter
$posts = model('PostModel')->where('status', 'published')->findAll();

// Use with care — bypasses soft deletes
$db    = db_connect();
$posts = $db->table('posts')->where('status', 'published')->get()->getResultObject();