0

I want to use batch in POST in rest endpoints in Drupal 8, but my batch is not getting progressed. I am creating rest endpoints for my module which will send email to bulk users. So for this purpose I need to use batch which will send email to all users without giving time out error. Please help.

my source code is:

class BatchController {

    public static function callBatch() {

        $batch = array(
        'title' => t('Sending ...'),
        'operations' => [],
        'init_message' => t('Sending is starting.'),
        'finished' => '\Drupal\module_mail\sendEmails::sendEmailsItemCallback',
        );

        $subject='hello';
        $body='body';

        $data = array(0 => array('id'=>1,'Fullname'=>"joy", 'Email'=>"[email protected]")
            );
        foreach($data as $row) {
        $batch['operations'][] = ['\Drupal\module_mail\sendEmails::sendEmailsUserItem',[$row,$subject,$body]];
        }
        batch_set($batch);
        return batch_process('home');
    }


}


namespace Drupal\module_mail;

class sendEmails {

    public static function sendEmailsUserItem($user, $subject, $body, &$context)
    {           
        $message = 'Sending emails';        
        $mailManager = \Drupal::service('plugin.manager.mail');
        $module = 'module_mail';
        $key = '';
        $to= $user['Email'];
        $params['email_body'] = $body;
        $params['email_subject'] = $subject;
        $langcode = \Drupal::currentUser()->getPreferredLangcode();
        $send = true;
        $result = $mailManager->mail($module, $key, $to, $langcode, $params, NULL, $send);  
    }


        //batch completed
    public static function sendEmailsItemCallback($success, $results, $operations)
    {
        if ($success) {
            $message = \Drupal::translation()->formatPlural(
                    count($results),
                    'sent.', 
                    'Emails sent succesfully'
            );
        }
        else {
            $message = t('Finished with an error.');
        }
        drupal_set_message('Sent succesfully');

    }


}

Right now I am getting this output:batch is stuck at 0%. It is net even getting started.

5
  • Perhaps you should give some sample code
    – GiorgosK
    Feb 28, 2019 at 7:14
  • @GiorhosK i have added code.
    – user92338
    Feb 28, 2019 at 8:45
  • This is a good start but please make an effort to format the code properly and perhaps giving some more info on each part of the code might get someone to take a closer look at your problem.
    – GiorgosK
    Feb 28, 2019 at 9:26
  • @GiorgosK please see now.
    – user92338
    Feb 28, 2019 at 11:18
  • Batch API is a user-driven activity. Instead of thinking about it as a Batch, think about it like a Queue API doing the same thing (but without a cron worker). My general pattern is that the first request initiates the queue, but all further requests to the end point contain a queue item id. The response contains information about the progress of the queue, which is also stored with each queue item.
    – mradcliffe
    Feb 28, 2019 at 12:07

2 Answers 2

1

Batch API operations are driven by the end user and browser with meta refresh, which makes it not possible to use in web services. However we can think about implementing the same thing using Queue API.

This should give you enough to go on to re-implement your RestResource or Controller to use your own Queue service.

Summary

I've used the following pattern in the past.

  1. First request has a payload that can instantiate the operation.
    • It's important to make sure we're not flooding the serve so make sure you have flood protection.
    • Make sure that we have access to do the operation.
  2. A response is returned with progress state and a queue item ID.
  3. Subsequent requests have a payload with the queue item ID from the previous response.
    • These requests process 1..n queue items.
    • We need to claim queue items ourselves because we're processing queue items manually and not with a cron queue worker.
    • Make sure that things are valid and return the appropriate response code for validation, fatal or other errors.

Request payloads

  • Initial: {"queue": null}
  • Subsequent: {"queue": {"item_id": 1}}

Response payloads

Initial
{
   "success": true,
   "finished": false,
   "queue": {"item_id": 1},
   "progress": 0,
   "max": 23,
   "errors": [],
   "result": "something custom here",
}
Finished
{
   "success": true,
   "finished": true,
   "queue": null,
   "progress": 23,
   "max": 23,
   "errors": ["something wrong happened at some point"],
   "result": "something custom here",
}

Queue API

The complexity here is that we cannot use cron queue workers. So we need to implement the database calls to get and claim queue items ourselves. Unfortunately the current Queue API classes are pretty limited in both Drupal 7 and Drupal 8. In the former, I used custom functions to fetch and update queue items directly from the database while in the latter we can create our own service, based on DatabaseQueue, and inject that into your RestResource plugin or Controller.

2
  • pls explain in more details..
    – user92338
    Mar 1, 2019 at 17:15
  • I don't know what details you need more explanation for. Most of anything else would require more specific code examples on how you want to implement your batch.
    – mradcliffe
    Mar 1, 2019 at 21:02
0

I had the same trouble running batches like this before as the classes were not loading properly. I suspect you might have the same problem. You can probably find in your database logs something like this

Warning: call_user_func_array() expects parameter 1 to be a valid callback, class '\Drupal\module_mail\sendEmails' not found

In such situations I usually include the functions as a file.

Create a file in your MODULENAME/module_mail.emails_batch.inc and in it your email processing logic as functions and not as class methods.

function module_mail_sendEmailsUserItem($user, $subject, $body, &$context)
{  
// ...    
}

//batch completed
function module_mail_sendEmailsItemCallback($success, $results, $operations)
{    
// ...    
}

and modify finished and operarations and callback function keys as follows

$batch = array(
  // ...
  'finished' => 'module_mail_sendEmailsItemCallback',
  'file' => drupal_get_path('module', 'batch_emails') . '/batch_emails.emails_batch.inc',
);

also need to change callback function

foreach($data as $row) {
  $batch['operations'][] = ['module_mail_sendEmailsUserItem',[$row,$subject,$body]];
}    

NOTE: make sure batch_process('home'); home is a valid path perhaps you can use batch_process('<front>'); or batch_process('node/1');

1
  • this is not the problem..batch is working fine in the browser but not in postman hence no error in code.
    – user92338
    Mar 1, 2019 at 4:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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