While we are waiting for Drupal 9 core implementation of OOP Batches we need a start point for them.
Example is for Drupal 9.2.6+ (PHP 7.4.2+)
Possible path to modules: web/modules/custom
- Create module and name it
module_example
and add there/module_example/module_example.module
. It's required file so just place it in module folder.
module_example.module
<?php /* * Every module needs this file. */
- Add
/module_example/module_example.info.yml
file. Also required file, fill in info about module.
module_example.info.yml
# Change name, description, package on your needs. name: Module Example type: module description: Module example package: Module example core_version_requirement: ^9
- Add
/module_example/module_example.routing.yml
file. Fill in here route for our form from where we will be starting our batch.
module_example.routing.yml
# Change here routing name,path,_form,_title for your specific form. module_example.admin_form.example_form: path: '/admin/config/system/module-example' defaults: _form: '\Drupal\module_example\Form\ExampleForm' _title: 'Form example' requirements: _user_is_logged_in: 'TRUE' _permission: 'administer site configuration'
- Add
/module_example/module_example.services.yml
file. We need it for our batch, it will be a service because we haven't any special definition for it.
module_example.services.yml
# Change here service name, class and arguments to inject for your needs. services: module_example.batch_example: class: Drupal\module_example\Batch\BatchExample arguments: ['@messenger', '@extension.list.module']
- Create
/module_example/src
folder. - Create
/module_example/src/Form
folder. - Create
/module_example/src/Form/ExampleForm.php
file. We need this form to control our batch from somewhere. This example uses form.
ExampleForm.php
<?php namespace Drupal\module_example\Form; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Implements an Example form. */ class ExampleForm extends FormBase { /** * Batch injection. * * @var \Drupal\module_example\Batch\BatchExample * Grab the path from module_example.services.yml file. */ protected $batch; /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { // Instantiates this form class. $instance = parent::create($container); // Put here injection of your batch service by name from module_example.services.yml file. $instance->batch = $container->get('module_example.batch_example'); return $instance; } /** * {@inheritdoc} */ public function getFormId() { // Any name for your form to indicate it. return 'example_of_form'; } /** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { // No need for t() in admin part. // Triggers submitForm() function. $form['submit'] = [ '#type' => 'submit', '#prefix' => '<br>', '#value' => 'Start batch', ]; return $form; } /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { // As we don't need to change in our batch depending on inputs, // just passing empty array or removing param. $values = []; $this->batch->run($values); } }
- Create
/module_example/src/Batch
folder. - Add
/module_example/src/Batch/BatchExample.php
file.
BatchExample.php
<?php namespace Drupal\module_example\Batch; use Drupal\Core\Batch\BatchBuilder; use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Extension\ModuleExtensionList; use Drupal\Core\DependencyInjection\DependencySerializationTrait; /** * Batch Example. */ class BatchExample { use DependencySerializationTrait; /** * The messenger. * * @var \Drupal\Core\Messenger\MessengerInterface */ protected $messenger; /** * Module extension list. * * @var \Drupal\Core\Extension\ModuleExtensionList */ protected $moduleExtensionList; /** * Constructor. * * @param \Drupal\Core\Messenger\MessengerInterface $messenger * The messenger. * @param \Drupal\Core\Extension\ModuleExtensionList $moduleExtensionList * The module extension list. */ public function __construct( MessengerInterface $messenger, ModuleExtensionList $moduleExtensionList ) { $this->messenger = $messenger; $this->moduleExtensionList = $moduleExtensionList; } /** * The Starting point for batch. * * @param $values * If you need to change behavior of batch but not much, send values from external. */ public function run($values) { // File needs to be placed /module_example/src/Batch or hardcode the name of module. $moduleName = basename(dirname(__DIR__, 2)); $modulePath = $this->moduleExtensionList->getPath($moduleName); $batchBuilder = new BatchBuilder(); $batchBuilder // Change batch name here. ->setTitle('Example batch') ->setInitMessage('Initializing. <br/><b style="color: #f00;">Navigating away will stop the process.</b>') ->setProgressMessage('Completed @current of @total. <br/><b style="color: #f00;">Navigating away will stop the process.</b>') ->setErrorMessage('Batch has encountered an error.') ->setFile($modulePath . '/src/Batch/' . basename(__FILE__)); // Dummy data, grab data on this place. $items = [ ['id' => 0, 'data' => 'item'], ['id' => 1, 'data' => 'item'], ['id' => 2, 'data' => 'item'], ]; if (!empty($items)) { foreach ($items as $item) { // Adding operations that we will process on each item in a batch. $batchBuilder->addOperation([$this, 'process'], [ $item, // Add how many variables you need here. ]); } $batchBuilder->setFinishCallback([$this, 'finish']); // Changing it to array that we can set it in functional way (only way on this moment). $batch = $batchBuilder->toArray(); batch_set($batch); } else { $this->messenger->addMessage('No entities exists'); } } /** * Batch processor. */ public function process($item, &$context) { try { $id = $item['id']; // Display a progress message. $context['message'] = "Now processing {$id} entity..."; // Body of the batch, logic that needs to be presented place here. $changed = FALSE; // For example we will change item data. if (!empty($item['data'])) { $item['data'] .= $id; $changed = TRUE; } if ($changed === TRUE) { // Save the changes for your objects here. } else { // Skip this step if something went wrong or changes weren't presented. throw new \Exception('Skip if cant handle'); } } catch (\Throwable $th) { $this->messenger->addError($th->getMessage()); } } /** * Finish operation. */ public function finish($success, $results, $operations, $elapsed) { if ($success) { // Change success message here. $this->messenger->addMessage('Example batch is finished!'); } else { $error_operation = reset($operations); $arguments = print_r($error_operation[1], TRUE); $message = "An error occurred while processing {$error_operation[0]} with arguments: {$arguments}"; $this->messenger->addMessage($message, 'error'); } } }
It's only a skeleton for batch without full overview of it's functionality.
No $context['sandbox']
and else where used here but you can read about it in official documentation here - https://www.drupal.org/docs/7/api/batch-api/overview
Top comments (0)