Adding the ability to create custom field types: The field type list

I'll show how I moved the old static field type list to the database.

View Project

In a previous post, I review the current code in Laramanager to review how field types are embedded in the core. Currently, the list of field types are hard coded as a property of a controller. This list should be more accessable and obviously editable. Once the list is moved, I'll show how the field type references are updated.

Storing the list in the database

The object list is stored in the database, so I'll do the same for the field types. Since this process is basic CRUD code, I won't go through this process.

Check out the code on Github - added ability to populate a list of field types.

Now to update the controller to use the list of field types from the database.

PhilMareu/Laramanager/Http/Controllers/ResourceFieldController.php

class ResourceFieldController extends Controller {

    protected $fieldTypes;

    protected $resource;

    protected $resourceField;

    public function __construct(LaramanagerFieldType $fieldType, LaramanagerResource $resource, LaramanagerResourceField $resourceField)
    {
        $this->fieldTypes = $fieldType->where('active', 1)->orderBy('title')->get();
        $this->resource = $resource;
        $this->resourceField = $resourceField;
    }

    ...

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create($resourceId)
    {
        $resource = $this->resource->find($resourceId);

        return view('laramanager::resources.fields.create', ['resource' => $resource, 'fieldTypes' => $this->fieldTypes]);
    }

    ...
}

Migrate core list to database

Now that the field list is getting pulled from the database, it is time to populated the database with the currently core list of field types. I'll use a Illuminate\Database\Seeder class and make a seeder.

src/PhilMareu/Laramanager/Seeders/FieldTypesSeeder.php

class FieldTypesSeeder extends Seeder
{
    protected $coreFieldTypes = [
        [
            'id' => 1,
            'title' => 'Text',
            'slug' => 'text',
            'class' => 'PhilMareu\Laramanager\FieldTypes\TextFieldType',
            'active' => 1
        ],
        [
            'id' => 2,
            'title' => 'Email',
            'slug' => 'email',
            'class' => 'PhilMareu\Laramanager\FieldTypes\EmailFieldType',
            'active' => 1
        ],
        [
            'id' => 3,
            'title' => 'Slug',
            'slug' => 'slug',
            'class' => 'PhilMareu\Laramanager\FieldTypes\SlugFieldType',
            'active' => 1
        ],
        [
            'id' => 4,
            'title' => 'Password',
            'slug' => 'password',
            'class' => 'PhilMareu\Laramanager\FieldTypes\PasswordFieldType',
            'active' => 1
        ],
        [
            'id' => 5,
            'title' => 'Image',
            'slug' => 'image',
            'class' => 'PhilMareu\Laramanager\FieldTypes\ImageFieldType',
            'active' => 1
        ],
        [
            'id' => 6,
            'title' => 'Images',
            'slug' => 'images',
            'class' => 'PhilMareu\Laramanager\FieldTypes\ImagesFieldType',
            'active' => 1
        ],
        [
            'id' => 7,
            'title' => 'Checkbox',
            'slug' => 'checkbox',
            'class' => 'PhilMareu\Laramanager\FieldTypes\CheckboxFieldType',
            'active' => 1
        ],
        [
            'id' => 8,
            'title' => 'Textarea',
            'slug' => 'textarea',
            'class' => 'PhilMareu\Laramanager\FieldTypes\TextareaFieldType',
            'active' => 1
        ],
        [
            'id' => 9,
            'title' => 'CKEditor',
            'slug' => 'ckeditor',
            'class' => 'PhilMareu\Laramanager\FieldTypes\CKEditorFieldType',
            'active' => 1
        ],
        [
            'id' => 10,
            'title' => 'Select',
            'slug' => 'select',
            'class' => 'PhilMareu\Laramanager\FieldTypes\SelectFieldType',
            'active' => 1
        ],
        [
            'id' => 11,
            'title' => 'Date',
            'slug' => 'date',
            'class' => 'PhilMareu\Laramanager\FieldTypes\DateFieldType',
            'active' => 1
        ],
        [
            'id' => 12,
            'title' => 'Relational',
            'slug' => 'relational',
            'class' => 'PhilMareu\Laramanager\FieldTypes\RelationalFieldType',
            'active' => 1
        ],
        [
            'id' => 13,
            'title' => 'Markdown',
            'slug' => 'markdown',
            'class' => 'PhilMareu\Laramanager\FieldTypes\MarkdownFieldType',
            'active' => 1
        ]
    ];

    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        LaramanagerFieldType::insert($this->coreFieldTypes);
    }
}

I'll make a migration to handle adding these fields to the database.

src/database/migrations/2018_07_26_192301_populate_field_types_table.php

class PopulateFieldTypesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Artisan::call('db:seed', ['--class' => FieldTypesSeeder::class]);
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        LaramanagerFieldType::truncate();
    }
}

Updating field type relations

Currently, the field types slug name is stored in the "type" column in the "laramanager_resource_fields" table. I will need to update this relation to use the ID of the field type. First, I need to add the relation method to the Eloquent model.

src/PhilMareu/Laramanager/Models/LaramanagerResourceField.php

class LaramanagerResourceField extends Model {

    ...

    public function fieldType()
    {
        return $this->belongsTo(LaramanagerFieldType::class, 'laramanager_field_type_id');
    }

    ...

}

The following 3 migrations will take care of converting the old field references to the new relation. I like to break tasks like this into smaller migrations. It is way easier to debug.

src/database/migrations/2018_07_26_192749_alter_laramanager_resource_fields_add_field_type_id.php

class AlterLaramanagerResourceFieldsAddFieldTypeId extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('laramanager_resource_fields', function (Blueprint $table) {
            $table->unsignedInteger('laramanager_field_type_id');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('laramanager_resource_fields', function (Blueprint $table) {
            $table->dropColumn('laramanager_field_type_id');
        });
    }
}

src/database/migrations/2018_07_26_193101_update_type_field_to_use_new_field_type_relation.php

class UpdateTypeFieldToUseNewFieldTypeRelation extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        $resourceFields = LaramanagerResourceField::all();

        $resourceFields->filter(function(LaramanagerResourceField $field) {
            return $field->type == 'wysiwyg';
        })->each(function(LaramanagerResourceField $field) {
            $fieldType = LaramanagerFieldType::where('slug', 'ckeditor')->first();
            $field->fieldType()->associate($fieldType);
            $field->save();
        });

        $resourceFields->reject(function(LaramanagerResourceField $field) {
            return $field->type == 'wysiwyg';
        })->each(function(LaramanagerResourceField $field) {
            $fieldType = LaramanagerFieldType::where('slug', $field->type)->first();
            $field->fieldType()->associate($fieldType);
            $field->save();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        $resourceFields = LaramanagerResourceField::with('fieldType')->get();

        $resourceFields->filter(function(LaramanagerResourceField $field) {
            return $field->fieldType->slug == 'ckeditor';
        })->each(function(LaramanagerResourceField $field) {
            $field->update(['type' => 'wysiwyg']);
        });

        $resourceFields->reject(function(LaramanagerResourceField $field) {
            return $field->fieldType->slug == 'ckeditor';
        })->each(function(LaramanagerResourceField $field) {
            $field->update(['type' => $field->fieldType->slug]);
        });
    }
}

src/database/migrations/2018_07_26_193839_alter_laramanager_resource_fields_drop_type_add_key.php

class AlterLaramanagerResourceFieldsDropTypeAddKey extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('laramanager_resource_fields', function (Blueprint $table) {
            $table->foreign('laramanager_field_type_id')->references('id')->on('laramanager_field_types')->onUpdate('cascade')->onDelete('cascade');
            $table->dropColumn('type');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('laramanager_resource_fields', function (Blueprint $table) {
            $table->string('type');
            $table->dropForeign('laramanager_resource_fields_laramanager_field_type_id_foreign');
        });
    }
}

Ok, this is a great place to stop for this post. Next time I'll work on building the actually field type classes and get them working.

2018 Phil Mareu - Coder, Traveler & Disc Thrower