Skip to content

Commit 5a703fe

Browse files
committed
feat: implement metadata support
1 parent 39e02c8 commit 5a703fe

26 files changed

Lines changed: 494 additions & 120 deletions

.junie/guidelines.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,19 @@ final class ExampleTest extends TestCase
106106
4. Use `Set::createFrom()` for test data and `Target::createFrom()` for reflection
107107
5. Run tests via `make test` to verify
108108

109+
### Test Best Practices
110+
111+
**Avoid Redundant Type Checking:**
112+
113+
- Do not use `$this->assertInstanceOf(ClassName::class, $object)` immediately after creating an object with
114+
`new ClassName()` or calling a method that returns a specific type
115+
- If the constructor or method fails, PHP will throw an exception or type error; the test doesn't need to verify the
116+
object type
117+
- Instead, test meaningful functionality like method calls, state changes, return values, or object behavior
118+
- **Bad Example:** `$registry = $factory->make(); $this->assertInstanceOf(Registry::class, $registry);`
119+
- **Good Example:** `$registry = $factory->make(); $this->assertTrue($registry->hasSpec('required'));`
120+
- Focus on testing that the created object works correctly, not just that it exists
121+
109122
## Additional Development Information
110123

111124
### Project Architecture
@@ -120,6 +133,7 @@ final class ExampleTest extends TestCase
120133
## Coding Standards and Practices
121134

122135
This project adheres to strict coding standards and best practices:
136+
123137
- **Strict Types:** All files use `declare(strict_types=1);`
124138
- **PSR-12:** Follow PSR-12 coding standards for consistency
125139
- **Type Hints:** Just use type hints if the typing is not clear or is ambiguous

src/Core/Metadata/Schema.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
use Constructo\Core\Metadata\Schema\Field;
88
use Constructo\Core\Metadata\Schema\Field\Fieldset;
99
use Constructo\Core\Metadata\Schema\Field\Rules;
10-
use Constructo\Core\Metadata\Schema\Registry;
10+
use Constructo\Core\Metadata\Schema\Registry\Specs;
1111
use InvalidArgumentException;
1212

1313
use function array_map;
1414

1515
final readonly class Schema
1616
{
1717
public function __construct(
18-
private Registry $registry,
18+
private Specs $specs,
1919
private Fieldset $fieldset,
2020
) {
2121
}
@@ -27,7 +27,7 @@ public function add(string $name): Field
2727
return $field;
2828
}
2929

30-
$field = new Field($this->registry, new Rules(), $name);
30+
$field = new Field($this->specs, new Rules(), $name);
3131
$this->fieldset->add($name, $field);
3232
return $field;
3333
}

src/Core/Metadata/Schema/Field.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Closure;
1010
use Constructo\Core\Metadata\Schema\Field\Rules;
1111
use Constructo\Core\Metadata\Schema\Registry\Spec;
12+
use Constructo\Core\Metadata\Schema\Registry\Specs;
1213

1314
/**
1415
* # Global setup
@@ -113,7 +114,7 @@ final class Field
113114
private ?string $source = null;
114115

115116
public function __construct(
116-
public readonly Registry $registry,
117+
public readonly Specs $specs,
117118
private readonly Rules $rules,
118119
public readonly string $name,
119120
) {
@@ -159,7 +160,7 @@ public function __call(string $name, array $arguments): self
159160
$this->handleVisibility($name);
160161
return $this;
161162
}
162-
$spec = $this->registry->getSpec($name);
163+
$spec = $this->specs->get($name);
163164
if ($spec instanceof Spec) {
164165
$this->handleSpec($spec, $arguments);
165166
return $this;

src/Core/Metadata/Schema/Registry.php renamed to src/Core/Metadata/Schema/Registry/Specs.php

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,23 @@
22

33
declare(strict_types=1);
44

5-
namespace Constructo\Core\Metadata\Schema;
5+
namespace Constructo\Core\Metadata\Schema\Registry;
66

77
use Constructo\Contract\Formatter;
8-
use Constructo\Core\Metadata\Schema\Registry\Spec;
98
use Constructo\Support\Set;
109
use InvalidArgumentException;
1110

1211
use function class_exists;
13-
use function Constructo\Cast\stringify;
1412
use function Constructo\Notation\snakify;
1513
use function gettype;
1614
use function is_string;
1715
use function sprintf;
1816

19-
class Registry
17+
class Specs
2018
{
2119
private array $specs = [];
22-
private readonly array $types;
2320

24-
public function __construct(array $types = [])
25-
{
26-
$this->types = array_merge($this->defaultTypes(), $types);
27-
}
28-
29-
public function getSpec(string $name): ?Spec
21+
public function get(string $name): ?Spec
3022
{
3123
$name = snakify($name);
3224
$spec = $this->specs[$name] ?? null;
@@ -36,7 +28,7 @@ public function getSpec(string $name): ?Spec
3628
return null;
3729
}
3830

39-
public function registerSpec(string $name, array $data): void
31+
public function register(string $name, array $data): void
4032
{
4133
$name = snakify($name);
4234
$properties = Set::createFrom($data);
@@ -46,20 +38,12 @@ public function registerSpec(string $name, array $data): void
4638
$this->specs[$name] = $spec;
4739
}
4840

49-
public function hasSpec(string $name): bool
41+
public function has(string $name): bool
5042
{
5143
$name = snakify($name);
5244
return isset($this->specs[$name]);
5345
}
5446

55-
public function getType(string $source): ?string
56-
{
57-
$type = $this->types[$source] ?? null;
58-
return $type
59-
? stringify($type)
60-
: null;
61-
}
62-
6347
protected function defineFormatter(Set $properties): ?Formatter
6448
{
6549
$formatter = $properties->get('formatter');
@@ -79,13 +63,4 @@ protected function defineFormatter(Set $properties): ?Formatter
7963
}
8064
return $instance;
8165
}
82-
83-
protected function defaultTypes(): array
84-
{
85-
return [
86-
'DateTime' => 'date',
87-
'DateTimeImmutable' => 'date',
88-
'DateTimeInterface' => 'date',
89-
];
90-
}
9166
}

src/Core/Metadata/Schema/Registry/RegistryFactory.php renamed to src/Core/Metadata/Schema/Registry/SpecsFactory.php

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,26 @@
44

55
namespace Constructo\Core\Metadata\Schema\Registry;
66

7-
use Constructo\Core\Metadata\Schema\Registry;
87
use InvalidArgumentException;
98

109
use function assert;
1110
use function Constructo\Cast\arrayify;
1211
use function Constructo\Cast\stringify;
1312
use function gettype;
1413

15-
readonly class RegistryFactory
14+
readonly class SpecsFactory
1615
{
17-
public function __construct(
18-
private array $types = [],
19-
private array $specs = [],
20-
) {
16+
public function __construct(private array $specs = [])
17+
{
2118
}
2219

23-
public function make(): Registry
20+
public function make(): Specs
2421
{
25-
$registry = new Registry($this->types);
26-
assert($registry instanceof Registry);
22+
$registry = new Specs();
23+
assert($registry instanceof Specs);
2724
foreach ($this->specs as $key => $value) {
2825
$this->validate($key, $value);
29-
$registry->registerSpec(stringify($key), arrayify($value));
26+
$registry->register(stringify($key), arrayify($value));
3027
}
3128
return $registry;
3229
}

src/Core/Metadata/Schema/SchemaFactory.php

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,17 @@
66

77
use Constructo\Core\Metadata\Schema;
88
use Constructo\Core\Metadata\Schema\Field\Fieldset;
9-
use Constructo\Core\Metadata\Schema\Registry\RegistryFactory;
10-
use Constructo\Support\Cache;
11-
use Constructo\Support\Reflective\Schema\SchemaReflector;
12-
use ReflectionException;
9+
use Constructo\Core\Metadata\Schema\Registry\SpecsFactory;
1310

1411
class SchemaFactory
1512
{
16-
public function make(): Schema
13+
public function __construct(private readonly SpecsFactory $specsFactory)
1714
{
18-
$schemaRegistry = (new RegistryFactory())->make();
19-
return new Schema($schemaRegistry, new Fieldset());
2015
}
2116

22-
/**
23-
* @template T of object
24-
* @param class-string<T>|null $source
25-
* @throws ReflectionException
26-
*/
27-
public function makeFrom(string $source = null): Schema
17+
public function make(): Schema
2818
{
29-
$schemaRegistry = (new RegistryFactory())->make();
30-
$schema = new Schema($schemaRegistry, new Fieldset());
31-
return (new SchemaReflector(new Cache(), $schemaRegistry))->extract($source, $schema);
19+
$schemaRegistry = $this->specsFactory->make();
20+
return new Schema($schemaRegistry, new Fieldset());
3221
}
3322
}

src/Support/Reflective/Parameter/Chain.php renamed to src/Support/Reflective/Schema/Parameter/Chain.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@
22

33
declare(strict_types=1);
44

5-
namespace Constructo\Support\Reflective\Parameter;
5+
namespace Constructo\Support\Reflective\Schema\Parameter;
66

77
use Constructo\Core\Metadata\Schema\Field;
8-
use Constructo\Core\Metadata\Schema\Registry;
8+
use Constructo\Support\Reflective\Schema\Parameter\Registry\Types;
99
use ReflectionParameter;
1010

1111
abstract class Chain
1212
{
1313
protected ?Chain $previous = null;
1414

15-
public function __construct(protected readonly ?Registry $specs = null)
15+
public function __construct(protected readonly ?Types $types = null)
1616
{
1717
}
1818

src/Support/Reflective/Parameter/Type/BuiltinNamedTypeHandler.php renamed to src/Support/Reflective/Schema/Parameter/Field/BuiltinNamedTypeHandler.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
declare(strict_types=1);
44

5-
namespace Constructo\Support\Reflective\Parameter\Type;
5+
namespace Constructo\Support\Reflective\Schema\Parameter\Field;
66

77
use Constructo\Core\Metadata\Schema\Field;
8-
use Constructo\Support\Reflective\Parameter\Type\Contract\NamedTypeHandler;
9-
use Constructo\Support\Reflective\Parameter\Type\Contract\NamedTypeResolution;
8+
use Constructo\Support\Reflective\Schema\Parameter\Field\Contract\NamedTypeHandler;
9+
use Constructo\Support\Reflective\Schema\Parameter\Field\Contract\NamedTypeResolution;
1010
use ReflectionNamedType;
1111

1212
use function is_string;
@@ -19,7 +19,7 @@ protected function resolveNamedType(ReflectionNamedType $parameter, Field $field
1919
return NamedTypeResolution::NotResolved;
2020
}
2121
$type = $this->resolveBuiltinType($parameter->getName());
22-
if (is_string($type) && $field->registry->hasSpec($type)) {
22+
if (is_string($type) && $field->specs->has($type)) {
2323
$field->{$type}();
2424
}
2525
return NamedTypeResolution::Resolved;

src/Support/Reflective/Parameter/Type/Contract/NamedTypeHandler.php renamed to src/Support/Reflective/Schema/Parameter/Field/Contract/NamedTypeHandler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace Constructo\Support\Reflective\Parameter\Type\Contract;
5+
namespace Constructo\Support\Reflective\Schema\Parameter\Field\Contract;
66

77
use Constructo\Core\Metadata\Schema\Field;
88
use ReflectionNamedType;

src/Support/Reflective/Parameter/Type/Contract/NamedTypeResolution.php renamed to src/Support/Reflective/Schema/Parameter/Field/Contract/NamedTypeResolution.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace Constructo\Support\Reflective\Parameter\Type\Contract;
5+
namespace Constructo\Support\Reflective\Schema\Parameter\Field\Contract;
66

77
enum NamedTypeResolution
88
{

0 commit comments

Comments
 (0)