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 ;
24
+ use function array_map ;
23
25
use function array_values ;
24
26
use function implode ;
25
27
use function is_array ;
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,123 @@ 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 ;
455
+ }
456
+ }
457
+ }
458
+ }
459
+
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
+ $ foundImplementations = [];
483
+ foreach ($ this ->getTypeMap () as $ type ) {
484
+ if ($ type instanceof InterfaceType) {
485
+ if (! isset ($ foundImplementations [$ type ->name ])) {
486
+ $ foundImplementations [$ type ->name ] = ['objects ' => [], 'interfaces ' => []];
487
+ }
488
+
489
+ foreach ($ type ->getInterfaces () as $ iface ) {
490
+ if (! isset ($ foundImplementations [$ iface ->name ])) {
491
+ $ foundImplementations [$ iface ->name ] = ['objects ' => [], 'interfaces ' => []];
492
+ }
493
+ $ foundImplementations [$ iface ->name ]['interfaces ' ][] = $ type ;
494
+ }
495
+ } elseif ($ type instanceof ObjectType) {
496
+ foreach ($ type ->getInterfaces () as $ iface ) {
497
+ if (! isset ($ foundImplementations [$ iface ->name ])) {
498
+ $ foundImplementations [$ iface ->name ] = ['objects ' => [], 'interfaces ' => []];
499
+ }
500
+ $ foundImplementations [$ iface ->name ]['objects ' ][] = $ type ;
444
501
}
445
502
}
446
503
}
504
+ $ this ->implementationsMap = array_map (
505
+ static function (array $ implementations ) : InterfaceImplementations {
506
+ return new InterfaceImplementations ($ implementations ['objects ' ], $ implementations ['interfaces ' ]);
507
+ },
508
+ $ foundImplementations
509
+ );
447
510
}
448
511
449
- return $ this ->possibleTypeMap ;
512
+ return $ this ->implementationsMap ;
450
513
}
451
514
452
515
/**
516
+ * @deprecated as of 14.4.0 use isSubType instead, will be removed in 15.0.0.
517
+ *
453
518
* Returns true if object type is concrete type of given abstract type
454
519
* (implementation for interfaces and members of union type for unions)
455
520
*
456
521
* @api
522
+ * @codeCoverageIgnore
457
523
*/
458
- public function isPossibleType (AbstractType $ abstractType , ImplementingType $ possibleType ) : bool
524
+ public function isPossibleType (AbstractType $ abstractType , ObjectType $ possibleType ) : bool
459
525
{
460
- if ($ abstractType instanceof InterfaceType) {
461
- return $ possibleType ->implementsInterface ($ abstractType );
462
- }
526
+ return $ this ->isSubType ($ abstractType , $ possibleType );
527
+ }
528
+
529
+ /**
530
+ * Returns true if maybe sub type is a sub type of given abstract type.
531
+ *
532
+ * @param UnionType|InterfaceType $abstractType
533
+ * @param ObjectType|InterfaceType $maybeSubType
534
+ *
535
+ * @api
536
+ */
537
+ public function isSubType (AbstractType $ abstractType , ImplementingType $ maybeSubType ) : bool
538
+ {
539
+ if (! isset ($ this ->subTypeMap [$ abstractType ->name ])) {
540
+ $ this ->subTypeMap [$ abstractType ->name ] = [];
463
541
464
- if ($ abstractType instanceof UnionType) {
465
- return $ abstractType ->isPossibleType ($ possibleType );
542
+ if ($ abstractType instanceof UnionType) {
543
+ foreach ($ abstractType ->getTypes () as $ type ) {
544
+ $ this ->subTypeMap [$ abstractType ->name ][$ type ->name ] = true ;
545
+ }
546
+ } else {
547
+ $ implementations = $ this ->getImplementations ($ abstractType );
548
+ foreach ($ implementations ->objects () as $ type ) {
549
+ $ this ->subTypeMap [$ abstractType ->name ][$ type ->name ] = true ;
550
+ }
551
+ foreach ($ implementations ->interfaces () as $ type ) {
552
+ $ this ->subTypeMap [$ abstractType ->name ][$ type ->name ] = true ;
553
+ }
554
+ }
466
555
}
467
556
468
- throw InvariantViolation:: shouldNotHappen ( );
557
+ return isset ( $ this -> subTypeMap [ $ abstractType -> name ][ $ maybeSubType -> name ] );
469
558
}
470
559
471
560
/**
@@ -492,7 +581,8 @@ public function getAstNode() : ?SchemaDefinitionNode
492
581
/**
493
582
* Validates schema.
494
583
*
495
- * This operation requires full schema scan. Do not use in production environment.
584
+ * This operation requires full schema scan. Do not use in production
585
+ * environment.
496
586
*
497
587
* @throws InvariantViolation
498
588
*
@@ -532,7 +622,8 @@ public function assertValid()
532
622
/**
533
623
* Validates schema.
534
624
*
535
- * This operation requires full schema scan. Do not use in production environment.
625
+ * This operation requires full schema scan. Do not use in production
626
+ * environment.
536
627
*
537
628
* @return InvariantViolation[]|Error[]
538
629
*
0 commit comments