refactor: standardize design

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-03-03 22:10:46 -05:00
parent c0fa9cadfb
commit 36e25f967b
12 changed files with 750 additions and 1076 deletions

View File

@@ -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;
}