Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed
- `pollora-modules` skill rewritten for the modules work of the next framework release: `pollora:make:module` and its opt-in layers, `--module` generators, `@pollora/vite-config` and the `module.<name>` asset container, `pollora:module:frontend`, Plugins › Modules, activation connectors and `config/modules.php`, locked modules, Composer modules and `pollora:module:outdated`. Its post type example used `Pollora\Attributes\PostType\PostType`, which does not exist: it is `Pollora\Attributes\PostType`
- Core guidelines list `pollora:make:module`, `pollora:module:connector|frontend|outdated` and `--module` on `pollora:make:block`

## [1.5.0] - 2026-10-07

### Added
Expand Down
6 changes: 4 additions & 2 deletions resources/boost/guidelines/core.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,17 @@ class InternalHelper {}
- **Which template answered?** With `WP_DEBUG` on, every page carries `<!-- pollora:template="single" path="themes/x/resources/views/single.blade.php" -->` (hierarchy responses only, not `Route::wp()` or Laravel routes)
- **Meta, roles, bindings**: declare a meta with `#[Meta]` on a typed property rather than calling `register_meta()` and casting `get_post_meta()` by hand (`pollora-typed-meta` skill); declare roles with `#[Role]` / `#[ModifyRole]` rather than `add_role()`, and check a capability (`can:`, `@can`) rather than a role (`pollora-roles` skill); bind core blocks to a `#[BlockBinding]` source or `pollora/post-meta` before writing a custom block (`pollora-block-bindings` skill)
- **CSRF**: WordPress endpoints are excluded from Laravel CSRF — WordPress uses its own nonce system
- **Modules**: Use `nwidart/laravel-modules` for large projects — discovery works inside modules automatically
- **Modules**: Use `nwidart/laravel-modules` for large projects — `pollora:make:module` creates a lean module (no provider: discovery works inside modules), built with `@pollora/vite-config`; switch modules with `module:enable|disable` or Plugins › Modules (see the `pollora-modules` skill)

### Available Artisan Commands

- `pollora:install` — Full project installation (`--theme` to pick the generated theme; runs without interaction)
- `pollora:env:setup` — Install and configure WordPress
- `pollora:make:theme` / `pollora:theme:delete` / `pollora:theme:status` — Themes
- `pollora:make:plugin` / `pollora:plugin:list` / `pollora:plugin:status` — Plugins
- `pollora:make:block` — Generate a Gutenberg block (dynamic Blade by default, `--static` for `save.jsx`)
- `pollora:make:block` — Generate a Gutenberg block (dynamic Blade by default, `--static` for `save.jsx`; `--theme`, `--plugin` or `--module`)
- `pollora:make:module` — Generate a lean Laravel module (`--provider`, `--routes`, `--api`, `--config`, `--database`, `--tests`, `--full`, `--no-assets`)
- `pollora:module:connector` / `pollora:module:frontend` / `pollora:module:outdated` — Where module states live (and `--import` into another connector), move a module onto the template's Vite build, check Composer modules for updates
- `pollora:make:post-type` / `pollora:make:taxonomy` — Generate post type and taxonomy classes
- `pollora:make:action` / `pollora:make:filter` / `pollora:make:hook` — Generate hook classes (`pollora:make:action --async` adds `#[Async]`)
- `pollora:async:list` — Asynchronous actions, their driver and options, and where the default driver comes from
Expand Down
155 changes: 62 additions & 93 deletions resources/boost/skills/pollora-modules/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,137 +1,106 @@
---
name: pollora-modules
description: Develop Laravel Modules (nwidart) within Pollora projects with auto-discovery of post types, taxonomies, hooks, and more.
description: Create and run Laravel Modules (nwidart) in Pollora projects — lean scaffold, shared Vite build, discovery, activation connectors, the Plugins › Modules view and update checks.
---

# Pollora Module Development

## When to use this skill
Use this skill when creating or working with Laravel Modules (nwidart/laravel-modules) in a Pollora project to organize large applications into self-contained feature packages.
Use this skill when creating or working with Laravel Modules (nwidart/laravel-modules) in a Pollora project: a module groups a project's own features (post types, hooks, blocks, routes…) and can be switched on and off.

## Creating a Module

```bash
php artisan module:make Portfolio
php artisan pollora:make:module Portfolio
```

## Module Structure
It downloads the Pollora/module-default template, enables the module, runs `composer dump-autoload` and npm. The default module has **no service provider**: classes in `app/` declared with attributes are discovered.

```
Modules/Portfolio/
├── app/
│ ├── Http/
│ │ └── Controllers/
│ ├── Models/
│ │ ├── Project.php # Eloquent model + #[PostType] attribute
│ │ └── ProjectCategory.php # #[Taxonomy] attribute
│ ├── Hooks/
│ │ └── ProjectHooks.php # #[Action] / #[Filter] attributes
│ ├── Schedule/
│ │ └── ProjectSync.php # #[Schedule] attribute
│ ├── Api/
│ │ └── ProjectAPI.php # #[WpRestRoute] attribute
│ └── Providers/
│ └── PortfolioServiceProvider.php
├── config/
│ └── config.php
├── database/
│ ├── migrations/
│ └── seeders/
├── resources/
│ ├── assets/
│ └── views/
├── routes/
│ ├── api.php
│ └── web.php
├── tests/
├── composer.json
├── module.json
└── package.json
├── app/Cms/Hooks/PortfolioHooks.php # #[Action] example, discovered
├── resources/assets/app.js, app.css # Tailwind CSS v4 (no preflight)
├── resources/views/blocks/ # Gutenberg blocks, registered by Pollora
├── composer.json # PSR-4 Modules\Portfolio\ → app/
├── module.json # "providers": []
├── package.json
└── vite.config.js # @pollora/vite-config, type "module"
```

## Auto-Discovery in Modules
Laravel layers are opt-in: `--provider`, `--routes` (implies `--provider`), `--api` (implies `--routes`), `--config`, `--database`, `--tests` (Pest), `--full`; `--no-assets` for a PHP-only module; `--no-enable`, `--no-npm`, `--offline`. nwidart's `module:make` writes the same lean module unless the project published nwidart's own `config/modules.php`.

All Pollora attributes work inside modules without any additional configuration:
Generate into a module with `--module` — the class lands where nwidart found the module, under the namespace its `composer.json` maps onto `app/`:

```bash
php artisan pollora:make:post-type Project --module=Portfolio
php artisan pollora:make:block project-card --module=Portfolio # block "portfolio/project-card"
```

## Discovery

```php
// Modules/Portfolio/app/Models/Project.php
namespace Modules\Portfolio\Models;
// Modules/Portfolio/app/Cms/PostTypes/Project.php
namespace Modules\Portfolio\Cms\PostTypes;

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

#[PostType('project')]
#[PubliclyQueryable]
#[HasArchive]
#[ShowInRest]
class Project
{
public function configuring(\Pollora\Entity\Domain\Model\PostType $postType): void
{
$postType->labels([
'name' => __('Projects', 'portfolio'),
'singular_name' => __('Project', 'portfolio'),
]);
}
}
class Project {}
```

The discovery system scans module directories automatically — no manual registration needed.
Post types, taxonomies, `#[Action]`/`#[Filter]`, `#[WpRestRoute]`, `#[Schedule]` and blocks in `resources/views/blocks` are discovered in every enabled module. Run `php artisan discovery:clear` after adding such a class when the discovery cache is on.

## Module Routes with WordPress Conditions
## Frontend

```php
// Modules/Portfolio/routes/web.php
use Illuminate\Support\Facades\Route;
use Modules\Portfolio\Http\Controllers\ProjectController;
```js
// Modules/Portfolio/vite.config.js
import { defineConfig } from 'vite';
import pollora from '@pollora/vite-config';

Route::wp('single', [ProjectController::class, 'show']);
Route::wp('archive', [ProjectController::class, 'index']);
export default defineConfig({
plugins: [pollora({ type: 'module', name: 'portfolio' })],
});
```

## Module Management
Builds into `public/build/module/portfolio` (hot file `public/portfolio.hot`, dev server port 5175 or `VITE_PORT`). Every enabled module with a `vite.config.js` gets the `module.<kebab-name>` asset container:

```bash
php artisan module:make Portfolio # Create
php artisan module:enable Portfolio # Enable
php artisan module:disable Portfolio # Disable
php artisan module:list # List all modules
php artisan module:migrate Portfolio # Run migrations
php artisan module:seed Portfolio # Run seeders
```php
Asset::add('portfolio/app', 'app.js')->container('module.portfolio')->toFrontend()->useVite();
```

## Module Service Provider
A module made by nwidart's stock `module:make` builds into `public/build-<lower>`, where Pollora never looks: `php artisan pollora:module:frontend Portfolio` replaces its `package.json` and `vite.config.js` (keeping `.bak` copies).

```php
namespace Modules\Portfolio\Providers;

use Illuminate\Support\ServiceProvider;

class PortfolioServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->mergeConfigFrom(
module_path('Portfolio', 'config/config.php'),
'portfolio'
);
}

public function boot(): void
{
$this->loadMigrationsFrom(module_path('Portfolio', 'database/migrations'));
$this->loadViewsFrom(module_path('Portfolio', 'resources/views'), 'portfolio');
}
}
## Enabling and Disabling

```bash
php artisan module:enable Portfolio
php artisan module:disable Portfolio
```

Also from **Plugins › Modules** in wp-admin (capability `activate_plugins`). A change applies from the next request. `MODULES_ADMIN_TOGGLE=false` turns the admin switches off.

Where the state lives is a **connector**, chosen in a published `config/modules.php` (`php artisan vendor:publish --tag=pollora-modules`) — nwidart reads its activator before any provider, so never configure it from a provider:

- `json` (default): `modules_statuses.json` — reset by a deployment unless committed
- `database`: the `pollora_modules` WordPress option (survives deployments)
- `config`: `MODULES_ENABLED` / `MODULES_DISABLED`, read-only
- your own: a `ModuleStateConnector` class in `connectors.<name>.class`, or `ModuleConnectors::extend()` in `bootstrap/app.php`

`MODULES_LOCKED_ENABLED` / `MODULES_LOCKED_DISABLED` force states. Switch connector with `php artisan pollora:module:connector database --import`, then `MODULES_CONNECTOR=database`. Switches fire `ModuleEnabled` / `ModuleDisabled`.

## Composer modules and updates

A module installed with Composer lands in `Modules/` through the skeleton's `installer-paths` rule and carries its package version; local modules have none. `php artisan pollora:module:outdated` checks for newer releases (also shown in Plugins › Modules, Site Health and `pollora:status`).

## Important Notes

- **Auto-discovery works across modules** — post types, taxonomies, hooks, schedules, and REST routes defined in modules are discovered automatically
- Module views are namespaced: `view('portfolio::project.show')`
- Each module can have its own `composer.json` for module-specific dependencies
- Run `php artisan discovery:clear` after adding attribute-decorated classes in modules
- Modules can define their own Vite config and assets
- Use `module_path('ModuleName', 'relative/path')` to reference module files
- Prefer attributes in `app/` over a service provider; add one with `--provider` only when the module registers services, config or routes
- Module views are namespaced by the lower-case module name: `view('portfolio::project.show')` (with `--provider`)
- `pollora:doctor` checks module builds and activation (fallback connector, states for missing modules, stale caches, ignored `MODULES_*` settings)
- Use `module_path('Portfolio', 'relative/path')` to reference module files
Loading