Code Scaffolding Reference

Overview

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.

Available Generators

All scaffolding commands are provided by the neuron-php/scaffolding component.

scaffold:generate

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.

Usage

./vendor/bin/neuron scaffold:generate <name> [options]

Arguments

Options

Field Type Mapping

When using --fields, the following types are supported:

Examples

# 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"

Generated Files

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:

Views (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

Next Steps

After generating a scaffold:

  1. Run the migration:

    ./vendor/bin/neuron db:migrate:run
    
  2. Implement repository logic: Replace TODO comments in controller with actual repository calls

  3. Customize views: Update Bootstrap 5 views to match your design

  4. Add validation: Implement input validation using Neuron's validation component

  5. Add authorization: Apply route filters for authentication/authorization

API Scaffold Example

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 );
}

Requirements

Naming Conventions

The scaffold generator follows consistent naming conventions:

controller:generate

Generate RESTful controllers with Bootstrap 5 views and route definitions.

Usage

./vendor/bin/neuron controller:generate <name> [options]

Arguments

Options

Examples

# 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

Generated Files

Controller: app/Controllers/PostController.php

Contains RESTful methods:

Views (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

Generated Controller Structure

<?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' );
    }
}

API Controller Structure

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'
        ] );
    }
}

event:generate

Generate event classes for the event system.

Usage

./vendor/bin/neuron event:generate <name>

Arguments

Examples

# Generate event for user creation
./vendor/bin/neuron event:generate UserCreated

# Generate event for post publishing
./vendor/bin/neuron event:generate PostPublished

Generated Files

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;
    }
}

Usage Example

use App\Events\UserCreated;
use Neuron\Patterns\Registry;

// Dispatch event
$event = new UserCreated( $user );
Registry::getInstance()->get( 'EventEmitter' )->emit( $event );

listener:generate

Generate event listener classes with automatic registration in config/event-listeners.yaml.

Usage

./vendor/bin/neuron listener:generate <name> <event>

Arguments

Examples

# 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

Generated Files

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

job:generate

Generate job classes for background processing with optional scheduling.

Usage

./vendor/bin/neuron job:generate <name> [options]

Arguments

Options

Cron Expression Format

Standard 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:

Examples

# 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 * * * *"

Generated Files

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"

Job Usage

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]);

Customization

Custom Stubs

All generators use stub templates located in:

vendor/neuron-php/mvc/src/Mvc/Cli/Commands/Generate/stubs/

To customize generated code:

  1. Copy stub files to your project
  2. Modify as needed
  3. Use --template option to reference custom stub

Example:

./vendor/bin/neuron controller:generate Post --template=stubs/custom-controller.stub

Namespace Customization

Generated code uses namespaces based on project structure:

To use different namespaces, modify generated files after creation.

Best Practices

Naming Conventions

Controllers:

Events:

Listeners:

Jobs:

Post-Generation Tasks

After generating code:

  1. Implement logic: Replace TODO comments with actual implementation
  2. Add validation: Implement input validation in controllers
  3. Add authorization: Protect routes with authentication filters
  4. Add tests: Create unit tests for generated classes
  5. Review routes: Verify route definitions meet requirements

Avoiding Conflicts

Before generating:

  1. Check for existing files: Generators will not overwrite existing files
  2. Review routes: Ensure route names don't conflict
  3. Verify namespaces: Confirm namespace structure matches project

Troubleshooting

File Already Exists

Generators refuse to overwrite existing files. Solutions:

Route Conflicts

If generated routes conflict with existing routes:

  1. Edit config/routes.yaml
  2. Rename conflicting routes
  3. Update controller methods if needed

Permission Errors

Ensure 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

Additional Resources