Your first module

We'll create a very simple module Person for understanding how is very simple to create new modules with Uccello.

Even if it is easier to create a module with Module Designer, we'll create it manually to understand how it works.

The creation of a new module involves three steps:

  1. Create a model and a table that will contain the data

  2. Create the module's structure that will generate the views

  3. Create a language file

First of all, let's create a new model:

php artisan make:model Person

This command line creates the file app/Person.php.

Use UccelloModule trait and define the label to use for each record:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Uccello\Core\Support\Traits\UccelloModule;

class Person extends Model
{
    use UccelloModule;
    use SoftDeletes;
    
    /**
    * Returns record label
    *
    * @return string
    */
    public function getRecordLabelAttribute() : string
    {
        return trim($this->first_name.' '.$this->last_name);
    }
}

Now we can create a new migration:

php artisan make:migration create_person_module

This command line creates the file database/migrations/DATE_create_people_module.php.

Put this code to create the people table:

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePersonModule extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('people', function (Blueprint $table) {
            $table->increments('id');
            $table->string('first_name');
            $table->string('last_name');
            $table->date('birthday')->nullable();
            $table->unsignedInteger('domain_id');
            $table->timestamps();
            $table->datetime('deleted_at')->nullable();
            
            $table->foreign('domain_id')->references('id')->on('uccello_domains');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('people');
    }
}

Now we want to create the module's structure. Let's add the following code into the migration file:

<?php

...
use Uccello\Core\Models\Domain;
use Uccello\Core\Models\Module;
use Uccello\Core\Models\Tab;
use Uccello\Core\Models\Block;
use Uccello\Core\Models\Field;
use Uccello\Core\Models\Filter;

class CreatePersonModule extends Migration
{
    public function up()
    {
        ...

        // Create the module's structure
        $this->createModuleStructure();
    }
    
    public function down()
    {
        ...

        // Remove automaticaly the module and its structure
        Module::where('name', 'person')->delete();
    }
    
    /**
     * Create the module's structure.
     */
    protected function createModuleStructure()
    {
        $module = $this->createModule();
        $this->createTabsBlocksFields($module);
        $this->createFilters($module);
        $this->activateModuleOnDomains($module);
    }

    /**
     * Create the module.
     */
    protected function createModule()
    {
        return Module::create([
            'name' => 'person',
            'icon' => 'person',
            'model_class' => 'App\Person',
            'data' => null
        ]);
    }
    
    /**
     * Activate module in all domains
     */
    protected function activateModuleOnDomains($module)
    {
        $domains = Domain::all();
        foreach ($domains as $domain) {
            $domain->modules()->attach($module);
        }
    }

    /**
     * Create all tabs, blocks and fields.
     */
    protected function createTabsBlocksFields($module)
    {
        // Main tab
        $tab = Tab::create([
            'label' => 'tab.main',
            'icon' => null,
            'sequence' => $module->tabs()->count(),
            'module_id' => $module->id
        ]);
        
        // General block
        $block = Block::create([
            'label' => 'block.general',
            'icon' => 'info',
            'sequence' => $tab->blocks()->count(),
            'tab_id' => $tab->id,
            'module_id' => $module->id
        ]);
        
        // First name
        Field::create([
            'name' => 'first_name',
            'uitype_id' => uitype('text')->id,
            'displaytype_id' => displaytype('everywhere')->id,
            'data' => [ 'rules' => 'required' ],
            'sequence' => $block->fields()->count(),
            'block_id' => $block->id,
            'module_id' => $module->id
        ]);
        
        // Last name
        Field::create([
            'name' => 'last_name',
            'uitype_id' => uitype('text')->id,
            'displaytype_id' => displaytype('everywhere')->id,
            'data' => [ 'rules' => 'required' ],
            'sequence' => $block->fields()->count(),
            'block_id' => $block->id,
            'module_id' => $module->id
        ]);

        // Birthday
        Field::create([
            'name' => 'birthday',
            'uitype_id' => uitype('date')->id,
            'displaytype_id' => displaytype('everywhere')->id,
            'data' => null,
            'sequence' => $block->fields()->count(),
            'block_id' => $block->id,
            'module_id' => $module->id
        ]);
    }

    /**
     * Create the default filter.
     */
    protected function createFilters($module)
    {
        Filter::create([
            'module_id' => $module->id,
            'domain_id' => null,
            'user_id' => null,
            'name' => 'filter.all',
            'type' => 'list',
            'columns' => [ 'first_name', 'last_name', 'birthday' ],
            'conditions' => null,
            'order' => null,
            'is_default' => true,
            'is_public' => false,
            'data' => [ 'readonly' => true ],
        ]);
    }
}

Now we can run the migration with the following commands:

php artisan migrate
php artisan cache:clear

Finally we can create a new language file resources/lang/en/person.php:

The file's name must be the same as the module's name. Here we create the file person.php because the module's name is person.

<?php

return [
    'person' => 'People', // Translation of the module's name

    'tab' => [
        'main' => 'Detail',
    ],

    'block' => [
        'general' => 'General information',
    ],

    'field' => [
        'first_name' => 'First name',
        'last_name' => 'Last name',
        'birthday' => 'Birthday',
    ],
];

Congratulations! Your first module is available here: http://localhost:8000/person/list.

Last updated