. */ declare(strict_types=1); namespace Fisharebest\Webtrees\Http\RequestHandlers; use Fisharebest\Webtrees\GedcomRecord; use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Module\ModuleDataFixInterface; use Fisharebest\Webtrees\Services\DataFixService; use Fisharebest\Webtrees\Services\ModuleService; use Fisharebest\Webtrees\Tree; use Fisharebest\Webtrees\Validator; use Illuminate\Support\Collection; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use function assert; use function json_encode; use function response; use const JSON_THROW_ON_ERROR; /** * Run a data-fix. */ class DataFixUpdateAll implements RequestHandlerInterface { // Process this number of records in each HTTP request private const CHUNK_SIZE = 250; private DataFixService $data_fix_service; private ModuleService $module_service; /** * @param DataFixService $data_fix_service * @param ModuleService $module_service */ public function __construct( DataFixService $data_fix_service, ModuleService $module_service ) { $this->data_fix_service = $data_fix_service; $this->module_service = $module_service; } /** * @param ServerRequestInterface $request * * @return ResponseInterface */ public function handle(ServerRequestInterface $request): ResponseInterface { $tree = Validator::attributes($request)->tree(); $data_fix = Validator::attributes($request)->string('data_fix', ''); $module = $this->module_service->findByName($data_fix); assert($module instanceof ModuleDataFixInterface); $params = $request->getQueryParams(); $rows = $module->recordsToFix($tree, $params); if ($rows->isEmpty()) { return response([]); } $start = Validator::queryParams($request)->string('start', ''); $end = Validator::queryParams($request)->string('end', ''); if ($start === '' || $end === '') { return $this->createUpdateRanges($tree, $module, $rows, $params); } /** @var Collection $records */ $records = $rows ->map(fn(object $row): ?GedcomRecord => $this->data_fix_service->getRecordByType($row->xref, $tree, $row->type)) ->filter(static fn(?GedcomRecord $record): bool => $record instanceof GedcomRecord && !$record->isPendingDeletion() && $module->doesRecordNeedUpdate($record, $params)); foreach ($records as $record) { $module->updateRecord($record, $params); } return response(); } /** * @param Tree $tree * @param ModuleDataFixInterface $module * @param Collection $rows * @param array $params * * @return ResponseInterface */ private function createUpdateRanges( Tree $tree, ModuleDataFixInterface $module, Collection $rows, array $params ): ResponseInterface { $total = $rows->count(); $updates = $rows ->chunk(self::CHUNK_SIZE) ->map(static function (Collection $chunk) use ($module, $params, $tree, $total): object { static $count = 0; $count += $chunk->count(); $start = $chunk->first()->xref; $end = $chunk->last()->xref; $url = route(self::class, [ 'tree' => $tree->name(), 'data_fix' => $module->name(), 'start' => $start, 'end' => $end, ] + $params); return (object) [ 'url' => $url, 'percent' => (100.0 * $count / $total) . '%', 'progress' => I18N::percentage($count / $total, 1), ]; }) ->all(); return response(json_encode($updates, JSON_THROW_ON_ERROR)); } }