|null}> $operations * @param bool $atomic All or nothing. * @param bool $rollback Undo everything even when all succeeded — a dry run. * @return array{results: array>, committed: bool} */ public function run(Request $parent, array $operations, bool $atomic = false, bool $rollback = false): array { $results = []; $execute = function () use ($parent, $operations, $atomic, &$results): void { foreach ($operations as $index => $operation) { $result = $this->dispatch($parent, $operation); $results[] = ['index' => $index, ...$result]; if ($atomic && $result['status'] >= 400) { throw new BatchAborted; } } }; if (! $atomic && ! $rollback) { $execute(); return ['results' => $results, 'committed' => true]; } try { DB::transaction(function () use ($execute, $rollback): void { $execute(); if ($rollback) { throw new BatchAborted; } }); } catch (BatchAborted) { return ['results' => $results, 'committed' => false]; } return ['results' => $results, 'committed' => true]; } /** * @param array{method: string, path: string, body?: array|null} $operation * @return array{method: string, path: string, status: int, body: mixed} */ protected function dispatch(Request $parent, array $operation): array { $method = strtoupper($operation['method']); $path = $operation['path']; $request = Request::create( '/api'.$path, $method, server: array_filter([ 'HTTP_AUTHORIZATION' => $parent->header('Authorization'), 'HTTP_ACCEPT' => 'application/json', 'HTTP_ACCEPT_LANGUAGE' => $parent->header('Accept-Language'), 'CONTENT_TYPE' => 'application/json', 'REMOTE_ADDR' => $parent->ip(), ]), content: $method === 'GET' ? null : (string) json_encode((object) ($operation['body'] ?? [])), ); try { $response = $this->kernel->handle($request); } catch (Throwable $e) { report($e); return ['method' => $method, 'path' => $path, 'status' => 500, 'body' => null]; } finally { // The kernel swapped the current request for the operation's; // whatever runs after this one still belongs to the batch's. app()->instance('request', $parent); } $content = $response->getContent(); return [ 'method' => $method, 'path' => $path, 'status' => $response->getStatusCode(), 'body' => $content === '' || $content === false ? null : json_decode($content, true), ]; } }