PHPackages                             lornequinn/blog-core - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. lornequinn/blog-core

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

lornequinn/blog-core
====================

Core package for the LQ blog suite — Post, Tag, DataType registry, Component registry.

04PHP

Since May 18Pushed 3w agoCompare

[ Source](https://github.com/lornequinn/blog-core)[ Packagist](https://packagist.org/packages/lornequinn/blog-core)[ RSS](/packages/lornequinn-blog-core/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependenciesVersions (1)Used By (0)

lornequinn/blog-core
====================

[](#lornequinnblog-core)

Core package for the LQ blog suite — Post + Tag models, publishing states, DataType + Component registries, and the polymorphic link table that DataType packages plug into.

Part of the modular [`lornequinn/blog`](https://github.com/lornequinn/blog) package suite. This is the only required package; everything else (markdown rendering, MCP editor, SEO, RSS, feeds, search, comments) is an optional add-on.

Installation
------------

[](#installation)

```
composer require lornequinn/blog-core
```

Publish migrations and run them:

```
php artisan vendor:publish --tag=blog-core-migrations
php artisan migrate
```

(Optional — publish the config to override defaults.)

```
php artisan vendor:publish --tag=blog-core-config
```

What's in the box
-----------------

[](#whats-in-the-box)

### `Post`

[](#post)

`title`, `slug`, `body`, `excerpt`, `published_at`, `status`. Auto-generates a unique slug from the title on creation (`Hello World` → `hello-world`, `hello-world-2`, …). Status is a backed enum — `Draft`, `Scheduled`, `Published`.

```
use LorneQuinn\Blog\Core\Models\Post;
use LorneQuinn\Blog\Core\Enums\PostStatus;

$post = Post::create([
    'title' => 'Hello World',
    'body' => 'Body markdown goes here.',
]);

$post->update([
    'status' => PostStatus::Published,
    'published_at' => now(),
]);
```

### `Tag`

[](#tag)

Flat taxonomy. Same slug auto-generation pattern as `Post`. Pivot table `post_tag`.

```
use LorneQuinn\Blog\Core\Models\Tag;

$tag = Tag::create(['name' => 'PHP']);
$post->tags()->attach($tag);
```

### `DataTypeRegistry`

[](#datatyperegistry)

A DataType is a structured record attached to a Post — typically the queryable data behind a Component (`RaceResults`, `EpisodeRatings`, `MovieMetadata`). Register your DataTypes in your app's service provider:

```
use LorneQuinn\Blog\Core\DataType\DataType;
use LorneQuinn\Blog\Core\DataType\DataTypeRegistry;

class RaceResults extends DataType
{
    public function name(): string { return 'race-results'; }
    public function model(): string { return \App\Site\Models\RaceResult::class; }
    public function rules(): array { return [
        'driver' => ['required', 'string'],
        'position' => ['required', 'integer', 'min:1'],
    ]; }
}

// AppServiceProvider::boot()
app(DataTypeRegistry::class)->register(new RaceResults());
```

### `ComponentRegistry`

[](#componentregistry)

A Component is a Blade view that renders a DataType. Register Components similarly:

```
use LorneQuinn\Blog\Core\Component\Component;
use LorneQuinn\Blog\Core\Component\ComponentRegistry;

class FinishingTable extends Component
{
    public function name(): string { return 'finishing-table'; }
    public function dataType(): string { return 'race-results'; }
    public function view(): string { return 'site::components.finishing-table'; }
}

app(ComponentRegistry::class)->register(new FinishingTable());
```

Once registered, the renderer resolves `[[finishing-table]]` shortcodes in a Post body to the matching Component bound against the Post's attached DataType records.

### `ShortcodeParser`

[](#shortcodeparser)

Resolves `[[name attr=value]]` tokens in Post bodies. Names are kebab-case-lowercase. Attribute values can be bare (`a=1`), double-quoted (`a="hello world"`), or single-quoted (`a='single'`). Escape a literal token with a leading backslash: `\[[demo]]` renders as `[[demo]]`. Malformed tokens pass through unchanged.

The parser is wired into the `BodyPipeline` at priority 0 (runs first) by `BlogCoreServiceProvider`. Its resolver looks up Components in the `ComponentRegistry` and renders their Blade views with the parsed attributes as view data.

### `BodyPipeline`

[](#bodypipeline)

Ordered chain of body renderers. Each renderer is `(string): string`. Register in your service provider:

```
use LorneQuinn\Blog\Core\Rendering\BodyPipeline;

app(BodyPipeline::class)->register(function (string $body): string {
    return strtoupper($body); // contrived
}, priority: 50);
```

Lower priority runs earlier. Default priority is 0. Within the same priority, registration order is preserved.

Standard pipeline composition (with `lornequinn/blog-markdown` installed):

PriorityRendererNotes0`ShortcodeParser`Resolves `[[…]]` tokens to rendered Component HTML100`MarkdownRenderer` (from `blog-markdown`)Markdown → HTML, with `html_input='allow'` so resolved HTML survives### Route + view

[](#route--view)

`blog-core` registers `GET /posts/{slug}` → `PostController@show`, named `blog.posts.show`. The controller filters to `PostStatus::Published` with `published_at
