Skip to content

Commit

Permalink
Merge branch '9.0' into testSuite
Browse files Browse the repository at this point in the history
  • Loading branch information
Bernhard Schmitt committed Aug 17, 2023
2 parents 86071fd + a301d85 commit b79dcb4
Show file tree
Hide file tree
Showing 45 changed files with 434 additions and 1,261 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ private function createNodeTable(Schema $schema): void
->setDefault(null);
$table
->setPrimaryKey(['relationanchorpoint'])
->addIndex(['nodeaggregateid'], 'NODE_AGGREGATE_ID')
->addIndex(['nodetypename'], 'NODE_TYPE_NAME');
->addIndex(['nodeaggregateid'])
->addIndex(['nodetypename']);
}

private function createHierarchyRelationTable(Schema $schema): void
Expand All @@ -91,10 +91,10 @@ private function createHierarchyRelationTable(Schema $schema): void
->setLength(255)
->setNotnull(true);
$table
->addIndex(['childnodeanchor'], 'CHILDNODEANCHOR')
->addIndex(['contentstreamid'], 'CONTENTSTREAMID')
->addIndex(['parentnodeanchor'], 'PARENTNODEANCHOR')
->addIndex(['contentstreamid', 'dimensionspacepointhash'], 'SUBGRAPH_ID');
->addIndex(['childnodeanchor'])
->addIndex(['contentstreamid'])
->addIndex(['parentnodeanchor'])
->addIndex(['contentstreamid', 'dimensionspacepointhash']);
}

private function createReferenceRelationTable(Schema $schema): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
use Neos\ContentRepository\Core\Projection\ContentGraph\Reference;
use Neos\ContentRepository\Core\Projection\ContentGraph\References;
use Neos\ContentRepository\Core\Projection\ContentGraph\Timestamps;
use Neos\ContentRepository\Core\SharedModel\Node\PropertyName;
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
use Neos\ContentRepository\Core\SharedModel\Node\ReferenceName;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
use Neos\ContentRepository\Core\SharedModel\Exception\NodeTypeNotFoundException;
Expand Down Expand Up @@ -122,7 +122,7 @@ public function mapReferenceRowsToReferences(
);
$result[] = new Reference(
$node,
PropertyName::fromString($nodeRow['referencename']),
ReferenceName::fromString($nodeRow['referencename']),
$nodeRow['referenceproperties']
? $this->createPropertyCollectionFromJsonString($nodeRow['referenceproperties'])
: null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ private function createNodeTable(Schema $schema): void

$table
->setPrimaryKey(['relationanchorpoint'])
->addIndex(['origindimensionspacepointhash'], 'node_origin')
->addIndex(['nodeaggregateid'], 'node_aggregate_identifier')
->addIndex(['origindimensionspacepointhash'])
->addIndex(['nodeaggregateid'])
/** NOTE: the GIN index on properties is added in {@see HypergraphProjection::setupTables()} */
->addIndex(['nodename'], 'node_name');
->addIndex(['nodename']);
}

private function createHierarchyHyperrelationTable(Schema $schema): void
Expand All @@ -92,10 +92,10 @@ private function createHierarchyHyperrelationTable(Schema $schema): void
->setNotnull(true);
$table
->setPrimaryKey(['contentstreamid', 'parentnodeanchor', 'dimensionspacepointhash'])
->addIndex(['contentstreamid'], 'hierarchy_content_stream_identifier')
->addIndex(['parentnodeanchor'], 'hierarchy_parent')
->addIndex(['contentstreamid'])
->addIndex(['parentnodeanchor'])
/** NOTE: the GIN index on childnodeanchors is added in {@see HypergraphProjection::setupTables()} */
->addIndex(['dimensionspacepointhash'], 'hierarchy_dimension_space_point');
->addIndex(['dimensionspacepointhash']);
}

private function createReferenceRelationTable(Schema $schema): void
Expand All @@ -117,8 +117,8 @@ private function createReferenceRelationTable(Schema $schema): void

$table
->setPrimaryKey(['sourcenodeanchor', 'name', 'position'])
->addIndex(['sourcenodeanchor'], 'reference_source')
->addIndex(['targetnodeaggregateid'], 'reference_target');
->addIndex(['sourcenodeanchor'])
->addIndex(['targetnodeaggregateid']);
}

private function createRestrictionHyperrelationTable(Schema $schema): void
Expand All @@ -142,9 +142,9 @@ private function createRestrictionHyperrelationTable(Schema $schema): void
'dimensionspacepointhash',
'originnodeaggregateid'
])
->addIndex(['contentstreamid'], 'restriction_content_stream_identifier')
->addIndex(['dimensionspacepointhash'], 'restriction_dimension_space_point')
->addIndex(['originnodeaggregateid'], 'restriction_origin');
->addIndex(['contentstreamid'])
->addIndex(['dimensionspacepointhash'])
->addIndex(['originnodeaggregateid']);
/** NOTE: the GIN index on affectednodeaggregateids is added in {@see HypergraphProjection::setupTables()} */
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
use Neos\ContentRepository\Core\Projection\ContentGraph\Subtree;
use Neos\ContentRepository\Core\Projection\ContentGraph\Subtrees;
use Neos\ContentRepository\Core\Projection\ContentGraph\Timestamps;
use Neos\ContentRepository\Core\SharedModel\Node\PropertyName;
use Neos\ContentRepository\Core\SharedModel\Node\ReferenceName;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
use Neos\ContentRepository\Core\NodeType\NodeTypeName;
Expand Down Expand Up @@ -139,7 +139,7 @@ public function mapReferenceRowsToReferences(
null,
$contentStreamId
),
PropertyName::fromString($referenceRow['referencename']),
ReferenceName::fromString($referenceRow['referencename']),
$referenceRow['referenceproperties']
? new PropertyCollection(
SerializedPropertyValues::fromJsonString($referenceRow['referenceproperties']),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -443,3 +443,67 @@ Feature: Create node aggregate with node
And I expect this node to have no succeeding siblings
And I expect this node to have no references
And I expect this node to not be referenced

Scenario: Create node aggregate with node with tethered child node of invalid configured name
Given I have the following NodeTypes configuration:
"""
'Neos.ContentRepository:Root': []
'Neos.ContentRepository.Testing:SubNode': []
'Neos.ContentRepository.Testing:NodeWithTetheredChildNodes':
childNodes:
invalidCasedName:
# transliterated invalidcasedname
type: 'Neos.ContentRepository.Testing:SubNode'
'invalidChäractörs':
# transliterated invalidcharactors
type: 'Neos.ContentRepository.Testing:SubNode'
"""

When the command CreateNodeAggregateWithNodeAndSerializedProperties is executed with payload:
| Key | Value |
| nodeAggregateId | "sir-david-nodenborough" |
| nodeTypeName | "Neos.ContentRepository.Testing:NodeWithTetheredChildNodes" |
| originDimensionSpacePoint | {} |
| parentNodeAggregateId | "lady-eleonode-rootford" |
| nodeName | "node" |
| tetheredDescendantNodeAggregateIds | {"invalidcasedname": "nody-mc-nodeface", "invalidcharactors": "lord-from-nodding-hill"} |
And the graph projection is fully up to date

And I expect the node aggregate "lady-eleonode-rootford" to exist
And I expect this node aggregate to be classified as "root"
And I expect this node aggregate to be of type "Neos.ContentRepository:Root"
And I expect this node aggregate to be unnamed
And I expect this node aggregate to occupy dimension space points [[]]
And I expect this node aggregate to cover dimension space points [[]]
And I expect this node aggregate to disable dimension space points []
And I expect this node aggregate to have no parent node aggregates
And I expect this node aggregate to have the child node aggregates ["sir-david-nodenborough"]

And I expect the node aggregate "sir-david-nodenborough" to exist
And I expect this node aggregate to be classified as "regular"
And I expect this node aggregate to be of type "Neos.ContentRepository.Testing:NodeWithTetheredChildNodes"
And I expect this node aggregate to be named "node"
And I expect this node aggregate to occupy dimension space points [[]]
And I expect this node aggregate to cover dimension space points [[]]
And I expect this node aggregate to have the parent node aggregates ["lady-eleonode-rootford"]
And I expect this node aggregate to have the child node aggregates ["nody-mc-nodeface", "lord-from-nodding-hill"]

And I expect the node aggregate "nody-mc-nodeface" to exist
And I expect this node aggregate to be classified as "tethered"
And I expect this node aggregate to be of type "Neos.ContentRepository.Testing:SubNode"
And I expect this node aggregate to be named "invalidcasedname"
And I expect this node aggregate to occupy dimension space points [[]]
And I expect this node aggregate to cover dimension space points [[]]
And I expect this node aggregate to disable dimension space points []
And I expect this node aggregate to have the parent node aggregates ["sir-david-nodenborough"]
And I expect this node aggregate to have no child node aggregates

And I expect the node aggregate "lord-from-nodding-hill" to exist
And I expect this node aggregate to be classified as "tethered"
And I expect this node aggregate to be of type "Neos.ContentRepository.Testing:SubNode"
And I expect this node aggregate to be named "invalidcharactors"
And I expect this node aggregate to occupy dimension space points [[]]
And I expect this node aggregate to cover dimension space points [[]]
And I expect this node aggregate to disable dimension space points []
And I expect this node aggregate to have the parent node aggregates ["sir-david-nodenborough"]
And I expect this node aggregate to have no child node aggregates
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,22 @@ final class PropertyType

public const PATTERN_ARRAY_OF = '/array<[^>]+>/';

/** only set if {@see sef:: isArrayOf()} */
private self $arrayOfType;

private function __construct(
public readonly string $value,
public readonly bool $isNullable
public readonly string $value
) {
if ($this->isArrayOf()) {
$arrayOfType = self::tryFromString($this->getArrayOf());
if (!$arrayOfType || $arrayOfType->isArray()) {
throw new \DomainException(sprintf(
'Array declaration "%s" has invalid subType. Expected either class string or int',
$this->value
));
}
$this->arrayOfType = $arrayOfType;
}
}

/**
Expand All @@ -52,23 +64,29 @@ public static function fromNodeTypeDeclaration(
PropertyName $propertyName,
NodeTypeName $nodeTypeName
): self {
if (\mb_strpos($declaration, '?') === 0) {
$declaration = \mb_substr($declaration, 1);
$isNullable = true;
}
// we always assume nullability for now
$isNullable = true;
if ($declaration === 'reference' || $declaration === 'references') {
throw PropertyTypeIsInvalid::becauseItIsReference($propertyName, $nodeTypeName);
}
$type = self::tryFromString($declaration);
if (!$type) {
throw PropertyTypeIsInvalid::becauseItIsUndefined($propertyName, $declaration, $nodeTypeName);
}
return $type;
}

private static function tryFromString(string $declaration): ?self
{
if ($declaration === 'reference' || $declaration === 'references') {
return null;
}
if ($declaration === 'bool' || $declaration === 'boolean') {
return self::bool($isNullable);
return self::bool();
}
if ($declaration === 'int' || $declaration === 'integer') {
return self::int($isNullable);
return self::int();
}
if ($declaration === 'float' || $declaration === 'double') {
return self::float($isNullable);
return self::float();
}
if (
in_array($declaration, [
Expand All @@ -80,7 +98,7 @@ public static function fromNodeTypeDeclaration(
'\DateTimeInterface'
])
) {
return self::date($isNullable);
return self::date();
}
if ($declaration === 'Uri' || $declaration === Uri::class || $declaration === UriInterface::class) {
$declaration = Uri::class;
Expand All @@ -96,35 +114,35 @@ public static function fromNodeTypeDeclaration(
&& !interface_exists($className)
&& !preg_match(self::PATTERN_ARRAY_OF, $declaration)
) {
throw PropertyTypeIsInvalid::becauseItIsUndefined($propertyName, $declaration, $nodeTypeName);
return null;
}

return new self($declaration, $isNullable);
return new self($declaration);
}

public static function bool(bool $isNullable): self
public static function bool(): self
{
return new self(self::TYPE_BOOL, $isNullable);
return new self(self::TYPE_BOOL);
}

public static function int(bool $isNullable): self
public static function int(): self
{
return new self(self::TYPE_INT, $isNullable);
return new self(self::TYPE_INT);
}

public static function string(bool $isNullable): self
public static function string(): self
{
return new self(self::TYPE_STRING, $isNullable);
return new self(self::TYPE_STRING);
}

public static function float(bool $isNullable): self
public static function float(): self
{
return new self(self::TYPE_FLOAT, $isNullable);
return new self(self::TYPE_FLOAT);
}

public static function date(bool $isNullable): self
public static function date(): self
{
return new self(self::TYPE_DATE, $isNullable);
return new self(self::TYPE_DATE);
}

public function isBool(): bool
Expand Down Expand Up @@ -167,9 +185,9 @@ public function getValue(): string
return $this->value;
}

public function getArrayOfClassName(): string
private function getArrayOf(): string
{
return \mb_substr($this->value, 6, \mb_strlen($this->value) - 7);
return \mb_substr($this->value, 6, -1);
}

public function isMatchedBy(mixed $propertyValue): bool
Expand All @@ -196,15 +214,15 @@ public function isMatchedBy(mixed $propertyValue): bool
return $propertyValue instanceof \DateTimeInterface;
}
if ($this->isArrayOf()) {
if (is_array($propertyValue)) {
$className = $this->getArrayOfClassName();
foreach ($propertyValue as $object) {
if (!$object instanceof $className) {
return false;
}
if (!is_array($propertyValue)) {
return false;
}
foreach ($propertyValue as $value) {
if (!$this->arrayOfType->isMatchedBy($value)) {
return false;
}
return true;
}
return true;
}

$className = $this->value[0] != '\\'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public function findSubtree(NodeAggregateId $entryNodeAggregateId, Filter\FindSu
*
* A reference is a node property of type "reference" or "references"
* Because each reference has a name and can contain properties itself, this method does not return the target nodes
* directly, but actual {@see \Neos\ContentRepository\Core\Projection\ContentGraph\Reference} instances.
* directly, but a collection of references {@see References}.
* The corresponding nodes can be retrieved via {@see References::getNodes()}
*/
public function findReferences(NodeAggregateId $nodeAggregateId, Filter\FindReferencesFilter $filter): References;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@

namespace Neos\ContentRepository\Core\Projection\ContentGraph;

use Neos\ContentRepository\Core\SharedModel\Node\PropertyName;
use Neos\ContentRepository\Core\SharedModel\Node\ReferenceName;

/**
* A reference to a given node by a name and with optional reference properties
*
* {@see References}
*
* @api
*/
final class Reference
{
public function __construct(
public readonly Node $node,
public readonly PropertyName $name,
public readonly ReferenceName $name,
public readonly ?PropertyCollectionInterface $properties
) {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,23 @@
namespace Neos\ContentRepository\Core\Projection\ContentGraph;

/**
* An immutable, type-safe collection of Reference objects
* An immutable, 0-indexed, type-safe collection of Reference objects
*
* A reference is a node property of type "reference" or "references"
*
* Each reference describes the edge with its properties to another node.
*
* - references:
* In the case of "outgoing" references {@see ContentSubgraphInterface::findReferences()}
* each reference name {@see Reference::$name} corresponds to the reference property name from the "outgoing" node, to wich node {@see Reference::$node} the reference points to.
*
* - back-references:
* In the case of "incoming" references {@see ContentSubgraphInterface::findBackReferences()}
* each reference name {@see Reference::$name} corresponds to the reference property name from wich "incoming" node {@see Reference::$node} the reference points from.
*
* The properties {@see Reference::$properties} are declared directly on the reference, and can provide information how one node is linked to another.
*
* If multiple "outgoing" references are allowed via type "references", this collection will return multiple references with the same name {@see Reference::$name}.
*
* @implements \IteratorAggregate<int,Reference>
* @implements \ArrayAccess<int,Reference>
Expand Down
Loading

0 comments on commit b79dcb4

Please sign in to comment.