asm/markdown-content-bundle
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require asm/markdown-content-bundle
Add the bundle to AppKernel.php:
new Asm\MarkdownContentBundle\AsmMarkdownContentBundle(),
Configure:
Add to config.yml:
asm_markdown_content:
content_directory: '%kernel.root_dir%/../src/AppBundle/Resources/markdown'
route_prefix: 'content'
markdown_provider: 'parsedown' # or 'php-markdown'
Create Markdown Files:
Place .md files in src/AppBundle/Resources/markdown/ (e.g., about.md).
Example content:
---
title: About Us
description: Learn more about our team
---
# Welcome to Our Team
This is a markdown file rendered via the bundle.
Access Content:
Visit /content/about (or /en_US/content/about if using locales).
.md files under content_directory.
Example: about.md → /content/about.locale_url: true in config to use /en_US/content/about.{{ content.data.title }} {# Front matter title #}
{{ content.html }} {# Rendered HTML #}
Pre-Content Hooks: Modify raw markdown (e.g., inject placeholders).
# services.yml
app.markdown_hook:
class: AppBundle\Hook\CustomPreHook
tags:
- { name: asm_markdown_content.hook, alias: custom_pre }
// CustomPreHook.php
class CustomPreHook implements HookDataInterface {
public function getType() { return 'pre'; }
public function workContent(array $content) {
$content['content'][] = '---\ncustom: injected\n---';
return $content;
}
}
Post-Content Hooks: Modify HTML output (e.g., add classes).
class CustomPostHook implements HookContentInterface {
public function getType() { return 'post'; }
public function workContent(array $content) {
$content['html'] = str_replace('<h1>', '<h1 class="custom">', $content['html']);
return $content;
}
}
vendor/asm/markdown-content-bundle/Resources/views/layout.html.twig → AppBundle/Resources/views/layout.html.twigvendor/asm/markdown-content-bundle/Resources/views/Content/index.html.twig → AppBundle/Resources/views/Content/index.html.twig{# content.data #} {# Front matter #}
{# content.html #} {# Rendered content #}
{# content.path #} {# File path #}
php app/console asm:markdown:export /path/to/static/dir
php app/console asm:markdown:export /static/dir -i /source/markdown/dir
File Permissions:
Ensure content_directory is writable if using hooks or export.
chmod -R 755 src/AppBundle/Resources/markdown
Front Matter Parsing:
--- in front matter (e.g., --- must be the first line)."key: value" or key\: value).Route Conflicts:
Avoid naming .md files to match existing Symfony routes (e.g., contact.md if /contact is a route).
Solution: Use route_prefix to namespace routes (e.g., content/contact).
Parser Dependencies:
parsedown (default) is lighter but lacks some PHP-Markdown features (e.g., tables).config.yml:
markdown_provider: 'php-markdown'
composer require michelf/php-markdown
Locale Handling:
content/{locale}/ (e.g., content/en_US/about.md).config.yml:
framework:
default_locale: en_US
Check Loaded Content: Dump content in a Twig template or controller:
{{ dump(content) }}
Or in PHP:
$content = $this->get('asm_markdown_content.content_loader')->load('about');
var_dump($content);
Hook Debugging:
workContent():
error_log(print_r($content, true));
Export Issues:
absolute_export_dir is writable.Custom Loaders:
Implement ContentLoaderInterface for non-file sources (e.g., S3, API).
Example:
class ApiContentLoader implements ContentLoaderInterface {
public function load($path) {
$apiResponse = file_get_contents("https://api.example.com/$path.md");
return ['content' => $apiResponse, 'data' => []];
}
}
Register as a service:
app.api_loader:
class: AppBundle\Loader\ApiContentLoader
tags:
- { name: asm_markdown_content.content_loader, alias: api }
Custom Parsers:
Implement ParserInterface for non-Markdown formats (e.g., BBCode).
Example:
class BbcodeParser implements ParserInterface {
public function parse($content) {
return convertBbcodeToHtml($content);
}
}
Register:
app.bbcode_parser:
class: AppBundle\Parser\BbcodeParser
tags:
- { name: asm_markdown_content.parser, alias: bbcode }
Caching: The bundle lacks built-in caching. Add a post-hook to cache rendered HTML:
class CacheHook implements HookContentInterface {
public function getType() { return 'post'; }
public function workContent(array $content) {
$cacheKey = 'markdown_' . md5($content['path']);
$cache = $this->get('cache.app');
$cache->set($cacheKey, $content['html'], 3600); // Cache for 1 hour
return $content;
}
}
Performance:
class MarkdownContentPass implements CompilerPassInterface {
public function process(ContainerBuilder $container) {
$loader = $container->getParameter('asm_markdown_content.content_directory');
// Pre-cache file list
}
}
Register in services.yml:
services:
app.markdown_compiler_pass:
class: AppBundle\DependencyInjection\Compiler\MarkdownContentPass
tags:
- { name: kernel.compiler_pass }
content_path_depth:
Set in config.yml to control subdirectory depth for routing:
asm_markdown_content:
content_path_depth: 2 # Routes: /content/subdir/file
Default: 1 (routes like /content/subdir/file become /content/file).
Route Generation:
The bundle uses FOSRouterBundle-like syntax. Override route generation by extending the ContentRouter service:
app.content_router:
class: AppBundle\Routing\CustomContentRouter
decorates: asm_markdown_content.content_router
arguments: ['@app.content_router.inner']
How can I help you explore Laravel packages today?