Installation
composer require checlou/flat-file-cms-bundle
Add to config/bundles.php:
return [
// ...
Checlou\FlatFileCMSBundle\CheclouFlatFileCMSBundle::class => ['all' => true],
];
Configuration Publish the default config:
php bin/console checlou:flat-file-cms:install
Edit config/packages/checlou_flat_file_cms.yaml to define:
pages_directory: Path to your Markdown files (e.g., data/cms/pages/).default_locale: Default language (e.g., en).First Use Case
Create a Markdown file (e.g., data/cms/pages/home.md):
---
title: Home
slug: home
---
# Welcome to our site!
This is a **Markdown** CMS page.
Access it in a controller:
use Checlou\FlatFileCMSBundle\Service\PageService;
class PageController extends AbstractController
{
public function show(PageService $pageService)
{
$page = $pageService->getPage('home');
return $this->render('page/show.html.twig', ['page' => $page]);
}
}
Page Management
pages_directory.is_published: false in YAML frontmatter.{{ page.content|raw }}
Routing
Dynamically generate routes in config/routes.yaml:
checlou_flat_file_cms.page:
path: /{slug}
controller: Checlou\FlatFileCMSBundle\Controller\PageController::show
methods: GET
Localization
Organize files by locale (e.g., data/cms/pages/en/home.md, data/cms/pages/fr/home.md).
Fetch pages with:
$pageService->getPage('home', 'fr');
Templating
Extend Twig with custom filters (e.g., markdown_to_html) or create reusable blocks:
{% extends 'base.html.twig' %}
{% block content %}
{{ include('cms/page_content.html.twig', {'content': page.content}) }}
{% endblock %}
Asset Handling Reference images/attachments in Markdown:

Serve static files via Symfony’s asset system or a custom route.
title, slug) with a custom form type.$pageService->getAllPages(); // Returns array of Page objects
php bin/console cache:clear
checlou_flat_file_cms.page.save to trigger post-save logic.File Permissions
Ensure the pages_directory is writable by the web server:
chmod -R 775 data/cms/
Debug: Check storage/logs/dev.log for Permission denied errors.
Frontmatter Parsing
Slug Conflicts
home-v2) or use UUIDs.Markdown Limitations
league/commonmark.Case Sensitivity
slug: "{{ slug|lower }}").pages_directory path in config and file existence.content is rendered as raw Markdown (use |raw filter).default_locale is set in config.Custom Fields
Extend the Page entity or use a custom frontmatter parser:
# config/packages/checlou_flat_file_cms.yaml
checlou_flat_file_cms:
custom_fields:
- 'meta_title'
- 'meta_description'
File Watcher
Auto-reload pages on file changes (e.g., with Symfony’s FileSystemWatcher or a custom CLI command).
Media Library Integrate with VichUploaderBundle or Spatie Media Library for file attachments:
---
attachments:
- { path: 'uploads/docs/guide.pdf', label: 'User Guide' }
---
Revision History Use Git hooks or a database-backed system (e.g., Doctrine) to track changes.
cache_enabled in config for development:
checlou_flat_file_cms:
cache_enabled: false
.md files are parsed by default. Add support for .markdown in config:
checlou_flat_file_cms:
allowed_extensions: ['md', 'markdown']
How can I help you explore Laravel packages today?