ToManyRelationship.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <?php declare(strict_types=1);
  2. namespace Grav\Framework\Relationships;
  3. use ArrayIterator;
  4. use Grav\Framework\Compat\Serializable;
  5. use Grav\Framework\Contracts\Object\IdentifierInterface;
  6. use Grav\Framework\Contracts\Relationships\ToManyRelationshipInterface;
  7. use Grav\Framework\Relationships\Traits\RelationshipTrait;
  8. use function count;
  9. use function is_callable;
  10. /**
  11. * Class ToManyRelationship
  12. *
  13. * @template T of IdentifierInterface
  14. * @template P of IdentifierInterface
  15. * @template-implements ToManyRelationshipInterface<T,P>
  16. */
  17. class ToManyRelationship implements ToManyRelationshipInterface
  18. {
  19. /** @template-use RelationshipTrait<T> */
  20. use RelationshipTrait;
  21. use Serializable;
  22. /** @var IdentifierInterface[] */
  23. protected $identifiers = [];
  24. /**
  25. * ToManyRelationship constructor.
  26. * @param string $name
  27. * @param IdentifierInterface $parent
  28. * @param iterable<IdentifierInterface> $identifiers
  29. */
  30. public function __construct(IdentifierInterface $parent, string $name, array $options, iterable $identifiers = [])
  31. {
  32. $this->parent = $parent;
  33. $this->name = $name;
  34. $this->parseOptions($options);
  35. $this->addIdentifiers($identifiers);
  36. $this->modified = false;
  37. }
  38. /**
  39. * @return string
  40. * @phpstan-pure
  41. */
  42. public function getCardinality(): string
  43. {
  44. return 'to-many';
  45. }
  46. /**
  47. * @return int
  48. * @phpstan-pure
  49. */
  50. public function count(): int
  51. {
  52. return count($this->identifiers);
  53. }
  54. /**
  55. * @return array
  56. */
  57. public function fetch(): array
  58. {
  59. $list = [];
  60. foreach ($this->identifiers as $identifier) {
  61. if (is_callable([$identifier, 'getObject'])) {
  62. $identifier = $identifier->getObject();
  63. }
  64. $list[] = $identifier;
  65. }
  66. return $list;
  67. }
  68. /**
  69. * @param string $id
  70. * @param string|null $type
  71. * @return bool
  72. * @phpstan-pure
  73. */
  74. public function has(string $id, string $type = null): bool
  75. {
  76. return $this->getIdentifier($id, $type) !== null;
  77. }
  78. /**
  79. * @param positive-int $pos
  80. * @return IdentifierInterface|null
  81. */
  82. public function getNthIdentifier(int $pos): ?IdentifierInterface
  83. {
  84. $items = array_keys($this->identifiers);
  85. $key = $items[$pos - 1] ?? null;
  86. if (null === $key) {
  87. return null;
  88. }
  89. return $this->identifiers[$key] ?? null;
  90. }
  91. /**
  92. * @param string $id
  93. * @param string|null $type
  94. * @return IdentifierInterface|null
  95. * @phpstan-pure
  96. */
  97. public function getIdentifier(string $id, string $type = null): ?IdentifierInterface
  98. {
  99. if (null === $type) {
  100. $type = $this->getType();
  101. }
  102. if ($type === 'media' && !str_contains($id, '/')) {
  103. $name = $this->name;
  104. $id = $this->parent->getType() . '/' . $this->parent->getId() . '/'. $name . '/' . $id;
  105. }
  106. $key = "{$type}/{$id}";
  107. return $this->identifiers[$key] ?? null;
  108. }
  109. /**
  110. * @param string $id
  111. * @param string|null $type
  112. * @return T|null
  113. */
  114. public function getObject(string $id, string $type = null): ?object
  115. {
  116. $identifier = $this->getIdentifier($id, $type);
  117. if ($identifier && is_callable([$identifier, 'getObject'])) {
  118. $identifier = $identifier->getObject();
  119. }
  120. return $identifier;
  121. }
  122. /**
  123. * @param IdentifierInterface $identifier
  124. * @return bool
  125. */
  126. public function addIdentifier(IdentifierInterface $identifier): bool
  127. {
  128. return $this->addIdentifiers([$identifier]);
  129. }
  130. /**
  131. * @param IdentifierInterface|null $identifier
  132. * @return bool
  133. */
  134. public function removeIdentifier(IdentifierInterface $identifier = null): bool
  135. {
  136. return !$identifier || $this->removeIdentifiers([$identifier]);
  137. }
  138. /**
  139. * @param iterable<IdentifierInterface> $identifiers
  140. * @return bool
  141. */
  142. public function addIdentifiers(iterable $identifiers): bool
  143. {
  144. foreach ($identifiers as $identifier) {
  145. $type = $identifier->getType();
  146. $id = $identifier->getId();
  147. $key = "{$type}/{$id}";
  148. $this->identifiers[$key] = $this->checkIdentifier($identifier);
  149. $this->modified = true;
  150. }
  151. return true;
  152. }
  153. /**
  154. * @param iterable<IdentifierInterface> $identifiers
  155. * @return bool
  156. */
  157. public function replaceIdentifiers(iterable $identifiers): bool
  158. {
  159. $this->identifiers = [];
  160. $this->modified = true;
  161. return $this->addIdentifiers($identifiers);
  162. }
  163. /**
  164. * @param iterable<IdentifierInterface> $identifiers
  165. * @return bool
  166. */
  167. public function removeIdentifiers(iterable $identifiers): bool
  168. {
  169. foreach ($identifiers as $identifier) {
  170. $type = $identifier->getType();
  171. $id = $identifier->getId();
  172. $key = "{$type}/{$id}";
  173. unset($this->identifiers[$key]);
  174. $this->modified = true;
  175. }
  176. return true;
  177. }
  178. /**
  179. * @return iterable<IdentifierInterface>
  180. * @phpstan-pure
  181. */
  182. public function getIterator(): iterable
  183. {
  184. return new ArrayIterator($this->identifiers);
  185. }
  186. /**
  187. * @return array
  188. */
  189. public function jsonSerialize(): array
  190. {
  191. $list = [];
  192. foreach ($this->getIterator() as $item) {
  193. $list[] = $item->jsonSerialize();
  194. }
  195. return $list;
  196. }
  197. /**
  198. * @return array
  199. */
  200. public function __serialize(): array
  201. {
  202. return [
  203. 'parent' => $this->parent,
  204. 'name' => $this->name,
  205. 'type' => $this->type,
  206. 'options' => $this->options,
  207. 'modified' => $this->modified,
  208. 'identifiers' => $this->identifiers,
  209. ];
  210. }
  211. /**
  212. * @param array $data
  213. * @return void
  214. */
  215. public function __unserialize(array $data): void
  216. {
  217. $this->parent = $data['parent'];
  218. $this->name = $data['name'];
  219. $this->type = $data['type'];
  220. $this->options = $data['options'];
  221. $this->modified = $data['modified'];
  222. $this->identifiers = $data['identifiers'];
  223. }
  224. }