Custom Content Types
Pages, posts and archives cover a lot — but a portfolio, a job board or documentation deserve their own content type with its own fields, URLs and admin section. This article shows how to register one.
What a content type gives you
Registering a type adds a navigation entry under Contents in the admin, a filtered list, the full editing form (fields, SEO, taxonomies, builder) and frontend rendering via your theme (themes.{theme}::{type}.show). The docs plugin's doc type — which renders this very page — is the reference implementation.
Registering a type
In a service provider's register() method (your app's AppServiceProvider or a plugin provider):
use Laravix\Cms\Support\ContentTypeDefinition;
use Laravix\Cms\Support\ContentTypeRegistry;
ContentTypeRegistry::register(
ContentTypeDefinition::make('project')
->label('Project')
->pluralLabel('Projects')
->linkableInNavigation()
->taxonomyTypes(['category']),
);
The definition methods:
| Method | Effect |
|---|---|
make(string $key) |
Unique type key, stored in contents.type |
label() / pluralLabel() |
Singular/plural names in the admin (strings or translation keys) |
linkableInNavigation() |
Published items of this type are offered in the menu editor's page select |
builder(false) |
Hide the visual builder tab for this type |
routePrefix('projects') |
Public URLs become /projects/{slug} instead of /{slug} |
taxonomyTypes(['category']) |
Restrict which taxonomy types can be assigned (empty = all) |
Warning: A
routePrefixcomes with homework: the built-in catch-all only 301-redirects/{slug}to/projects/{slug}— serving the prefixed URL is up to routes you register throughRouteRegistry(exactly what the docs plugin does for/docs/...). If you don't want to write controllers, leave the prefix off and the built-in rendering serves the type at/{slug}.
Adding fields to the type
Register code-defined fields for the type — they appear in the content form automatically:
use Laravix\Cms\Enums\FieldType;
use Laravix\Cms\Support\FieldDefinition;
use Laravix\Cms\Support\FieldRegistry;
FieldRegistry::contentType('project', [
FieldDefinition::make('client')->label('Client'),
FieldDefinition::make('year')->type(FieldType::NUMBER)->label('Year'),
FieldDefinition::make('cover')->type(FieldType::IMAGE)->label('Cover image'),
]);
Field values live in the content_fields table and are available in templates via $content->fields. Full field-type reference: Custom Fields.
Rendering the type
Create themes/{yourtheme}/views/project/show.blade.php. It receives all the standard view data (see Templates and View Data); without it, the theme's default.blade.php renders the type generically.
Custom taxonomy types
If the type deserves its own vocabulary — like the docs plugin's doc-category — register a taxonomy type and reference it:
use Laravix\Cms\Support\TaxonomyTypeRegistry;
TaxonomyTypeRegistry::register('project-category', 'Project category');
Editors can then create taxonomies of this type in the admin and assign them to projects.
A complete real-world example
The docs plugin registers everything discussed here in ~30 lines — content type with routePrefix('docs') and taxonomyTypes(['doc-category']), a markdown body field, a taxonomy type and custom routes. Read packages/laravix/docs-plugin/src/DocsServiceProvider.php in the monorepo as a template.