jcc/blog
jcc/blog is a ready-to-use blog package for Laravel that helps you add and manage blog content quickly. It integrates cleanly into existing Laravel applications, with configurable features for publishing posts and running a complete blog.
PJ Blog is an application, not a library. There is no composer require jcc/blog — the repository ships a complete Laravel + Vue.js blog (frontend, admin SPA, API, database schema). "Day-to-day leverage" therefore means forking, configuring and extending it, not importing it.
Minimal local bootstrap:
git clone https://github.com/jcc/blog.git pjblog && cd pjblog
composer install
cp .env.example .env
php artisan key:generate
# set DB_*, APP_URL, MAIL_* in .env
php artisan migrate --seed
php artisan storage:link
npm install && npm run dev # or npm run prod
php artisan serve
Runtime expectations: PHP 7.x with the usual Laravel extensions (mbstring, openssl, pdo_mysql, tokenizer, xml, gd/imagick for uploads), MySQL/MariaDB, and a Node version contemporary with the 2019 release (see Gotchas — Node 10/12 is the safe bet).
Where to look first, in this order:
routes/web.php and routes/api.php — the split tells you immediately which surface is Blade-rendered and which is the JSON contract the Vue admin consumes.app/ — the domain models (Post, Category, Tag, Comment, User, plus a settings/config model). Read the relationships before touching anything.app/Http/Controllers — separate namespaces for the public site, the admin/API endpoints, and auth.resources/assets/js — the Vue admin (Element UI components, router, store). This is where 80% of "how do I add a field" work happens.resources/views — the public theme (layouts, partials, post/category templates).database/migrations and database/seeds — the schema and the seeded admin account (check the seeders for the actual default credentials; change them before anything is exposed).First use case to run end-to-end: publish a post through the admin — create a category, add tags, upload a featured image via the media picker, write in Markdown, publish, then view the rendered page. Watch the Network tab while you do it. That single flow exercises migrations, storage, the API controller, validation, the Vue form component, Markdown rendering, and the Blade template — and it shows you the exact seam to copy when you add your own content type or field.
Extend content by vertical slice, not by editing originals. Adding a field to posts means five coordinated edits: a new migration (add_x_to_posts_table), the model's $fillable/casts, the API controller's validation rules and response resource, the Vue form component in the admin, and the Blade/theme output. Keep your migration as a separate file rather than editing the shipped one — this keeps deploys repeatable and makes upstream diffs reviewable.
Treat the Vue admin as a module registry. Each admin resource follows the same shape: a router entry, a list view, a form view, and an API endpoint pair. Copy an existing simple resource (tags or categories) as a scaffold rather than inventing structure. Reuse Element UI components (el-table, el-form, el-upload) so styling and validation behaviour stay consistent. Never edit compiled bundles in public/js or public/css — change resources/assets/js and rebuild.
Theme through Blade, not through the SPA. The public site is server-rendered, so site-specific work (custom layouts, structured data, ad slots, related-post blocks) belongs in resources/views. Create your own layout that @extends or copies the existing one, and register any new partials there. If you need dynamic islands, mount small Vue components on the existing markup rather than converting the whole page.
Use the API for anything external. Once you've confirmed the guard in config/auth.php and the middleware on routes/api.php, adding endpoints alongside the existing ones gives you a mobile app, static-site generator feed, or third-party integration for free. Return the same resource shape the admin consumes so you only maintain one serialization path.
Cross-cutting concerns belong in observers, events, and middleware. Slug generation, cache invalidation on publish, sitemap and RSS regeneration, search indexing, and notification dispatch should hang off model events (saved, deleted) rather than being sprinkled into controllers. This is the single highest-leverage refactor in the codebase because it survives both admin edits and API writes.
Settings are key/value rows. New configuration is typically a seeded settings row plus a field in the admin settings form plus a read through the settings helper/model. Don't add .env entries for things editors should control; do add .env entries for infrastructure (disk, mail, queue).
Queues and storage for media. Route image resizing/thumbnailing and any external calls through queued jobs so uploads stay responsive, and confirm the filesystem disk in config/filesystems.php matches where your web server serves from (storage/app/public via the symlink).
The release date is the headline risk. Last release was November 2019, which pins the stack to Laravel 5.x, PHP 7.x, Vue 2, and a Node toolchain of that era. On PHP 8 you will hit fatal errors and deprecations deep in dependencies; on Node 14+ node-sass will fail to build. Pin PHP 7.4 and Node 10/12 via nvm for a faithful run, or budget an explicit framework upgrade as a separate project — do not treat it as a "just bump composer" task.
npm install failing is almost always the Node version, not a corrupt cache. If you can't downgrade Node, swap node-sass for sass (dart-sass) in package.json and adjust the build config.
php artisan storage:link is mandatory for the media library. If uploads succeed but images 404, check the symlink, the public disk config, and directory permissions on storage/ — in that order.
Change the seeded admin credentials and rotate APP_KEY before any deployment. Demo seed data, default passwords, and APP_DEBUG=true are the three things that turn a blog into an incident.
Caches mask your changes. After editing routes, config, or views, run php artisan optimize:clear. A "my new route 404s" report in this codebase is usually a cached route table.
Debugging map: Laravel-side errors → storage/logs/laravel.log with APP_DEBUG=true; admin SPA issues → Vue Devtools plus the Network tab (a 422 means validation in the API controller, a 401/403 means the API guard, not the session guard); rendering issues → confirm whether the page is Blade or Vue before you start editing.
Slugs and uniqueness will bite you. Public post URLs collide if slug generation is naive. Enforce a unique index and generate slugs in a model observer (with a -2, -3 suffix fallback) so both the admin and any API import path behave identically.
Extension points, ranked by safety: service providers and observers (safe) → new migrations and new Vue modules (safe) → overriding Blade views (safe) → editing existing app models/controllers (proceed with care) → editing vendor/ or compiled assets (never). Keep a running list of the files you touch so an upstream sync or a content migration stays tractable.
Assume you own the code forever. With zero dependents and no active maintenance, there is no ecosystem to lean on and no security patching upstream. Fork it deliberately, keep your customizations on a branch with clear commits, and remember the content itself lives in plain MySQL tables — if you ever outgrow the platform, a posts/categories/tags export moves cleanly into a modern Laravel app.
How can I help you explore Laravel packages today?