Skip to content

Commit b751f4a

Browse files
committed
Improve Schema API
1 parent e551b0f commit b751f4a

24 files changed

Lines changed: 214 additions & 120 deletions

docs/9.0/connections/instantiation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Alternatively, you can use the <code>fromStream</code> method.</p>
6161
```php
6262
public static AbstractCsv::fromStream(SplFileObject|resource $stream): self
6363
```
64+
6465
Creates a new object from a stream resource or a streaming object.
6566

6667
```php

docs/9.0/reader/record-mapping.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ description: Converts your CSV records into PHP objects using PHP's powerful Ref
88

99
<p class="message-notice">New in version <code>9.12.0</code></p>
1010

11-
If you are working with a class which implements the `TabularDataReader` interface you can now deserialize
11+
If you are working with a class which implements the `TabularData` interface you can now deserialize
1212
your data using the `TabularDataReader::getRecordsAsObject` method. The method will convert your document records
1313
into objects using PHP's powerful Reflection API.
1414

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 inferSchema(?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 inferRecords(?Inspector $inspector = null, array $header = []): Iterator
233+
{
234+
return $this->inferSchema($inspector, $header)->parse($this);
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 inferSchema(?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 inferRecords(?Inspector $inspector = null, array $header = []): Iterator
427+
{
428+
return $this->inferSchema($inspector, $header)->parse($this);
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 inferSchema(?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 inferRecords(?Inspector $inspector = null, array $header = []): Iterator
217+
{
218+
return $this->inferSchema($inspector, $header)->parse($this);
214219
}
215220

216221
/**

src/Schema/AbstractField.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ public function evaluate(mixed $value): int
6969
return null !== $this->parse($value) ? 1 : -1;
7070
}
7171

72-
public function metadata(): Metadata
72+
public function metadata(): FieldMetadata
7373
{
74-
return new Metadata();
74+
return new FieldMetadata();
7575
}
7676
}

src/Schema/DateField.php

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
use DateTimeImmutable;
1717
use DateTimeInterface;
18+
use DateTimeZone;
1819
use Exception;
1920
use ValueError;
2021

@@ -23,22 +24,35 @@
2324

2425
final class DateField extends AbstractField
2526
{
26-
public function __construct(public readonly string $format = '', float $confidenceThreshold = 0.8)
27+
public readonly string $format;
28+
public readonly ?DateTimeZone $timeZone;
29+
30+
public function __construct(string $format, DateTimeZone|string|null $timeZone = null, float $confidenceThreshold = 0.8)
2731
{
32+
$format = trim($format);
33+
'' !== $format || throw new ValueError('The date field format can not be empty.');
34+
$timeZone = self::filterTimezone($timeZone);
35+
2836
parent::__construct($confidenceThreshold);
37+
$this->format = $format;
38+
$this->timeZone = $timeZone;
2939
}
3040

31-
public static function native(float $confidenceThreshold = 0.8): self
41+
private static function filterTimezone(DateTimeZone|string|null $timeZone): ?DateTimeZone
3242
{
33-
return new self(format: '', confidenceThreshold: $confidenceThreshold);
34-
}
43+
if (null === $timeZone) {
44+
return null;
45+
}
3546

36-
public static function withFormat(string $format, float $confidenceThreshold = 0.8): self
37-
{
38-
$format = trim($format);
39-
'' !== $format || throw new ValueError('The date field strategy format can not be empty.');
47+
if ($timeZone instanceof DateTimeZone) {
48+
return $timeZone;
49+
}
4050

41-
return new self(format: $format, confidenceThreshold: $confidenceThreshold);
51+
try {
52+
return new DateTimeZone($timeZone);
53+
} catch (Exception $exception) {
54+
throw new ValueError('The date field timezone value `'.$timeZone.'` is invalid.', previous: $exception);
55+
}
4256
}
4357

4458
public function type(): FieldType
@@ -51,11 +65,6 @@ public function name(): string
5165
return FieldType::Date->value;
5266
}
5367

54-
public function format(): string
55-
{
56-
return $this->format;
57-
}
58-
5968
public function parse(mixed $value): ?DateTimeImmutable
6069
{
6170
if ($value instanceof DateTimeInterface) {
@@ -72,22 +81,30 @@ public function parse(mixed $value): ?DateTimeImmutable
7281
}
7382

7483
try {
75-
if ('' !== $this->format) {
76-
$value = DateTimeImmutable::createFromFormat($this->format, $value);
84+
$value = DateTimeImmutable::createFromFormat($this->format, $value, $this->timeZone);
85+
if (false === $value) {
86+
return null;
87+
}
7788

78-
return false === $value ? null : $value;
89+
$errors = DateTimeImmutable::getLastErrors();
90+
if (
91+
(isset($errors['warning_count']) && 0 < $errors['warning_count']) ||
92+
(isset($errors['error_count']) && 0 < $errors['error_count'])
93+
) {
94+
return null;
7995
}
8096

81-
return new DateTimeImmutable($value);
82-
} catch (Exception) {
97+
return $value;
98+
} catch (ValueError) {
8399
return null;
84100
}
85101
}
86102

87-
public function metadata(): Metadata
103+
public function metadata(): FieldMetadata
88104
{
89-
return new Metadata([
105+
return new FieldMetadata([
90106
'format' => $this->format,
107+
'timezone' => $this->timeZone?->getName(),
91108
]);
92109
}
93110
}

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/EnumField.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ public function parse(mixed $value): ?UnitEnum
107107
return $enumClass::tryFrom($value);
108108
}
109109

110-
public function metadata(): Metadata
110+
public function metadata(): FieldMetadata
111111
{
112-
return new Metadata([
112+
return new FieldMetadata([
113113
'class' => $this->enumClass,
114114
]);
115115
}

src/Schema/Field.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ public function name(): string;
3434
*/
3535
public function parse(mixed $value): mixed;
3636

37+
/**
38+
* Returns the confidence on the field value.
39+
*
40+
* The range of valide value is from 0.0 up to including 1.0
41+
*/
3742
public function confidenceThreshold(): float;
3843

3944
/**
@@ -58,5 +63,5 @@ public function score(iterable $values): float;
5863
*/
5964
public function evaluate(mixed $value): int;
6065

61-
public function metadata(): Metadata;
66+
public function metadata(): FieldMetadata;
6267
}

0 commit comments

Comments
 (0)