Feedback Stay at home and get up to 40% off with STAYATHOME coupon code | 0d 0h 0m 0s left

Submitting Data

#Submitting Form

To demonstrate the submission process of Laraform let's create a simple newsletter subscription form at app\Forms\NewsletterForm.php:

<?php

namespace App\Forms;

class NewsletterForm extends \Laraform
{
  public function schema() {
    return [
      'email' => [
        'type' => 'text',
        'placeholder' => 'Email address'
      ]
    ];
  }

  public function buttons() {
    return [[
      'label' => 'Submit'
    ]];
  }
}

We are going to build up this form through the chapter to become a fully functioning newsletter subscription form.

Now, assign this form to your view using app('App\Forms\NewsletterForm') and render it with ->render() method as we've learned in the previous chapter. You should see a simple form with an Email address field and Submit button.

#Auto-Processing

What happens if you press Submit? It seems like nothing but in fact the form is being submitted to /laraform/process endpoint (open the browser's Network tab to see it).

Laraform offers out of the box data processing at this /laraform/process endpoint, which enables you to assign an Eloquent model to a form and have everything validated, inserted and updated automatically. This saves you a lot of time and you can also add hooks to implement custom logic, as we'll learn later this chapter.

#Assigning Eloquent Model

Now that we have our basic newsletter subscription form, let's assign an Eloquent model:

class NewsletterForm extends \Laraform
{
  public $model = \App\Subscriber::class;

  // ...
}

As we don't have the Subscriber model yet, let's create it along with the migration.

Migration

<?php

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

class CreateSubscribersTable extends Migration
{
  public function up()
  {
    Schema::create('subscribers', function (Blueprint $table) {
      $table->bigIncrements('id');
      $table->string('email');
      $table->timestamps();
    });
  }

  public function down()
  {
    Schema::dropIfExists('subscribers');
  }
}

Model

Let's place the model at app/Subscriber.php:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Subscriber extends Model
{
  protected $fillable = ['email'];
}

After you run the migration you have everything set up to receive new subscribers. If you submit the form now, you should see the new email address added to subscribers table with timestamps.

#Using Hooks

What if we want to save the user's IP address along with the email or if we want to forward this data to a 3rd party email service? Let's see how we can do those with hooks.

#Before Hook

Let's extend our form with a meta field, called ip:

public function schema() {
  return [
    'email' => [
      'type' => 'text',
      'placeholder' => 'Email address'
    ],
    'ip' => [
      'type' => 'meta'
    ],
  ];
}
The meta field is used to store date invisible to users but important to us.

Once we have a field for the IP address we need to fill in its value before the data is saved by Laraform. This can be done using before hook:

class NewsletterForm extends \Laraform
{
  public $model = \App\Subscriber::class;

  public function schema() {
    return [
      'email' => [
        'type' => 'text',
        'placeholder' => 'Email address'
      ],
      'ip' => [
        'type' => 'meta'
      ]
    ];
  }

  public function buttons() {
    // ...
  }

  public function before() {
    $this->data['ip'] = \Request::ip();
  }
}
Laraform processes the entity based on the schema, so everything added to the submitted data will be ignored, unless you define an element for it.

To finish this example let's update our migration and model.

Migration

public function up()
{
  Schema::create('subscribers', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->string('email');
    $table->string('ip');
    $table->timestamps();
  });
}

Model

class Subscriber extends Model
{
  protected $fillable = ['email', 'ip'];
}

After you run the migration and submit a new email address, you should see the IP address being saved to the database as well.

#After Hook

What if we want to do something after the entity is saved? As you probably guessed, we can use the after hook. Let's save the user's IP address this time using after hook.

class NewsletterForm extends \Laraform
{
  public $model = \App\Subscriber::class;

  public function schema() {
    return [
      'email' => [
        'type' => 'text',
        'placeholder' => 'Email address'
      ]
    ];
  }

  public function buttons() {
    // ...
  }

  public function after() {
    // Retrieving Subscriber model
    $subscriber = $this->database->getEntity();

    // Set entity 'ip' value
    $subscriber->ip = \Request::ip();

    // Save the model
    $subscriber->save();
  }
}

As you can see in this case we don't have to define a meta element for ip as we are directly updating the Subscriber model after the data is saved.

Let's try this example in your browser and see that the IP is getting saved in this case too.

#Processing Without Model

It's also possible to process the data without assigning an Eloquent model. The most straightforward is to use after hook for this purpose:

class NewsletterForm extends \Laraform
{
  public function schema() {
    return [
      'email' => [
        'type' => 'text',
        'placeholder' => 'Email address'
      ]
    ];
  }

  public function buttons() {
    // ...
  }

  public function after() {
    $subscriber = new \App\Subscriber;

    $subscriber->email = $this->data['email'];
    $subscriber->ip = \Request::ip();

    $subscriber->save();
  }
}

#Other Hooks & API

The before and after hooks are not the only ones. Find more at Events & hooks section.

Also, if you are using hooks there's a chance you want rely on some methods of Laraform itself. To learn more about them check out its Laraform reference.

#Sending Response

Once the form is submitted and successfully processed by the backend you'll receive the following response:

{
  status: 'success',
  messages: [],
  payload: []
}

You can add custom messages or payload as well as setting the status in the before and after hook using fail() and success() methods. Let's see a complete example staying with the first version of our newsletter form:

class NewsletterForm extends \Laraform
{
  public $model = \App\Subscriber::class;

  public function schema() {
    return [
      'email' => [
        'type' => 'text',
        'placeholder' => 'Email address'
      ]
    ];
  }

  public function buttons() {
    // ...
  }

  public function before() {
    if ($this->exists()) {
      return $this->fail('Already subscribed');
    }
  }

  public function after() {
    return $this->success('Successfully subscribed');
  }

  protected function extists() {
    return !!\App\Subscriber::where('email', $this->data['email'])->count();
  }
}

Now if you try to add an email address that already exists in the database you'll receive the following response:

{
  status: 'fail',
  messages: ['Already subscribed'],
  payload: []
}

While if you successfully add a new email you'll get this:

{
  status: 'success',
  messages: ['Successfully subscribed'],
  payload: []
}
If the before or after hook have a return value that form's response will be instantly returned and no further processing will take place.
The way of validating the existence of an email address in this example is not the recommended way of doing that. Check out our Async Rules example to see how it can be done correctly.

#Handling Response

The response sent by the backend does not do anything by itself. This is because displaying messages to users can be implemented in many different ways and Laraform does not want to force a solution. Instead it provides events on the frontend that you can subscribe to and deal with the response yourself.

To see that in action first create a frontend form and attach it to our existing NewsletterForm.

Create NewsletterForm.vue

Let's create a new frontend form at resources/js/components/forms/NewsletterForm.vue:

<script>
  export default {
    mixins: [Laraform],
    created() {
      alert('Form is created')
    }
  }
</script>

Attach To Backend

Keeping our form from the beginning of this section, let's add the newsletter-form component to our backend:

class NewsletterForm extends \Laraform
{
  public $component = 'newsletter-form';

  // ...
}

Register Component

Open up the app.js (or main JS file) and add the NewsletterForm as a component.

import Vue from 'vue'
import NewsletterForm from './components/forms/NewsletterForm'

Vue.component('newsletter-form', NewsletterForm)

// ...

There you go - if you refresh the page now an alert should appear saying Form is created which means you've successfully connected the frontend form.

Subscribe To response Event

The next is to replace the content of created() hook with a subscription for response event:

export default {
  mixins: [Laraform],
  created() {
    this.on('response', (response) => {
      alert(response.data.messages[0])
    })
  }
}

If you compile your assets, refresh the page and submit the form now you'll see the response message sent by the backend in the alert window.

#The `success`, `fail` and `error` Events

The response event is a general one to process every successful response with 200 like HTTP statuses. If you want to differentiatate between fail and success responses based on the value of status you can use fail and success events:

export default {
  mixins: [Laraform],
  created() {
    this.on('fail', (response) => {
      alert('Failed: ' + response.data.messages[0])
    })

    this.on('success', (response) => {
      alert('Successful: ' + response.data.messages[0])

      // Resetting the form
      this.reset()
    })
  }
}

If the server is responding with an error (HTTP status of 500 for example) you catch that by subscribing to error event:

export default {
  mixins: [Laraform],
  created() {
    this.on('error', (error) => {
      console.log(error)
    })
  }
}

#Custom Endpoint

Now that we know the basics of submitting forms, let's go one step further and see how you can replace auto-processing.

To provide a custom endpoint, where the form should submit, just add endpoint property to the form:

class Form extends \Laraform
{
  public $endpoint = '/my/custom/endpoint'

  // ...
}

You can also do this globally in by changing the endpoint property in configuration:

/*
|--------------------------------------------------------------------------
| Endpoint
|--------------------------------------------------------------------------
|
| Default endpoint where the form should submit.
|
*/
'endpoint' => '/my/custom/endpoint',

#Defining Endpoint On Frontend

If you are not using Laravel or you are not rendering a form from the backend you might define the endpoint in the frontend form component:

export default {
  mixins: [Laraform],
  data() {
    return {
      endpoint: '/my/custom/endpoint',
      // ...
    }
  }
}

Or in the frontend configuration as well:

import Vue from 'vue'
import Laraform from '@laraform/laraform'

Laraform.config({
  endpoints: {
    process: '/my/custom/endpoint'
  }
})

// ...
You must define the endpoint (and in fact any other condfiguration) either only on backend or frontend. If you are using Laraform backend package make sure to use that for all the configurations.

#Processing Traits

If you don't want to use Laraform's FormController@process action to process the form, but still rely on the auto-processing feature, you can simply add ProcessesForm trait to your controller:

<?php

namespace App\Http\Controllers;

use Laraform\Traits\ProcessesForm;

class FormController extends Controller
{
  use ProcessesForm;

  // ...
}

This is useful if you want to implement Authorization for example.

In this case you need to set up your own route to deal with the endpoint:

Route::post('/process-form', 'FormController@process');

Also you need to set the /process-form endpoint globally or on a form level, as we've discussed.

#Using Resource Controllers

If you are using Resource Controllers and want to add auto-processing feature to those, you can add StoresForm and UpdatesForm traits along with form method, which should return the related form class name. This will add store and update methods to your resource controller, which will auto-process the form:

<?php

namespace App\Http\Controllers;

use App\Subscriber;
use Laraform\Traits\StoresForm;
use Laraform\Traits\UpdatesForm;
use App\Forms\NewsletterForm;

class SubscriberController extends Controller
{
  use StoresForm, UpdatesForm;

  public function form() {
    return app(NewsletterForm::class);
  }

  public function index() {
    // ...
  }

  public function create() {
    // ...

    return view('subscriber.create', [
      'form' => $this->form()
    ]);
  }

  public function show() {
    // ...
  }

  public function edit(Subscriber $subscriber) {
    // ...

    $form = $this->form();

    return view('subscriber.edit', [
      'form' => $form->setEndpoint($form->getEndpoint() . '/' . $subscriber->id)
                     ->load($subscriber->id)
    ]);
  }

  public function destroy() {
    // ...
  }
}

Notice On Using Resource Controller With Laraform

A minor fix must be added to resource routes when using with Laraform. As PUT and PATCH methods are expected to send data similar to GET (as query params) we need to change this, because sending whole form data, including even files eg. wouldn't make sense in our case.

When creating routes for the resource just exclude update and add it manually as a POST request:

Route::resource('subscribers', 'SubscriberController')->except(['update']);
Route::post('subscribers/{subscriber}', 'SubscriberController@update');

#Using Auto-Processing Manually

If you'd like to use Laraform's auto-processing feature with different method than process or the aforementioned resource actions, you can use the Laraform\Process\AutoProcess class directly:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Laraform\Process\AutoProcess;

class CustomController extends Controller
{
  public function action(Request $request) {
    return (new AutoProcess())->process($request);
  }
}

By default Laraform will try to parse the name of the backend form class from key property. You might optionally pass a Laraform\Laraform instance as a second parameter if you want to override this with one exact form:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Laraform\Process\AutoProcess;
use App\Forms\PostForm;

class CustomController extends Controller
{
  public $form = PostForm::class;

  public function action(Request $request) {
    return (new AutoProcess())->process($request, app($this->form));
  }
}

#Custom Submission

If you want to implement the submission logic all by yourself, you can absolutely do that. Let's see how.

#Overriding Frontend Submission

On the frontend just replace send() method of main Laraform component to overwrite submission logic.

Let's create a new component at resources/js/components/forms/Laraform.vue:

<script>
  export default {
    mixins: [Laraform],
    methods: {
      send() {
        // implement your logic here
      }
    }
  }
</script>

Then use this component as a mixin for each of your forms instead of the global Laraform mixin:

<script>
  import Laraform from './Laraform'

  export default {
    mixins: [Laraform]
  }
</script>

If you are interested in the default implementation of send by Laraform go to: node_modules/@laraform/laraform/src/components/Laraform.vue and search for send() method.

#Custom Backend Processing

If you want to completely or partially replace the auto-processing logic of Laraform just create your own action as a processor as described in Custom endpoint section.

For this it might be useful to check out the Laraform backend Reference to see what methods and properties you can rely on. You can also check out the default implementation at vendor/laraform/laraform-laravel/src/Controllers/FormController.php@process.