packages/sql-catalog/src/Core/Extension/Model/ModelSet.php
1<?php
2
3declare(strict_types=1);
4
5namespace SqlCatalog\Core\Extension\Model;
6
7use Closure;
8use SqlCatalog\Core\Evaluation\Domain;
9
10/**
11 * Call transformations, statement compilers and type relations registered by enabled extensions.
12 *
13 * @visibility public
14 *
15 * @example Registering a function that returns a SQL fragment
16 * $models = new \SqlCatalog\Core\Extension\Model\ModelSet(calls: [
17 * static fn (\SqlCatalog\Core\Extension\Model\CallContext $call): ?\SqlCatalog\Core\Evaluation\Domain =>
18 * $call->name === 'today_sql' ? \SqlCatalog\Core\Evaluation\Domain::literal('CURRENT_DATE') : null,
19 * ]);
20 * count($models->calls) // => 1
21 */
22final class ModelSet
23{
24 /**
25 * @param list<Closure(CallContext): ?Domain> $calls Registered with the shared function-model registry; later registrations take priority.
26 * @param array<string, QueryModelInterface> $queries Statement models keyed by SinkSpec::model.
27 * @param list<Closure(string, string): bool> $classRelations Additional known subtype relations.
28 */
29 public function __construct(public readonly array $calls = [], public readonly array $queries = [], public readonly array $classRelations = [])
30 {
31 }
32
33 /**
34 * Combines registrations in extension order; later statement keys replace earlier ones.
35 */
36 public function merge(self $other): self
37 {
38 return new self(array_merge($this->calls, $other->calls), array_merge($this->queries, $other->queries), array_merge($this->classRelations, $other->classRelations));
39 }
40
41 /**
42 * Whether an extension declares a subtype relation absent from application source.
43 */
44 public function matchesClass(string $class, string $expected): bool
45 {
46 foreach ($this->classRelations as $relation) {
47 if ($relation($class, $expected)) {
48 return true;
49 }
50 }
51
52 return false;
53 }
54}
55