Skip to content

Commit d6cd619

Browse files
author
Philip Norton
committed
Added sub modules for all examples, with minimal unit tests for each form.
1 parent 44c6c47 commit d6cd619

37 files changed

+1444
-1
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
# drupal-batch-examples
1+
# Drupal Batch Examples

drupal_batch_examples.info.yml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
name: 'Drupal Batch Examples'
2+
type: module
3+
description: 'A set of modules that show examples of the batch API.'
4+
package: Custom
5+
core_version_requirement: ^10 || ^11

drupal_batch_examples.links.menu.yml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
drupal_batch_examples:
2+
title: 'Drupal Batch Examples'
3+
parent: system.admin
4+
description: 'Drupal batch examples.'
5+
route_name: drupal_batch_examples
6+
weight: 1

drupal_batch_examples.routing.yml

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
drupal_batch_examples:
2+
path: '/drupal-batch-examples'
3+
defaults:
4+
_controller: '\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage'
5+
_title: 'Drupal Batch Examples'
6+
requirements:
7+
_permission: 'access content'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
name: 'Batch additions example'
2+
type: module
3+
description: 'An example of adding a batch inside a running batch.'
4+
package: Custom
5+
core_version_requirement: ^10 || ^11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
batch_addition_example:
2+
title: 'Batch Addition Example'
3+
parent: drupal_batch_examples
4+
description: 'Batch addition example.'
5+
route_name: batch_addition_example
6+
weight: 2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
batch_addition_example:
2+
path: '/drupal-batch-examples/batch-addition-example'
3+
defaults:
4+
_title: 'Batch Addition Example'
5+
_form: 'Drupal\batch_addition_example\Form\BatchForm'
6+
requirements:
7+
_permission: 'access content'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<?php
2+
3+
namespace Drupal\batch_addition_example\Batch;
4+
5+
use Drupal\Core\Batch\BatchBuilder;
6+
7+
/**
8+
* Class to show how to set up a batch within a batch process.
9+
*/
10+
class BatchClass {
11+
12+
/**
13+
* Process a batch operation.
14+
*
15+
* @param int $batchId
16+
* The batch ID.
17+
* @param array $chunk
18+
* The chunk to process.
19+
* @param bool $addBatch
20+
* Trigger to create an additional batch within this process.
21+
* @param array $context
22+
* Batch context.
23+
*/
24+
public static function batchProcess(int $batchId, array $chunk, bool $addBatch, array &$context): void {
25+
if (!isset($context['sandbox']['progress'])) {
26+
$context['sandbox']['progress'] = 0;
27+
$context['sandbox']['max'] = 1000;
28+
}
29+
if (!isset($context['results']['updated'])) {
30+
$context['results']['updated'] = 0;
31+
$context['results']['skipped'] = 0;
32+
$context['results']['failed'] = 0;
33+
$context['results']['progress'] = 0;
34+
}
35+
36+
if ($addBatch === TRUE) {
37+
// For every "original" batch run we add another chunk of numbers to be
38+
// processed. This simulates generating an additional bach run inside an
39+
// existing batch.
40+
$batch = new BatchBuilder();
41+
$batch->setTitle('Running batch process.')
42+
->setFinishCallback([BatchClass::class, 'batchFinished'])
43+
->setInitMessage('Commencing')
44+
->setProgressMessage('Processing...')
45+
->setErrorMessage('An error occurred during processing.');
46+
47+
// Add a new chunk to process.
48+
$args = [
49+
$batchId + 1000,
50+
range(1, 100),
51+
FALSE,
52+
];
53+
$batch->addOperation([BatchClass::class, 'batchProcess'], $args);
54+
55+
batch_set($batch->toArray());
56+
}
57+
58+
// Keep track of progress.
59+
$context['results']['progress'] += count($chunk);
60+
$context['results']['process'] = 'Addition batch completed';
61+
62+
// Message above progress bar.
63+
$context['message'] = t('Processing batch #@batch_id batch size @batch_size for total @count items.', [
64+
'@batch_id' => number_format($batchId),
65+
'@batch_size' => number_format(count($chunk)),
66+
'@count' => number_format($context['sandbox']['max']),
67+
]);
68+
69+
foreach ($chunk as $number) {
70+
// Sleep for a bit to simulate work being done.
71+
usleep(4000 + $number);
72+
// Decide on the result of the batch.
73+
$result = rand(1, 4);
74+
switch ($result) {
75+
case '1':
76+
case '2':
77+
$context['results']['updated']++;
78+
break;
79+
80+
case '3':
81+
$context['results']['skipped']++;
82+
break;
83+
84+
case '4':
85+
$context['results']['failed']++;
86+
break;
87+
}
88+
}
89+
}
90+
91+
/**
92+
* Handle batch completion.
93+
*
94+
* @param bool $success
95+
* TRUE if all batch API tasks were completed successfully.
96+
* @param array $results
97+
* An array of processed node IDs.
98+
* @param array $operations
99+
* A list of the operations that had not been completed.
100+
* @param string $elapsed
101+
* Batch.inc kindly provides the elapsed processing time in seconds.
102+
*/
103+
public static function batchFinished(bool $success, array $results, array $operations, string $elapsed): void {
104+
$messenger = \Drupal::messenger();
105+
if ($success) {
106+
// Show success message to the user.
107+
$messenger->addMessage(t('@process processed @count, skipped @skipped, updated @updated, failed @failed in @elapsed.', [
108+
'@process' => $results['process'],
109+
'@count' => $results['progress'],
110+
'@skipped' => $results['skipped'],
111+
'@updated' => $results['updated'],
112+
'@failed' => $results['failed'],
113+
'@elapsed' => $elapsed,
114+
]));
115+
// Log the batch success.
116+
\Drupal::logger('delete_orphan')->info(
117+
'@process processed @count, skipped @skipped, updated @updated, failed @failed in @elapsed.', [
118+
'@process' => $results['process'],
119+
'@count' => $results['progress'],
120+
'@skipped' => $results['skipped'],
121+
'@updated' => $results['updated'],
122+
'@failed' => $results['failed'],
123+
'@elapsed' => $elapsed,
124+
]);
125+
}
126+
else {
127+
// An error occurred. $operations contains the operations that remained
128+
// unprocessed. Pick the last operation and report on what happened.
129+
$error_operation = reset($operations);
130+
if ($error_operation) {
131+
$message = t('An error occurred while processing %error_operation with arguments: @arguments', [
132+
'%error_operation' => print_r($error_operation[0]),
133+
'@arguments' => print_r($error_operation[1], TRUE),
134+
]);
135+
$messenger->addError($message);
136+
}
137+
}
138+
}
139+
140+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
namespace Drupal\batch_addition_example\Form;
4+
5+
use Drupal\batch_addition_example\Batch\BatchClass;
6+
use Drupal\Core\Batch\BatchBuilder;
7+
use Drupal\Core\Form\FormBase;
8+
use Drupal\Core\Form\FormStateInterface;
9+
use Drupal\Core\Url;
10+
11+
/**
12+
* Form that triggers a batch run.
13+
*/
14+
class BatchForm extends FormBase {
15+
16+
/**
17+
* {@inheritdoc}
18+
*/
19+
public function getFormId() {
20+
return 'batch_addition_example';
21+
}
22+
23+
/**
24+
* {@inheritdoc}
25+
*/
26+
public function buildForm(array $form, FormStateInterface $form_state) {
27+
$form['actions'] = [
28+
'#type' => 'actions',
29+
'submit' => [
30+
'#type' => 'submit',
31+
'#value' => $this->t('Run batch'),
32+
],
33+
];
34+
35+
return $form;
36+
}
37+
38+
/**
39+
* {@inheritdoc}
40+
*/
41+
public function submitForm(array &$form, FormStateInterface $form_state): void {
42+
$batch = new BatchBuilder();
43+
$batch->setTitle('Running batch process.')
44+
->setFinishCallback([BatchClass::class, 'batchFinished'])
45+
->setInitMessage('Commencing')
46+
->setProgressMessage('Processing...')
47+
->setErrorMessage('An error occurred during processing.');
48+
49+
// Create 10 chunks of 100 items.
50+
$chunks = array_chunk(range(1, 1000), 100);
51+
52+
// Process each chunk in the array.
53+
foreach ($chunks as $id => $chunk) {
54+
$args = [
55+
$id,
56+
$chunk,
57+
TRUE,
58+
];
59+
$batch->addOperation([BatchClass::class, 'batchProcess'], $args);
60+
}
61+
batch_set($batch->toArray());
62+
63+
$form_state->setRedirectUrl(new Url($this->getFormId()));
64+
}
65+
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Drupal\Tests\batch_addition_example\Functional;
4+
5+
use Drupal\Tests\BrowserTestBase;
6+
7+
/**
8+
* Test the functionality of the batch addition form example form.
9+
*
10+
* @group content_links
11+
*/
12+
class BatchFormTest extends BrowserTestBase {
13+
14+
/**
15+
* Modules to enable.
16+
*
17+
* @var array
18+
*/
19+
protected static $modules = [
20+
'batch_addition_example',
21+
];
22+
23+
/**
24+
* The theme to install as the default for testing.
25+
*
26+
* @var string
27+
*/
28+
public $defaultTheme = 'stark';
29+
30+
/**
31+
* Test that the batch operation runs.
32+
*/
33+
public function testBatchOperationRuns() {
34+
$user = $this->createUser(['access content']);
35+
$this->drupalLogin($user);
36+
37+
$this->drupalGet('drupal-batch-examples/batch-addition-example');
38+
39+
$this->submitForm([], 'Run batch');
40+
$this->assertSession()->pageTextContains('Addition batch completed processed');
41+
}
42+
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
name: 'Batch class example'
2+
type: module
3+
description: 'An example of running batch via a class. Includes Drush support.'
4+
package: Custom
5+
core_version_requirement: ^10 || ^11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
batch_class_example:
2+
title: 'Batch Class Example'
3+
parent: drupal_batch_examples
4+
description: 'Batch class example.'
5+
route_name: batch_class_example
6+
weight: 2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
batch_class_example:
2+
path: '/drupal-batch-examples/batch-class-example'
3+
defaults:
4+
_title: 'Batch Class Example'
5+
_form: 'Drupal\batch_class_example\Form\BatchForm'
6+
requirements:
7+
_permission: 'access content'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
services:
2+
batch_class_example.commands:
3+
class: \Drupal\batch_class_example\Commands\BatchCommands
4+
tags:
5+
- { name: drush.command }
6+
arguments: ['@logger.factory']

0 commit comments

Comments
 (0)