Blog
The filament-blog plugin is the CMS half of a blog: a blog_posts table, a Filament PostResource, markdown body editing, an optional thumbnail, and a Draft / Published workflow. It is a first-party Qcentic package (Plugins), one of the Docs stack plugins enabled per clone.
What the plugin owns
- Table —
blog_posts(id,user_id→users,title,slug,bodylongText,published_atnullable, timestamps). The migration ships with the package. - Resource —
PostResourcein the panel: title, unique slug,MediaLibraryFileUploadthumbnail,MediaMarkdownEditorbody, aPublishStatusfield, andSeoFieldsfrom the SEO plugin. - Model —
PostusesInteractsWithMedia(Spatie),HasSeo, andCanBePreviewed.thumbnailis a single-file collection on thes3disk;bodyis a media collection for inline images. - Status —
published_atis the single source of truth.Post::isPublished()and thepublishedscope return rows wherepublished_at <= now(); everything else is a draft.
What the plugin does NOT ship
Public HTML. The package deliberately stops at the CMS and table. The clone renders /blog/{slug} with its own Blade layout so crawlers see real HTML and the product keeps its own look (Build a product). Post::publicPath() defaults to /blog/{slug}; override the method in the clone if the product uses another prefix.
Draft preview
The Preview action on the edit and list pages opens publicPath() in a new tab. Logged-in editors and super_admin (or the row's author) see the draft rendered by the clone; guests still get a 404. Preview is not a Filament page — it is the real public URL with the visibility gate described in Draft preview.
SEO and the Content API
Posts use the SEO plugin (HasSeo): per-post title, description, and JSON-LD, with the thumbnail as the social image (Post::seoThumbnailUrl()). Posts can also be written programmatically through the Content API at /api/v1/posts when the API key carries posts:* abilities.
Writing on edge.qcentic.com
No posts are published yet on this site. Writers are welcome — contact info@qcentic.com (shown as an image on the /blog page, not plain text, to deter scraping).
flowchart LR
A["PostResource (panel)"] --> B["blog_posts table"]
B --> C{"published_at <= now()?"}
C -- yes --> D["clone /blog/{slug} view"]
C -- no --> E["draft: editors only"]
E --> D