> */ public function rowsFromCsv(UploadedFile $file): array { $handle = fopen($file->getRealPath(), 'r'); $header = null; $rows = []; while (($line = fgetcsv($handle, escape: '\\')) !== false) { if ($line === [null]) { continue; } if ($header === null) { // A byte-order mark is what a spreadsheet saving "CSV UTF-8" // puts before the first column's name. $header = array_map(fn ($name) => trim((string) preg_replace('/^\xEF\xBB\xBF/', '', (string) $name)), $line); continue; } $row = []; foreach ($header as $i => $column) { $value = $line[$i] ?? ''; if ($column === '' || $value === '') { continue; } Arr::set($row, $column, $this->cell($column, $value)); } $rows[] = $row; } fclose($handle); return $rows; } /** * @param array> $rows * @return array}> */ public function operations(string $resource, array $rows, ?string $match = null): array { if (count($rows) > self::MAX_ROWS) { throw ValidationException::withMessages(['rows' => [__('rules.batch.too_many', ['max' => self::MAX_ROWS])]]); } return array_map(function (array $row) use ($resource, $match): array { $id = $row['id'] ?? $this->existing($resource, $row, $match); unset($row['id']); return $id === null ? ['method' => 'POST', 'path' => "/v1/{$resource}", 'body' => $row] : ['method' => 'PATCH', 'path' => "/v1/{$resource}/{$id}", 'body' => $row]; }, $rows); } /** * The record a row names through its `match` field, if any: a slug is * looked up in the default language when it is translated. * * @param array $row */ protected function existing(string $resource, array $row, ?string $match): mixed { if ($match === null || ! array_key_exists($match, $row)) { return null; } $model = AdminContent::model($resource); $instance = new $model; $value = $row[$match]; $translated = in_array(HasTranslations::class, class_uses_recursive($model), true) && in_array($match, $instance->translatable, true); if ($translated) { $locale = (string) config('locales.default'); $value = is_array($value) ? ($value[$locale] ?? null) : $value; $column = "{$match}->{$locale}"; } else { $column = $match; } if ($value === null || is_array($value)) { return null; } $query = $model::query(); if (method_exists($instance, 'bootSoftDeletes')) { $query->withTrashed(); } return $query->where($column, $value)->value($instance->getKeyName()); } protected function cell(string $column, string $value): mixed { $base = explode('.', $column)[0]; return match (true) { in_array($base, self::LISTS, true) => array_map( fn ($item) => is_numeric($item) ? (int) $item : $item, array_values(array_filter(array_map('trim', explode('|', $value)), fn ($item) => $item !== '')), ), strtolower($value) === 'true' => true, strtolower($value) === 'false' => false, strtolower($value) === 'null' => null, default => $value, }; } }