Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
bwaidelich authored and mhsdesign committed Oct 1, 2024
1 parent f3f9571 commit fab9d2d
Show file tree
Hide file tree
Showing 75 changed files with 827 additions and 1,986 deletions.
165 changes: 154 additions & 11 deletions Neos.ContentGraph.DoctrineDbalAdapter/src/ContentGraphFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,19 @@
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStream;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreams;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamStatus;
use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace;
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
use Neos\ContentRepository\Core\SharedModel\Workspace\Workspaces;
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceStatus;
use Neos\EventStore\Model\Event\Version;

/**
* @internal only used inside the
* @see ContentGraphFinder
* @see ContentGraphAdapter
*/
final readonly class ContentGraphFactory implements ContentGraphFactoryInterface
{
Expand All @@ -42,32 +49,168 @@ public function __construct(

public function buildForWorkspace(WorkspaceName $workspaceName): ContentGraph
{
$currentContentStreamIdStatement = <<<SQL
$workspace = $this->findWorkspaceByName($workspaceName);

if ($workspace === null) {
throw WorkspaceDoesNotExist::butWasSupposedTo($workspaceName);
}
return $this->buildForWorkspaceAndContentStream($workspace->workspaceName, $workspace->currentContentStreamId);
}

public function buildForWorkspaceAndContentStream(WorkspaceName $workspaceName, ContentStreamId $contentStreamId): ContentGraph
{
return new ContentGraph($this->dbal, $this->nodeFactory, $this->contentRepositoryId, $this->nodeTypeManager, $this->tableNames, $workspaceName, $contentStreamId);
}

public function findWorkspaceByName(WorkspaceName $workspaceName): ?Workspace
{
$workspaceByNameStatement = <<<SQL
SELECT
currentcontentstreamid
name, baseWorkspaceName, currentContentStreamId, status
FROM
{$this->tableNames->workspace()}
WHERE
workspaceName = :workspaceName
name = :workspaceName
LIMIT 1
SQL;
try {
$currentContentStreamId = $this->dbal->fetchOne($currentContentStreamIdStatement, [
$row = $this->dbal->fetchAssociative($workspaceByNameStatement, [
'workspaceName' => $workspaceName->value,
]);
} catch (Exception $e) {
throw new \RuntimeException(sprintf('Failed to load workspace content stream id from database: %s', $e->getMessage()), 1716486077, $e);
throw new \RuntimeException(sprintf('Failed to load workspace from database: %s', $e->getMessage()), 1716486077, $e);
}
if ($row === false) {
return null;
}
return self::workspaceFromDatabaseRow($row);
}

if ($currentContentStreamId === false) {
throw WorkspaceDoesNotExist::butWasSupposedTo($workspaceName);
public function getWorkspaces(): Workspaces
{
$workspacesStatement = <<<SQL
SELECT
name, baseWorkspaceName, currentContentStreamId, status
FROM
{$this->tableNames->workspace()}
SQL;
try {
$rows = $this->dbal->fetchAllAssociative($workspacesStatement);
} catch (Exception $e) {
throw new \RuntimeException(sprintf('Failed to load workspaces from database: %s', $e->getMessage()), 1716902981, $e);
}
return Workspaces::fromArray(array_map(self::workspaceFromDatabaseRow(...), $rows));
}

public function findContentStreamById(ContentStreamId $contentStreamId): ?ContentStream
{
$contentStreamByIdStatement = <<<SQL
SELECT
id, sourceContentStreamId, status, version
FROM
{$this->tableNames->contentStream()}
WHERE
id = :contentStreamId
LIMIT 1
SQL;
try {
$row = $this->dbal->fetchAssociative($contentStreamByIdStatement, [
'contentStreamId' => $contentStreamId->value,
]);
} catch (Exception $e) {
throw new \RuntimeException(sprintf('Failed to load content stream from database: %s', $e->getMessage()), 1716903166, $e);
}
if ($row === false) {
return null;
}
return self::contentStreamFromDatabaseRow($row);
}

return $this->buildForWorkspaceAndContentStream($workspaceName, ContentStreamId::fromString($currentContentStreamId));
public function getContentStreams(): ContentStreams
{
$contentStreamsStatement = <<<SQL
SELECT
id, sourceContentStreamId, status, version
FROM
{$this->tableNames->contentStream()}
SQL;
try {
$rows = $this->dbal->fetchAllAssociative($contentStreamsStatement);
} catch (Exception $e) {
throw new \RuntimeException(sprintf('Failed to load content streams from database: %s', $e->getMessage()), 1716903042, $e);
}
return ContentStreams::fromArray(array_map(self::contentStreamFromDatabaseRow(...), $rows));
}

public function buildForWorkspaceAndContentStream(WorkspaceName $workspaceName, ContentStreamId $contentStreamId): ContentGraph
public function getUnusedAndRemovedContentStreamIds(): iterable
{
return new ContentGraph($this->dbal, $this->nodeFactory, $this->contentRepositoryId, $this->nodeTypeManager, $this->tableNames, $workspaceName, $contentStreamId);
$removedContentStreamIdsStatement = <<<SQL
WITH RECURSIVE transitiveUsedContentStreams (id) AS (
-- initial case: find all content streams currently in direct use by a workspace
SELECT
id
FROM
{$this->tableNames->contentStream()}
WHERE
status = :inUseStatus
AND removed = false
UNION
-- now, when a content stream is in use by a workspace, its source content stream is
-- also "transitively" in use.
SELECT
sourceContentStreamId
FROM
{$this->tableNames->contentStream()}
JOIN transitiveUsedContentStreams ON {$this->tableNames->contentStream()}.id = transitiveUsedContentStreams.id
WHERE
{$this->tableNames->contentStream()}.sourceContentStreamId IS NOT NULL
)
-- now, we check for removed content streams which we do not need anymore transitively
SELECT
id
FROM
{$this->tableNames->contentStream()} AS cs
WHERE
removed = true
AND NOT EXISTS (
SELECT 1
FROM transitiveUsedContentStreams
WHERE cs.id = transitiveUsedContentStreams.id
)
SQL;
try {
$contentStreamIds = $this->dbal->fetchFirstColumn($removedContentStreamIdsStatement, [
'inUseStatus' => ContentStreamStatus::IN_USE_BY_WORKSPACE->value
]);
} catch (Exception $e) {
throw new \RuntimeException(sprintf('Failed to load unused and removed content stream ids from database: %s', $e->getMessage()), 1716914615, $e);
}
return array_map(ContentStreamId::fromString(...), $contentStreamIds);
}

/**
* @param array<string, mixed> $row
*/
private static function workspaceFromDatabaseRow(array $row): Workspace
{
return new Workspace(
WorkspaceName::fromString($row['name']),
isset($row['baseWorkspaceName']) ? WorkspaceName::fromString($row['baseWorkspaceName']) : null,
ContentStreamId::fromString($row['currentContentStreamId']),
WorkspaceStatus::from($row['status']),
);
}

/**
* @param array<string, mixed> $row
*/
private static function contentStreamFromDatabaseRow(array $row): ContentStream
{
return new ContentStream(
ContentStreamId::fromString($row['id']),
isset($row['sourceContentStreamId']) ? ContentStreamId::fromString($row['sourceContentStreamId']) : null,
ContentStreamStatus::from($row['status']),
Version::fromInteger((int)$row['version']),
);
}
}
Loading

0 comments on commit fab9d2d

Please sign in to comment.