bytes-commerce/easy-blog
Drop-in Symfony blog bundle with EasyAdmin CRUD. Manage posts, hierarchical categories and FAQs, plus built-in SEO fields. Ships with responsive Twig templates and integrates with your app’s User entity for post authors.
Installation:
composer require bytes-commerce/easy-blog
Add to config/bundles.php and configure easy_blog.yaml.
First Post Creation:
Post entity via the dashboard.Category (create one if needed) and an Author (your User entity).Viewing Content:
/beitraege/{slug} (e.g., /beitraege/my-first-post).blog.post Twig template to render it in your frontend.Posts and Categories CRUD interfaces.templates/bundles/EasyBlog/blog/post/view.html.twig for custom post rendering.blog.post and blog.category.all-posts for frontend integration.Post Management:
$post = new Post();
$post->setTitle('Hello World')
->setSlug('hello-world')
->setContent('...')
->setAuthor($user);
$entityManager->persist($post);
$entityManager->flush();
Category Hierarchy:
$parent = $categoryRepository->findOneBy(['slug' => 'parent']);
$child->setParent($parent);
SEO Optimization:
metaTitle, metaDescription, keywords) in the EasyAdmin form or via code:
$post->setMetaTitle('Custom SEO Title');
FAQ Integration:
$faq = new Faq();
$faq->setQuestion('FAQ Question')->setAnswer('FAQ Answer');
$post->addFaq($faq);
Frontend Rendering: Use Twig to fetch and display posts:
{% for post in posts %}
<h2>{{ post.title }}</h2>
<p>{{ post.content|truncate(200) }}</p>
<a href="{{ path('blog.post', {'slug': post.slug}) }}">Read More</a>
{% endfor %}
Pagination:
Leverage the pagination.page_size config (default: 5) or override in templates:
{{ knp_pagination_render(posts) }}
API Endpoints:
Extend the blog.ajax.posts route for dynamic content (e.g., post sliders):
fetch('/ajax/posts')
.then(response => response.json())
.then(posts => {
// Render posts dynamically
});
Custom Fields:
Extend the Post entity (e.g., add featuredImage):
#[ORM\Column(type: 'string', nullable: true)]
private ?string $featuredImage = null;
Update the EasyAdmin CRUD configuration to include the new field.
User Entity Requirement:
AuthorAwareInterface or configure user_entity in easy_blog.yaml will break post-author associations.User entity implements getPosts() and addPost() methods.Slug Conflicts:
/beitraege/hello-world) will cause 404 errors.slugify logic in your User entity.Template Overrides:
templates/EasyBlog/ instead of templates/bundles/EasyBlog/) will fail silently.templates/bundles/EasyBlog/blog/.VichUploader Misconfiguration:
vich_uploader is enabled but upload_dir is incorrect, images won’t save.upload_dir to a writable directory (e.g., public/uploads/blog/).EasyAdmin Permissions:
ROLE_ADMIN may not see the blog menu items.security:
access_control:
- { path: ^/admin/blog, roles: ROLE_ADMIN }
Doctrine Events:
Listen for post.persist or post.update to log or validate data:
$eventManager->addEventListener(Post::class, function (LifecycleEventArgs $args) {
$post = $args->getObject();
if ($post->getSlug() === null) {
throw new \RuntimeException('Slug cannot be null!');
}
});
Cache Issues: Clear the cache after configuration changes:
php bin/console cache:clear
php bin/console easyadmin:cache:clear
Route Debugging:
Dump routes to verify blog.post exists:
php bin/console debug:router | grep blog.post
Custom Post Types:
Extend the Post entity or create a trait for reusable logic:
trait CustomPostFields {
#[ORM\Column(type: 'boolean')]
private bool $isFeatured = false;
public function isFeatured(): bool { return $this->isFeatured; }
}
Event Subscribers: Hook into post lifecycle events (e.g., send notifications on publish):
class PostSubscriber implements EventSubscriber {
public static function getSubscribedEvents(): array {
return [
PostEvents::POST_PUBLISH => 'onPostPublish',
];
}
public function onPostPublish(PostEvent $event): void {
// Send email, log activity, etc.
}
}
Repository Extensions:
Add custom query methods to PostRepository:
public function findFeaturedPosts(int $limit = 3): array {
return $this->createQueryBuilder('p')
->where('p.isFeatured = :featured')
->setParameter('featured', true)
->orderBy('p.createdAt', 'DESC')
->setMaxResults($limit)
->getQuery()
->getResult();
}
Twig Extensions: Create a custom Twig filter for post excerpts:
class PostExtension extends \Twig\Extension\AbstractExtension {
public function getFilters(): array {
return [
new \Twig\TwigFilter('post_excerpt', [$this, 'excerptFilter']),
];
}
public function excerptFilter(string $content, int $length = 100): string {
return substr($content, 0, $length) . '...';
}
}
Register it in services.yaml:
services:
App\Twig\PostExtension:
tags: ['twig.extension']
Form Customization:
Override EasyAdmin’s post form in your config/easyadmin.yaml:
easy_admin:
entities:
Post:
form:
fields:
- { property: 'title', type: 'text' }
- { property: 'content', type: 'ckeditor' }
- { property: 'featuredImage', type: 'vich_image' }
How can I help you explore Laravel packages today?