orchid/press
orchid/press adds a lightweight blog and publishing engine to Laravel Orchid admin panels. It provides posts, categories, and simple content management tools, making it easy to add a CMS-style section to your Laravel application with minimal setup and no custom admin code. Designed for Orchid Platfo...
Before you install, lock the Orchid version. Press is a thin CRUD layer over Orchid's screens, so a major mismatch between orchid/platform and orchid/press is the single most common install failure.
composer show orchid/platform | head -20
composer require orchid/press
Discover what the package actually ships — it has no docs site worth trusting, so enumerate its published assets:
php artisan vendor:publish --list | grep -i press
# or target the provider directly
php artisan vendor:publish --provider="Orchid\Press\PressServiceProvider"
Read these in order:
src/PressServiceProvider.php — what it registers, publishes, and whether it auto-loads routes or needs a manual Route:: entry.database/migrations/ — the posts (and likely categories/tags) schema you'll build against.src/Screen/ — the PostListScreen / PostEditScreen classes and their name() route keys.src/Model/ (or src/Models/) — the Post model, its traits (Attach, Filterable, etc.), and casts.config/press.php — if present, this is the extension surface.Wire it into the admin menu. Register the package screen in config/platform.php:
'menu' => [
// ...
[
'label' => 'News',
'icon' => 'bs.newspaper',
'route' => 'platform.press.posts.list', // confirm via `php artisan route:list | grep press`
],
],
Then php artisan migrate and you're in. If the route 404s, the menu entry is wrong — check route:list before touching anything else.
First use case — a News/Blog section in under an hour. Admin list + edit screen come from the package; you write one frontend controller:
Route::get('/news/{post:slug}', fn ($post) => view('news.show', compact('post')));
with the model scoped to published posts. That's the whole loop: editors write in Orchid, you render the model on the public site.
Extend the model in your app, not in vendor. Subclass the package Post, keep additions in your own migration, and rebind via the config if the package exposes a model key:
namespace App\Models;
class Post extends \Orchid\Press\Models\Post
{
protected $fillable = [...parent::fillable, 'seo_title', 'canonical'];
protected $casts = ['published_at' => 'datetime', 'meta' => 'array'];
}
Override screens rather than editing them. The edit screen is the natural extension point — subclass it and override fields() / layout():
class PostEditScreen extends \Orchid\Press\Screen\PostEditScreen
{
public function name(): ?string { return 'platform.news.edit'; }
public function fields(): array
{
return array_merge(parent::fields(), [
Input::make('seo_title'),
Attach::make('cover')->target('cover'),
TextArea::make('description')->rows(3),
]);
}
}
Register the override in the service provider's boot() (or the package's screen-registration hook) and point the menu route at your class.
Frontend scoping, not admin logic. Keep "what's public" in a query scope on the model so the admin screen never needs to know:
public function scopePublished($q)
{
return $q->where('status', 'published')
->where('published_at', '<=', now());
}
Attachments/cover images — use Orchid's Attach trait + Single/Attach fields, then render $post->attachment('cover')?->url(). Don't store raw file paths in a text field; you lose the media manager and thumbnails.
Scheduling and drafts — a status enum plus nullable published_at is usually what the package gives you. If it gives you only a boolean published, add published_at in your own migration and a scope; a schedule command isn't necessary, the scope is enough.
Dashboard visibility — drop a small Block or ListMetric on the platform dashboard showing drafts and "published in last 7 days" so editors don't dig through the menu.
Permissions — wrap the screens in Orchid's role permissions (platform.news.*). Press is editor-facing; don't leave it open to every admin user.
Reuse Orchid's UI kit (TD, Layout, Rows, Modal) inside any custom screen you add, so the press section looks native rather than bolted on.
Treat it as a small, unmaintained dependency. Zero stars and zero dependents means no community safety net: pin an exact version in composer.json, commit composer.lock, and be prepared to vendor the src/ into app/ if the package goes dark. The migrations and model are trivial to absorb; the screens are the only real work.
Route names drift between versions. Never hardcode them from a README — php artisan route:list | grep -i press is the source of truth after every upgrade.
Migration ordering. Platform migrations must run first. If press migrations carry an early timestamp or the provider merges paths oddly, you'll get foreign-key failures. Check vendor/orchid/press/database/migrations timestamps; if needed, copy them into your app's database/migrations with corrected names rather than fighting load order.
Config publish is destructive. vendor:publish --force will silently overwrite your tweaks. Publish once, then diff before ever re-publishing; or copy the config into config/press.php manually and delete the --force habit.
Slug collisions. Most such packages rely on a plain Str::slug with no uniqueness check. Add a unique index and either a suffix loop in a model observer or a booted() hook — otherwise the second "Hello World" throws a DB error at save time, in front of an editor.
View overrides go in resources/views/vendor/press/ after publishing views. Follow with php artisan view:clear — stale compiled views are the usual "my override does nothing" answer.
Route/config caching. After any menu or config change: php artisan optimize:clear. Orchid reads config/platform.php at boot, so a cached config hides new menu entries entirely.
Debugging checklist, in order: php artisan orchid:admin to get a working login → storage/logs/laravel.log for screen/field exceptions → php artisan tinker on the Post model to verify casts, relations, and the published scope actually return rows → route:list for the screen route.
Find the real extension points by grepping, not guessing: grep -rn "event(\|dispatch(" vendor/orchid/press/src reveals any lifecycle events (PostCreated, PostUpdated); grep -rn "Dashboard::configure\|Route::screen" src shows how screens register. Those two greps usually tell you whether you can hook in cleanly or need to subclass.
If the package is too stale to keep, the migration path is straightforward: copy the schema into your app, copy the Post model, and rewrite the edit screen against current Orchid field APIs. Budget half a day — the list/edit screens are the bulk of it, and you gain full control over versioning.
How can I help you explore Laravel packages today?