refactor: standardize design
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -7,11 +7,9 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\FileProviderLocal\Store;
|
||||
namespace KTXM\ProviderLocalDocuments\Store;
|
||||
|
||||
use KTXC\Db\DataStore;
|
||||
use KTXF\Files\Node\INodeCollectionMutable;
|
||||
use KTXF\Files\Node\INodeEntityMutable;
|
||||
use KTXF\Files\Node\NodeType;
|
||||
use KTXF\Resource\Filter\Filter;
|
||||
use KTXF\Resource\Filter\FilterComparisonOperator;
|
||||
@@ -21,13 +19,15 @@ use KTXF\Resource\Range\IRangeTally;
|
||||
use KTXF\Resource\Range\RangeType;
|
||||
use KTXF\Resource\Sort\Sort;
|
||||
use KTXF\Utile\UUID;
|
||||
use KTXM\FileProviderLocal\Providers\Personal\NodeCollection;
|
||||
use KTXM\FileProviderLocal\Providers\Personal\NodeEntity;
|
||||
use KTXM\ProviderLocalDocuments\Providers\Personal\CollectionResource;
|
||||
use KTXM\ProviderLocalDocuments\Providers\Personal\EntityResource;
|
||||
use KTXM\ProviderLocalDocuments\Providers\Personal\NodeCollection;
|
||||
|
||||
class MetaStore {
|
||||
|
||||
protected string $_NodeTable = 'file_provider_local_node';
|
||||
protected string $_ChronicleTable = 'file_provider_local_chronicle';
|
||||
private const ROOT_COLLECTION_ID = '00000000-0000-0000-0000-000000000000';
|
||||
protected string $_NodeTable = 'provider_local_documents_node';
|
||||
protected string $_ChronicleTable = 'provider_local_documents_chronicle';
|
||||
|
||||
protected array $_CollectionFilterAttributeMap = [
|
||||
'id' => 'nid',
|
||||
@@ -137,7 +137,7 @@ class MetaStore {
|
||||
$query = [
|
||||
'tid' => $tenantId,
|
||||
'uid' => $userId,
|
||||
'pid' => $location,
|
||||
'cid' => empty($location) ? self::ROOT_COLLECTION_ID : $location,
|
||||
'type' => NodeType::Collection->value,
|
||||
];
|
||||
|
||||
@@ -157,8 +157,8 @@ class MetaStore {
|
||||
$cursor = $this->_store->selectCollection($this->_NodeTable)->find($query, $options);
|
||||
$list = [];
|
||||
foreach ($cursor as $entry) {
|
||||
$node = (new NodeCollection())->fromStore($entry);
|
||||
$list[$node->id()] = $node;
|
||||
$node = $this->collectionFresh()->fromStore($entry);
|
||||
$list[$node->identifier()] = $node;
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
@@ -166,14 +166,22 @@ class MetaStore {
|
||||
/**
|
||||
* Check if a collection exists
|
||||
*/
|
||||
public function collectionExtant(string $tenantId, string $userId, string|int $identifier): bool {
|
||||
$cursor = $this->_store->selectCollection($this->_NodeTable)->findOne([
|
||||
public function collectionExtant(string $tenantId, string $userId, string|int ...$identifiers): array {
|
||||
$cursor = $this->_store->selectCollection($this->_NodeTable)->find([
|
||||
'tid' => $tenantId,
|
||||
'uid' => $userId,
|
||||
'nid' => $identifier,
|
||||
'nid' => ['$in' => $identifiers],
|
||||
'type' => NodeType::Collection->value,
|
||||
]);
|
||||
return $cursor !== null;
|
||||
], ['projection' => ['nid' => 1]]);
|
||||
$found = [];
|
||||
foreach ($cursor as $entry) {
|
||||
$found[(string)$entry['nid']] = true;
|
||||
}
|
||||
$result = [];
|
||||
foreach ($identifiers as $id) {
|
||||
$result[$id] = isset($found[(string)$id]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,47 +197,50 @@ class MetaStore {
|
||||
|
||||
$list = [];
|
||||
foreach ($cursor as $entry) {
|
||||
$node = (new NodeCollection())->fromStore($entry);
|
||||
$list[$node->id()] = $node;
|
||||
$node = $this->collectionFresh()->fromStore($entry);
|
||||
$list[$node->identifier()] = $node;
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fresh collection instance
|
||||
*/
|
||||
protected function collectionFresh(): CollectionResource {
|
||||
return new CollectionResource();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a collection
|
||||
*/
|
||||
public function collectionCreate(string $tenantId, string $userId, string|int|null $location, INodeCollectionMutable $collection, array $options = []): NodeCollection {
|
||||
public function collectionCreate(string $tenantId, string $userId, string|int|null $location, CollectionResource $collection, array $options = []): CollectionResource {
|
||||
$data = [
|
||||
'tid' => $tenantId,
|
||||
'uid' => $userId,
|
||||
'cid' => empty($location) ? self::ROOT_COLLECTION_ID : $location,
|
||||
'nid' => UUID::v4(),
|
||||
'pid' => $location,
|
||||
'type' => NodeType::Collection->value,
|
||||
'createdBy' => $userId,
|
||||
'createdOn' => date('c'),
|
||||
'modifiedBy' => $userId,
|
||||
'modifiedOn' => date('c'),
|
||||
'owner' => $userId,
|
||||
'label' => $collection->getLabel(),
|
||||
'created' => date('c'),
|
||||
'modified' => date('c'),
|
||||
'properties' => $collection->getProperties()->toStore(),
|
||||
];
|
||||
$data['signature'] = md5(json_encode([$data['label'], $data['modifiedOn']]));
|
||||
$data['signature'] = time();
|
||||
|
||||
$this->_store->selectCollection($this->_NodeTable)->insertOne($data);
|
||||
$this->chronicleNode($tenantId, $userId, $data['nid'], 1);
|
||||
|
||||
return (new NodeCollection())->fromStore($data);
|
||||
return $this->collectionFresh()->fromStore($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify a collection
|
||||
*/
|
||||
public function collectionModify(string $tenantId, string $userId, string|int $identifier, INodeCollectionMutable $collection): NodeCollection {
|
||||
public function collectionModify(string $tenantId, string $userId, string|int $identifier, CollectionResource $collection): CollectionResource {
|
||||
$data = [
|
||||
'modifiedOn' => date('c'),
|
||||
'modifiedBy' => $userId,
|
||||
'label' => $collection->getLabel(),
|
||||
'modified' => date('c'),
|
||||
'properties' => $collection->getProperties()->toStore(),
|
||||
];
|
||||
$data['signature'] = md5(json_encode([$data['label'], $data['modifiedOn']]));
|
||||
$data['signature'] = time();
|
||||
|
||||
$this->_store->selectCollection($this->_NodeTable)->updateOne(
|
||||
['tid' => $tenantId, 'uid' => $userId, 'nid' => $identifier],
|
||||
@@ -273,11 +284,10 @@ class MetaStore {
|
||||
/**
|
||||
* Move a collection
|
||||
*/
|
||||
public function collectionMove(string $tenantId, string $userId, string|int $identifier, string|int|null $location): NodeCollection {
|
||||
public function collectionMove(string $tenantId, string $userId, string|int $identifier, string|int|null $location): CollectionResource {
|
||||
$data = [
|
||||
'pid' => $location,
|
||||
'modifiedBy' => $userId,
|
||||
'modifiedOn' => date('c'),
|
||||
'cid' => empty($location) ? self::ROOT_COLLECTION_ID : $location,
|
||||
'modified' => date('c'),
|
||||
];
|
||||
|
||||
$this->_store->selectCollection($this->_NodeTable)->updateOne(
|
||||
@@ -293,14 +303,14 @@ class MetaStore {
|
||||
/**
|
||||
* Copy a collection
|
||||
*/
|
||||
public function collectionCopy(string $tenantId, string $userId, string|int $identifier, string|int|null $location): NodeCollection {
|
||||
public function collectionCopy(string $tenantId, string $userId, string|int $identifier, string|int|null $location): CollectionResource {
|
||||
$collections = $this->collectionFetch($tenantId, $userId, $identifier);
|
||||
if (!isset($collections[$identifier])) {
|
||||
throw new \RuntimeException("Collection not found: $identifier");
|
||||
}
|
||||
|
||||
$source = $collections[$identifier];
|
||||
$newCollection = new NodeCollection();
|
||||
$newCollection = $this->collectionFresh();
|
||||
$newCollection->setLabel($source->getLabel());
|
||||
|
||||
$newNode = $this->collectionCreate($tenantId, $userId, $location, $newCollection);
|
||||
@@ -309,9 +319,9 @@ class MetaStore {
|
||||
$children = $this->nodeList($tenantId, $userId, $identifier, false);
|
||||
foreach ($children as $childId => $child) {
|
||||
if ($child->isCollection()) {
|
||||
$this->collectionCopy($tenantId, $userId, $childId, $newNode->id());
|
||||
$this->collectionCopy($tenantId, $userId, $childId, $newNode->identifier());
|
||||
} else {
|
||||
$this->entityCopy($tenantId, $userId, $identifier, $childId, $newNode->id());
|
||||
$this->entityCopy($tenantId, $userId, $identifier, $childId, $newNode->identifier());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,6 +330,13 @@ class MetaStore {
|
||||
|
||||
// ========== Entity Operations ==========
|
||||
|
||||
/**
|
||||
* Fresh entity instance
|
||||
*/
|
||||
protected function entityFresh(): EntityResource {
|
||||
return new EntityResource();
|
||||
}
|
||||
|
||||
/**
|
||||
* List entities in a collection
|
||||
*/
|
||||
@@ -327,7 +344,7 @@ class MetaStore {
|
||||
$query = [
|
||||
'tid' => $tenantId,
|
||||
'uid' => $userId,
|
||||
'pid' => $collection,
|
||||
'cid' => $collection,
|
||||
'type' => NodeType::Entity->value,
|
||||
];
|
||||
|
||||
@@ -352,8 +369,8 @@ class MetaStore {
|
||||
$cursor = $this->_store->selectCollection($this->_NodeTable)->find($query, $options);
|
||||
$list = [];
|
||||
foreach ($cursor as $entry) {
|
||||
$node = (new NodeEntity())->fromStore($entry);
|
||||
$list[$node->id()] = $node;
|
||||
$node = $this->entityFresh()->fromStore($entry);
|
||||
$list[$node->identifier()] = $node;
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
@@ -365,7 +382,7 @@ class MetaStore {
|
||||
$cursor = $this->_store->selectCollection($this->_NodeTable)->findOne([
|
||||
'tid' => $tenantId,
|
||||
'uid' => $userId,
|
||||
'pid' => $collection,
|
||||
'cid' => $collection,
|
||||
'nid' => $identifier,
|
||||
'type' => NodeType::Entity->value,
|
||||
]);
|
||||
@@ -379,15 +396,15 @@ class MetaStore {
|
||||
$cursor = $this->_store->selectCollection($this->_NodeTable)->find([
|
||||
'tid' => $tenantId,
|
||||
'uid' => $userId,
|
||||
'pid' => $collection,
|
||||
'cid' => $collection,
|
||||
'nid' => ['$in' => $identifiers],
|
||||
'type' => NodeType::Entity->value,
|
||||
]);
|
||||
|
||||
$list = [];
|
||||
foreach ($cursor as $entry) {
|
||||
$node = (new NodeEntity())->fromStore($entry);
|
||||
$list[$node->id()] = $node;
|
||||
$node = $this->entityFresh()->fromStore($entry);
|
||||
$list[$node->identifier()] = $node;
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
@@ -395,48 +412,52 @@ class MetaStore {
|
||||
/**
|
||||
* Create an entity
|
||||
*/
|
||||
public function entityCreate(string $tenantId, string $userId, string|int|null $collection, INodeEntityMutable $entity, array $options = []): NodeEntity {
|
||||
$data = [
|
||||
public function entityCreate(string $tenantId, string $userId, string|int|null $collection, EntityResource $entity, array $options = []): EntityResource {
|
||||
|
||||
$entity = [
|
||||
'tid' => $tenantId,
|
||||
'uid' => $userId,
|
||||
'cid' => empty($collection) ? self::ROOT_COLLECTION_ID : $collection,
|
||||
'nid' => UUID::v4(),
|
||||
'pid' => $collection,
|
||||
'type' => NodeType::Entity->value,
|
||||
'createdOn' => date('c'),
|
||||
'createdBy' => $userId,
|
||||
'modifiedOn' => date('c'),
|
||||
'modifiedBy' => $userId,
|
||||
'size' => 0,
|
||||
'mime' => $entity->getMime(),
|
||||
'format' => $entity->getFormat(),
|
||||
'encoding' => $entity->getEncoding(),
|
||||
'label' => $entity->getLabel(),
|
||||
'created' => date('c'),
|
||||
'modified' => date('c'),
|
||||
'properties' => $entity->getProperties()->toStore(),
|
||||
];
|
||||
$data['signature'] = md5(json_encode([$data['label'], $data['size'], $data['mime'], $data['modifiedOn']]));
|
||||
$entity['signature'] = time();
|
||||
|
||||
$this->_store->selectCollection($this->_NodeTable)->insertOne($data);
|
||||
$this->chronicleNode($tenantId, $userId, $data['nid'], 1);
|
||||
$this->_store->selectCollection($this->_NodeTable)->insertOne($entity);
|
||||
$this->chronicleNode($tenantId, $userId, $entity['nid'], 1);
|
||||
|
||||
return (new NodeEntity())->fromStore($data);
|
||||
return $this->entityFresh()->fromStore($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify an entity
|
||||
*/
|
||||
public function entityModify(string $tenantId, string $userId, string|int|null $collection, string|int $identifier, INodeEntityMutable $entity): NodeEntity {
|
||||
$data = [
|
||||
'label' => $entity->getLabel(),
|
||||
'mime' => $entity->getMime(),
|
||||
'format' => $entity->getFormat(),
|
||||
'encoding' => $entity->getEncoding(),
|
||||
'modifiedOn' => date('c'),
|
||||
'modifiedBy' => $userId,
|
||||
];
|
||||
$data['signature'] = md5(json_encode([$data['label'], $data['mime'], $data['modifiedOn']]));
|
||||
public function entityModify(string $tenantId, string $userId, string|int|null $collection, string|int $identifier, EntityResource|array $data, bool $partial = false): EntityResource {
|
||||
$attributes = [];
|
||||
|
||||
if ($data instanceof EntityResource) {
|
||||
$data = $data->getProperties()->toStore();
|
||||
}
|
||||
|
||||
if ($partial) {
|
||||
foreach ($data['properties'] ?? [] as $key => $value) {
|
||||
if ($value !== null) {
|
||||
$attributes["properties.$key"] = $value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$attributes['properties'] = $data['properties'] ?? [];
|
||||
}
|
||||
|
||||
$attributes['modified'] = date('c');
|
||||
$attributes['signature'] = time();
|
||||
|
||||
$this->_store->selectCollection($this->_NodeTable)->updateOne(
|
||||
['tid' => $tenantId, 'uid' => $userId, 'nid' => $identifier],
|
||||
['$set' => $data]
|
||||
['tid' => $tenantId, 'uid' => $userId, 'cid' => $collection, 'nid' => $identifier],
|
||||
['$set' => $attributes]
|
||||
);
|
||||
$this->chronicleNode($tenantId, $userId, $identifier, 2);
|
||||
|
||||
@@ -444,58 +465,31 @@ class MetaStore {
|
||||
return $entities[$identifier];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update entity attributes
|
||||
*
|
||||
* Supported attributes: size, format, mime, encoding, label
|
||||
*
|
||||
* @param string $tenantId tenant identifier
|
||||
* @param string $userId user identifier
|
||||
* @param string|int $collection collection identifier
|
||||
* @param string|int $identifier entity identifier
|
||||
* @param array $attributes key-value pairs of attributes to update
|
||||
*/
|
||||
public function entityUpdate(string $tenantId, string $userId, string|int $collection, string|int $identifier, array $attributes): void {
|
||||
// Filter to allowed attributes only
|
||||
$allowed = ['size', 'format', 'mime', 'encoding', 'label'];
|
||||
$data = array_intersect_key($attributes, array_flip($allowed));
|
||||
|
||||
if (empty($data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Always update modification timestamp
|
||||
$data['modifiedOn'] = date('c');
|
||||
|
||||
$this->_store->selectCollection($this->_NodeTable)->updateOne(
|
||||
['tid' => $tenantId, 'uid' => $userId, 'nid' => $identifier],
|
||||
['$set' => $data]
|
||||
);
|
||||
$this->chronicleNode($tenantId, $userId, $identifier, 2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Destroy an entity
|
||||
*/
|
||||
public function entityDestroy(string $tenantId, string $userId, string|int|null $collection, string|int $identifier): bool {
|
||||
|
||||
$result = $this->_store->selectCollection($this->_NodeTable)->deleteOne([
|
||||
'tid' => $tenantId,
|
||||
'uid' => $userId,
|
||||
'cid' => $collection,
|
||||
'nid' => $identifier
|
||||
]);
|
||||
|
||||
|
||||
if ($result->getDeletedCount() === 1) {
|
||||
$this->chronicleNode($tenantId, $userId, $identifier, 3);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move an entity to another collection
|
||||
*/
|
||||
public function entityMove(string $tenantId, string $userId, string|int|null $collection, string|int $identifier, string|int|null $destination): NodeEntity {
|
||||
public function entityMove(string $tenantId, string $userId, string|int|null $collection, string|int $identifier, string|int|null $destination): EntityResource {
|
||||
$data = [
|
||||
'pid' => $destination,
|
||||
'modifiedOn' => date('c'),
|
||||
@@ -503,7 +497,7 @@ class MetaStore {
|
||||
];
|
||||
|
||||
$this->_store->selectCollection($this->_NodeTable)->updateOne(
|
||||
['tid' => $tenantId, 'uid' => $userId, 'nid' => $identifier],
|
||||
['tid' => $tenantId, 'uid' => $userId, 'nid' => $identifier, 'cid' => $collection],
|
||||
['$set' => $data]
|
||||
);
|
||||
$this->chronicleNode($tenantId, $userId, $identifier, 2);
|
||||
@@ -515,14 +509,14 @@ class MetaStore {
|
||||
/**
|
||||
* Copy an entity
|
||||
*/
|
||||
public function entityCopy(string $tenantId, string $userId, string|int|null $collection, string|int $identifier, string|int|null $destination): NodeEntity {
|
||||
public function entityCopy(string $tenantId, string $userId, string|int|null $collection, string|int $identifier, string|int|null $destination): EntityResource {
|
||||
$entities = $this->entityFetch($tenantId, $userId, $collection, $identifier);
|
||||
if (!isset($entities[$identifier])) {
|
||||
throw new \RuntimeException("Entity not found: $identifier");
|
||||
}
|
||||
|
||||
$source = $entities[$identifier];
|
||||
$newEntity = new NodeEntity();
|
||||
$newEntity = new EntityResource();
|
||||
$newEntity->setLabel($source->getLabel());
|
||||
$newEntity->setMime($source->getMime());
|
||||
$newEntity->setFormat($source->getFormat());
|
||||
@@ -588,7 +582,7 @@ class MetaStore {
|
||||
if ($nodeType === NodeType::Collection->value) {
|
||||
$node = (new NodeCollection())->fromStore($entry);
|
||||
} else {
|
||||
$node = (new NodeEntity())->fromStore($entry);
|
||||
$node = (new EntityResource())->fromStore($entry);
|
||||
}
|
||||
$list[$node->id()] = $node;
|
||||
}
|
||||
@@ -737,7 +731,7 @@ class MetaStore {
|
||||
if ($nodeType === NodeType::Collection->value) {
|
||||
$node = (new NodeCollection())->fromStore($entry);
|
||||
} else {
|
||||
$node = (new NodeEntity())->fromStore($entry);
|
||||
$node = (new EntityResource())->fromStore($entry);
|
||||
}
|
||||
$list[$node->id()] = $node;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user