diff --git a/README.md b/README.md index cdade9a..20984b0 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,8 @@ This migration will move the `id`, `email` and `name` fields from the the `users ### Source data validation You can use UXDM to validate the source data. If validation fails part way through a migration, the migration will -halt and a `ValidationException` will be thrown. +halt and a `ValidationException` will be thrown. However, if `->validateBeforeMigrating()` is called, all data rows +will be preemptively validated before the migration begins. The code below shows how to validate various fields. @@ -119,6 +120,7 @@ $migrator->setSource($pdoSource) 'email' => [new Required(), new IsString(), new IsEmail()], 'name' => [new Required(), new IsString()], ]) + // ->validateBeforeMigrating() ->setKeyFields(['id']) ->withProgressBar() ->migrate(); diff --git a/src/Objects/DataRow.php b/src/Objects/DataRow.php index 7dca418..06866bb 100755 --- a/src/Objects/DataRow.php +++ b/src/Objects/DataRow.php @@ -73,7 +73,7 @@ public function prepare(array $validationRules, array $keyFields, array $fieldMa } } - private function validate(array $validationRules) + public function validate(array $validationRules) { if (!$this->dataItems) { throw new NoDataItemsInDataRowException('Data row contains no data items. The specified source may be producing an invalid data row.'); @@ -82,9 +82,7 @@ private function validate(array $validationRules) if ($validationRules) { $validator = new Validator($this->toArray(), $validationRules); if ($validator->fails()) { - $messages = print_r($validator->messages(), true); - - throw new ValidationException($messages); + throw new ValidationException($validator->messages(), $this); } } } diff --git a/src/Objects/Exceptions/ValidationException.php b/src/Objects/Exceptions/ValidationException.php index 8aaabb8..b762edc 100644 --- a/src/Objects/Exceptions/ValidationException.php +++ b/src/Objects/Exceptions/ValidationException.php @@ -2,8 +2,39 @@ namespace DivineOmega\uxdm\Objects\Exceptions; +use DivineOmega\uxdm\Objects\DataRow; use Exception; class ValidationException extends Exception { + private $validationMessages; + private $dataRow; + + public function __construct(array $validationMessages, DataRow $dataRow) + { + $this->validationMessages = $validationMessages; + $this->dataRow = $dataRow; + + parent::__construct($this->buildExceptionMessage()); + } + + private function buildExceptionMessage(): string + { + $message = 'Validation exception: '.PHP_EOL; + $message .= print_r($this->validationMessages, true).PHP_EOL; + $message .= 'Data row: '.PHP_EOL; + $message .= print_r($this->dataRow->toArray(), true.PHP_EOL); + + return $message; + } + + public function getValidationMessages(): array + { + return $this->validationMessages; + } + + public function getDataRow(): DataRow + { + return $this->dataRow; + } } diff --git a/src/Objects/Migrator.php b/src/Objects/Migrator.php index cdc3a1d..4122207 100644 --- a/src/Objects/Migrator.php +++ b/src/Objects/Migrator.php @@ -28,6 +28,8 @@ class Migrator private $sourceCacheKey; private $sourceCacheExpiresAfter; private $showProgressBar = false; + private $validateBeforeMigrating = false; + private $progressBar = null; /** * Set the source object to migrate data from. @@ -257,6 +259,18 @@ public function withProgressBar() return $this; } + /** + * Enables validation of all data rows before the migration begins. + * + * @return $this + */ + public function validateBeforeMigrating() + { + $this->validateBeforeMigrating = true; + + return $this; + } + /** * Retrieves one page of data rows from the source. * @@ -337,6 +351,33 @@ public function migrate(): void { $this->sanityCheck(); + if ($this->validateBeforeMigrating) { + if ($this->showProgressBar) { + $this->progressBar = new ProgressBar(); + $this->progressBar->setMessage('Validating...'); + $this->progressBar->setMaxProgress($this->source->countPages() * count($this->destinationContainers)); + $this->progressBar->display(); + } + + for ($page = 1; $page < PHP_INT_MAX; $page++) { + $dataRows = $this->getSourceDataRows($page); + + if (!$dataRows) { + break; + } + + foreach ($dataRows as $key => $dataRow) { + $dataRow->validate(); + } + + $this->advanceProgressBar(); + } + + if ($this->showProgressBar) { + $this->progressBar->complete(); + } + } + if ($this->showProgressBar) { $progressBar = new ProgressBar(); $progressBar->setMaxProgress($this->source->countPages() * count($this->destinationContainers));