0

I am wondering whether it's possible to programmatically auto-submit a Webform after visiting its URL and to save the user from a 'submit' button click.

The below webform can be accessed via /form/my-invoices:

My Webform

and has 3 elements:

  • Key: invoice_number
  • Key: amount
  • Key: seller_name

Use Case:

I have paper invoice with a printed barcode and a barcode reader device.

When a user will scan the barcode on the paper invoice, The barcode result will be generated and the user will be redirected to:

www.example.com/form/my-invoices?invoice_number=123456789&amount=1200&seller_name:john-smith

I want to auto-submit the Webform when the above link will be visited !

So in steps, below what will really happen:

  1. A user will scan the barcode.
  2. Barcode will then redirect the user to www.example.com/form/my-invoices?invoice_number=123456789&amount=1200&seller_name:john-smith
  3. AUTOMATICALLY, the elements of the webform (form/my-invoices) will be populated from the link query parameter and the webform will be auto-submitted without having the user to click on the "Submit" button.
  4. After auto submit of the webform, the user will be redirected to: /specific-url and with a confirmation message something similar to: "the webform has been auto-submitted on your behalf".

So in summary, how to programmatically auto-submit a webform when the webform url with query parameters is visited ?

Thanks!


UPDATE #2 (edit #2):

After changing the route path to: /form/my-invoices/autosubmit and visit that page, the webform is indeed successfully submitted by there is no value validation occured:

autosubmit_barcode_results.routing.yml

autosubmit_barcode_results.autosubmitform:
  path: '/form/my-invoices/autosubmit'
  defaults:
    _controller: '\Drupal\autosubmit_barcode_results\Controller\AutoSubmitBarcodeController::autosubmitform'
  requirements:
    _permission: 'access content'

AutoSubmitBarcodeController.php

<?php

namespace Drupal\autosubmit_barcode_results\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\webform\Entity\Webform;
use Drupal\webform\WebformSubmissionForm;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;

class AutoSubmitBarcodeController extends ControllerBase {

  /**
   * Display the markup.
   *
   * @return array
   */
  public function autosubmitform() {

// Get and prepare variables.
$request = Request::createFromGlobals();
$invoive_number = $request->query->get('invoive_number'); 

// Get submission values and data.
$values = [
  'webform_id' => 'my_invoices',
  'entity_type' => NULL,
  'entity_id' => NULL,
  'in_draft' => FALSE,
  'uid' => '1',
  'langcode' => 'en',
  'token' => 'pgmJREX2l4geg2RGFp0p78Qdfm1ksLxe6IlZ-mN9GZI',
  'uri' => '/webform/my_invoices/api',
  'remote_addr' => '',
  'data' => [
    'invoice_number' => $invoice_number,
  ],
];

// Validate submission.
  $errors = WebformSubmissionForm::validateFormValues($values);

// Check there are no validation errors.
  if (!empty($errors)) {
     $message = $errors['invoice_number'];
     $message = drupal_set_message($message, 'error');
     return $message;
  }
  else {
// If no validation errors, then Submit values and get submission ID.
    $webform_submission = WebformSubmissionForm::submitFormValues($values);


// Redirect the user.
return new RedirectResponse('/all-my-scanned-invoices');

// Show a success message to the user on the redirected page.
 $mesagesuccess = 'New invoice has been successfully scanned.';
 $mesagesuccess = drupal_set_message($mesagesuccess, 'status');
 return $mesagesuccess;

    }
  }
}

However when an invoice is scanned twice, the $error is being displayed properly but along with another error:

You have already scanned this invoice once, please scan another one!

User error: "0" is an invalid render array key in Drupal\Core\Render\Element::children() (line 97 of core\lib\Drupal\Core\Render\Element.php).
Drupal\Core\Render\Element::children(Array, 1) (Line: 408)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 450)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 195)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 226)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 582)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 227)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 117)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 111)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch('kernel.view', Object) (Line: 156)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 68)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 99)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 78)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 52)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 693)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
1

You can do it basically in 3 steps:

1.Get and prepare your variables:

use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
$invoice_number = $request->query->get('invoice_number');
$amount = ...;
$seller_name = ...;

2.Create a submission when /form/my-invoices is requested and all of three variables are present/valid. (meet whatever conditions applicable to your case). This step can be done in various ways, such as a controller/service, or a preprocess hook...Well, step 1 and 3 are actually all part of this function.

The preprocess function (mymodule_preprocess _page(&$var)) can go into the my_module/my_module.module file, while a controller can go into my_module/src/Controller/FormSubmitController.php file.
Preprocess function does check all incoming requests, but as long as you return early, it's not too bad IMO... However the Drupal8 OOP elegant way is to provide a route and trigger the controller when route is active. You can look up some tutorials on that topic. For validating your webform, search for webform validation tutorials.

3.Upon successful creation, use

use Symfony\Component\HttpFoundation\RedirectResponse;
return new RedirectResponse('http://yoursite.com/specific-url');

Do a security check on the variables before the submission creation if necessary and you should be all set.

Update about validation question:

First of all, if your barcode is capable of generating unique invoice numbers, and as long as you make sure it doesn't get altered before the submit function is triggered, we don't necessarily have to validate it.
In this specific case, I believe all you need to do is going into the webform build page, and check the unique box for the invoice_number field. The great Webform module already has it built in so if there's an duplicate, it will not submit, and you will have something in the $error variable. You can get the error message and display it however you want.

Check this page for all the methods you can use on \Drupal::messenger()

// Validate submission.
  $errors = WebformSubmissionForm::validateFormValues($values);

// Check there are no validation errors.
  if (!empty($errors)) { 
    //$message = $errors['invoice_number'];
    // \Drupal::messenger()->deleteAll(); use this if you don't want to show the field level error message. It delete all messages.
    \Drupal::messenger()->addError(t('An error occurred and processing did not complete'));
    $render = [
        '#type' => 'StatusMessages', // use this render array type to display messages. 
    ];
    return $render;
  }
  else {
// If no validation errors, then Submit values and get submission ID.
    $webform_submission = WebformSubmissionForm::submitFormValues($values);
    \Drupal::messenger()->addMessage(t('New invoice has been successfully scanned.'));
    return new RedirectResponse('/all-my-scanned-invoices');
  }
  • 1
    I would follow along that routing/controller tutorial link I sent and start from there. If you are not ready for OOP yet, use the preprocess function in the mymodule.module file. – cchen Apr 24 at 1:37
  • 1
    I see, I think you are very close here. So it looks like the webform route is taking precedence over your custom route (/form/my-invoices). For testing purpose, change your custom route to /form/my-invoices/submit, go to that page, and see if the webform creation works. I also notice the autosubmitform function does not return anything, you can actually put step3 code here as the return statement. – cchen Apr 24 at 3:23
  • 1
    Yeah the error is complaining about there's no return statement in your controller. Once you set a proper return the error should be fixed. Please see last part of my previous comment. – cchen Apr 24 at 3:56
  • 1
    I added the validation section at the bottom of my answer, also you have a typo invoice in the code haha – cchen Apr 24 at 17:45
  • 1
    They need to be returned respectively within the if/else. Please see my updated code, thanks! – cchen Apr 24 at 19:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.