evolution-cms/evolution
Evolution CMS is an open source content management system and application framework built on PHP. It offers fast, lightweight site building with user groups, document management, snippets, and chunks for dynamic websites.
Evolution CMS isn't a Composer library you require into an existing Laravel app — it's a full CMS/application skeleton that happens to be built on Laravel 13 components. "Installing the package" means either running the Evolution CMS Installer or unpacking a release/nightly into a docroot.
Minimal path to a running instance:
nightly-3.5.x build — it's a pre-installed SQLite docroot. Unpack, open in a browser, log in at /manager with admin / Passw0rd123. Treat this as a sandbox only; the credentials are published.agents/AGENTS.md — the two disagree, so check the release notes for the version you actually pin).agents/AGENTS.md in the repo is the only concise map of the repo layout and is written for exactly this purpose — read it before the docs site. Then docs.evo.im, the evolution-cms/docs repo, and the manager itself (/manager) which is the primary UI for everything you'd otherwise do in code.core/ (application), assets/ (your code, templates, plugins, assets/vendor), manager/ (admin UI).Your first use case should be the canonical one: create a Template, drop in Chunks for the surrounding markup, define a couple of TVs (template variables) for per-page fields, and render them from the template. Then write one Snippet that returns a string, and call it from the template with [[!mySnippet? &limit=3]]. That single loop — Template → Chunk → TV → Snippet → Plugin — is the whole platform in miniature, and everything else is a variation on it.
Model your content as the resource tree, not as Eloquent models. Documents are hierarchical resources with a template, TVs, and permissions. Resist the urge to build a parallel posts table until you've confirmed the tree genuinely doesn't fit; you lose ACLs, aliases, caching, and the manager UI the moment you step outside it.
Snippets are the unit of business logic. They're PHP files returning output, invoked from templates, chunks, or other snippets. Keep them pure-ish: read params from the $params argument, hit the DB through the provided API or DocLister, return a string. Don't echo.
Use DocLister for listings. Hand-rolled SQL for "give me the children of resource X, filtered, paginated" is the single most common reinvention in this ecosystem. DocLister already handles the tree, TVs, filters, and pagination, and it's the de facto standard across Extras.
Plugins are your integration seam. Everything in the manager and front end fires events; plugins subscribe to them. For day-to-day work the useful ones are the request/render lifecycle events (e.g. OnWebPagePrerender for output rewriting, manager init events for admin-side hooks) and the save/publish events for side effects like cache busting, search indexing, or webhook dispatch. A plugin is the right answer for cross-cutting concerns; a snippet is the right answer for "this block of the page needs data."
Reach for modules for admin UI. If a client needs a custom management screen, a Module gets you a tab in the manager with your own controller/views, rather than bolting a page onto the front end with an ACL fudge.
Put your Composer dependencies in assets/, not the repo root. This is called out explicitly in the 1.4.13 notes and remains true: assets/vendor is autoloaded, whereas root composer.json changes are lost on update. Initialize Composer in assets/ and install there.
Treat upgrades as a workflow, not a command. The 3.5.7 release added a manager-driven update flow with core migrations, Composer steps, custom package preservation, branch-based updates, and a pre-update database backup. Use it, but still take your own filesystem + DB snapshot first — the notes say as much for projects with custom Composer packages. Verify your CLI, manager, and installer flows agree if you maintain more than one.
Use artisan deprecated:list (3.5.5+). It enumerates code marked for removal by semantic version, which is the only practical way to track the MODX_* → EVO_* transition across a custom codebase.
$modx is deprecated. Since 3.5.4 the global $modx object is on the way out; 3.5.5 "finalized the MODX_* to EVO_* compatibility transition and prepared legacy constants for removal." Most tutorials, forum posts, and Extras you'll find online still use $modx. Prefer the evo() accessor / modern API in new code and treat every $modx-> you copy from the internet as technical debt you just accepted.
Two incompatible lineages share one repo. The release list mixes 1.4.x (older, PHP 8.0-era) and 3.5.x (Laravel components, PHP 8.3+). Read the tag before you read the note. Pin the branch in your composer.json/deploy scripts, because advice for one is actively wrong for the other.
PHP version mismatch in the docs. README says >= 8.3, agents/AGENTS.md says >= 8.4.1. The 3.5.7 notes mention PHP 8.4 deprecation fixes. Pick 8.4 and you satisfy both.
Element caching will lie to you. Chunks, snippets, and plugins are cached, and the ability to disable that caching is a config toggle. When you edit a snippet and the page doesn't change, suspect the cache before your code. Keep the cache off in dev and be deliberate about invalidation in prod.
Alias listing can be disabled entirely (1.4.13+) — useful for large trees, but it silently changes what [*alias*]-style lookups and URL generation can return. Don't flip it on a live site without testing every template that references an alias.
Session config has bitten this project repeatedly. 3.5.4 fixed "inability to login if NO_SESSION is set" and reverted prefix placeholder support that broke login. If auth behaves strangely after an upgrade, check session-related system settings first, and read the changelog for the version you jumped from rather than the one you landed on.
Ecosystem is thin — budget for reading core. 257 stars and essentially no dependents on Packagist means fewer Stack Overflow answers, fewer battle-tested third-party packages, and a real chance you'll be the first person to hit a given problem. The Telegram group and forum are the live channels; the GitHub issues archive is often more informative than the docs.
Docs are split across three places — docs.evo.im, the evolution-cms/docs repo, and agents/AGENTS.md. They drift. When they conflict with the code, the code (and the release notes) win.
GPL-3.0 matters commercially. If you're shipping a client site, fine. If you're distributing a derived product or a SaaS where you redistribute modifications, read the license before you architect around it — this is a materially different constraint than an MIT-licensed package.
Don't ship the nightly. It exists to let you evaluate fast, not to deploy. Published admin credentials, SQLite, automated builds.
How can I help you explore Laravel packages today?