17
17
use GraphQL \Type \Definition \ObjectType ;
18
18
use GraphQL \Type \Definition \Type ;
19
19
use GraphQL \Type \Definition \UnionType ;
20
+ use GraphQL \Utils \InterfaceImplementations ;
20
21
use GraphQL \Utils \TypeInfo ;
21
22
use GraphQL \Utils \Utils ;
22
23
use Traversable ;
23
24
use function array_values ;
25
+ use function array_map ;
24
26
use function implode ;
25
27
use function is_array ;
26
28
use function is_callable ;
30
32
* Schema Definition (see [related docs](type-system/schema.md))
31
33
*
32
34
* A Schema is created by supplying the root types of each type of operation:
33
- * query, mutation (optional) and subscription (optional). A schema definition is
34
- * then supplied to the validator and executor. Usage Example:
35
+ * query, mutation (optional) and subscription (optional). A schema definition
36
+ * is then supplied to the validator and executor. Usage Example:
35
37
*
36
38
* $schema = new GraphQL\Type\Schema([
37
39
* 'query' => $MyAppQueryRootType,
@@ -63,7 +65,14 @@ class Schema
63
65
*
64
66
* @var array<string, array<string, ObjectType|UnionType>>
65
67
*/
66
- private $ possibleTypeMap ;
68
+ private $ subTypeMap ;
69
+
70
+ /**
71
+ * Lazily initialised
72
+ *
73
+ * @var array<string, InterfaceImplementations>
74
+ */
75
+ private $ implementationsMap ;
67
76
68
77
/**
69
78
* True when $resolvedTypes contain all possible schema types
@@ -190,10 +199,11 @@ private function resolveAdditionalTypes()
190
199
}
191
200
192
201
/**
193
- * Returns array of all types in this schema. Keys of this array represent type names, values are instances
194
- * of corresponding type definitions
202
+ * Returns array of all types in this schema. Keys of this array represent
203
+ * type names, values are instances of corresponding type definitions
195
204
*
196
- * This operation requires full schema scan. Do not use in production environment.
205
+ * This operation requires full schema scan. Do not use in production
206
+ * environment.
197
207
*
198
208
* @return Type[]
199
209
*
@@ -407,7 +417,8 @@ public static function resolveType($type) : Type
407
417
* Returns all possible concrete types for given abstract type
408
418
* (implementations for interfaces and members of union type for unions)
409
419
*
410
- * This operation requires full schema scan. Do not use in production environment.
420
+ * This operation requires full schema scan. Do not use in production
421
+ * environment.
411
422
*
412
423
* @param InterfaceType|UnionType $abstractType
413
424
*
@@ -427,45 +438,122 @@ public function getPossibleTypes(Type $abstractType) : array
427
438
*/
428
439
private function getPossibleTypeMap () : array
429
440
{
430
- if (! isset ($ this ->possibleTypeMap )) {
431
- $ this ->possibleTypeMap = [];
441
+ if (! isset ($ this ->subTypeMap )) {
442
+ $ this ->subTypeMap = [];
432
443
foreach ($ this ->getTypeMap () as $ type ) {
433
444
if ($ type instanceof ObjectType) {
434
445
foreach ($ type ->getInterfaces () as $ interface ) {
435
446
if (! ($ interface instanceof InterfaceType)) {
436
447
continue ;
437
448
}
438
449
439
- $ this ->possibleTypeMap [$ interface ->name ][$ type ->name ] = $ type ;
450
+ $ this ->subTypeMap [$ interface ->name ][$ type ->name ] = $ type ;
440
451
}
441
452
} elseif ($ type instanceof UnionType) {
442
453
foreach ($ type ->getTypes () as $ innerType ) {
443
- $ this ->possibleTypeMap [$ type ->name ][$ innerType ->name ] = $ innerType ;
454
+ $ this ->subTypeMap [$ type ->name ][$ innerType ->name ] = $ innerType ;
444
455
}
445
456
}
446
457
}
447
458
}
448
459
449
- return $ this ->possibleTypeMap ;
460
+ return $ this ->subTypeMap ;
461
+ }
462
+
463
+ /**
464
+ * Returns all types that implement a given interface type.
465
+ *
466
+ * This operations requires full schema scan. Do not use in production
467
+ * environment.
468
+ *
469
+ * @api
470
+ */
471
+ public function getImplementations (InterfaceType $ abstractType ) : InterfaceImplementations
472
+ {
473
+ return $ this ->collectImplementations ()[$ abstractType ->name ];
474
+ }
475
+
476
+ /**
477
+ * @return array<string, InterfaceImplementations>
478
+ */
479
+ private function collectImplementations () : array
480
+ {
481
+ if (! isset ($ this ->implementationsMap )) {
482
+ $ this ->implementationsMap = [];
483
+ $ foundImplementations = [];
484
+ foreach ($ this ->getTypeMap () as $ type ) {
485
+ if ($ type instanceof InterfaceType) {
486
+ if (! isset ($ foundImplementations [$ type ->name ])) {
487
+ $ foundImplementations [$ type ->name ] = ['objects ' => [], 'interfaces ' => []];
488
+ }
489
+
490
+ foreach ($ type ->getInterfaces () as $ iface ) {
491
+ if (! isset ($ foundImplementations [$ iface ->name ])) {
492
+ $ foundImplementations [$ iface ->name ] = ['objects ' => [], 'interfaces ' => []];
493
+ }
494
+ $ foundImplementations [$ iface ->name ]['interfaces ' ][] = $ type ;
495
+ }
496
+ } elseif ($ type instanceof ObjectType) {
497
+ foreach ($ type ->getInterfaces () as $ iface ) {
498
+ if (! isset ($ foundImplementations [$ iface ->name ])) {
499
+ $ foundImplementations [$ iface ->name ] = ['objects ' => [], 'interfaces ' => []];
500
+ }
501
+ $ foundImplementations [$ iface ->name ]['objects ' ][] = $ type ;
502
+ }
503
+ }
504
+ }
505
+ $ this ->implementationsMap = array_map (
506
+ static function (array $ implementations ) : InterfaceImplementations {
507
+ return new InterfaceImplementations ($ implementations ['objects ' ], $ implementations ['interfaces ' ]);
508
+ },
509
+ $ foundImplementations
510
+ );
511
+ }
512
+
513
+ return $ this ->implementationsMap ;
450
514
}
451
515
452
516
/**
453
517
* Returns true if object type is concrete type of given abstract type
454
518
* (implementation for interfaces and members of union type for unions)
455
519
*
456
520
* @api
521
+ * @deprecated use isSubType instead - will be removed in v16.
457
522
*/
458
- public function isPossibleType (AbstractType $ abstractType , ImplementingType $ possibleType ) : bool
523
+ public function isPossibleType (AbstractType $ abstractType , ObjectType $ possibleType ) : bool
459
524
{
460
- if ($ abstractType instanceof InterfaceType) {
461
- return $ possibleType ->implementsInterface ($ abstractType );
462
- }
525
+ return $ this ->isSubType ($ abstractType , $ possibleType );
526
+ }
463
527
464
- if ($ abstractType instanceof UnionType) {
465
- return $ abstractType ->isPossibleType ($ possibleType );
528
+ /**
529
+ * Returns true if maybe sub type is a sub type of given abstract type.
530
+ *
531
+ * @param UnionType|ObjectType|InterfaceType $abstractType
532
+ * @param ObjectType|InterfaceType $maybeSubType
533
+ *
534
+ * @api
535
+ */
536
+ public function isSubType (AbstractType $ abstractType , ImplementingType $ maybeSubType ) : bool
537
+ {
538
+ if (! isset ($ this ->subTypeMap [$ abstractType ->name ])) {
539
+ $ this ->subTypeMap [$ abstractType ->name ] = [];
540
+
541
+ if ($ abstractType instanceof UnionType) {
542
+ foreach ($ abstractType ->getTypes () as $ type ) {
543
+ $ this ->subTypeMap [$ abstractType ->name ][$ type ->name ] = true ;
544
+ }
545
+ } else {
546
+ $ implementations = $ this ->getImplementations ($ abstractType );
547
+ foreach ($ implementations ->objects () as $ type ) {
548
+ $ this ->subTypeMap [$ abstractType ->name ][$ type ->name ] = true ;
549
+ }
550
+ foreach ($ implementations ->interfaces () as $ type ) {
551
+ $ this ->subTypeMap [$ abstractType ->name ][$ type ->name ] = true ;
552
+ }
553
+ }
466
554
}
467
555
468
- throw InvariantViolation:: shouldNotHappen ( );
556
+ return isset ( $ this -> subTypeMap [ $ abstractType -> name ][ $ maybeSubType -> name ] );
469
557
}
470
558
471
559
/**
@@ -492,7 +580,8 @@ public function getAstNode() : ?SchemaDefinitionNode
492
580
/**
493
581
* Validates schema.
494
582
*
495
- * This operation requires full schema scan. Do not use in production environment.
583
+ * This operation requires full schema scan. Do not use in production
584
+ * environment.
496
585
*
497
586
* @throws InvariantViolation
498
587
*
@@ -532,7 +621,8 @@ public function assertValid()
532
621
/**
533
622
* Validates schema.
534
623
*
535
- * This operation requires full schema scan. Do not use in production environment.
624
+ * This operation requires full schema scan. Do not use in production
625
+ * environment.
536
626
*
537
627
* @return InvariantViolation[]|Error[]
538
628
*
0 commit comments