Examples

Practical code examples for building with WPZylos Scaffold.

Basic Controller

<?php
// app/Http/Controllers/DashboardController.php

namespace MyPlugin\Http\Controllers;

use WPZylos\Framework\Views\ViewFactory;
use WPZylos\Framework\Core\Contracts\ContextInterface;

class DashboardController
{
    public function __construct(
        private ViewFactory $view,
        private ContextInterface $context
    ) {}

    public function index(): void
    {
        echo $this->view->render('admin/dashboard', [
            'title' => __('Dashboard', $this->context->textDomain()),
            'stats' => $this->getStats(),
        ]);
    }

    private function getStats(): array
    {
        return [
            'total_items' => 150,
            'active_items' => 42,
        ];
    }
}

Service with Database

<?php
// app/Services/ItemService.php

namespace MyPlugin\Services;

use WPZylos\Framework\Database\Connection;
use WPZylos\Framework\Core\Contracts\ContextInterface;

class ItemService
{
    private string $table;

    public function __construct(
        private Connection $db,
        private ContextInterface $context
    ) {
        $this->table = $context->tableName('items');
    }

    public function all(): array
    {
        return $this->db->getResults(
            "SELECT * FROM {$this->table} ORDER BY created_at DESC"
        );
    }

    public function find(int $id): ?object
    {
        return $this->db->getRow(
            $this->db->prepare(
                "SELECT * FROM {$this->table} WHERE id = %d",
                $id
            )
        );
    }

    public function create(array $data): int
    {
        return $this->db->insert($this->table, $data);
    }

    public function update(int $id, array $data): int
    {
        return $this->db->update($this->table, $data, ['id' => $id]);
    }

    public function delete(int $id): int
    {
        return $this->db->delete($this->table, ['id' => $id]);
    }
}

Migration

<?php
// database/migrations/2024_01_01_000000_create_items_table.php

use WPZylos\Framework\Migrations\Migration;

return new class extends Migration
{
    public function up(): void
    {
        $table = $this->context->tableName('items');

        $sql = "CREATE TABLE {$table} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            name varchar(255) NOT NULL,
            description text,
            status varchar(50) NOT NULL DEFAULT 'active',
            user_id bigint(20) unsigned NOT NULL,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            PRIMARY KEY  (id),
            KEY user_id (user_id),
            KEY status (status)
        ) {$this->charset};";

        $this->dbDelta($sql);
    }

    public function down(): void
    {
        $this->dropTable('items');
    }
};

Admin Form with Nonce

<?php
// app/Http/Controllers/SettingsController.php

namespace MyPlugin\Http\Controllers;

use WPZylos\Framework\Views\ViewFactory;
use WPZylos\Framework\Security\Nonce;
use WPZylos\Framework\Security\Gate;
use WPZylos\Framework\Security\Sanitizer;
use WPZylos\Framework\Core\Contracts\ContextInterface;

class SettingsController
{
    public function __construct(
        private ViewFactory $view,
        private Nonce $nonce,
        private Gate $gate,
        private Sanitizer $sanitizer,
        private ContextInterface $context
    ) {}

    public function index(): void
    {
        echo $this->view->render('admin/settings', [
            'title' => __('Settings', $this->context->textDomain()),
            'nonce' => $this->nonce->create('save_settings'),
            'options' => get_option($this->context->optionKey('settings'), []),
        ]);
    }

    public function save(): void
    {
        // 1. Verify nonce
        if (!$this->nonce->verify($_POST['_nonce'] ?? '', 'save_settings')) {
            wp_die(__('Invalid request', $this->context->textDomain()));
        }

        // 2. Check capability
        if (!$this->gate->can('manage_options')) {
            wp_die(__('Unauthorized', $this->context->textDomain()));
        }

        // 3. Sanitize and save
        $settings = [
            'enabled' => isset($_POST['enabled']),
            'api_key' => $this->sanitizer->text($_POST['api_key'] ?? ''),
            'limit'   => absint($_POST['limit'] ?? 10),
        ];

        update_option($this->context->optionKey('settings'), $settings);

        wp_redirect(admin_url('admin.php?page=' . $this->context->slug() . '-settings&saved=1'));
        exit;
    }
}

View Template

<?php
// resources/views/admin/settings.php
/** @var string $title */
/** @var string $nonce */
/** @var array $options */
/** @var \WPZylos\Framework\Core\Contracts\ContextInterface $context */
?>
<div class="wrap">
    <h1><?php echo esc_html($title); ?></h1>

    <?php if (isset($_GET['saved'])): ?>
        <div class="notice notice-success is-dismissible">
            <p><?php esc_html_e('Settings saved!', $context->textDomain()); ?></p>
        </div>
    <?php endif; ?>

    <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
        <input type="hidden" name="action" value="<?php echo esc_attr($context->slug()); ?>_save_settings">
        <input type="hidden" name="_nonce" value="<?php echo esc_attr($nonce); ?>">

        <table class="form-table">
            <tr>
                <th><?php esc_html_e('Enable Feature', $context->textDomain()); ?></th>
                <td>
                    <label>
                        <input type="checkbox" name="enabled" <?php checked($options['enabled'] ?? false); ?>>
                        <?php esc_html_e('Enable', $context->textDomain()); ?>
                    </label>
                </td>
            </tr>
            <tr>
                <th><?php esc_html_e('API Key', $context->textDomain()); ?></th>
                <td>
                    <input type="text" name="api_key" value="<?php echo esc_attr($options['api_key'] ?? ''); ?>" class="regular-text">
                </td>
            </tr>
            <tr>
                <th><?php esc_html_e('Limit', $context->textDomain()); ?></th>
                <td>
                    <input type="number" name="limit" value="<?php echo esc_attr($options['limit'] ?? 10); ?>" min="1" max="100">
                </td>
            </tr>
        </table>

        <?php submit_button(); ?>
    </form>
</div>

Service Provider

<?php
// app/Providers/AppServiceProvider.php

namespace MyPlugin\Providers;

use WPZylos\Framework\Core\ServiceProvider;
use MyPlugin\Services\ItemService;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->container->singleton(ItemService::class, function ($container) {
            return new ItemService(
                $container->get('database'),
                $container->get('context')
            );
        });
    }

    public function boot(): void
    {
        // Register hooks, shortcodes, etc.
        add_action('admin_menu', [$this, 'registerMenus']);
    }

    public function registerMenus(): void
    {
        add_menu_page(
            __('My Plugin', $this->context->textDomain()),
            __('My Plugin', $this->context->textDomain()),
            'manage_options',
            $this->context->slug(),
            [$this, 'renderDashboard'],
            'dashicons-admin-generic'
        );
    }

    public function renderDashboard(): void
    {
        $controller = $this->container->get(DashboardController::class);
        $controller->index();
    }
}

Routes File

<?php
// routes/web.php

use WPZylos\Framework\Routing\Router;
use MyPlugin\Http\Controllers\ItemController;
use MyPlugin\Http\Controllers\SettingsController;

return static function (Router $router): void {
    // Admin routes
    $router->admin('', [ItemController::class, 'index']);
    $router->admin('add', [ItemController::class, 'create']);
    $router->admin('edit/{id}', [ItemController::class, 'edit']);
    $router->admin('settings', [SettingsController::class, 'index']);

    // POST routes
    $router->post('store', [ItemController::class, 'store']);
    $router->post('update/{id}', [ItemController::class, 'update']);
    $router->post('delete/{id}', [ItemController::class, 'destroy']);
    $router->post('save-settings', [SettingsController::class, 'save']);
};