polishsymfonycommunity/blog-bundle
A Symfony bundle from the Polish Symfony Community for adding a blog to your application, with straightforward post management and integration into existing Symfony projects to help you publish and organize articles quickly.
This bundle maps an existing WordPress database onto Doctrine entities so a Symfony app can render blog content that editors still manage in wp-admin. It is archived and has zero dependents, so treat it as a starting point you will fork and own, not a maintained dependency.
Minimal setup:
composer require polishsymfonycommunity/blog-bundle (or vendor the repo and add a repositories entry — the archived state makes the Packagist route fragile). Check composer.json for the PHP/Symfony constraints; it targets the Symfony 2/3 era, so on modern Symfony you will likely need to bump constraints and fix deprecations before it boots.new PolishSymfonyCommunity\BlogBundle\PSSBlogBundle() in AppKernel, or add it to config/bundles.php.doctrine.dbal.wp_connection) with the WordPress credentials, and configure the bundle to use it along with your table prefix:pss_blog:
connection: wp_connection
table_prefix: wp_ # change for installs using a custom prefix
php bin/console doctrine:mapping:info --em=wp should list Post, Comment, Term/Tag/Category, and User entities. If it is empty, the bundle was not registered or the EM name is wrong.Where to look first: the bundle's Entity/ directory (the canonical list of what fields you actually get), then Resources/config/ for the service IDs and the ORM mapping, then the controllers for the assumed routing. The README is thin — the mapping files are the real documentation.
First use case: a read-only blog index plus a single-post page. Wire /blog to the post repository filtered to published posts, paginate, and /blog/{slug} to a findOneBySlug-style lookup. Comments and taxonomy archives can come later.
Separate entity manager, read-mostly. The cleanest integration is a dedicated wp entity manager so the bundle's entities never leak into your app's schema and doctrine:schema:update can never touch WordPress tables. Point the bundle's services at that EM and keep your domain entities on the default one.
Repository-driven controllers. The bundle's repositories are the intended entry point. Typical query shape for an index page:
$posts = $postRepository->createPublishedQueryBuilder()
->orderBy('p.postDate', 'DESC')
->setMaxResults($limit)
->setFirstResult($offset)
->getQuery()
->getResult();
Filter explicitly — WordPress stores pages, attachments, revisions, and auto-drafts in the same wp_posts table.
Taxonomy rendering. Tags and categories live in wp_terms joined through wp_term_taxonomy and wp_term_relationships. Build archive routes (/tag/{slug}, /category/{slug}) against the term repository, and render the term's count for "N posts" labels.
Comments as a second-class citizen. Reading approved comments is trivial; writing them means inserting into wp_comments with correct comment_approved, comment_agent, and comment_parent values, or editors will not see them in wp-admin. Prefer proxying writes back through WordPress (a small REST call or WP-CLI invocation) if comment volume matters, and only render the read side from Symfony.
Caching is the integration contract. WordPress writes bypass Doctrine's identity map and any second-level cache, so cache rendered fragments with a short TTL (or purge via a webhook from WordPress) rather than caching entities indefinitely.
Hybrid deployment. The pattern that actually works long-term: WordPress stays the editorial back end on its own host, Symfony owns the public front end and SEO surface. Keep the WP database connection read-only at the MySQL user level for the Symfony app, and give the comment-proxy service the only write credentials.
Overriding presentation. Copy the bundle's Twig templates into templates/bundles/PSSBlogBundle/ and edit there; do not patch vendor code for styling. Decorate repositories in services.yaml if you need extra query methods rather than editing the bundle's classes.
post_status and post_type are not optional filters. Without post_status = 'publish' and post_type = 'post' you will render drafts, revisions, menus, and media attachments. Password-protected posts (post_password != '') need a separate guard.post_date is site-local; post_date_gmt is UTC. Use the GMT column for ordering and formatting in Symfony, or timezone drift will scramble your archive.wp_postmeta and wp_options. Any meta value is a PHP-serialized blob until proven otherwise; unserialize() with allowed_classes => false, or parse with a JSON-first fallback. Do not assume meta_value is a string.post_content contains shortcodes, <!--more--> markers, and block comments. Either render raw and accept broken shortcodes, or strip/transform them in a Twig extension. Escape nothing you did not generate yourself, but sanitize before output if untrusted authors exist.post_excerpt is often blank. Fall back to a truncated, tag-stripped post_content.doctrine:schema:update, no migrations. The mapping is descriptive, not authoritative — WordPress owns the schema.wp_2_, wp_3_). The single table_prefix config cannot express that; you need one connection/config per site or a runtime-switched prefix.comment_parent; build the tree yourself (comment_approved = '1', parent 0 roots) rather than assuming the bundle returns nested objects.post_name and joins through term_relationships can be slow on large sites — add read-replica indexes if you cannot cache.How can I help you explore Laravel packages today?