Processing Data

#Using Eloquent

Laraform was built with Eloquent in mind to make it easy to integrate with your existing routines. By simply assigning a model to a form, its data, once filled will be saved based on the provided model:

<?php

// App/Article.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    public $fillable = ['title', 'content'];
}
<?php

// App/Forms/ArticleForm.php

namespace App\Forms;

use Laraform;
use App\Article;

class ArticleForm extends Laraform
{
  public $model = Article::class;

  public $schema = [
    'title' => [
      'type' => 'text',
      'label' => 'Title'
    ],
    'content' => [
      'type' => 'textarea',
      'label' => 'Content'
    ],
  ];
}

Now when you render the form and hit Submit you will see that the data will be stored in your articles table.

#Auto-Processing

By default Laraform provides a default endpoint /laraform/process which takes care of processing your data. First it validates the incoming data using Laravel's validation logic then, if any model is provided saves them.

Saving data for certain elements can be disabled by setting persist to false in the element's schema.

After saving data, finally it returns a response that you can be further processed on the frontend. The response structure looks like this:

$response = [
  'status' => '',   // success|fail
  'messages' => [],  // error messages if any
  'payload' => [    // response payload if any
    'updates' => []  // form udpates if any
  ]
]

New entity IDs or final filenames are being returned in the updates array, which automatically updates the form values, otherwise an empty payload is returned by default.

#Manual Processing

In this section you will learn how to use different methods of Laraform in case you decide to create your own processing logic.

#Accessing Form

Once you created your form class extending Laraform as described in Rendering chapter you can access it by using Laravel's app() method:

$form = app('App\Forms\MyForm');

The reason for this is that Laraform was built with testing in mind and therefore all of its dependencies are provided in it's constructor, which is more convenient to be auto-resolved by Laravel.

#Dependency Injection

If you want to inject dependencies to your form class you can use its boot() method for that matter:

<?php

namespace App\Forms;

use Laraform;
use Path\To\Dependency;

class MyForm extends Laraform
{
  public $dependency;

  public function boot(Dependency $dependency) {
    $this->dependency = $dependency;
  }
}

#Setting Data

Form data can be set using setData() method of Laraform:

<?php

namespace App\Forms;

use Laraform;
use App\Article;

class ArticleForm extends Laraform
{
  public $schema = [
    'title' => [
      'type' => 'text',
      'label' => 'Title'
    ],
    'content' => [
      'type' => 'textarea',
      'label' => 'Content'
    ],
  ];
}
$form = app('App\Forms\ArticleForm');

$form->setData([
  'title' => 'Lorem ipsum dolor',
  'content' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
]);

#Validating Data

You can define validation rules for elements using their rules attribute which will have effect both on the frontend and the backend. Laraform uses Laraform's validation logic on the backend and you can run it by calling validate() method:

<?php

namespace App\Forms;

use Laraform;
use App\Article;

class ArticleForm extends Laraform
{
  public $schema = [
    'title' => [
      'type' => 'text',
      'label' => 'Title',
      'rules' => 'required'
    ],
    'content' => [
      'type' => 'textarea',
      'label' => 'Content',
      'rules' => 'required'
    ],
  ];
}
$form = app('App\Forms\ArticleForm');

$form->setData([
  'title' => 'Lorem ipsum dolor',
  'content' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
]);

$form->validate();

Once your data is validated you can determine if the rules are met with isInvalid() method and retrieve error messages using getErrors() method:

// ...

$form->validate();

if ($form->isInvalid()) {
    $errors = $form->getErrors();
}

You may specify different validation rules for backend and frontend:

<?php

namespace App\Forms;

use Laraform;
use App\Article;

class ArticleForm extends Laraform
{
  public $schema = [
    'title' => [
      'type' => 'text',
      'label' => 'Title',
      'rules' => [
        'backend' => [
          'required'
        ],
        'frontend' => [
          'required',
          'someCustomFrontendRule',
        ],
      ]
    ],
    // ...
  ];
}

#Saving Data

After validating your form data you can save them by using save() method if the form has a $model assigned:

<?php

namespace App\Forms;

use Laraform;
use App\Article;

class ArticleForm extends Laraform
{
  public $model = Article::class;

  public $schema = [
    'title' => [
      'type' => 'text',
      'label' => 'Title',
      'rules' => 'required'
    ],
    'content' => [
      'type' => 'textarea',
      'label' => 'Content',
      'rules' => 'required'
    ],
  ];
}
$form = app('App\Forms\ArticleForm');

$form->setData([
  'title' => 'Lorem ipsum dolor',
  'content' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
]);

$form->validate();

if ($form->isInvalid()) {
  $errors = $form->getErrors();

  // manage errors
  return;
}

$form->save();

Alternatively you can use insert() or update($key) method to insert a new record or update an existing entity using its primary key. Also if you do not need to encrypt the ID you can set the secret to false which is its default value.

#Changing Element Name

By default each element will be saved in the database under their name (which is the key of the element's descriptor in the schema). You can set a custom column or attribute name for the element using the attribute option, eg.:

public $schema = [
  'title' => [
    'type' => 'text',
    'label' => 'Title',
    'rules' => 'required',
    'attribute' => 'head', // this field will be saved as `head` attribute
  ],
  // ...
];

#Using Primary Key

When you have a form which can either insert a new record or update an existing one you need to have its primary key field among its elements. For this purpose, there is the key element, which is basically a hidden input field with some extra. What's unqiue about this element is that on the backend it has an option called secret which is a boolean and can automatically encrypt entity IDs on the frontend.

When you are using this an identifier for your form it will be automatically filled in once a record is inserted and later submits on the same form will result in updating the existing entity instead of creating a new one.

Let's see these in practice - try inserting a new record and see how an encrypted ID is being filled in:

Form `data`:

    
      
<?php

namespace App\Forms;

use Laraform;

class UsingIdForm extends Laraform
{
  public $schema = [
    'id' => [
      'type' => 'key',
      'secret' => true
    ],
    'title' => [
      'type' => 'text',
      'label' => 'Title'
    ],
    'content' => [
      'type' => 'textarea',
      'label' => 'Content'
    ],
  ];
}
      
    

The ID is filled in because by default auto-processing returns any newly inserted entity keys in its payload.updates response object.

Changing Primary Key

You may change the primary key of the form model by setting $primaryKey property if you are using something else than id:

<?php

namespace App\Forms;

use Laraform;
use App\Article;

class ArticleForm extends Laraform
{
  public $primaryKey = 'article_id';

  public $model = Article::class;

  public $schema = [
    'article_id' => [
      'type' => 'key'
    ],
    'title' => [
      'type' => 'text',
      'label' => 'Title'
    ],
    'content' => [
      'type' => 'textarea',
      'label' => 'Content'
    ],
  ];
}

#Sending Response

After validating and saving your data the final step usually is to send a response for the frontend. When you are using a controller action it's pretty straightforward if you are familiar with Laravel but for the sake of completeness let's see a full example of implementing everything discussed until this point including sending response.

So we have the following form class:

<?php

namespace App\Forms;

use Laraform;
use App\Article;

class ArticleForm extends Laraform
{
  public $endpoint = '/article'

  public $model = Article::class;

  public $schema = [
    'id' => [
      'type' => 'key',
      'secret' => 'true'
    ],
    'title' => [
      'type' => 'text',
      'label' => 'Title',
      'rules' => 'required'
    ],
    'content' => [
      'type' => 'textarea',
      'label' => 'Content',
      'rules' => 'required'
    ],
  ];
}

Along with the Article model:

<?php

// App/Article.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    public $fillable = ['title', 'content'];
}

To handle its endpoint let's add a new route which points to the store action of ArticleController:

Route::post('/article', 'ArticleController@store');
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ArticleController extends Controller
{
  public function store(Request $request)
  {
    // Obtaining a form instance.
    $form = app('App\Forms\ArticleForm');

    // First we set the form's data and its primary key, if any.
    $form->setData($request->data);
    $form->setKeyFromData($request->data);

    // Validate the form.
    $form->validate();

    // If invalid, send a 'fail' response.
    if ($form->isInvalid()) {
      return response([
        'status' => 'fail',
        'messages' => $form->getErrors(),
        'payload' => []
      ], 422);
    }

    // If we are still on the go and we have a model
    // let's save data. (if primary key is available
    // it will update otherwise insert).
    if ($form->hasModel()) {
      $form->save();
    }

    // Retrieve form updates which contains possible
    // primary key for a newly inserted entity.
    $updates = $form->getUpdates();

    // Finally send a response.
    return response([
      'status' => 'success',
      'messages' => [],
      'payload' => count($updates) > 0 ? [
        'updates' => $updates
      ] : []
    ], 200);
  }
}

#Loading Data

Once you've inserted a record into you database you can load its data into the form using load() method:

$form = app('App\Forms\ArticleForm');

$form->load(1);

$form->render();

This will render the form along with id = 1 entity's data.

#Hooks & Events

Laraform has certain hooks and events that are triggered during its data processing. Events and hooks are basically the same the only thing differentiates them is how they are defined.

#Defining Hooks

To define a hook, simply create a new method with its name in your form class:

<?php

namespace App\Forms;

use Laraform;

class MyForm extends Laraform
{
  public $schema = [
    'title' => [
      'type' => 'text',
      'label' => 'Title'
    ]
  ];

  public function validated() {
    // do something
  }
}

The validated() method will be called once the form's data is validated.

#Subscribing For Events

Subscribing for events has the same effect as creating hooks, but they can be defined inline using on() method:

<?php

namespace App\Forms;

use Laraform;

class MyForm extends Laraform
{
  public $schema = [
    'title' => [
      'type' => 'text',
      'label' => 'Title'
    ]
  ];

  public function boot() {
    $this->on('validated', function(){
      // do something
    });
  }
}

This anonym function will be called when the form's data is validated.

#Pushing Response

In every hooks and event you can anytime send an instant response by using Laravel's send() function on response():

<?php

namespace App\Forms;

use Laraform;

class MyForm extends Laraform
{
  public $schema = [
    'title' => [
      'type' => 'text',
      'label' => 'Title'
    ]
  ];

  public function validated() {
    // ...

    response()->json([
      'status' => 'fail',
      'messages' => ['Additional validation failed.'],
      'payload' => []
    ], 422)->send();
  }
}

# Available Hooks & Events

The following events are triggered within Laraform:

  • validating - before validation starts
  • validated - after validation ended
  • saving - before data starts being saved
  • saved - after data is saved
  • inserting - before data starts being inserted
  • inserted - after data is inserted
  • updating - before data starts being updated
  • updated - after data is updated
  • loading - before data starts being loaded
  • loaded - after data is loaded

These two events are only available if you are using auto-processing:

  • before - before validation starts, after data is already set
  • after - after data is saved

#Relationships

Laraform can handle nested elements which translates to relationships on the backend when using models.

#HasOne

HasOne type of relationships typically represent an Object type elements. Having an attribute with a HasOne relationship will save the related object as a separate entity.

Table Structure

Let's assumne we have the following tables:

users
    id - integer
    email - string

profiles
    id - integer
    user_id - integer
    name - string
    bio - string

Model Structure

The User has a profile attribute defined as a hasOne relationship type in its model:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    public $fillable = ['email'];

    public function profile() {
      return $this->hasOne(Profile::class);
    }
}

Form Structure

The following form will save the profile object to profiles table and will automatically associate with the user:

<?php

namespace App\Forms;

use Laraform;
use App\User;

class UserForm extends Laraform
{
  public $model = User::class;

  public $schema = [
    'email' => [
      'type' => 'text',
      'label' => 'Email'
    ],
    'profile' => [
      'type' => 'object',
      'schema' => [
        'name' => [
          'type' => 'text',
          'label' => 'Name'
        ],
        'bio' => [
          'type' => 'textarea',
          'label' => 'Bio'
        ]
      ]
    ],
  ];
}

Compatible Elements

The following elements are designed to be used with hasOne relationship type:

#HasMany

HasMany type relationships are usually associated with List type elements and they can store multiple related entities.

Table Structure

Let's assume our user can have multiple phones and their type and number need to be stored:

users
    id - integer

phones
    id - integer
    user_id - integer
    type - string
    number - string

Model Structure

The User model has the phones attribute defined as a hasMany relationship:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    public function phones() {
      return $this->hasMany(Phone::class);
    }
}

Form Structure

The following form will store its phones object list in the phones table:

<?php

namespace App\Forms;

use Laraform;
use App\User;

class UserForm extends Laraform
{
  public $model = User::class;

  public $schema = [
    'phones' => [
      'type' => 'list',
      'label' => 'Phones',
      'object' => [
        'schema' => [
          'type' => [
            'type' => 'text',
            'placeholder' => 'Type'
          ],
          'number' => [
            'type' => 'textarea',
            'placeholder' => 'Number'
          ]
        ]
      ]
    ],
  ];
}

Compatible Elements

The following elements are designed to be used with hasMany relationship type:

#BelongsToMany

BelongsToMany relationships are used to assign entities of two database tables as a many-to-many relationship.

Table Structure

Let's see an example where the user can be associated with different roles:

users
    id - integer

roles
    id - integer
    name - string

role_user
    id - integer
    user_id - integer
    role_id - integer

Model Structure

The User model has the roles attribute defined as a belongsToMany relationship:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    public function roles() {
      return $this->belongsToMany(Role::class);
    }
}

Form Structure

Assuming we have 3 roles stored in the roles table 1 - Admin, 2 - Editor, 3 - User, the following form will store the user's roles in the role_user table:

<?php

namespace App\Forms;

use Laraform;
use App\User;

class UserForm extends Laraform
{
  public $model = User::class;

  public $schema = [
    'roles' => [
      'type' => 'checkboxgroup',
      'label' => 'Roles',
      'items' => [
        1 => 'Admin',
        2 => 'Editor',
        3 => 'User'
      ]
    ],
  ];
}

Compatible Elements

The following elements are designed to be used with belongsToMany relationship type:

#BelongsTo

On a form level belongsTo type of relationships are really just a simple field that contains an ID associated with a relationship's ID in an other table. For this reason we are not getting into any examples as they should be quite straightforward. However it's recommended to use select or radio type elements to cover belongsTo relationships.

#MorphMany

MorphMany relationships are used when you have a certain type of entity (eg. photo) that can be associated with different type of models (eg. photo of a user, photo of a post).

Table Structure

Let's see an example where we have the following tables:

users
    id - integer

posts
    id - integer

photos
    id - integer
    photo - string
    photoable_id - integer
    photoable_type - integer

Model Structure

The User model is associated with the photos using a morphMany relationship, while the Photo model has a photoable property which defines that it is being morphed to something:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    public function photos() {
      return $this->morphMany(Photo::class);
    }
}
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Photo extends Model
{
    public $fillable = ['photo'];

    public function photoable() {
      return $this->morphTo();
    }
}

Form Structure

Given the models above the following form will save the user's photos to the photos table and associate them with the user's ID and App\User photoable_type:

<?php

namespace App\Forms;

use Laraform;
use App\User;

class UserForm extends Laraform
{
  public $model = User::class;

  public $schema = [
    'photos' => [
      'type' => 'gallery',
      'label' => 'Photos',

      // This is the name of the `photo` column
      // in the `photos` table where the actual
      // url of the photo will be stored.
      //
      // Learn more at: /uploading-files
      'storeFile' => 'photo'
    ],
  ];
}

Compatible Elements

The following elements are designed to be used with morphMany relationship type:

#MorphToMany

MorphToMany relationships are often used when entities of a certain database table can be assigned to different type of models.

Table Structure

Let's see an example where we have a common tags table and tags can be assigned to posts and videos via taggables table:

posts
    id - integer
    name - string

videos
    id - integer
    name - string

tags
    id - integer
    name - string

taggables
    tag_id - integer
    taggable_id - integer
    taggable_type - string

Model Structure

Here we have the model of Post where tags is defined as a morphToMany relationship:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    public function tags() {
      return $this->morphToMany(Tag::class, 'taggable');
    }
}

Form Structure

Assuming we have to tags stored in the tags table 1 - php, 2 - javascript, the following form willl automatically save its tags to taggables table using the post's id as taggable_id and App\Post as taggable_type:

<?php

namespace App\Forms;

use Laraform;
use App\Post;

class PostForm extends Laraform
{
  public $model = Post::class;

  public $schema = [
    'tags' => [
      'type' => 'tags',
      'label' => 'Tags',
      'items' => [
        1 => 'php',
        2 => 'javascript'
      ]
    ],
  ];
}

Compatible Elements

The following elements are designed to be used with morphToMany relationship type:

#MorphTo

Just like BelongsTo, morphTo type of relationships are simple fields that contains IDs associated with another relationship's ID. For the same reason reason we are not getting into any examples as it should be quite straightforward. Also, it's recommended to use select or radio type elements to cover morphTo relationships too.