mirror of
https://github.com/snipe/snipe-it.git
synced 2024-11-16 02:24:09 -08:00
250 lines
5.4 KiB
PHP
250 lines
5.4 KiB
PHP
|
<?php
|
||
|
|
||
|
declare(strict_types=1);
|
||
|
|
||
|
namespace Tests\Support\Importing;
|
||
|
|
||
|
use Illuminate\Support\Str;
|
||
|
use Illuminate\Support\Collection;
|
||
|
use League\Csv\Reader;
|
||
|
use OutOfBoundsException;
|
||
|
|
||
|
/**
|
||
|
* @template Row of array
|
||
|
*/
|
||
|
abstract class FileBuilder
|
||
|
{
|
||
|
/**
|
||
|
* The import file rows.
|
||
|
*
|
||
|
* @var Collection<Row>
|
||
|
*/
|
||
|
protected Collection $rows;
|
||
|
|
||
|
/**
|
||
|
* Define the builders default row.
|
||
|
*
|
||
|
* @return Row
|
||
|
*/
|
||
|
abstract public function definition();
|
||
|
|
||
|
/**
|
||
|
* @param array<Row> $rows
|
||
|
*/
|
||
|
public function __construct(array $rows = [])
|
||
|
{
|
||
|
$this->rows = new Collection($rows);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get a new file builder instance.
|
||
|
*
|
||
|
* @param Row $attributes
|
||
|
*
|
||
|
* @return static
|
||
|
*/
|
||
|
public static function new(array $attributes = [])
|
||
|
{
|
||
|
$instance = new static;
|
||
|
|
||
|
return $instance->push($instance->definition())->replace($attributes);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get a new file builder instance from an import file.
|
||
|
*
|
||
|
* @return static
|
||
|
*/
|
||
|
public static function fromFile(string $filepath)
|
||
|
{
|
||
|
$instance = new static;
|
||
|
|
||
|
$reader = Reader::createFromPath($filepath);
|
||
|
$importFileHeaders = $reader->first();
|
||
|
$dictionary = array_flip($instance->getDictionary());
|
||
|
|
||
|
foreach ($reader->getRecords() as $key => $record) {
|
||
|
$row = [];
|
||
|
|
||
|
//Skip header.
|
||
|
if ($key === 0) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
foreach ($record as $index => $value) {
|
||
|
$columnNameInImportFile = $importFileHeaders[$index];
|
||
|
|
||
|
//Try to map the value to a dictionary or use the file's
|
||
|
//column if the key is not defined in the dictionary.
|
||
|
$row[$dictionary[$columnNameInImportFile] ?? $columnNameInImportFile] = $value;
|
||
|
}
|
||
|
|
||
|
$instance->push($row);
|
||
|
}
|
||
|
|
||
|
return $instance;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get a new builder instance for the given number of rows.
|
||
|
*
|
||
|
* @return static
|
||
|
*/
|
||
|
public static function times(int $amountOfRows = 1)
|
||
|
{
|
||
|
$instance = new static;
|
||
|
|
||
|
for ($i = 1; $i <= $amountOfRows; $i++) {
|
||
|
$instance->push($instance->definition());
|
||
|
}
|
||
|
|
||
|
return $instance;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* The the dictionary for mapping row keys to the corresponding import file headers.
|
||
|
*
|
||
|
* @return array<string,string>
|
||
|
*/
|
||
|
protected function getDictionary(): array
|
||
|
{
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add a new row.
|
||
|
*
|
||
|
* @param Row $row
|
||
|
*
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function push(array $row)
|
||
|
{
|
||
|
if (!empty($row)) {
|
||
|
$this->rows->push($row);
|
||
|
}
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Pluck an array of values from the rows.
|
||
|
*/
|
||
|
public function pluck(string $key): array
|
||
|
{
|
||
|
return $this->rows->pluck($key)->all();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Replace the keys in each row with the values of the given replacement if they exist.
|
||
|
*
|
||
|
* @param array<Row> $replacement
|
||
|
*
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function replace(array $replacement)
|
||
|
{
|
||
|
$this->rows = $this->rows->map(function (array $row) use ($replacement) {
|
||
|
foreach ($replacement as $key => $value) {
|
||
|
if (!array_key_exists($key, $row)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$row[$key] = $value;
|
||
|
}
|
||
|
|
||
|
return $row;
|
||
|
});
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove the the given keys from all rows.
|
||
|
*
|
||
|
* @param string|array<string> $keys
|
||
|
*
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function forget(array|string $keys)
|
||
|
{
|
||
|
$keys = (array) $keys;
|
||
|
|
||
|
$this->rows = $this->rows->map(function (array $row) use ($keys) {
|
||
|
foreach ($keys as $key) {
|
||
|
unset($row[$key]);
|
||
|
}
|
||
|
|
||
|
return $row;
|
||
|
});
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
public function toCsv(): array
|
||
|
{
|
||
|
if ($this->rows->isEmpty()) {
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
$headers = [];
|
||
|
$rows = $this->rows;
|
||
|
$dictionary = $this->getDictionary();
|
||
|
|
||
|
foreach (array_keys($rows->first()) as $key) {
|
||
|
$headers[] = $dictionary[$key] ?? $key;
|
||
|
}
|
||
|
|
||
|
return $rows
|
||
|
->map(fn (array $row) => array_values(array_combine($headers, $row)))
|
||
|
->prepend($headers)
|
||
|
->all();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Save the rows to the imports folder as a csv file.
|
||
|
*
|
||
|
* @return string The filename.
|
||
|
*/
|
||
|
public function saveToImportsDirectory(?string $filename = null): string
|
||
|
{
|
||
|
$filename ??= Str::random(40) . '.csv';
|
||
|
|
||
|
try {
|
||
|
$stream = fopen(config('app.private_uploads') . "/imports/{$filename}", 'w');
|
||
|
|
||
|
foreach ($this->toCsv() as $row) {
|
||
|
fputcsv($stream, $row);
|
||
|
}
|
||
|
|
||
|
return $filename;
|
||
|
} finally {
|
||
|
if (is_resource($stream)) {
|
||
|
fclose($stream);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the first row of the import file.
|
||
|
*
|
||
|
* @throws OutOfBoundsException
|
||
|
*
|
||
|
* @return Row
|
||
|
*/
|
||
|
public function firstRow(): array
|
||
|
{
|
||
|
return $this->rows->first(null, fn () => throw new OutOfBoundsException('Could not retrieve row from collection.'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the all the rows of the import file.
|
||
|
*
|
||
|
* @return array<Row>
|
||
|
*/
|
||
|
public function all(): array
|
||
|
{
|
||
|
return $this->rows->all();
|
||
|
}
|
||
|
}
|