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
Agents
banulakwin/laravel-page-builder
claude-code
cursor
cms
laravel
content-management
Repeater
page-builder
dynamic-fields

Use this file when adding or changing CMS-backed pages in any Laravel app that uses this package.

Install
php artisan boost:add-skill banulakwin/laravel-page-builder

Save this content to: AGENTS.md

# Agent guide: `banulakwin/laravel-page-builder`

Use this file when adding or changing CMS-backed pages in **any** Laravel app that uses this package.

## Mental model

- **Section order on the site** follows your Inertia page and component tree (and how you call `getPage()` / pass `cms`), not a `sort_order` field in CMS definitions.
- **Files** (`app/Cms/Pages/{key}.php` by default) = schema + **default values** for sync only.
- **Database** (`page_contents`) = runtime source of truth for content (via `PageService::getPage()` / `page()` helper).
- **`php artisan page:sync`** = insert **missing** `(page, section, key)` rows with defaults; **never** overwrites existing values (including after soft-delete restore).
- **`config/pages.php` → `common_sections`** = shared sections synced under reserved page key **`__common`**; merged into **`PageService::getPage()`** only (not **`getSection()`**).

## Adding a new static page (e.g. `promotions`)

1. Pick a **page key** (slug) used in your app and controller: e.g. `promotions`.
2. Create **`app/Cms/Pages/promotions.php`** returning:

```php
<?php

declare(strict_types=1);

return [
    'sections' => [
        'promotions-header' => [
            'fields' => [
                'title' => ['type' => 'text', 'default' => 'Promotions'],
                'subtitle' => ['type' => 'textarea', 'default' => ''],
            ],
        ],
    ],
];
```

The **section array key** (`promotions-header`) is the section slug at runtime—do **not** duplicate `slug` or `name` as fields unless you need them as editable content. With **`@banulakwin/inertia-page-builder`**, `parseCmsSection(cms, 'promotions-header')` sets `slug` from that key and returns dynamic `fields` for any section shape. Pass optional **field definitions** as a third argument when you need `label` / `meta` / `disk` / `path` on the client (use `PageRegistry::publicFieldMetadataBySection($pageKey)[$sectionSlug]` — no `default` or `rules` in that export).

3. Run **`php artisan page:sync`**.
4. Add **`promotions`** to **`config/pages.php`** → **`catalog`** with **`['enabled' => true]`** (or boolean shorthand) when you use catalog gating.
5. In your controller, pass **`PageService::getPageWhenEnabled('promotions')`** (or **`page_when_enabled('promotions')`**) as the **`cms`** prop, **or** attach route middleware **`page-builder:promotions`** and use **`getPage()`** if you prefer the check in the router only.

## Adding a section to an existing page file

1. Open **`app/Cms/Pages/{pageKey}.php`**.
2. Add a new key under **`sections`** with a **`fields`** array.
3. Run **`php artisan page:sync`** so new keys get default rows.

## Validation errors

`PageRegistry::validate()` throws `InvalidArgumentException` if:

- the file does not return an array;
- there is no top-level **`sections`** array;
- any section omits **`fields`**.

## Optional field metadata

Fields may include **`label`**, **`rules`** (string rules array), **`meta`** (free-form array), **`disk`**, and **`path`** (storage directory on that disk). They are optional; `banulakwin/filament-cms-admin` and HTTP saves via **`FieldResolver`** honor `disk` / `path` for images when set.

## Optional cache

If `page-builder.registry_cache_ttl` > 0, **`page:sync`** clears the registry cache so new files apply immediately.

## Do not

- Load page definition files on normal web requests (use `PageService` / DB only).
- Use `updateOrCreate` in custom sync code if you intend to preserve Filament or user edits; use the same semantics as this package's sync command.
- Commit generated files (`vendor/`, `composer.lock`, `coverage/`).

## Testing & Quality

```bash
composer test          # PHPUnit
composer pint          # Laravel Pint code style fix
composer pint:check    # Pint check only (no fix)
composer phpstan       # PHPStan level max on src/
composer quality       # All: pint + phpstan + test
```

## CI

GitHub Actions runs tests, Pint, and PHPStan on push/PR (`.github/workflows/tests.yml`).

Agent guide: banulakwin/laravel-page-builder

Use this file when adding or changing CMS-backed pages in any Laravel app that uses this package.

Mental model

  • Section order on the site follows your Inertia page and component tree (and how you call getPage() / pass cms), not a sort_order field in CMS definitions.
  • Files (app/Cms/Pages/{key}.php by default) = schema + default values for sync only.
  • Database (page_contents) = runtime source of truth for content (via PageService::getPage() / page() helper).
  • php artisan page:sync = insert missing (page, section, key) rows with defaults; never overwrites existing values (including after soft-delete restore).
  • config/pages.php → common_sections = shared sections synced under reserved page key __common; merged into PageService::getPage() only (not getSection()).

Adding a new static page (e.g. promotions)

  1. Pick a page key (slug) used in your app and controller: e.g. promotions.
  2. Create app/Cms/Pages/promotions.php returning:
<?php

declare(strict_types=1);

return [
    'sections' => [
        'promotions-header' => [
            'fields' => [
                'title' => ['type' => 'text', 'default' => 'Promotions'],
                'subtitle' => ['type' => 'textarea', 'default' => ''],
            ],
        ],
    ],
];

The section array key (promotions-header) is the section slug at runtime—do not duplicate slug or name as fields unless you need them as editable content. With @banulakwin/inertia-page-builder, parseCmsSection(cms, 'promotions-header') sets slug from that key and returns dynamic fields for any section shape. Pass optional field definitions as a third argument when you need label / meta / disk / path on the client (use PageRegistry::publicFieldMetadataBySection($pageKey)[$sectionSlug] — no default or rules in that export).

  1. Run php artisan page:sync.
  2. Add promotions to config/pages.php → catalog with ['enabled' => true] (or boolean shorthand) when you use catalog gating.
  3. In your controller, pass PageService::getPageWhenEnabled('promotions') (or page_when_enabled('promotions')) as the cms prop, or attach route middleware page-builder:promotions and use getPage() if you prefer the check in the router only.

Adding a section to an existing page file

  1. Open app/Cms/Pages/{pageKey}.php.
  2. Add a new key under sections with a fields array.
  3. Run php artisan page:sync so new keys get default rows.

Validation errors

PageRegistry::validate() throws InvalidArgumentException if:

  • the file does not return an array;
  • there is no top-level sections array;
  • any section omits fields.

Optional field metadata

Fields may include label, rules (string rules array), meta (free-form array), disk, and path (storage directory on that disk). They are optional; banulakwin/filament-cms-admin and HTTP saves via FieldResolver honor disk / path for images when set.

Optional cache

If page-builder.registry_cache_ttl > 0, page:sync clears the registry cache so new files apply immediately.

Do not

  • Load page definition files on normal web requests (use PageService / DB only).
  • Use updateOrCreate in custom sync code if you intend to preserve Filament or user edits; use the same semantics as this package's sync command.
  • Commit generated files (vendor/, composer.lock, coverage/).

Testing & Quality

composer test          # PHPUnit
composer pint          # Laravel Pint code style fix
composer pint:check    # Pint check only (no fix)
composer phpstan       # PHPStan level max on src/
composer quality       # All: pint + phpstan + test

CI

GitHub Actions runs tests, Pint, and PHPStan on push/PR (.github/workflows/tests.yml).

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console