Unverified Commit ac703dca authored by alexpott's avatar alexpott

Issue #2401797 by John Cook, larowlan, Jo Fitzgerald, dawehner, alexpott,...

Issue #2401797 by John Cook, larowlan, Jo Fitzgerald, dawehner, alexpott, Mile23, kim.pepper, phenaproxima, borisson_, andypost, voleger: Introduce a batch builder class to make the batch API easier to use
parent 00a526a9
<?php
namespace Drupal\Core\Batch;
use Drupal\Core\Queue\QueueInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
/**
* Builds an array for a batch process.
*
* Example code to create a batch:
* @code
* $batch_builder = (new BatchBuilder())
* ->setTitle(t('Batch Title'))
* ->setFinishCallback('batch_example_finished_callback')
* ->setInitMessage(t('The initialization message (optional)'));
* foreach ($ids as $id) {
* $batch_builder->addOperation('batch_example_callback', [$id]);
* }
* batch_set($batch_builder->toArray());
* @endcode
*/
class BatchBuilder {
/**
* The set of operations to be processed.
*
* Each operation is a tuple of the function / method to use and an array
* containing any parameters to be passed.
*
* @var array
*/
protected $operations = [];
/**
* The title for the batch.
*
* @var string|\Drupal\Core\StringTranslation\TranslatableMarkup
*/
protected $title;
/**
* The initializing message for the batch.
*
* @var string|\Drupal\Core\StringTranslation\TranslatableMarkup
*/
protected $initMessage;
/**
* The message to be shown while the batch is in progress.
*
* @var string|\Drupal\Core\StringTranslation\TranslatableMarkup
*/
protected $progressMessage;
/**
* The message to be shown if a problem occurs.
*
* @var string|\Drupal\Core\StringTranslation\TranslatableMarkup
*/
protected $errorMessage;
/**
* The name of a function / method to be called when the batch finishes.
*
* @var string
*/
protected $finished;
/**
* The file containing the operation and finished callbacks.
*
* If the callbacks are in the .module file or can be autoloaded, for example,
* static methods on a class, then this does not need to be set.
*
* @var string
*/
protected $file;
/**
* An array of libraries to be included when processing the batch.
*
* @var string[]
*/
protected $libraries = [];
/**
* An array of options to be used with the redirect URL.
*
* @var array
*/
protected $urlOptions = [];
/**
* Specifies if the batch is progressive.
*
* If true, multiple calls are used. Otherwise an attempt is made to process
* the batch in a single run.
*
* @var bool
*/
protected $progressive = TRUE;
/**
* The details of the queue to use.
*
* A tuple containing the name of the queue and the class of the queue to use.
*
* @var array
*/
protected $queue;
/**
* Sets the default values for the batch builder.
*/
public function __construct() {
$this->title = new TranslatableMarkup('Processing');
$this->initMessage = new TranslatableMarkup('Initializing.');
$this->progressMessage = new TranslatableMarkup('Completed @current of @total.');
$this->errorMessage = new TranslatableMarkup('An error has occurred.');
}
/**
* Sets the title.
*
* @param string|\Drupal\Core\StringTranslation\TranslatableMarkup $title
* The title.
*
* @return $this
*/
public function setTitle($title) {
$this->title = $title;
return $this;
}
/**
* Sets the finished callback.
*
* This callback will be executed if the batch process is done.
*
* @param callable $callback
* The callback.
*
* @return $this
*/
public function setFinishCallback(callable $callback) {
$this->finished = $callback;
return $this;
}
/**
* Sets the displayed message while processing is initialized.
*
* Defaults to 'Initializing.'.
*
* @param string|\Drupal\Core\StringTranslation\TranslatableMarkup $message
* The text to display.
*
* @return $this
*/
public function setInitMessage($message) {
$this->initMessage = $message;
return $this;
}
/**
* Sets the message to display when the batch is being processed.
*
* Defaults to 'Completed @current of @total.'.
*
* @param string|\Drupal\Core\StringTranslation\TranslatableMarkup $message
* The text to display. Available placeholders are:
* - '@current'
* - '@remaining'
* - '@total'
* - '@percentage'
* - '@estimate'
* - '@elapsed'.
*
* @return $this
*/
public function setProgressMessage($message) {
$this->progressMessage = $message;
return $this;
}
/**
* Sets the message to display if an error occurs while processing.
*
* Defaults to 'An error has occurred.'.
*
* @param string|\Drupal\Core\StringTranslation\TranslatableMarkup $message
* The text to display.
*
* @return $this
*/
public function setErrorMessage($message) {
$this->errorMessage = $message;
return $this;
}
/**
* Sets the file that contains the callback functions.
*
* The path should be relative to base_path(), and thus should be built using
* drupal_get_path(). Defaults to {module_name}.module.
*
* @param string $filename
* The path to the file.
*
* @return $this
*/
public function setFile($filename) {
$this->file = $filename;
return $this;
}
/**
* Sets the libraries to use when processing the batch.
*
* Adds the libraries for use on the progress page. Any previously added
* libraries are removed.
*
* @param string[] $libraries
* The libraries to be used.
*
* @return $this
*/
public function setLibraries(array $libraries) {
$this->libraries = $libraries;
return $this;
}
/**
* Sets the options for redirect URLs.
*
* @param array $options
* The options to use.
*
* @return $this
*
* @see \Drupal\Core\Url
*/
public function setUrlOptions(array $options) {
$this->urlOptions = $options;
return $this;
}
/**
* Sets the batch to run progressively.
*
* @param bool $is_progressive
* (optional) A Boolean that indicates whether or not the batch needs to run
* progressively. TRUE indicates that the batch will run in more than one
* run. FALSE indicates that the batch will finish in a single run. Defaults
* to TRUE.
*
* @return $this
*/
public function setProgressive($is_progressive = TRUE) {
$this->progressive = $is_progressive;
return $this;
}
/**
* Sets an override for the default queue.
*
* The class will typically either be \Drupal\Core\Queue\Batch or
* \Drupal\Core\Queue\BatchMemory. The class defaults to Batch if progressive
* is TRUE, or to BatchMemory if progressive is FALSE.
*
* @param string $name
* The unique identifier for the queue.
* @param string $class
* The fully qualified name of a class that implements
* \Drupal\Core\Queue\QueueInterface.
*
* @return $this
*/
public function setQueue($name, $class) {
if (!class_exists($class)) {
throw new \InvalidArgumentException('Class ' . $class . ' does not exist.');
}
if (!in_array(QueueInterface::class, class_implements($class))) {
throw new \InvalidArgumentException(
'Class ' . $class . ' does not implement \Drupal\Core\Queue\QueueInterface.'
);
}
$this->queue = [
'name' => $name,
'class' => $class,
];
return $this;
}
/**
* Adds a batch operation.
*
* @param callable $callback
* The name of the callback function.
* @param array $arguments
* An array of arguments to pass to the callback function.
*
* @return $this
*/
public function addOperation(callable $callback, array $arguments = []) {
$this->operations[] = [$callback, $arguments];
return $this;
}
/**
* Converts a \Drupal\Core\Batch\Batch object into an array.
*
* @return array
* The array representation of the object.
*/
public function toArray() {
$array = [
'operations' => $this->operations ?: [],
'title' => $this->title ?: '',
'init_message' => $this->initMessage ?: '',
'progress_message' => $this->progressMessage ?: '',
'error_message' => $this->errorMessage ?: '',
'finished' => $this->finished,
'file' => $this->file,
'library' => $this->libraries ?: [],
'url_options' => $this->urlOptions ?: [],
'progressive' => $this->progressive,
];
if ($this->queue) {
$array['queue'] = $this->queue;
}
return $array;
}
}
<?php
namespace Drupal\Tests\system\Unit\Batch;
use Drupal\Core\Batch\BatchBuilder;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Tests\UnitTestCase;
/**
* Tests for the batch builder class.
*
* @coversDefaultClass \Drupal\Core\Batch\BatchBuilder
*
* @group system
*/
class BatchBuilderTest extends UnitTestCase {
/**
* Tests the default values.
*
* @covers ::toArray
*/
public function testDefaultValues() {
$batch = (new BatchBuilder())->toArray();
$this->assertInternalType('array', $batch);
$this->assertArrayHasKey('operations', $batch);
$this->assertInternalType('array', $batch['operations']);
$this->assertEmpty($batch['operations'], 'Operations array is empty.');
$this->assertEquals(new TranslatableMarkup('Processing'), $batch['title']);
$this->assertEquals(new TranslatableMarkup('Initializing.'), $batch['init_message']);
$this->assertEquals(new TranslatableMarkup('Completed @current of @total.'), $batch['progress_message']);
$this->assertEquals(new TranslatableMarkup('An error has occurred.'), $batch['error_message']);
$this->assertNull($batch['finished']);
$this->assertNull($batch['file']);
$this->assertArrayHasKey('library', $batch);
$this->assertInternalType('array', $batch['library']);
$this->assertEmpty($batch['library']);
$this->assertArrayHasKey('url_options', $batch);
$this->assertInternalType('array', $batch['url_options']);
$this->assertEmpty($batch['url_options']);
$this->assertArrayHasKey('progressive', $batch);
$this->assertTrue($batch['progressive']);
$this->assertArrayNotHasKey('queue', $batch);
}
/**
* Tests setTitle().
*
* @covers ::setTitle
*/
public function testSetTitle() {
$batch = (new BatchBuilder())
->setTitle(new TranslatableMarkup('New Title'))
->toArray();
$this->assertEquals(new TranslatableMarkup('New Title'), $batch['title']);
}
/**
* Tests setFinishCallback().
*
* @covers ::setFinishCallback
*/
public function testSetFinishCallback() {
$batch = (new BatchBuilder())
->setFinishCallback('\Drupal\Tests\system\Unit\Batch\BatchBuilderTest::finishedCallback')
->toArray();
$this->assertEquals('\Drupal\Tests\system\Unit\Batch\BatchBuilderTest::finishedCallback', $batch['finished']);
}
/**
* Tests setInitMessage().
*
* @covers ::setInitMessage
*/
public function testSetInitMessage() {
$batch = (new BatchBuilder())
->setInitMessage(new TranslatableMarkup('New initialization message.'))
->toArray();
$this->assertEquals(new TranslatableMarkup('New initialization message.'), $batch['init_message']);
}
/**
* Tests setProgressMessage().
*
* @covers ::setProgressMessage
*/
public function testSetProgressMessage() {
$batch = (new BatchBuilder())
->setProgressMessage(new TranslatableMarkup('Batch in progress...'))
->toArray();
$this->assertEquals(new TranslatableMarkup('Batch in progress...'), $batch['progress_message']);
}
/**
* Tests setErrorMessage().
*/
public function testSetErrorMessage() {
$batch = (new BatchBuilder())
->setErrorMessage(new TranslatableMarkup('Oops. An error has occurred :('))
->toArray();
$this->assertEquals(new TranslatableMarkup('Oops. An error has occurred :('), $batch['error_message']);
}
/**
* Tests setFile().
*
* @covers ::setFile
*/
public function testSetFile() {
$batch = (new BatchBuilder())
->setFile('filename.php')
->toArray();
$this->assertEquals('filename.php', $batch['file']);
}
/**
* Tests setting and adding libraries.
*
* @covers ::setLibraries
*/
public function testAddingLibraries() {
$batch = (new BatchBuilder())
->setLibraries(['only/library'])
->toArray();
$this->assertEquals(['only/library'], $batch['library']);
}
/**
* Tests setProgressive().
*
* @covers ::setProgressive
*/
public function testSetProgressive() {
$batch_builder = new BatchBuilder();
$batch = $batch_builder
->setProgressive(FALSE)
->toArray();
$this->assertFalse($batch['progressive']);
$batch = $batch_builder
->setProgressive(TRUE)
->toArray();
$this->assertTrue($batch['progressive']);
}
/**
* Tests setQueue().
*
* @covers ::setQueue
*/
public function testSetQueue() {
$batch = (new BatchBuilder())
->setQueue('BatchName', '\Drupal\Core\Queue\Batch')
->toArray();
$this->assertArrayEquals([
'name' => 'BatchName',
'class' => '\Drupal\Core\Queue\Batch',
], $batch['queue'], 'Batch queue has been set.');
}
/**
* Tests queue class exists.
*
* @covers ::setQueue
*/
public function testQueueExists() {
$batch_builder = (new BatchBuilder());
$this->setExpectedException(\InvalidArgumentException::class, 'Class \ThisIsNotAClass does not exist.');
$batch_builder->setQueue('BatchName', '\ThisIsNotAClass');
}
/**
* Tests queue class implements \Drupal\Core\Queue\QueueInterface.
*
* @covers ::setQueue
*/
public function testQueueImplements() {
$batch_builder = (new BatchBuilder());
$this->setExpectedException(\InvalidArgumentException::class, 'Class Exception does not implement \Drupal\Core\Queue\QueueInterface.');
$batch_builder->setQueue('BatchName', \Exception::class);
}
/**
* Tests setUrlOptions().
*
* @covers ::setUrlOptions
*/
public function testSetUrlOptions() {
$options = [
'absolute' => TRUE,
'language' => 'de',
];
$batch = (new BatchBuilder())
->setUrlOptions($options)
->toArray();
$this->assertEquals($options, $batch['url_options']);
}
/**
* Tests addOperation().
*
* @covers ::addOperation
*/
public function testAddOperation() {
$batch_builder = new BatchBuilder();
$batch = $batch_builder
->addOperation('\Drupal\Tests\system\Unit\Batch\BatchBuilderTest::operationCallback')
->toArray();
$this->assertEquals([
['\Drupal\Tests\system\Unit\Batch\BatchBuilderTest::operationCallback', []],
], $batch['operations']);
$batch = $batch_builder
->addOperation('\Drupal\Tests\system\Unit\Batch\BatchBuilderTest::operationCallback', [2])
->addOperation('\Drupal\Tests\system\Unit\Batch\BatchBuilderTest::operationCallback', [3])
->toArray();
$this->assertEquals([
['\Drupal\Tests\system\Unit\Batch\BatchBuilderTest::operationCallback', []],
['\Drupal\Tests\system\Unit\Batch\BatchBuilderTest::operationCallback', [2]],
['\Drupal\Tests\system\Unit\Batch\BatchBuilderTest::operationCallback', [3]],
], $batch['operations']);
}
/**
* Empty callback for the tests.
*
* @internal
*/
public static function finishedCallback() {
}
/**
* Empty callback for the tests.
*
* @internal
*/
public static function operationCallback() {
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment