Search by

Reusable public CMS route runtime for Lemonade.

Package info

github.com/johnnyxlemonade/cms-core

pkg:composer/johnnyxlemonade/cms-core

Statistics

Installs: 807

Dependents: 6

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-11-09 13:04 UTC

This package is auto-updated.

Last update: 2026-11-09 13:07:33 UTC


README

PHPStan Tests Coding Standards License

johnnyxlemonade/cms-core provides reusable public CMS routing for Lemonade Framework applications. It owns the canonical cms_route URL index, route reservation and resolution, locale-aware public routing, and contracts for module-provided public detail and collection handlers.

The package is runtime-only. It does not own Admin UI, permissions, DataGrid, AdminEditor, public HTML, content publication policy, or file storage. The host supplies locale and module-state runtime ports. A host or content module registers the handler that authoritatively renders a public CMS response.

Installation

The package requires PHP >=8.3 <8.6 and johnnyxlemonade/framework.

composer require johnnyxlemonade/cms-core:dev-main

Register Lemonade\Cms\CmsCoreServiceProvider in the host application and bind implementations of PublicLocaleRegistryInterface and PublicModuleStateResolverInterface.

Public CMS routing

CMS Core resolves a public request through the current PublicLocaleResolution, the canonical cms_route record, module lifecycle state, and the host-provided public handler. A route record establishes a canonical address; it is not proof that the target content remains public. The module handler remains authoritative for publication state, soft deletion, visibility windows, and locale-specific content availability.

Detail handlers implement PublicCmsRouteHandlerInterface. Collection pages are ordinary host application routes and remain outside CMS Core.

Package boundaries

  • johnnyxlemonade/framework owns the base application runtime and generic infrastructure.
  • CMS Core owns public CMS route resolution and handler contracts.
  • The host owns public presentation, module publication rules, locale ports and concrete handlers.
  • Admin packages must not become a dependency of CMS Core.

Development and QA

From the package root, run:

composer cs:check
composer stan
composer test
composer qa

Non-hierarchical content reads

CmsContentReadModel(Database, CmsContentDefinition) supplies exists(), ownerId(), metadata(), translation(), publication(), findForEditor(), tagNamesForLocale(), editorLocales(), dataGridLocales() and enabledLocales(). CmsDomainMapping::columns() reads the same CmsRoot and CmsLocalized attributes used by CmsContentLifecycle; modules do not maintain another list of domain database columns or implement a read adapter. Explicitly attributed private properties are supported as well; unannotated private state is excluded. Public domain properties retain the requirement for exactly one mapping attribute.

The editor projection includes all stored translations, ordered localized tags, canonical paths, domain values, ownership and audit names. Active reads exclude soft-deleted roots; owner lookup includes them. Translation and publication lookups retain the stored mutations of deleted roots. Editor languages include enabled languages plus disabled languages with a stored mutation; the grid language list considers mutations across the whole module, including deleted records. Tag suggestions retain their existing locale-wide alphabetical order.

CmsContentQueries contains the shared QueryBuilder projections and canonical and audit joins. It has no Admin dependency. Pagination, search, filters and DataGrid transport are provided by admin-platform.

Shared-table content definitions

CmsContentDefinition::rootScope optionally constrains a shared root table to an explicit partition (for example a content type ID). CMS root reads, locks, writes, translation reads and locale projections preserve that scope; domain mapping cannot overwrite it. Existing definitions have an empty scope and retain their behavior.

An optional CmsContentValueWriter participates in CmsContentLifecycle's existing transaction. It checks creation before root insertion, initializes draft defaults, validates/prepares values after the effective publication state is resolved, and writes them once the translation exists. Preparation runs even on otherwise unchanged published content; the change flag participates in CMS no-op detection. System data, related values, canonical reservations and audit remain atomic. The writer must not start a parallel lifecycle or dispatch an independent audit.