Skip to content

Standards and Conventions

Brent Cordis edited this page Feb 21, 2026 · 5 revisions

Standards and Conventions

This document outlines the coding standards, conventions, and established patterns used in the Proclaim project.

Code Style

PHP Standards

  • PSR-12 coding standard with Joomla conventions
  • PHP 8.3+ features encouraged (typed properties, union types, match expressions, named arguments)
  • Configuration: PHP CS Fixer (.php-cs-fixer.dist.php)
  • Run composer lint:fix before committing

Key PSR-12 rules:

  • Type casts with space: (int) $var, (string) $value
  • 4-space indentation (no tabs)
  • Line length should not exceed 120 characters
  • Opening brace on same line for control structures, new line for classes/methods

JavaScript Standards

  • ES6+ syntax throughout (const/let, arrow functions, template literals, destructuring)
  • ESLint 10 with flat config (build/eslint.config.mjs) — js.configs.recommended base
  • Source files in build/media_source/js/ with .es6.js extension
  • Run npm run lint:js before committing
  • Jest for unit tests (tests/js/*.test.js)

Class Naming

  • Use the Cwm prefix for component classes (e.g., CwmparamsModel, CwmteacherTable)
  • Entity names follow Joomla singular/plural convention:
    • Singular controller (FormController): CwmserieController, CwmteacherController
    • Plural controller (AdminController): CwmseriesController, CwmteachersController
    • Table: CwmserieTable, CwmteacherTable
    • Model (edit): CwmserieModel / Model (list): CwmseriesModel

Namespacing

  • Administrator: CWM\Component\Proclaim\Administrator
  • Site (Frontend): CWM\Component\Proclaim\Site

Build Pipeline

Source files live in build/media_source/, the media/ output directory is gitignored, and npm run build generates it.

npm run build          # Build all: JS + CSS + assets (images, vendor libs)
npm run build:js       # Rollup + Terser JS compilation only
npm run build:css      # CSS minification only
npm run build:assets   # Copy images and vendor libraries

After git clone: npm install && npm run build generates the full media/ tree before running composer symlink.

Joomla API Conventions

Deprecated APIs (Do NOT Use)

Deprecated Replacement
jimport() Composer autoloader / use statements
getErrorMsg(), getError(), setError() Throw exceptions
new Input() for request data $this->input in controllers, Factory::getApplication()->getInput() elsewhere
getSession()->get('user') $app->getIdentity() or Factory::getApplication()->getIdentity()
$this->_db / getDbo() $this->getDatabase()
addScript(), addStyleSheet(), addScriptDeclaration() Web Asset Manager (WAM)
HTMLHelper::_('behavior.framework'), formbehavior.chosen, etc. Native Bootstrap 5 / Joomla core assets
Table::getInstance() MVCFactory $this->getTable()
BaseDatabaseModel::getInstance() MVCFactory injection

Joomla 5 vs 6 Compatibility

  • BaseController in Joomla 5 does NOT have getInput() or getApplication() methods. Use $this->input and $this->app directly.
  • Typed event classes (ContentPrepareEvent, etc.) with ->getResult() are NOT available in Joomla 5. Use $app->triggerEvent('onEventName', [...]).
  • new Input([...]) with array data is OK (typed data bag for method signatures).
  • Code must run natively on Joomla 6 WITHOUT the backward compatibility plugin.

Web Asset Manager (WAM)

All JS/CSS loading MUST use the Web Asset Manager via joomla.asset.json.

Asset URI rules:

  • Do NOT include css/ or js/ subdirectory in URIs. Joomla auto-resolves com_proclaim/foo.css -> media/com_proclaim/css/foo.css
  • Vendor assets (e.g., media/fancybox/) can't use auto-resolution -- use full literal path with registerAndUseScript()

Inline patterns:

  • addInlineStyle() for dynamic CSS vars
  • addInlineScript() with PHP heredoc for scripts needing PHP data

Language Strings in JavaScript

Use CwmlangHelper::registerAllForJs() in any admin template that needs JS language strings. This parses the component .ini file and calls Text::script() for every key automatically.

use CWM\Component\Proclaim\Administrator\Helper\CwmlangHelper;
CwmlangHelper::registerAllForJs();

JS access: Joomla.Text._('JBS_ADM_KEY')

Quirk: Joomla.Text._() returns the raw key string (truthy) when unregistered -- || 'fallback' never fires. Compare result against the key to detect missing registrations.

MVC Pattern

Directory Structure

Directory Purpose
Controller/ Request handlers
Model/ Business logic and data access
View/ View classes (HtmlView.php)
Helper/ Utility classes
Table/ Database table classes (admin only)
Field/ Custom form fields (admin only)
tmpl/ PHP template files

Controller Types

  • AdminController (plural, e.g., CwmseriesController): List management -- does NOT have a batch() method
  • FormController (singular, e.g., CwmserieController): Edit/save -- HAS batch() method

Batch data-submit-task attributes must use the singular controller name with cwm prefix: cwmserie.batch, NOT serie.batch or cwmseries.batch.

Batch Processing

The generic cwmadmin-batch-footer.es6.js handles all entity types. It reads the data-submit-task attribute from the button and validates it ends with .batch.

To add batch support for a new entity:

  1. Add $batch_commands property to the Model (maps form field names to methods)
  2. Add batch method (e.g., batchLocation()) to the Model
  3. Add batch body template with form fields (e.g., Cwmhtml::location())
  4. Use useScript('com_proclaim.cwmadmin-batch-footer') in the footer template
  5. Set data-submit-task="cwm{entity}.batch" on the submit button

Table Classes

  • check() method: Use \UnexpectedValueException with Text::_() keys for validation errors
  • Normalize sentinel values (e.g., -1 from "Select" dropdowns) to NULL in check()
  • Always declare class properties (dynamic properties deprecated in PHP 8.2+)

Template Files

  • Template files in tmpl/ and layouts/ are excluded from linting
  • Keep business logic out of templates
  • Use $this->escape() for all output
  • Dark mode: Use Bootstrap 5.3 color-adaptive classes. NEVER use bg-light (stays white in dark mode). Use bg-body-secondary or bg-body-tertiary instead.

Database Conventions

Query Building

  • Always use $db->quoteName() for identifiers
  • Use $query->whereIn(), never raw implode(',', ...) IN()
  • Use prepared statements via Joomla's query builder

Access Level Filtering (Multi-Campus Pattern)

All 11 admin list models MUST have BOTH:

  1. Filter dropdown -- UX feature letting users narrow by specific access level
  2. View-level access check -- security enforcing non-super-admin users only see records in their authorized view levels
$user = $this->getCurrentUser();

// Filter by access level (dropdown)
if ($access = $this->getState('filter.access')) {
    $query->where($db->qn('alias.access') . ' = ' . (int) $access);
}

// Restrict non-admin users to their authorised view levels
if (!$user->authorise('core.admin')) {
    $query->whereIn($db->qn('alias.access'), $user->getAuthorisedViewLevels());
}

Rules:

  • Use $this->getCurrentUser() (from CurrentUserTrait) in list models
  • Super admins (core.admin) bypass view-level filtering
  • Do NOT remove the view-level check -- it's essential for multi-tenant deployments

Location Filtering

Use CwmlocationHelper::getUserLocations() for consistent campus resolution across models, views, and batch operations. The helper resolves accessible location IDs via Joomla view levels.

Security

  • Validate and sanitize all user input
  • Use Session::checkToken() (CSRF) on all form submissions
  • Use $user->authorise() checks in controllers before operations
  • Never trust client-side validation alone
  • URL form fields use type="url" + validate="url"
  • Numeric fields use filter="int"

Testing

PHP Tests (PHPUnit)

  • Tests in tests/unit/ mirroring source structure
  • Base class: CWM\Component\Proclaim\Tests\ProclaimTestCase
  • Stubs: tests/unit/Stubs/JoomlaCmsStubs.php (40+ Joomla CMS class/interface/trait stubs)
  • Run: composer test (all), composer test:unit, composer test:integration

JavaScript Tests (Jest)

  • Tests in tests/js/*.test.js
  • Run: npm test, npm run test:watch, npm run test:coverage

E2E Tests (Playwright)

  • Config: playwright.config.js at project root
  • 4 projects: admin-j5, site-j5, admin-j6, site-j6
  • Run: npx playwright test

Validation Tools

Run these before submitting code:

# PHP syntax check
composer lint:syntax

# PHP CS Fixer - check style (dry-run)
composer lint

# PHP CS Fixer - auto-fix style
composer lint:fix

# Run lint + PHP tests
composer check

# Run all checks + all tests (PHP + JS)
composer check:all

# JS linting
npm run lint:js

# JS tests
npm test

Using the @ Error Suppression Operator

The @ operator suppresses error messages from function calls. While generally discouraged, there are cases where it's necessary when the error originates outside the component's logic.

If using @, you MUST:

  • Thoroughly explain the need in a comment
  • Document why the error cannot be handled through normal means

Documentation

Where to Put Documentation

All project documentation should be maintained in the Proclaim.wiki repository, not in the main Proclaim repository.

Location Content
Proclaim.wiki All detailed documentation, guides, release notes, tasks, plans
Main repo README.md Brief overview and quick-start only, link to wiki for details
Main repo CLAUDE.md AI assistant guidance for development

Documentation Guidelines

  • Release Notes: Create a new Whats-New-X.X.md file for each minor version
  • Tasks: Update Tasks when features are completed
  • Keep README minimal: The main repository README should contain only essential quick-start information
  • Link don't duplicate: Reference wiki pages rather than duplicating content

Wiki Repository

The wiki is a separate Git repository:

Version Tracking

Check build/versions.json for correct version numbers:

  • next.minor -- Use for @since tags and migrations
  • next.major -- Use for major breaking changes
  • current.version -- Last stable release

Clone this wiki locally