Relationships.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <?php declare(strict_types=1);
  2. namespace Grav\Framework\Relationships;
  3. use Grav\Framework\Contracts\Object\IdentifierInterface;
  4. use Grav\Framework\Contracts\Relationships\RelationshipInterface;
  5. use Grav\Framework\Contracts\Relationships\RelationshipsInterface;
  6. use Grav\Framework\Flex\FlexIdentifier;
  7. use RuntimeException;
  8. use function count;
  9. /**
  10. * Class Relationships
  11. *
  12. * @template T of \Grav\Framework\Contracts\Object\IdentifierInterface
  13. * @template P of \Grav\Framework\Contracts\Object\IdentifierInterface
  14. * @implements RelationshipsInterface<T,P>
  15. */
  16. class Relationships implements RelationshipsInterface
  17. {
  18. /** @var P */
  19. protected $parent;
  20. /** @var array */
  21. protected $options;
  22. /** @var RelationshipInterface<T,P>[] */
  23. protected $relationships;
  24. /**
  25. * Relationships constructor.
  26. * @param P $parent
  27. * @param array $options
  28. */
  29. public function __construct(IdentifierInterface $parent, array $options)
  30. {
  31. $this->parent = $parent;
  32. $this->options = $options;
  33. $this->relationships = [];
  34. }
  35. /**
  36. * @return bool
  37. * @phpstan-pure
  38. */
  39. public function isModified(): bool
  40. {
  41. return !empty($this->getModified());
  42. }
  43. /**
  44. * @return RelationshipInterface<T,P>[]
  45. * @phpstan-pure
  46. */
  47. public function getModified(): array
  48. {
  49. $list = [];
  50. foreach ($this->relationships as $name => $relationship) {
  51. if ($relationship->isModified()) {
  52. $list[$name] = $relationship;
  53. }
  54. }
  55. return $list;
  56. }
  57. /**
  58. * @return int
  59. * @phpstan-pure
  60. */
  61. public function count(): int
  62. {
  63. return count($this->options);
  64. }
  65. /**
  66. * @param string $offset
  67. * @return bool
  68. * @phpstan-pure
  69. */
  70. public function offsetExists($offset): bool
  71. {
  72. return isset($this->options[$offset]);
  73. }
  74. /**
  75. * @param string $offset
  76. * @return RelationshipInterface<T,P>|null
  77. */
  78. public function offsetGet($offset): ?RelationshipInterface
  79. {
  80. if (!isset($this->relationships[$offset])) {
  81. $options = $this->options[$offset] ?? null;
  82. if (null === $options) {
  83. return null;
  84. }
  85. $this->relationships[$offset] = $this->createRelationship($offset, $options);
  86. }
  87. return $this->relationships[$offset];
  88. }
  89. /**
  90. * @param string $offset
  91. * @param mixed $value
  92. * @return never-return
  93. */
  94. public function offsetSet($offset, $value)
  95. {
  96. throw new RuntimeException('Setting relationship is not supported', 500);
  97. }
  98. /**
  99. * @param string $offset
  100. * @return never-return
  101. */
  102. public function offsetUnset($offset)
  103. {
  104. throw new RuntimeException('Removing relationship is not allowed', 500);
  105. }
  106. /**
  107. * @return RelationshipInterface<T,P>|null
  108. */
  109. public function current(): ?RelationshipInterface
  110. {
  111. $name = key($this->options);
  112. if ($name === null) {
  113. return null;
  114. }
  115. return $this->offsetGet($name);
  116. }
  117. /**
  118. * @return string
  119. * @phpstan-pure
  120. */
  121. public function key(): string
  122. {
  123. return key($this->options);
  124. }
  125. /**
  126. * @return void
  127. * @phpstan-pure
  128. */
  129. public function next(): void
  130. {
  131. next($this->options);
  132. }
  133. /**
  134. * @return void
  135. * @phpstan-pure
  136. */
  137. public function rewind(): void
  138. {
  139. reset($this->options);
  140. }
  141. /**
  142. * @return bool
  143. * @phpstan-pure
  144. */
  145. public function valid(): bool
  146. {
  147. return key($this->options) !== null;
  148. }
  149. /**
  150. * @return array
  151. */
  152. public function jsonSerialize(): array
  153. {
  154. $list = [];
  155. foreach ($this as $name => $relationship) {
  156. $list[$name] = $relationship->jsonSerialize();
  157. }
  158. return $list;
  159. }
  160. /**
  161. * @param string $name
  162. * @param array $options
  163. * @return ToOneRelationship|ToManyRelationship
  164. */
  165. private function createRelationship(string $name, array $options): RelationshipInterface
  166. {
  167. $data = null;
  168. $parent = $this->parent;
  169. if ($parent instanceof FlexIdentifier) {
  170. $object = $parent->getObject();
  171. if (!method_exists($object, 'initRelationship')) {
  172. throw new RuntimeException(sprintf('Bad relationship %s', $name), 500);
  173. }
  174. $data = $object->initRelationship($name);
  175. }
  176. $cardinality = $options['cardinality'] ?? '';
  177. switch ($cardinality) {
  178. case 'to-one':
  179. $relationship = new ToOneRelationship($parent, $name, $options, $data);
  180. break;
  181. case 'to-many':
  182. $relationship = new ToManyRelationship($parent, $name, $options, $data ?? []);
  183. break;
  184. default:
  185. throw new RuntimeException(sprintf('Bad relationship cardinality %s', $cardinality), 500);
  186. }
  187. return $relationship;
  188. }
  189. }