|
@@ -0,0 +1,259 @@
|
|
|
+<?php declare(strict_types=1);
|
|
|
+
|
|
|
+namespace Grav\Framework\Relationships;
|
|
|
+
|
|
|
+use ArrayIterator;
|
|
|
+use Grav\Framework\Compat\Serializable;
|
|
|
+use Grav\Framework\Contracts\Object\IdentifierInterface;
|
|
|
+use Grav\Framework\Contracts\Relationships\ToManyRelationshipInterface;
|
|
|
+use Grav\Framework\Relationships\Traits\RelationshipTrait;
|
|
|
+use function count;
|
|
|
+use function is_callable;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Class ToManyRelationship
|
|
|
+ *
|
|
|
+ * @template T of IdentifierInterface
|
|
|
+ * @template P of IdentifierInterface
|
|
|
+ * @template-implements ToManyRelationshipInterface<T,P>
|
|
|
+ */
|
|
|
+class ToManyRelationship implements ToManyRelationshipInterface
|
|
|
+{
|
|
|
+ /** @template-use RelationshipTrait<T> */
|
|
|
+ use RelationshipTrait;
|
|
|
+ use Serializable;
|
|
|
+
|
|
|
+ /** @var IdentifierInterface[] */
|
|
|
+ protected $identifiers = [];
|
|
|
+
|
|
|
+ /**
|
|
|
+ * ToManyRelationship constructor.
|
|
|
+ * @param string $name
|
|
|
+ * @param IdentifierInterface $parent
|
|
|
+ * @param iterable<IdentifierInterface> $identifiers
|
|
|
+ */
|
|
|
+ public function __construct(IdentifierInterface $parent, string $name, array $options, iterable $identifiers = [])
|
|
|
+ {
|
|
|
+ $this->parent = $parent;
|
|
|
+ $this->name = $name;
|
|
|
+
|
|
|
+ $this->parseOptions($options);
|
|
|
+ $this->addIdentifiers($identifiers);
|
|
|
+
|
|
|
+ $this->modified = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return string
|
|
|
+ * @phpstan-pure
|
|
|
+ */
|
|
|
+ public function getCardinality(): string
|
|
|
+ {
|
|
|
+ return 'to-many';
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return int
|
|
|
+ * @phpstan-pure
|
|
|
+ */
|
|
|
+ public function count(): int
|
|
|
+ {
|
|
|
+ return count($this->identifiers);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public function fetch(): array
|
|
|
+ {
|
|
|
+ $list = [];
|
|
|
+ foreach ($this->identifiers as $identifier) {
|
|
|
+ if (is_callable([$identifier, 'getObject'])) {
|
|
|
+ $identifier = $identifier->getObject();
|
|
|
+ }
|
|
|
+ $list[] = $identifier;
|
|
|
+ }
|
|
|
+
|
|
|
+ return $list;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param string $id
|
|
|
+ * @param string|null $type
|
|
|
+ * @return bool
|
|
|
+ * @phpstan-pure
|
|
|
+ */
|
|
|
+ public function has(string $id, string $type = null): bool
|
|
|
+ {
|
|
|
+ return $this->getIdentifier($id, $type) !== null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param positive-int $pos
|
|
|
+ * @return IdentifierInterface|null
|
|
|
+ */
|
|
|
+ public function getNthIdentifier(int $pos): ?IdentifierInterface
|
|
|
+ {
|
|
|
+ $items = array_keys($this->identifiers);
|
|
|
+ $key = $items[$pos - 1] ?? null;
|
|
|
+ if (null === $key) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ return $this->identifiers[$key] ?? null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param string $id
|
|
|
+ * @param string|null $type
|
|
|
+ * @return IdentifierInterface|null
|
|
|
+ * @phpstan-pure
|
|
|
+ */
|
|
|
+ public function getIdentifier(string $id, string $type = null): ?IdentifierInterface
|
|
|
+ {
|
|
|
+ if (null === $type) {
|
|
|
+ $type = $this->getType();
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($type === 'media' && !str_contains($id, '/')) {
|
|
|
+ $name = $this->name;
|
|
|
+ $id = $this->parent->getType() . '/' . $this->parent->getId() . '/'. $name . '/' . $id;
|
|
|
+ }
|
|
|
+
|
|
|
+ $key = "{$type}/{$id}";
|
|
|
+
|
|
|
+ return $this->identifiers[$key] ?? null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param string $id
|
|
|
+ * @param string|null $type
|
|
|
+ * @return T|null
|
|
|
+ */
|
|
|
+ public function getObject(string $id, string $type = null): ?object
|
|
|
+ {
|
|
|
+ $identifier = $this->getIdentifier($id, $type);
|
|
|
+ if ($identifier && is_callable([$identifier, 'getObject'])) {
|
|
|
+ $identifier = $identifier->getObject();
|
|
|
+ }
|
|
|
+
|
|
|
+ return $identifier;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param IdentifierInterface $identifier
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public function addIdentifier(IdentifierInterface $identifier): bool
|
|
|
+ {
|
|
|
+ return $this->addIdentifiers([$identifier]);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param IdentifierInterface|null $identifier
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public function removeIdentifier(IdentifierInterface $identifier = null): bool
|
|
|
+ {
|
|
|
+ return !$identifier || $this->removeIdentifiers([$identifier]);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param iterable<IdentifierInterface> $identifiers
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public function addIdentifiers(iterable $identifiers): bool
|
|
|
+ {
|
|
|
+ foreach ($identifiers as $identifier) {
|
|
|
+ $type = $identifier->getType();
|
|
|
+ $id = $identifier->getId();
|
|
|
+ $key = "{$type}/{$id}";
|
|
|
+
|
|
|
+ $this->identifiers[$key] = $this->checkIdentifier($identifier);
|
|
|
+ $this->modified = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param iterable<IdentifierInterface> $identifiers
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public function replaceIdentifiers(iterable $identifiers): bool
|
|
|
+ {
|
|
|
+ $this->identifiers = [];
|
|
|
+ $this->modified = true;
|
|
|
+
|
|
|
+ return $this->addIdentifiers($identifiers);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param iterable<IdentifierInterface> $identifiers
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public function removeIdentifiers(iterable $identifiers): bool
|
|
|
+ {
|
|
|
+ foreach ($identifiers as $identifier) {
|
|
|
+ $type = $identifier->getType();
|
|
|
+ $id = $identifier->getId();
|
|
|
+ $key = "{$type}/{$id}";
|
|
|
+
|
|
|
+ unset($this->identifiers[$key]);
|
|
|
+ $this->modified = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return iterable<IdentifierInterface>
|
|
|
+ * @phpstan-pure
|
|
|
+ */
|
|
|
+ public function getIterator(): iterable
|
|
|
+ {
|
|
|
+ return new ArrayIterator($this->identifiers);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public function jsonSerialize(): array
|
|
|
+ {
|
|
|
+ $list = [];
|
|
|
+ foreach ($this->getIterator() as $item) {
|
|
|
+ $list[] = $item->jsonSerialize();
|
|
|
+ }
|
|
|
+
|
|
|
+ return $list;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public function __serialize(): array
|
|
|
+ {
|
|
|
+ return [
|
|
|
+ 'parent' => $this->parent,
|
|
|
+ 'name' => $this->name,
|
|
|
+ 'type' => $this->type,
|
|
|
+ 'options' => $this->options,
|
|
|
+ 'modified' => $this->modified,
|
|
|
+ 'identifiers' => $this->identifiers,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param array $data
|
|
|
+ * @return void
|
|
|
+ */
|
|
|
+ public function __unserialize(array $data): void
|
|
|
+ {
|
|
|
+ $this->parent = $data['parent'];
|
|
|
+ $this->name = $data['name'];
|
|
|
+ $this->type = $data['type'];
|
|
|
+ $this->options = $data['options'];
|
|
|
+ $this->modified = $data['modified'];
|
|
|
+ $this->identifiers = $data['identifiers'];
|
|
|
+ }
|
|
|
+}
|