The Neuron MVC component provides code generation commands for scaffolding controllers, events, listeners, and jobs. These generators create production-ready code following framework conventions and best practices.
All scaffolding commands are provided by the neuron-php/scaffolding component.
Generate a complete CRUD scaffold including controller, views, routes, and database migration. This is the fastest way to prototype new resources, similar to Rails' scaffold generator.
./vendor/bin/neuron scaffold:generate <name> [options]
name (required): Resource name in PascalCase (e.g., Post, User, Admin/Article)--fields=<definitions>: Field definitions for migration (e.g., "title:string,body:text,published:boolean")--api: Generate API controller (JSON responses, no views)--no-migration: Skip migration generation--namespace=<namespace>: Controller namespace (default: App\Controllers)--filter=<filter>: Route filter to apply (e.g., auth)--force: Overwrite existing filesWhen using --fields, the following types are supported:
string, varchar → varchar(255)text → textinteger, int → integerbiginteger, bigint → bigintegerfloat → floatdecimal → decimalboolean, bool → booleandate → datedatetime, timestamp → datetimetime → timejson → json# Generate complete scaffold with fields
./vendor/bin/neuron scaffold:generate Post --fields="title:string,body:text,published:boolean"
# Generate API-only scaffold (no views)
./vendor/bin/neuron scaffold:generate Article --api --fields="title:string,content:text"
# Generate admin scaffold with auth filter
./vendor/bin/neuron scaffold:generate Admin/Post --fields="title:string,body:text" --filter=auth
# Generate without migration
./vendor/bin/neuron scaffold:generate Comment --no-migration
# Generate with custom namespace
./vendor/bin/neuron scaffold:generate Post --namespace="MyApp\\Controllers" --fields="title:string"
Migration: db/migrate/YYYYMMDDHHMMSS_create_posts_table.php
<?php
use Phinx\Migration\AbstractMigration;
class CreatePostsTable extends AbstractMigration
{
public function change(): void
{
$table = $this->table( 'posts' );
$table->addColumn( 'title', 'string', ['limit' => 255] );
$table->addColumn( 'body', 'text', ['null' => true] );
$table->addColumn( 'published', 'boolean', ['default' => false] );
$table->addTimestamps()
->create();
}
}
Controller: app/Controllers/PostController.php
Contains RESTful methods with database integration points:
index(): Display listingcreate(): Show create formstore(): Process create submissionedit($id): Show edit formupdate($id): Process update submissiondestroy($id): Delete itemViews (unless --api):
resources/views/posts/
├── index.php # Listing page
├── create.php # Create form
└── edit.php # Edit form
Routes: Appended to config/routes.yaml
posts_index:
method: GET
route: /posts
controller: App\Controllers\PostController@index
posts_create:
method: GET
route: /posts/create
controller: App\Controllers\PostController@create
posts_store:
method: POST
route: /posts
controller: App\Controllers\PostController@store
posts_edit:
method: GET
route: /posts/:id/edit
controller: App\Controllers\PostController@edit
posts_update:
method: PUT
route: /posts/:id
controller: App\Controllers\PostController@update
posts_destroy:
method: DELETE
route: /posts/:id
controller: App\Controllers\PostController@destroy
After generating a scaffold:
Run the migration:
./vendor/bin/neuron db:migrate:run
Implement repository logic: Replace TODO comments in controller with actual repository calls
Customize views: Update Bootstrap 5 views to match your design
Add validation: Implement input validation using Neuron's validation component
Add authorization: Apply route filters for authentication/authorization
When using --api, controllers return JSON responses:
public function index( array $Parameters ): string
{
// TODO: Fetch posts
$posts = [];
$this->renderJson( [
'success' => true,
'data' => $posts
] );
}
public function store( array $Parameters ): string
{
// TODO: Validate and save post
$post = null;
$this->renderJson( [
'success' => true,
'data' => $post
], 201 );
}
neuron-php/mvc componentcomposer require neuron-php/mvcThe scaffold generator follows consistent naming conventions:
Post (singular, PascalCase)posts (plural, lowercase snake_case)CreatePostsTable (PascalCase)PostControllerposts_index, posts_create, etc.resources/views/posts/Generate RESTful controllers with Bootstrap 5 views and route definitions.
./vendor/bin/neuron controller:generate <name> [options]
name (required): Controller name in PascalCase without "Controller" suffix (e.g., Post, User, Article)--no-views: Skip view generation--no-routes: Skip route generation--api: Generate API controller (JSON responses, no views)# Generate complete RESTful controller
./vendor/bin/neuron controller:generate Post
# Generate controller without views
./vendor/bin/neuron controller:generate Post --no-views
# Generate API controller
./vendor/bin/neuron controller:generate Post --api
# Generate controller without routes
./vendor/bin/neuron controller:generate Post --no-routes
Controller: app/Controllers/PostController.php
Contains RESTful methods:
index(): Display listingcreate(): Show create formstore(): Process create submissionshow($id): Display single itemedit($id): Show edit formupdate($id): Process update submissiondestroy($id): Delete itemViews (if not using --no-views):
resources/views/post/
├── index.php # Listing page
├── create.php # Create form
├── edit.php # Edit form
└── show.php # Detail view
Views are generated with Bootstrap 5 styling and responsive design.
Routes (if not using --no-routes):
Appends to config/routes.yaml:
# Post routes
posts_index:
method: GET
route: /posts
controller: App\Controllers\PostController@index
posts_create:
method: GET
route: /posts/create
controller: App\Controllers\PostController@create
posts_store:
method: POST
route: /posts
controller: App\Controllers\PostController@store
posts_show:
method: GET
route: /posts/{id}
controller: App\Controllers\PostController@show
posts_edit:
method: GET
route: /posts/{id}/edit
controller: App\Controllers\PostController@edit
posts_update:
method: PUT
route: /posts/{id}
controller: App\Controllers\PostController@update
posts_destroy:
method: DELETE
route: /posts/{id}
controller: App\Controllers\PostController@destroy
<?php
namespace App\Controllers;
use Neuron\Mvc\Controllers\Base;
class PostController extends Base
{
public function index()
{
// TODO: Fetch posts from repository
$posts = [];
$this->renderHtml( 'post/index', [
'posts' => $posts
] );
}
public function create()
{
$this->renderHtml( 'post/create' );
}
public function store()
{
// TODO: Validate and save post
// Redirect to posts_index on success
$this->redirect( '/posts' );
}
public function show( int $id )
{
// TODO: Fetch post by ID
$post = null;
$this->renderHtml( 'post/show', [
'post' => $post
] );
}
public function edit( int $id )
{
// TODO: Fetch post by ID
$post = null;
$this->renderHtml( 'post/edit', [
'post' => $post
] );
}
public function update( int $id )
{
// TODO: Validate and update post
// Redirect to posts_show on success
$this->redirect( "/posts/{$id}" );
}
public function destroy( int $id )
{
// TODO: Delete post
// Redirect to posts_index
$this->redirect( '/posts' );
}
}
When using --api flag:
<?php
namespace App\Controllers;
use Neuron\Mvc\Controllers\Base;
class PostController extends Base
{
public function index()
{
// TODO: Fetch posts
$posts = [];
$this->renderJson( [
'success' => true,
'data' => $posts
] );
}
public function store()
{
// TODO: Validate and save post
$post = null;
$this->renderJson( [
'success' => true,
'data' => $post
], 201 );
}
public function show( int $id )
{
// TODO: Fetch post by ID
$post = null;
$this->renderJson( [
'success' => true,
'data' => $post
] );
}
public function update( int $id )
{
// TODO: Validate and update post
$post = null;
$this->renderJson( [
'success' => true,
'data' => $post
] );
}
public function destroy( int $id )
{
// TODO: Delete post
$this->renderJson( [
'success' => true,
'message' => 'Post deleted'
] );
}
}
Generate event classes for the event system.
./vendor/bin/neuron event:generate <name>
name (required): Event name in PascalCase (e.g., UserCreated, PostPublished)# Generate event for user creation
./vendor/bin/neuron event:generate UserCreated
# Generate event for post publishing
./vendor/bin/neuron event:generate PostPublished
Event Class: app/Events/UserCreated.php
<?php
namespace App\Events;
class UserCreated
{
private $user;
public function __construct( $user )
{
$this->_user = $user;
}
public function getUser()
{
return $this->_user;
}
}
use App\Events\UserCreated;
use Neuron\Patterns\Registry;
// Dispatch event
$event = new UserCreated( $user );
Registry::getInstance()->get( 'EventEmitter' )->emit( $event );
Generate event listener classes with automatic registration in config/event-listeners.yaml.
./vendor/bin/neuron listener:generate <name> <event>
name (required): Listener name in PascalCase (e.g., SendWelcomeEmail, LogUserActivity)event (required): Fully qualified event class name (e.g., App\Events\UserCreated)# Generate listener for UserCreated event
./vendor/bin/neuron listener:generate SendWelcomeEmail App\\Events\\UserCreated
# Generate listener for PostPublished event
./vendor/bin/neuron listener:generate NotifySubscribers App\\Events\\PostPublished
Listener Class: app/Listeners/SendWelcomeEmail.php
<?php
namespace App\Listeners;
use App\Events\UserCreated;
class SendWelcomeEmail
{
public function handle( UserCreated $event )
{
$user = $event->getUser();
// TODO: Send welcome email to user
}
}
Event Registration:
Automatically appends to config/event-listeners.yaml:
App\Events\UserCreated:
- App\Listeners\SendWelcomeEmail
Generate job classes for background processing with optional scheduling.
./vendor/bin/neuron job:generate <name> [options]
name (required): Job name in PascalCase (e.g., ProcessReports, CleanupOldLogs)--cron=<expression>: Add job to schedule with cron expressionStandard cron syntax:
* * * * *
│ │ │ │ │
│ │ │ │ └─── Day of week (0-6, Sunday=0)
│ │ │ └───── Month (1-12)
│ │ └─────── Day of month (1-31)
│ └───────── Hour (0-23)
└─────────── Minute (0-59)
Common expressions:
* * * * *: Every minute0 * * * *: Every hour0 0 * * *: Daily at midnight0 0 * * 0: Weekly on Sunday0 0 1 * *: Monthly on first day# Generate basic job
./vendor/bin/neuron job:generate ProcessReports
# Generate job with daily schedule
./vendor/bin/neuron job:generate CleanupOldLogs --cron="0 2 * * *"
# Generate job with hourly schedule
./vendor/bin/neuron job:generate SyncData --cron="0 * * * *"
Job Class: app/Jobs/ProcessReports.php
<?php
namespace App\Jobs;
use Neuron\Jobs\Job;
class ProcessReports extends Job
{
public function handle()
{
// TODO: Implement job logic
}
}
Schedule Configuration (if using --cron):
Appends to config/schedule.yaml:
jobs:
- class: App\Jobs\CleanupOldLogs
schedule: "0 2 * * *"
description: "Clean up old log files daily at 2 AM"
Jobs can be executed in three ways:
1. Scheduled Execution:
Jobs with cron schedules run automatically via scheduler:
./vendor/bin/neuron jobs:run
2. Queue Dispatch:
use App\Jobs\ProcessReports;
use function Neuron\Jobs\dispatch;
dispatch( new ProcessReports(), ['user_id' => 123]);
3. Immediate Execution:
use App\Jobs\ProcessReports;
use function Neuron\Jobs\dispatchNow;
dispatchNow( new ProcessReports(), ['user_id' => 123]);
All generators use stub templates located in:
vendor/neuron-php/mvc/src/Mvc/Cli/Commands/Generate/stubs/
To customize generated code:
--template option to reference custom stubExample:
./vendor/bin/neuron controller:generate Post --template=stubs/custom-controller.stub
Generated code uses namespaces based on project structure:
App\ControllersApp\EventsApp\ListenersApp\JobsTo use different namespaces, modify generated files after creation.
Controllers:
Post, User, ArticleBlogPost, UserProfileEvents:
UserCreated, PostPublished, OrderShippedPaymentProcessed, EmailVerifiedListeners:
SendWelcomeEmail, LogActivity, UpdateCacheNotifyAdminOfNewUser vs NotifyJobs:
ProcessReports, CleanupLogs, SyncDataGenerateMonthlyReport vs GenerateAfter generating code:
Before generating:
Generators refuse to overwrite existing files. Solutions:
If generated routes conflict with existing routes:
config/routes.yamlEnsure write permissions for directories:
chmod 755 app/Controllers
chmod 755 app/Events
chmod 755 app/Listeners
chmod 755 app/Jobs
chmod 644 config/routes.yaml
chmod 644 config/event-listeners.yaml
chmod 644 config/schedule.yaml