Skip to content

Create a Model

This guide shows two ways to scaffold a new model for WNCMS:

  • Use Laravel’s built-in Artisan generators
  • Use the WNCMS helper command wncms:create-model (recommended for backend CRUD)

The result is a local model that extends WNCMS’s BaseModel, plus an admin controller, migration, views, permissions, and routes.


Before you start

  • Ensure WNCMS is installed and autoloaded.
  • Confirm routes/custom_backend.php is included by your routes/web.php (WNCMS does this by default).
  • Decide your model’s singular and plural names. Example in this doc: Article / articles.

Option A — Laravel built-in generators

1) Generate files

bash
php artisan make:model Article -m
php artisan make:controller Backend/ArticleController --resource --model=Article

2) Update the model to extend BaseModel

php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Wncms\Models\BaseModel;

class Article extends BaseModel
{
    use HasFactory;

    protected $guarded = [];

    // Optional: tag types used by this model
    // public const TAG_TYPES = ['article_category', 'article_tag'];

    // Optional: casts, translations, etc.
    // protected $casts = ['published_at' => 'datetime'];
    // protected $translatable = ['title', 'excerpt', 'content'];
}

3) Make the backend controller extend WNCMS BackendController

php
<?php

namespace App\Http\Controllers\Backend;

use Wncms\Http\Controllers\Backend\BackendController;

class ArticleController extends BackendController
{
    // Usually no need to override anything for basic CRUD.
    // You can customize policies, validation, columns, etc.
}

4) Create a migration

Edit the generated migration (example columns):

php
Schema::create('articles', function (Blueprint $table) {
    $table->id();
    $table->unsignedBigInteger('website_id')->nullable()->index(); // multisite support
    $table->string('title');
    $table->string('slug')->unique();
    $table->text('excerpt')->nullable();
    $table->longText('content')->nullable();
    $table->timestamp('published_at')->nullable();
    $table->timestamps();
});

Run it:

bash
php artisan migrate

5) Register routes (if not using the WNCMS command below)

Append to routes/custom_backend.php:

php
<?php

use App\Http\Controllers\Backend\ArticleController;

Route::get('articles', [ArticleController::class, 'index'])->middleware('can:article_index')->name('articles.index');
Route::get('articles/create', [ArticleController::class, 'create'])->middleware('can:article_create')->name('articles.create');
Route::get('articles/create/{id}', [ArticleController::class, 'create'])->middleware('can:article_clone')->name('articles.clone');
Route::get('articles/{id}/edit', [ArticleController::class, 'edit'])->middleware('can:article_edit')->name('articles.edit');
Route::post('articles/store', [ArticleController::class, 'store'])->middleware('can:article_create')->name('articles.store');
Route::patch('articles/{id}', [ArticleController::class, 'update'])->middleware('can:article_edit')->name('articles.update');
Route::delete('articles/{id}', [ArticleController::class, 'destroy'])->middleware('can:article_delete')->name('articles.destroy');
Route::post('articles/bulk_delete', [ArticleController::class, 'bulk_delete'])->middleware('can:article_bulk_delete')->name('articles.bulk_delete');

6) Create backend views

WNCMS expects resources/views/backend/{plural_snake}/:

resources/views/backend/articles/
  ├─ index.blade.php
  ├─ create.blade.php
  └─ edit.blade.php

Name your routes and views using the plural snake case (e.g., backend.articles.index) to match WNCMS conventions.

  • Create permissions like article_index, article_create, etc. using your preferred method or a small seeder.
  • Add a backend menu item pointing to route('articles.index').

Use the all-in-one WNCMS command to generate model, migration, controller, views, permissions, and routes. It also normalizes snake/camel names inside the generated controller.

bash
php artisan wncms:create-model Article

What it does:

  • make:model Article

  • make:migration create_articles_table

  • make:controller Backend/ArticleController --resource --model=Article

    • Rewrites view names (backend.articles.*), route names (articles.*), cache tags, and model word helpers to snake case
  • wncms:create-model-view article to create index/create/edit views

  • wncms:create-model-permission article to seed basic permissions

  • Optionally appends a full routes block to routes/custom_backend.php and prepends the use statement for the controller

You will see a confirmation prompt before routes are appended.

Run migration:

bash
php artisan migrate

After scaffolding

Translation keys

If you use BaseModel::getModelName() or wncms_model_word('article'), add translations:

resources/lang/en/wncms.php
  'word' => [
    'article' => 'Article',
    'articles' => 'Articles',
  ],

Or package-scoped keys if you later move this into a package.

Tag types (optional)

If your model needs tags, declare:

php
public const TAG_TYPES = ['article_category', 'article_tag'];

WNCMS will auto-register them at model boot.

Media & translations (optional)

To use media library or translations:

php
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Wncms\Translatable\Traits\HasTranslations;

class Article extends BaseModel implements HasMedia
{
    use InteractsWithMedia;
    use HasTranslations;

    protected $translatable = ['title', 'excerpt', 'content'];
}

Common tips

  • Keep controller names pluralized and placed under App\Http\Controllers\Backend\.
  • Use plural snake for route and view names (e.g., articles.index) to match WNCMS helpers and stubs.
  • Include website_id in migrations if your site uses multisite.
  • Clear caches when adding menus or permissions if they’re cached.
  • Follow WNCMS’s naming: columns like status, timestamps, and slugs make integration smoother.

Quick checklist

  • Model extends Wncms\Models\BaseModel
  • Backend controller extends Wncms\Http\Controllers\Backend\BackendController
  • Migration created and migrated
  • Views under resources/views/backend/{plural}/
  • Routes registered to routes/custom_backend.php
  • Permissions created and assigned
  • Optional: TAG_TYPES, translations, media collections, and translatable fields added

Built with ❤️ for WNCMS