Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Blogged Laravel Package

binarytorch/blogged

Blogged is a Laravel package for adding a blog to your application. It provides models, migrations, routes, and an admin interface for managing posts, categories, tags, and comments, so you can organize and publish content without building core blog functionality from scratch.

View on GitHub
Deep Wiki
Context7

Getting Started

Blogged is a batteries-included blog engine for Laravel: it ships migrations, Eloquent models (Article, Category, Tag), a Markdown editor UI, image upload handling, slug generation, publish/draft state, and a front-end blog controller with Blade views. The value is that you get an admin CRUD + authoring experience in about ten minutes instead of building it.

Compatibility, up front: v1.4.0 raised the ceiling to Laravel 7. That's the whole release — no new features, no API changes. So the package covers Laravel 5.5 through 7 (roughly PHP 7.1.3–7.4). If you're on Laravel 8+, nothing here helps you; see Gotchas.

Install and scaffold

composer require binarytorch/blogged
php artisan vendor:publish --provider="BinaryTorch\Blogged\BloggedServiceProvider"
php artisan migrate
php artisan storage:link   # only if you'll use the editor's image upload

The service provider is auto-discovered on Laravel 5.5+. Publishing drops a config/blogged.php, the package migrations, and (optionally) the views into resources/views/vendor/blogged.

If composer resolution stalls, the constraint is the thing to check first: composer why-not binarytorch/blogged ^1.4 will tell you whether it's the illuminate/* pins or the Markdown parser dependency holding you back.

Read these three things first, in order

  1. config/blogged.php — this is where middleware, route prefix/domain, per_page, the editor options, and the uploads disk live. Everything you'll want to change on day one is here.
  2. vendor/binarytorch/blogged/src/Models/Article.php — the fields, casts, scopes, and relations you'll be querying from your own code.
  3. vendor/binarytorch/blogged/src/Http/Controllers — the admin and front-end controllers, so you know which routes exist before you accidentally duplicate them.

First use case

Log in (the admin routes are behind web + auth by default — see middleware in the config, and confirm your config/auth.php user provider matches your User model), visit the configured admin prefix (typically /blogged), create a Category, then an Article: title, Markdown body, tags, category, save as draft, hit Preview, then Publish. The front-end route (typically /blog) should now list it. That round-trip validates migrations, auth middleware, the editor assets, and the front-end views in one pass.

If the editor loads but images 404, it's almost always a missing storage:link or a disk/visibility mismatch — fix that before touching anything else.

Implementation Patterns

Decide early: built-in routes or your own front end. The package's front-end routes are fine for a side project, but most real apps disable them ('routes' => false-style flag or by not registering them) and use the models inside their own controllers, layouts, and SEO stack. The admin side is where the package actually earns its keep; the public side is usually five lines of controller you'd rather own.

Query and render pattern (the one you'll write most often):

// app/Http/Controllers/BlogController.php
public function index()
{
    $articles = Article::query()
        ->published()                 // check the exact scope name in the model
        ->with(['category', 'tags', 'author'])
        ->latest('published_at')
        ->paginate(15);

    return view('blog.index', compact('articles'));
}

public function show(Article $article)   // slug route-model binding
{
    abort_unless($article->isPublished(), 404);

    return view('blog.show', compact('article'));
}

Render Markdown once, at write time — not per request. The biggest day-to-day performance win with any Markdown blog is storing the rendered HTML alongside the source. Add a content_html column (or a cache keyed by updated_at) and hydrate it in a model observer/mutator:

static::saving(function (Article $article) {
    $article->content_html = Str::markdown($article->content); // Laravel 5.5/6/7: use the package's renderer
});

Then your Blade view is just {!! $article->content_html !!}. Note that Str::markdown only exists from Laravel 9 — on the versions this package targets you'll call the parser directly (Parsedown/CommonMark) or reuse whatever the package's editor already wires up.

Wire the package into your app's navigation and auth. The user_id on articles gives you an author box and an "only the author or an admin can edit" policy. Add a Gate/Policy in front of the admin routes rather than relying on the default auth middleware alone — auth lets any logged-in user write, which is rarely what you want.

Extend the editor instead of replacing it. The editor config in config/blogged.php accepts toolbar options. Add what your authors actually need (heading levels, code fences, tables) before you consider swapping in a different editor — swapping means forking the package's JS and views, which is a maintenance tax.

Override the published views surgically. Publish views, then edit resources/views/vendor/blogged/layouts/* to extend your app's layout (@extends('layouts.app')) so the admin blends in. Don't rewrite the article form views unless you have to — they carry the upload and slug logic.

Programmatic content. For imports or seeded demo content, create articles through the model directly, setting title, content, a unique slug, category_id, user_id, and published_at. Write your own factory — the package doesn't ship one — and use it in RefreshDatabase tests so you can assert on the published() scope and slug binding without hitting HTTP routes.

Feeds and sitemaps fall out naturally: one query over published() feeding an RSS Blade view and a sitemap.xml route. Don't install a separate blog package for that.

Pin the constraint, not the package. Since the only change in v1.4.0 is a widened illuminate/* bound, treat the version as a compatibility marker, not a feature line. In a long-lived Laravel 7 app, composer require "binarytorch/blogged:^1.4" documents intent better than *, and it stops a future (unlikely) 2.x from sliding in.

Gotchas and Tips

  • Laravel 7 is the ceiling, and the package is still effectively unmaintained. v1.4.0 adds Laravel 7 support and nothing else; the last substantive work predates it. On Laravel 8+ expect hard composer conflicts (the framework dropped a lot of what the package's dependency pins require). Check composer why-not before assuming it installs, and if you're on a modern app, budget for a fork with Str::slug, Arr::except, Str::markdown/CommonMark, and updated migration/return types — or treat this package as a reference implementation and copy the ~15 files you need.
  • On Laravel 6/7, watch for the legacy helpers. str_slug, array_except, str_plural and friends were pulled out of the framework in 6.0 and moved to laravel/helpers. If the package calls them and laravel/helpers isn't in your tree, you get Call to undefined function at runtime — not install time. Either require that package explicitly or run grep -rn "str_slug\|array_except\|str_plural" vendor/binarytorch/blogged/src once and know what you're dealing with.
  • route:cache may fail. Laravel 7 (like 5.5–7 generally) refuses to cache closure-based routes. If php artisan route:cache throws, it's almost certainly a closure route registered by the package's provider — move it to a controller in a local override, or skip route caching in that app.
  • vendor:publish overwrites. Re-publishing after an "upgrade" will clobber your edited config and views. Publish once, commit the output, and never re-run blindly. Use --force only after a diff. This matters more than usual here: the recommended upgrade path for v1.4.0 is literally just bumping the constraint, so there's no reason to re-publish at all.
  • Middleware and prefix collisions. The admin prefix defaults to something generic; if your app already owns that path or you have a catch-all route, you'll get silent 404s or the wrong controller. php artisan route:list | grep -i blogged is your first debugging step for any "route not found" report.
  • Auth redirect loops. The admin routes assume session auth. Behind an API guard, or with a custom login route, you'll bounce between redirects. Set middleware to include your app's real auth stack, and confirm the guard is web.
  • Markdown output is not sanitized. Authors are usually trusted, but if you ever accept contributed posts, raw HTML in Markdown is a stored-XSS vector. Sanitize with HTMLPurifier or strip HTML at render time — and never {!! !!} untrusted content.
  • Slug collisions and URL stability. Two articles titled "Hello World" need distinct slugs; check the model's slug generation and add a uniqueness guard. Once published, changing a slug breaks inbound links — add a redirect table or a "slug history" column if SEO matters.
  • Draft vs. scheduled publishing. If published() only checks a status flag, "publish at 9am tomorrow" doesn't work out of the box. Either add where('published_at', '<=', now()) to your own query or schedule an artisan command that flips drafts to published.
  • Uploads: three failure modes. (1) Missing storage:link; (2) writing to local instead of public visibility on the configured disk; (3) orphaned files when an article is deleted. Add a cleanup command for the third, and set upload_max_filesize/post_max_size for the first two if large images silently fail.
  • No model-swapping hook to rely on. If the config doesn't expose a models map, don't try to substitute your own Article class — extend the package model and change the config/route bindings, or fork. Verify before designing around it.
  • N+1 on the index page. The default front-end views are simple; eager load category, tags, and the author relation yourself, or a 15-post page becomes 45 queries.
  • Debugging checklist: config('blogged') in tinker to confirm the published config actually loaded → route:list for the prefix → check storage/logs/laravel.log for view-not-found errors (a strong sign views weren't published) → inspect the articles table for published_at/status values that don't match your published() expectation. After the v1.4.0 bump, also confirm php artisan --version is 7.x and that the lock file actually resolved the new constraint — a stale composer.lock is the usual reason "we upgraded" changes nothing.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib