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
pollora-post-types
pollora-post-types
pollora/nectar
claude-code
post-types
wordpress
php-attributes
pollora
cms
discovery

Create and configure WordPress custom post types using Pollora PHP 8 attributes and the discovery system.

Install
php artisan boost:add-skill pollora/nectar

Save this content to: .claude/skills/pollora-post-types/SKILL.md

# Pollora Post Types Development

## When to use this skill
Use this skill when creating, modifying, or configuring WordPress custom post types in a Pollora project.

## Creating a Post Type

Post types are PHP classes decorated with attributes. Place them where the discovery system scans — typically `app/Cms/PostTypes/` in themes or `app/Models/` in modules.

### Basic Post Type

```php
<?php

namespace Theme\MyTheme\Cms\PostTypes;

use Pollora\Attributes\PostType\PostType;
use Pollora\Attributes\PostType\PubliclyQueryable;
use Pollora\Attributes\PostType\HasArchive;
use Pollora\Attributes\PostType\Supports;
use Pollora\Attributes\PostType\MenuIcon;
use Pollora\Attributes\PostType\ShowInRest;

#[PostType('book')]
#[PubliclyQueryable]
#[HasArchive]
#[Supports(['title', 'editor', 'thumbnail', 'excerpt'])]
#[MenuIcon('dashicons-book')]
#[ShowInRest]
class Book
{
}
```

### Post Type with Custom Configuration

Use `configuring()` for dynamic configuration and `withArgs()` for additional arguments:

```php
#[PostType('book')]
#[PubliclyQueryable]
#[HasArchive]
#[Supports(['title', 'editor', 'thumbnail'])]
#[ShowInRest]
class Book
{
    public function configuring(\Pollora\Entity\Domain\Model\PostType $postType): void
    {
        $postType->labels([
            'name' => __('Books', 'my-theme'),
            'singular_name' => __('Book', 'my-theme'),
            'add_new' => __('Add New Book', 'my-theme'),
        ]);
        $postType->rewrite(['slug' => 'library/books']);
    }

public function withArgs(): array
    {
        return [
            'description' => 'Custom book post type for the library',
        ];
    }
}
```

### Available Attributes

| Attribute | Purpose | Example |
|-----------|---------|---------|
| `#[PostType('slug')]` | Register a post type | `#[PostType('book')]` |
| `#[PubliclyQueryable]` | Make queryable on frontend | `#[PubliclyQueryable]` or `#[PubliclyQueryable(false)]` |
| `#[PublicPostType]` | Shorthand for public + queryable | `#[PublicPostType]` |
| `#[HasArchive]` | Enable archive page | `#[HasArchive]` or `#[HasArchive('books')]` |
| `#[Supports([...])]` | Editor features | `#[Supports(['title', 'editor', 'thumbnail'])]` |
| `#[Hierarchical]` | Page-like hierarchy | `#[Hierarchical]` |
| `#[ShowInRest]` | Enable block editor + REST API | `#[ShowInRest]` |
| `#[MenuIcon('..')]` | Admin menu icon | `#[MenuIcon('dashicons-book')]` |
| `#[MenuPosition(n)]` | Admin menu position | `#[MenuPosition(25)]` |
| `#[CapabilityType('..')]` | Custom capability type | `#[CapabilityType('book')]` |
| `#[MapMetaCap]` | Map meta capabilities | `#[MapMetaCap]` |
| `#[Labels(...)]` | Override specific labels | `#[Labels(addNew: 'New Book')]` |
| `#[Description('..')]` | Post type description | `#[Description('Library books')]` |
| `#[Template([...])]` | Default block template | `#[Template([['core/heading'], ['core/paragraph']])]` |
| `#[TemplateLock('..')]` | Lock template | `#[TemplateLock('all')]` |
| `#[AdminCols([...])]` | Custom admin columns | See admin columns section |
| `#[Priority(n)]` | Registration order | `#[Priority(10)]` |

### Admin Columns

```php
#[PostType('book')]
#[AdminCols([
    'title' => ['title_cb' => fn() => 'Book Title'],
    'author' => ['taxonomy' => 'author'],
    'genre' => ['taxonomy' => 'genre'],
    'published' => ['meta_key' => 'published_date', 'date_format' => 'd/m/Y'],
])]
class Book {}
```

### Routing for Post Types

```php
// routes/web.php
Route::wp('single', [BookController::class, 'show']);        // All single posts
Route::wp('archive', [BookController::class, 'index']);      // All archives

// The controller receives WP_Post via type-hint
public function show(\WP_Post $post): \Illuminate\View\View
{
    return view('book.show', compact('post'));
}
```

### Important Notes

- **No manual registration needed** — the discovery system finds and registers post types automatically
- Run `php artisan discovery:clear` after creating a new post type class during development
- Labels are auto-generated from the class name if not specified
- The slug is derived from the `#[PostType('slug')]` attribute parameter
- Custom fields: declare them as `#[Meta]` typed properties on the same class (see the `pollora-typed-meta` skill), not with `register_meta()`
- `#[CapabilityType]` + `#[MapMetaCap]` give the post type its own capabilities, which no role has except the super roles: grant them with `#[GrantsPostType]` (see the `pollora-roles` skill)
- Generate a post type with `php artisan pollora:make:post-type`

Pollora Post Types Development

When to use this skill

Use this skill when creating, modifying, or configuring WordPress custom post types in a Pollora project.

Creating a Post Type

Post types are PHP classes decorated with attributes. Place them where the discovery system scans — typically app/Cms/PostTypes/ in themes or app/Models/ in modules.

Basic Post Type

<?php

namespace Theme\MyTheme\Cms\PostTypes;

use Pollora\Attributes\PostType\PostType;
use Pollora\Attributes\PostType\PubliclyQueryable;
use Pollora\Attributes\PostType\HasArchive;
use Pollora\Attributes\PostType\Supports;
use Pollora\Attributes\PostType\MenuIcon;
use Pollora\Attributes\PostType\ShowInRest;

#[PostType('book')]
#[PubliclyQueryable]
#[HasArchive]
#[Supports(['title', 'editor', 'thumbnail', 'excerpt'])]
#[MenuIcon('dashicons-book')]
#[ShowInRest]
class Book
{
}

Post Type with Custom Configuration

Use configuring() for dynamic configuration and withArgs() for additional arguments:

#[PostType('book')]
#[PubliclyQueryable]
#[HasArchive]
#[Supports(['title', 'editor', 'thumbnail'])]
#[ShowInRest]
class Book
{
    public function configuring(\Pollora\Entity\Domain\Model\PostType $postType): void
    {
        $postType->labels([
            'name' => __('Books', 'my-theme'),
            'singular_name' => __('Book', 'my-theme'),
            'add_new' => __('Add New Book', 'my-theme'),
        ]);
        $postType->rewrite(['slug' => 'library/books']);
    }

public function withArgs(): array
    {
        return [
            'description' => 'Custom book post type for the library',
        ];
    }
}

Available Attributes

Attribute Purpose Example
#[PostType('slug')] Register a post type #[PostType('book')]
#[PubliclyQueryable] Make queryable on frontend #[PubliclyQueryable] or #[PubliclyQueryable(false)]
#[PublicPostType] Shorthand for public + queryable #[PublicPostType]
#[HasArchive] Enable archive page #[HasArchive] or #[HasArchive('books')]
#[Supports([...])] Editor features #[Supports(['title', 'editor', 'thumbnail'])]
#[Hierarchical] Page-like hierarchy #[Hierarchical]
#[ShowInRest] Enable block editor + REST API #[ShowInRest]
#[MenuIcon('..')] Admin menu icon #[MenuIcon('dashicons-book')]
#[MenuPosition(n)] Admin menu position #[MenuPosition(25)]
#[CapabilityType('..')] Custom capability type #[CapabilityType('book')]
#[MapMetaCap] Map meta capabilities #[MapMetaCap]
#[Labels(...)] Override specific labels #[Labels(addNew: 'New Book')]
#[Description('..')] Post type description #[Description('Library books')]
#[Template([...])] Default block template #[Template([['core/heading'], ['core/paragraph']])]
#[TemplateLock('..')] Lock template #[TemplateLock('all')]
#[AdminCols([...])] Custom admin columns See admin columns section
#[Priority(n)] Registration order #[Priority(10)]

Admin Columns

#[PostType('book')]
#[AdminCols([
    'title' => ['title_cb' => fn() => 'Book Title'],
    'author' => ['taxonomy' => 'author'],
    'genre' => ['taxonomy' => 'genre'],
    'published' => ['meta_key' => 'published_date', 'date_format' => 'd/m/Y'],
])]
class Book {}

Routing for Post Types

// routes/web.php
Route::wp('single', [BookController::class, 'show']);        // All single posts
Route::wp('archive', [BookController::class, 'index']);      // All archives

// The controller receives WP_Post via type-hint
public function show(\WP_Post $post): \Illuminate\View\View
{
    return view('book.show', compact('post'));
}

Important Notes

  • No manual registration needed — the discovery system finds and registers post types automatically
  • Run php artisan discovery:clear after creating a new post type class during development
  • Labels are auto-generated from the class name if not specified
  • The slug is derived from the #[PostType('slug')] attribute parameter
  • Custom fields: declare them as #[Meta] typed properties on the same class (see the pollora-typed-meta skill), not with register_meta()
  • #[CapabilityType] + #[MapMetaCap] give the post type its own capabilities, which no role has except the super roles: grant them with #[GrantsPostType] (see the pollora-roles skill)
  • Generate a post type with php artisan pollora:make:post-type
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect