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

Node Bundle Laravel Package

kunstmaan/node-bundle

KunstmaanNodeBundle provides a hierarchical, translatable node tree for Symfony applications, forming the foundation of the Kunstmaan CMS. It includes Node and NodeTranslation entities, slug-based routing, and tools for managing nested content pages.

View on GitHub
Deep Wiki
Context7

Getting Started

First, a reality check: this is a Symfony 2 bundle, not a Laravel package. There is no service provider, no config/node.php, no facade, and no artisan integration. composer require kunstmaan/node-bundle in a Laravel project will resolve against a Symfony 2.3/2.4-era stack and either fail or drag in a decade-old dependency tree. So before anything else, decide whether you are:

  1. Running an actual Symfony app (legacy or maintained) where this bundle belongs, or
  2. Reading this as an architectural reference for a Laravel CMS you are building.

If Symfony: install alongside kunstmaan/admin-bundle and Doctrine, register both bundles in AppKernel, configure kunstmaan_node, and run the bundle's install command. You will need the full Kunstmaan CMS stack for the admin UI to be useful.

Where to look first:

  • Entity/Node.php — the tree node (internal name, timestamps, deletion flag)
  • Entity/NodeTranslation.php — one row per locale (title, slug, online flag)
  • Entity/Page/ — SinglePage / StructurePage (the layout)
  • Entity/PagePart/ — the content blocks (TextPagePart, ImagePagePart, …)
  • NodeManager.php / NodeRepository.php — the service layer you will actually call day-to-day

First use case: define a HomePage as a SinglePage, attach a TextPagePart and an ImagePagePart, create the Node with an English NodeTranslation and a slug, mark it online, and let the bundle's route loader serve /en/home without registering any route by hand.

The mental model is the whole point:

Node (tree position)
 └── NodeTranslation (per locale: title, slug, online)
      └── Page (single or structure)
           └── PageParts (ordered content blocks)

If you're doing this in Laravel, translate that model almost 1:1: Node hasMany NodeTranslation; NodeTranslation morphs to Page; Page hasMany (ordered) PageParts via a pivot or ordered column.

Implementation Patterns

The bundle earns its keep through a small number of conventions. These are the ones you'll touch every day:

1. Never put display fields on Node. Node is structural only. Titles, slugs, and publish state live on NodeTranslation. If you add title to Node you will break locale routing.

2. Page vs PagePart is layout vs content. The Page subclass owns the template/controller; PageParts are ordered blocks attached to the page. Reuse means new page parts, not new page types.

3. Routing is slug-driven. Slugs live on NodeTranslation. The bundle registers a route loader that maps /{slug} to _controller based on the page's controller convention. Do not hand-register routes for content pages — you'll fight the loader.

4. Manage slugs through NodeManager, not by hand. When a title changes, regenerate the slug via the manager/Slugifier service, and handle collisions per locale. Doing this inline in a controller is the most common source of dead links.

5. Use online as your publish flag. It is per-translation, not per-node — which is exactly what you want for "English live, French draft."

A typical Symfony call looks like:

$node = $nodeManager->createNode($locale, $user, 'HomePage', $parent);
$nodeManager->updateNode($node, $request); // title, slug, online, page

6. Extend by adding page parts, not by forking.

# config.yml
kunstmaan_node:
    page_parts:
        - { name: quote, class: Acme\Bundle\AcmeBundle\Entity\PagePart\QuotePagePart }

7. Admin CRUD comes free from kunstmaan/admin-bundle; extend the form type to surface your custom fields.

Laravel port pattern (if that's your goal):

  • nodes (id, parent_id, internal_name, deleted_at)
  • node_translations (node_id, locale, title, slug, online)
  • pages (node_translation_id or polymorphic, type, template)
  • page_parts (page_id, type, position, payload json)
  • A SlugResolver route macro (Route::get('/{slug}', ...)) that resolves slug → translation → page, with locale prefix handling.

This gives you the same flexibility without the Symfony coupling.

Gotchas and Tips

  • It is stale. Last release was 2014, targeting Symfony 2.3/2.4 and Doctrine 2.4. Do not put this in a modern app. Security and PHP-version incompatibility are real, not theoretical.
  • Tiny ecosystem. Packagist reports zero dependents; there is effectively no community, no recent CVE triage, and no one to ask when the route loader misbehaves. Plan to self-support.
  • Doctrine coupling is load-bearing. Ordered page parts rely on Doctrine's ordered collections; porting to Eloquent means re-implementing ordering and re-ordering yourself.
  • Missing translations break routing. A Node without a NodeTranslation in the requested locale has no slug and therefore no route. Always create a default translation.
  • Clear the router cache after adding nodes or page parts. The node route loader caches aggressively; "my new page 404s" is almost always a cache clear away.
  • online is per translation. A node can be published in English and invisible in French. This surprises people during QA.
  • Slug collisions and locale prefixes. Decide your URL scheme up front (/{_locale}/{slug} vs /{slug}) — changing later means rewriting every translation.
  • Page part reordering can silently drop parts if you save the page without persisting the container's order. Test reorder flows explicitly.
  • Extension points that are safe: AbstractPagePart subclasses, SinglePage/StructurePage subclasses, NodeManagerInterface, NodeRepository::getOnlineNodesForLocale(). Subclassing Node itself is possible but brittle — avoid.
  • Debugging: query nodes, node_translations, and the page-part tables directly; inspect the generated route for a slug; check the translation's online value before blaming the controller.
  • For a Laravel-only team: treat this bundle purely as a design reference. The Node → NodeTranslation → Page → PagePart split is a genuinely good CMS model and maps cleanly onto Eloquent — copy the schema, not the code.
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
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