Content Drivers
Contract and implementations for content storage backends.
Namespace: VelvetCMS\Contracts\ContentDriver
ContentDriver Interface #
Defines the storage contract for pages. VelvetCMS ships with File, DB, Hybrid, and Auto drivers.
Definition #
interface ContentDriver
{
/**
* @throws \VelvetCMS\Exceptions\NotFoundException
*/
public function load(string $slug): Page;
/**
* @throws \VelvetCMS\Exceptions\ValidationException
*/
public function save(Page $page): bool;
public function list(array $filters = []): Collection;
public function paginate(int $page = 1, int $perPage = 20, array $filters = []): Collection;
/**
* @throws \VelvetCMS\Exceptions\NotFoundException
*/
public function delete(string $slug): bool;
public function exists(string $slug): bool;
public function count(array $filters = []): int;
}
Methods #
load() #
Load a page by its slug.
public function load(string $slug): Page
| Parameter | Type | Description |
|---|---|---|
$slug |
string |
The page slug |
Returns: Page instance
Throws: NotFoundException if page doesn't exist
Example:
$driver = app('content.driver');
try {
$page = $driver->load('welcome');
echo $page->title;
} catch (NotFoundException $e) {
// Handle missing page
}
save() #
Save a page (create or update).
public function save(Page $page): bool
| Parameter | Type | Description |
|---|---|---|
$page |
Page |
The page to save |
Returns: true on success
Throws: ValidationException if page data is invalid
Example:
$page = new Page();
$page->slug = 'new-page';
$page->title = 'New Page';
$page->content = '# Hello World';
$page->status = 'draft';
$driver->save($page);
list() #
Get all pages matching filters.
public function list(array $filters = []): Collection
| Parameter | Type | Description |
|---|---|---|
$filters |
array |
Optional filter criteria |
Returns: Collection of Page objects
Example:
// All pages
$pages = $driver->list();
// Published pages only
$published = $driver->list(['status' => 'published']);
// With ordering
$recent = $driver->list([
'order_by' => 'updated_at',
'order' => 'desc',
'limit' => 10,
]);
paginate() #
Get paginated pages.
public function paginate(int $page = 1, int $perPage = 20, array $filters = []): Collection
| Parameter | Type | Default | Description |
|---|---|---|---|
$page |
int |
1 |
Page number |
$perPage |
int |
20 |
Items per page |
$filters |
array |
[] |
Filter criteria |
Returns: Collection of Page objects
Example:
$pages = $driver->paginate(2, 15, ['status' => 'published']);
delete() #
Delete a page by slug.
public function delete(string $slug): bool
| Parameter | Type | Description |
|---|---|---|
$slug |
string |
The page slug |
Returns: true on success
Throws: NotFoundException if page doesn't exist
Example:
$driver->delete('old-page');
exists() #
Check if a page exists.
public function exists(string $slug): bool
Example:
if ($driver->exists('about')) {
$page = $driver->load('about');
}
count() #
Count pages matching filters.
public function count(array $filters = []): int
Example:
$total = $driver->count();
$published = $driver->count(['status' => 'published']);
Filters #
All drivers accept these standard filters:
| Filter | Type | Description |
|---|---|---|
status |
string |
'draft' or 'published' |
order_by |
string |
Field to sort by (created_at, updated_at, title) |
order |
string |
'asc' or 'desc' |
limit |
int |
Maximum results |
offset |
int |
Skip first N results |
Example:
$pages = $driver->list([
'status' => 'published',
'order_by' => 'created_at',
'order' => 'desc',
'limit' => 5,
]);
Built-in Drivers #
FileDriver #
Stores pages as files in user/content/pages/.
Namespace: VelvetCMS\Drivers\Content\FileDriver
Features:
- Loads
.vltfiles first, then.md - Uses YAML frontmatter for metadata
- Maintains a lightweight index at
storage/cache/file-driver-index.json - Index auto-updates when file mtime changes
File Structure:
---
title: Welcome
status: published
created_at: 2026-01-01
---
# Welcome to VelvetCMS
Your content here...
Configuration:
// config/content.php
'driver' => 'file',
'drivers' => [
'file' => [
'path' => content_path('pages'),
],
],
DBDriver #
Stores pages in the pages database table.
Namespace: VelvetCMS\Drivers\Content\DBDriver
Features:
- Full metadata and content in database
- Efficient queries and filtering
- Supports complex search operations
Configuration:
// config/content.php
'driver' => 'db',
HybridDriver #
Stores metadata in database, content in files.
Namespace: VelvetCMS\Drivers\Content\HybridDriver
Features:
- Fast metadata queries via database
- Large content stored efficiently in files
- Best of both worlds
Configuration:
// config/content.php
'driver' => 'hybrid',
'drivers' => [
'hybrid' => [
'path' => content_path('pages'),
],
],
AutoDriver #
Automatically selects driver based on page count.
Namespace: VelvetCMS\Drivers\Content\AutoDriver
Features:
- Uses FileDriver for small sites
- Switches to HybridDriver above threshold
Configuration:
// config/content.php
'driver' => 'auto',
'drivers' => [
'auto' => [
'threshold' => 100, // Switch at 100 pages
],
],
Creating a Custom Driver #
Implement the ContentDriver interface:
use VelvetCMS\Contracts\ContentDriver;
use VelvetCMS\Models\Page;
use VelvetCMS\Database\Collection;
use VelvetCMS\Exceptions\NotFoundException;
class ApiDriver implements ContentDriver
{
public function __construct(
private readonly HttpClient $client,
private readonly string $baseUrl
) {}
public function load(string $slug): Page
{
$response = $this->client->get("{$this->baseUrl}/pages/{$slug}");
if ($response->status() === 404) {
throw new NotFoundException("Page '{$slug}' not found");
}
return Page::fromArray($response->json());
}
public function save(Page $page): bool
{
$response = $this->client->put(
"{$this->baseUrl}/pages/{$page->slug}",
$page->toArray()
);
return $response->successful();
}
public function list(array $filters = []): Collection
{
$response = $this->client->get("{$this->baseUrl}/pages", $filters);
$pages = array_map(
fn($data) => Page::fromArray($data),
$response->json()
);
return new Collection($pages);
}
public function paginate(int $page = 1, int $perPage = 20, array $filters = []): Collection
{
return $this->list(array_merge($filters, [
'page' => $page,
'per_page' => $perPage,
]));
}
public function delete(string $slug): bool
{
$response = $this->client->delete("{$this->baseUrl}/pages/{$slug}");
if ($response->status() === 404) {
throw new NotFoundException("Page '{$slug}' not found");
}
return $response->successful();
}
public function exists(string $slug): bool
{
$response = $this->client->head("{$this->baseUrl}/pages/{$slug}");
return $response->status() === 200;
}
public function count(array $filters = []): int
{
$response = $this->client->get("{$this->baseUrl}/pages/count", $filters);
return $response->json()['count'] ?? 0;
}
}
Register Custom Driver #
// In a service provider
$app->bind('content.driver', function($app) {
return new ApiDriver(
$app->make(HttpClient::class),
config('content.drivers.api.url')
);
});
Usage with PageService #
The PageService wraps content drivers with caching and events:
// Direct driver access (no caching)
$driver = app('content.driver');
$page = $driver->load('welcome');
// Via PageService (with caching and events)
$pages = app('pages');
$page = $pages->load('welcome'); // Cached for 300s
See PageService for the recommended high-level API.