Skip to content

Commit bbf1c29

Browse files
author
Wout Gevaert
committed
Merge main
2 parents e092054 + 19837f8 commit bbf1c29

File tree

4 files changed

+147
-0
lines changed

4 files changed

+147
-0
lines changed

src/Clauses/CallClause.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
namespace WikibaseSolutions\CypherDSL\Clauses;
4+
5+
use WikibaseSolutions\CypherDSL\Query;
6+
7+
/**
8+
* This class represents a CALL clause.
9+
*
10+
* @see https://neo4j.com/docs/cypher-manual/current/clauses/call-subquery/
11+
*/
12+
class CallClause extends Clause
13+
{
14+
/**
15+
* @var Query The query to call.
16+
*/
17+
private Query $subQuery;
18+
19+
/**
20+
* @param Query $subQuery The query to call.
21+
*/
22+
public function __construct(Query $subQuery)
23+
{
24+
$this->subQuery = $subQuery;
25+
}
26+
27+
/**
28+
* Returns the query that is being called.
29+
*
30+
* @return Query
31+
*/
32+
public function getSubQuery(): Query
33+
{
34+
return $this->subQuery;
35+
}
36+
37+
public function toQuery(): string
38+
{
39+
$subQuery = trim($this->subQuery->toQuery());
40+
41+
if ($subQuery === '') {
42+
return '';
43+
}
44+
45+
return sprintf('CALL { %s }', $subQuery);
46+
}
47+
48+
/**
49+
* @inheritDoc
50+
*/
51+
protected function getSubject(): string
52+
{
53+
return '{ ' . $this->subQuery->toQuery() . ' }';
54+
}
55+
56+
/**
57+
* @inheritDoc
58+
*/
59+
protected function getClause(): string
60+
{
61+
return 'CALL';
62+
}
63+
}

src/Query.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
namespace WikibaseSolutions\CypherDSL;
2323

2424
use function is_callable;
25+
use WikibaseSolutions\CypherDSL\Clauses\CallClause;
2526
use WikibaseSolutions\CypherDSL\Clauses\CallProcedureClause;
2627
use WikibaseSolutions\CypherDSL\Clauses\Clause;
2728
use WikibaseSolutions\CypherDSL\Clauses\CreateClause;
@@ -666,6 +667,29 @@ public function union($queryOrCallable, bool $all = false): self
666667
return $this;
667668
}
668669

670+
/**
671+
* Creates a CALL sub query clause.
672+
*
673+
* @param callable(Query)|Query $decoratorOrClause The callable decorating the pattern, or the actual CALL clause.
674+
*
675+
* @return Query
676+
*
677+
* @see https://neo4j.com/docs/cypher-manual/current/clauses/call-subquery/
678+
*/
679+
public function call($decoratorOrClause): self
680+
{
681+
if (is_callable($decoratorOrClause)) {
682+
$subQuery = self::new();
683+
$decoratorOrClause($subQuery);
684+
} else {
685+
$subQuery = $decoratorOrClause;
686+
}
687+
688+
$this->clauses[] = new CallClause($subQuery);
689+
690+
return $this;
691+
}
692+
669693
/**
670694
* Add a clause to the query.
671695
*

tests/Unit/Clauses/CallClauseTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace WikibaseSolutions\CypherDSL\Tests\Unit\Clauses;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use WikibaseSolutions\CypherDSL\Clauses\CallClause;
7+
use WikibaseSolutions\CypherDSL\Query;
8+
9+
class CallClauseTest extends TestCase
10+
{
11+
public function testCallClauseEmpty(): void
12+
{
13+
$query = Query::new();
14+
15+
$clause = new CallClause($query);
16+
17+
$this->assertEquals('', $clause->toQuery());
18+
$this->assertEquals(Query::new(), $clause->getSubQuery());
19+
}
20+
21+
public function testCallClauseFilled(): void
22+
{
23+
$query = Query::new()->match(Query::node('X')->named('x'))->returning(Query::rawExpression('*'));
24+
25+
$clause = new CallClause($query);
26+
27+
$this->assertEquals('CALL { MATCH (x:X) RETURN * }', $clause->toQuery());
28+
$this->assertEquals($query, $clause->getSubQuery());
29+
}
30+
}

tests/Unit/QueryTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,36 @@ public function testAutomaticIdentifierGeneration(): void
11071107
$this->assertInstanceOf(Variable::class, $node->getName());
11081108
}
11091109

1110+
public function testCallCallable(): void
1111+
{
1112+
$node = Query::node('X')->named('y');
1113+
$query = Query::new()->match($node)
1114+
->call(function (Query $subQuery) use ($node) {
1115+
$subQuery->with($node->getVariable())
1116+
->where($node->property('z')->equals(Query::literal('foo'), false))
1117+
->returning($node->property('z')->alias('foo'));
1118+
})
1119+
->returning(Query::variable('foo'));
1120+
1121+
$this->assertEquals("MATCH (y:X) CALL { WITH y WHERE y.z = 'foo' RETURN y.z AS foo } RETURN foo", $query->toQuery());
1122+
}
1123+
1124+
public function testCallClause(): void
1125+
{
1126+
$node = Query::node('X')->named('y');
1127+
1128+
$sub = Query::new()->with($node->getVariable())
1129+
->where($node->property('z')->equals(Query::literal('foo'), false))
1130+
->returning($node->property('z')->alias('foo'));
1131+
1132+
$query = Query::new()
1133+
->match($node)
1134+
->call($sub)
1135+
->returning(Query::variable('foo'));
1136+
1137+
$this->assertEquals("MATCH (y:X) CALL { WITH y WHERE y.z = 'foo' RETURN y.z AS foo } RETURN foo", $query->toQuery());
1138+
}
1139+
11101140
public function provideLiteralData(): array
11111141
{
11121142
return [

0 commit comments

Comments
 (0)