Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Article Bundle Laravel Package

sonata-project/article-bundle

Sonata Article Bundle adds article management to Symfony apps, offering SonataAdmin CRUD screens, publishing controls, categories, and Doctrine-backed content types for building news or blog sections.

View on GitHub
Deep Wiki
Context7

Getting Started

The bundle is archived (last release 2021-07-17), so treat this as: pin exact versions, vendor the source, and plan to fork. It is a Symfony 3.4/4.x-era bundle built on the Sonata stack — it will not install cleanly on Symfony 5+/6/7 without patching.

  1. Understand what you're installing. sonata-project/article-bundle is not a standalone CMS. It supplies abstract models and admin/block plumbing that sits on top of SonataAdminBundle, SonataMediaBundle (images), SonataFormatterBundle (rich-text/Markdown), SonataBlockBundle (frontend rendering) and optionally SonataClassificationBundle (tags/categories/collections). You must wire the concrete classes yourself.
  2. Install with pinned constraints and confirm the transitive set resolves together, since the Sonata bundles of that era are version-locked to each other:
    composer require sonata-project/article-bundle:^2.x --with-all-dependencies
    
  3. Register the bundle (SonataArticleBundle in AppKernel::registerBundles() or config/bundles.php for the 4.x layout) and make sure SonataMediaBundle, SonataFormatterBundle, SonataBlockBundle and SonataAdminBundle are registered first.
  4. Create your concrete entity. Read vendor/sonata-project/article-bundle/src/Entity/AbstractArticle.php first — it is a Doctrine MappedSuperclass carrying title, slug, abstract, content, image, publication dates, enabled and author. In src/AppBundle/Entity/Article.php:
    namespace AppBundle\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    use Sonata\ArticleBundle\Entity\AbstractArticle;
    
    /**
     * @ORM\Entity(repositoryClass="AppBundle\Repository\ArticleRepository")
     * @ORM\Table(name="app_article")
     */
    class Article extends AbstractArticle
    {
        /**
         * @ORM\Id
         * @ORM\Column(type="integer")
         * @ORM\GeneratedValue
         */
        protected $id;
    }
    
  5. Create your admin extending Sonata\ArticleBundle\Admin\ArticleAdmin and register it with the sonata.admin tag (manager_type="orm", label, code, group, pager_type).
  6. First use case: an "Articles" admin group where an editor writes a piece with a rich-text body and a media image, sets a publication window, and it appears in a listing block on the homepage. That single loop exercises the whole bundle: entity → admin → media → formatter → block.
  7. Where to look when something breaks: src/Entity/ (the abstract models), src/Admin/ (what's pre-wired in the forms/lists), src/Block/ (block services and their execute() query logic), and Resources/views/ (the block templates you will almost certainly override).

Implementation Patterns

Extend-and-configure, never edit vendor. The bundle's own docs and code assume you subclass AbstractArticle, AbstractFragment, AbstractFragmentContainer and their admins. Your app/config/config.yml then points Sonata's admin/block config at your classes. Keep this one-to-one: AppBundle\Entity\Article ↔ AppBundle\Admin\ArticleAdmin.

Admin registration. Everything else in Sonata keys off the admin code, so choose it deliberately (e.g. app.admin.article) and reference it in sonata_admin.templates, dashboard groups and block admin_code settings:

app.admin.article:
    class: AppBundle\Admin\ArticleAdmin
    arguments: [~, AppBundle\Entity\Article, SonataAdminBundle:CRUD]
    tags:
        - { name: sonata.admin, manager_type: orm, label: Articles, group: Content, pager_type: sonata.admin.pager.doctrine.orm }

Publishing is your job, not the bundle's. enabled plus publicationDateStart/publicationDateEnd are just columns. The base admin list will happily show drafts and future items. Put the canonical filter in a repository method and use it everywhere (controller, block, sitemap):

public function findPublished(\DateTime $now = null): array
{
    $now = $now ?: new \DateTime();

    return $this->createQueryBuilder('a')
        ->where('a.enabled = :enabled')
        ->andWhere('a.publicationDateStart <= :now')
        ->andWhere('a.publicationDateEnd IS NULL OR a.publicationDateEnd >= :now')
        ->setParameters(['enabled' => true, 'now' => $now])
        ->orderBy('a.publicationDateStart', 'DESC')
        ->getQuery()
        ->getResult();
}

Blocks are the rendering layer. Use the shipped block services (sonata.article.block.* — check Resources/config/ for the exact ids in your version) for "latest articles", article detail and fragment rendering rather than hand-rolling Twig queries. Configure them via sonata_block.blocks.<service>.settings and, critically, override templates there rather than copying vendor templates into your app.

Formatters over raw HTML. Store the author's raw source in content and render through the formatter pool (sonata.formatter.pool) so Markdown/richhtml both work. Do this in a Twig extension or your controller, not in the entity — it keeps the model renderer-agnostic and avoids calling services from entities.

Media wiring. The image association is a ManyToOne to SonataMediaBundle's Media. Configure at least one media context (e.g. default with a thumbnail format) and reference the same context in the admin form field; mismatched contexts are the usual cause of "image doesn't render" in block templates.

Frontend controller. Write your own ArticleController with /blog/{slug} routing and a findOneBySlug() lookup that also enforces the publication window (return 404 for drafts — do not rely on the admin enabled flag alone). Overriding the bundle's shipped frontend controller/routes is possible but ties you to its URL assumptions.

Fragments for reusable content. Fragment/FragmentContainer are for shared page chunks (promos, footers, CTA strips) that editors manage centrally. Model them as their own admin group and render via the fragment block, not by duplicating the text into every article.

Programmatic import. Because everything goes through Doctrine entities, bulk migration from an old CMS is straightforward: instantiate Article, set slug/dates/enabled, attach a Media (or null), persist+flush in batches. Run it as a console command with --env=prod --no-debug so you don't blow memory on the admin's form services.

Localisation. The shipped model has no translation layer. If you need multi-locale content, either add a Gedmo Translatable/Translation entity in your subclass or run one article per locale with a locale column — decide this before content exists.

Gotchas and Tips

  • Archived means unpatched. No security fixes, no PHP 8 / Symfony 6 compatibility work. Expect deprecation noise, ReturnTypeWillChange-class errors, and Doctrine DBAL 3 breakage in the admin's createQuery overrides. Budget for a fork; keep composer.lock committed and constrain sonata-project/* exactly.
  • Almost no community signal (11 stars, 0 dependents): there is no Stack Overflow corpus. Your debugging toolkit is the vendor source plus bin/console debug:container | grep sonata.article, debug:router, and sonata:admin:list.
  • Mapped superclass mistakes are the #1 first-run failure. Forgetting @ORM\Entity, forgetting the $id property, or placing the subclass in a directory outside Doctrine's mapping config produces "No identifier/No mapping" errors that look unrelated to the bundle.
  • Slugs. slug is populated by Gedmo Sluggable; if the listener isn't registered (stof/doctrine-extensions-bundle or manual gedmo.listener.sluggable tag) it stays null, your route 404s, and nothing warns you. Regenerating a slug after a title edit silently breaks inbound links — freeze the slug on first persist.
  • publicationDateEnd IS NULL means "never expires". Any query written as publicationDateEnd >= :now alone will hide every evergreen article. This is the classic silent-content-disappears bug.
  • Timezone drift. Dates are naive DATETIMEs. Editors scheduling "9am" get UTC. Normalise at write time in the admin or store an explicit timezone-aware policy.
  • Block caching. SonataBlockBundle caches block output; editors publish and see nothing change. Set cache: false (or a short TTL) in the block settings during authoring, and remember the block's cache key includes settings — changing templates in config may not invalidate it.
  • Admin access control. Registering an admin makes it reachable by anyone with the admin firewall role unless you set sonata_admin.security / ROLE_APP_ADMIN_ARTICLE_*. New admins silently widen your attack surface.
  • Deleting media orphans articles. The FK is on the article side; deleting a Media leaves a dangling reference and block templates throw on media_thumbnail. Add a Doctrine listener or a soft-delete policy on media.
  • Don't render from entities. Calling the formatter/media services from AbstractArticle subclasses will work in a controller and explode in a console command or fixture. Keep rendering in Twig extensions/services.
  • Extension points worth knowing: override getTemplate() in your admin for CRUD views, override block templates via sonata_block config, add AdminExtensions rather than editing ArticleAdmin, and decorate the manager/repository for custom publication rules. Those four cover nearly every customisation need without forking.
  • Exit strategy: if this is greenfield in 2024+, the honest recommendation is to model Article yourself (a slug, content, publishedAt, enabled) and skip the bundle — you'd be reimplementing about 200 lines of entity/admin/block code in exchange for maintained dependencies. Adopt the bundle only where an existing Sonata installation already carries it.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle