Skip to content

Commit a2ceb34

Browse files
committed
Improve Schema API
1 parent e551b0f commit a2ceb34

10 files changed

Lines changed: 75 additions & 45 deletions

src/Buffer.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,14 @@ public function reduce(callable $callback, mixed $initial = null): mixed
224224
return $initial;
225225
}
226226

227-
public function schema(?Inspector $inspector = null): Schema
227+
public function schema(?Inspector $inspector = null, array $header = []): Schema
228228
{
229-
return ($inspector ?? Inspector::default())->schema($this);
229+
return ($inspector ?? Inspector::default())->schema($this, $header);
230+
}
231+
232+
public function getTypedRecords(?Inspector $inspector = null, array $header = []): Iterator
233+
{
234+
return $this->schema($inspector)->parse($this, $header);
230235
}
231236

232237
/**

src/Reader.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,9 +418,14 @@ public function map(callable $callback): Iterator
418418
return MapIterator::fromIterable($this, $callback);
419419
}
420420

421-
public function schema(?Inspector $inspector = null): Schema
421+
public function schema(?Inspector $inspector = null, array $header = []): Schema
422422
{
423-
return ($inspector ?? Inspector::default())->schema($this);
423+
return ($inspector ?? Inspector::default())->schema($this, $header);
424+
}
425+
426+
public function getTypedRecords(?Inspector $inspector = null, array $header = []): Iterator
427+
{
428+
return $this->schema($inspector)->parse($this, $header);
424429
}
425430

426431
/**

src/ResultSet.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,14 @@ public function map(callable $callback): Iterator
208208
return MapIterator::fromIterable($this, $callback);
209209
}
210210

211-
public function schema(?Inspector $inspector = null): Schema
211+
public function schema(?Inspector $inspector = null, array $header = []): Schema
212212
{
213-
return ($inspector ?? Inspector::default())->schema($this);
213+
return ($inspector ?? Inspector::default())->schema($this, $header);
214+
}
215+
216+
public function getTypedRecords(?Inspector $inspector = null, array $header = []): Iterator
217+
{
218+
return $this->schema($inspector)->parse($this, $header);
214219
}
215220

216221
/**

src/Schema/DateField.php

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,15 @@
2323

2424
final class DateField extends AbstractField
2525
{
26-
public function __construct(public readonly string $format = '', float $confidenceThreshold = 0.8)
27-
{
28-
parent::__construct($confidenceThreshold);
29-
}
30-
31-
public static function native(float $confidenceThreshold = 0.8): self
32-
{
33-
return new self(format: '', confidenceThreshold: $confidenceThreshold);
34-
}
26+
public readonly string $format;
3527

36-
public static function withFormat(string $format, float $confidenceThreshold = 0.8): self
28+
public function __construct(string $format, float $confidenceThreshold = 0.8)
3729
{
3830
$format = trim($format);
3931
'' !== $format || throw new ValueError('The date field strategy format can not be empty.');
4032

41-
return new self(format: $format, confidenceThreshold: $confidenceThreshold);
33+
parent::__construct($confidenceThreshold);
34+
$this->format = $format;
4235
}
4336

4437
public function type(): FieldType
@@ -72,13 +65,9 @@ public function parse(mixed $value): ?DateTimeImmutable
7265
}
7366

7467
try {
75-
if ('' !== $this->format) {
76-
$value = DateTimeImmutable::createFromFormat($this->format, $value);
77-
78-
return false === $value ? null : $value;
79-
}
68+
$value = DateTimeImmutable::createFromFormat($this->format, $value);
8069

81-
return new DateTimeImmutable($value);
70+
return false === $value ? null : $value;
8271
} catch (Exception) {
8372
return null;
8473
}

src/Schema/DateFieldTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ final class DateFieldTest extends TestCase
2525

2626
protected function setUp(): void
2727
{
28-
$this->field = DateField::native();
28+
$this->field = new DateField('Y-m-d');
2929
}
3030

3131
public function testParseUsesNativeConstructorWhenFormatIsEmpty(): void
@@ -38,7 +38,7 @@ public function testParseUsesNativeConstructorWhenFormatIsEmpty(): void
3838

3939
public function testParseUsesCreateFromFormatWhenFormatIsProvided(): void
4040
{
41-
$field = DateField::withFormat('d-m-Y');
41+
$field = new DateField('d-m-Y');
4242
$result = $field->parse('01-01-2024');
4343

4444
self::assertInstanceOf(DateTimeImmutable::class, $result);

src/Schema/FieldList.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public static function default(): self
4242
new BooleanField(),
4343
new NumericField(),
4444
new DateField(format: 'Y-m-d'),
45+
new DateField(format: 'd/m/Y'),
46+
new DateField(format: 'Y-m-d H:i:s'),
4547
new JsonField(),
4648
RegexpField::uuid(),
4749
);

src/Schema/Inspector.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,11 @@ public static function default(int $sampleLimit = 10): self
6363
* @throws SyntaxError
6464
* @throws \League\Csv\Exception
6565
*/
66-
public function schema(TabularData $tabularData): Schema
66+
public function schema(TabularData $tabularData, array $header = []): Schema
6767
{
6868
$score = [];
6969
$counted = [];
70-
foreach ((new Statement())->limit($this->sampleLimit)->process($tabularData) as $record) {
70+
foreach ((new Statement())->limit($this->sampleLimit)->process($tabularData, $header) as $record) {
7171
foreach ($record as $column => $value) {
7272
$counted[$column] ??= 0;
7373
$score[$column] ??= [];

src/Schema/NumericField.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@
1313

1414
namespace League\Csv\Schema;
1515

16+
use function filter_var;
1617
use function is_float;
1718
use function is_int;
1819
use function is_numeric;
1920
use function is_string;
2021
use function trim;
2122

23+
use const FILTER_VALIDATE_INT;
24+
2225
final class NumericField extends AbstractField
2326
{
2427
public function type(): FieldType
@@ -31,7 +34,7 @@ public function name(): string
3134
return FieldType::Numeric->value;
3235
}
3336

34-
public function parse(mixed $value): ?float
37+
public function parse(mixed $value): int|float|null
3538
{
3639
if (is_float($value) || is_int($value)) {
3740
return $value;
@@ -42,9 +45,12 @@ public function parse(mixed $value): ?float
4245
}
4346

4447
$value = trim($value);
48+
if ('' === $value || !is_numeric($value)) {
49+
return null;
50+
}
51+
52+
$filterValue = filter_var($value, FILTER_VALIDATE_INT);
4553

46-
return ('' !== $value && is_numeric($value))
47-
? (float) $value
48-
: null;
54+
return false === $filterValue ? (float) $value : $filterValue;
4955
}
5056
}

src/Schema/NumericFieldTest.php

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,22 @@ protected function setUp(): void
3535
public static function provideValidNumericValues(): array
3636
{
3737
return [
38-
[10, 10.0],
39-
[-5, -5.0],
40-
[0, 0.0],
41-
42-
[10.5, 10.5],
43-
[-3.14, -3.14],
44-
45-
['10', 10.0],
46-
['10.5', 10.5],
47-
['-2', -2.0],
48-
49-
[' 12 ', 12.0],
50-
[' 3.14 ', 3.14],
38+
'positive int' => [10, 10],
39+
'negative int' => [-5, -5],
40+
'zero' => [0, 0],
41+
'positive float' => [10.5, 10.5],
42+
'negative float' => [-3.14, -3.14],
43+
'string positive int' => ['10', 10],
44+
'string positive float' => ['10.5', 10.5],
45+
'string negative int' => ['-2', -2],
46+
'string positive int with extra spaces' => [' 12 ', 12],
47+
'string positive float with extra spaces' => [' 3.14 ', 3.14],
48+
'string positive power float with extra spaces' => [' 3e14 ', 3e14],
5149
];
5250
}
5351

5452
#[DataProvider('provideValidNumericValues')]
55-
public function testParseValidValues(mixed $input, float $expected): void
53+
public function testParseValidValues(mixed $input, int|float $expected): void
5654
{
5755
self::assertSame($expected, $this->field->parse($input));
5856
}

src/Schema/Schema.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use Countable;
1717
use Iterator;
1818
use IteratorAggregate;
19+
use League\Csv\MapIterator;
20+
use League\Csv\TabularData;
1921
use ValueError;
2022

2123
use function array_key_exists;
@@ -112,4 +114,22 @@ public function map(callable $callback): Iterator
112114
yield $name => $callback($field, $name);
113115
}
114116
}
117+
118+
/**
119+
* @return Iterator<int, array<mixed>>
120+
*/
121+
public function parse(TabularData $tabularData, array $header = []): Iterator
122+
{
123+
return MapIterator::fromIterable($tabularData->getRecords($header), $this->format(...));
124+
}
125+
126+
public function format(array $row): array
127+
{
128+
$result = [];
129+
foreach ($this->fields as $column => $field) {
130+
$result[$column] = $field->parse($row[$column] ?? null);
131+
}
132+
133+
return $result;
134+
}
115135
}

0 commit comments

Comments
 (0)