zero-gravity/zero-gravity-cms
Zero Gravity CMS is a modular content management package for Laravel, giving you flexible content types, a clean admin experience, and a developer-friendly API so you can build and manage sites quickly without fighting the framework.
This package has no stars, no dependents, and no visible release history, so treat your first hour as reconnaissance rather than integration. Budget time to read the source before writing a line of application code.
1. Get it installed (GitLab-hosted, so expect friction)
It is not on Packagist. Add it as a VCS repository first:
"repositories": [
{ "type": "vcs", "url": "https://gitlab.com/zero-gravity/zero-gravity-cms.git" }
]
composer require zero-gravity/zero-gravity-cms:@dev
If the repo is private you will need a GitLab token in COMPOSER_AUTH (or SSH + gitlab-token config) locally and in CI. A bare 404 from Composer almost always means auth, not a missing package.
2. Read these files first, in this order
composer.json — PHP/Laravel version constraints, type, PSR-4 autoload roots, and whether extra.laravel.providers declares auto-discovery.src/*ServiceProvider.php) — this is the map of the whole package: bindings, singletons, published config/migrations, routes, middleware, facades, event listeners.config/ — every knob you will ever need to turn.migrations/ and routes/ — the actual schema and surface area.tests/ if present — often the only accurate documentation for an unstarred package.3. Boot it in a throwaway app
php artisan vendor:publish --provider="ZeroGravity\\Cms\\CmsServiceProvider"
php artisan migrate
php artisan route:list
php artisan about
If auto-discovery isn't declared, register the provider manually (bootstrap/providers.php on Laravel 11+, config/app.php on 10 and below) and re-run php artisan package:discover -v.
First real use case: don't build a feature — prove the wiring. Resolve the core service out of the container in Tinker and render one piece of content end to end:
app(\ZeroGravity\Cms\...::class); // whatever the provider binds
Once you can create/fetch one content record and see it rendered, you understand the package's central abstraction and can plan around it.
Treat it as a kernel, not an application. A "core library" CMS expects you to own the app layer: your models, your controllers, your admin UI. Resist the urge to build directly against its internals.
Wrap the core behind your own seam. Even a thin repository/action layer pays off immediately here, because the package has no community, no changelog, and no upgrade path guarantees:
final class ContentRepository
{
public function __construct(private PageContract $pages) {}
public function findPublished(string $slug): ?PageData { /* ... */ }
}
Inject contracts/interfaces from the provider's bindings rather than reaching for facades. It keeps your tests fast and makes a fork-and-replace realistic.
Extend in app/, never in vendor/. Subclass its Eloquent models in app/Models and point config at yours. Same for block/content types: define them in your own namespace and register them through whatever config array or registry the provider exposes.
Publish and own the migrations. Copy them into database/migrations with fresh timestamps so you control schema evolution. If you leave them in the package, a composer update can silently rewrite your production schema.
Keep shipped routes contained. If the package registers admin or preview routes, wrap them in auth + authorization middleware and a route prefix immediately:
Route::middleware(['auth', 'can:manage-content'])
->prefix('admin')
->group(base_path('vendor/.../routes/admin.php'));
Test at the boundary. One feature test that boots the provider, migrates an in-memory SQLite database, and asserts a page renders is worth more than unit-testing its internals:
protected function defineEnvironment($app) {
$app['config']->set('database.default', 'testing');
}
Pin hard. Use a tag if one exists, otherwise a commit SHA (dev-main#abc1234) or a fork under your own namespace. Commit composer.lock. With zero dependents there is no ecosystem pressure keeping the API stable.
package:discover -v.minimum-stability bites. Expect to need @dev or "minimum-stability": "dev" with "prefer-stable": true. If you have to loosen stability for the whole project, that's a signal to vendor the package instead.php artisan migrate --path= and renumber rather than editing in place.php artisan config:cache during debugging produces phantom behavior. Run config:clear before you conclude anything is broken.config/app.php for conflicts with your own facades — a silent alias overwrite is nasty to debug.php artisan vendor:publish --provider=..., php artisan event:list, php artisan route:list --path=, and tinker to inspect container bindings (app()->getBindings()). composer show -t zero-gravity/zero-gravity-cms shows what it drags in.Contracts/, Concerns/, and Events/ directories before subclassing. Extension points (events, interfaces, macros) are the intended customization path; inheritance against an unversioned core is how you end up maintaining a permanent fork.mixed leaking out of its return types will silently degrade your own static analysis.vendor/ when you update. With no changelog and no semver discipline, composer update can break you invisibly. Keep the diff in the PR.How can I help you explore Laravel packages today?