Skip to content

Commit 19837f8

Browse files
authored
Merge pull request #40 from transistive/call-clause
Add support of the CALL clause to the package. Great work!
2 parents 8fffe51 + 75be26d commit 19837f8

File tree

4 files changed

+149
-0
lines changed

4 files changed

+149
-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: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
namespace WikibaseSolutions\CypherDSL;
2323

24+
use Closure;
25+
use Prophecy\Call\Call;
26+
use WikibaseSolutions\CypherDSL\Clauses\CallClause;
2427
use WikibaseSolutions\CypherDSL\Clauses\CallProcedureClause;
2528
use WikibaseSolutions\CypherDSL\Clauses\Clause;
2629
use WikibaseSolutions\CypherDSL\Clauses\CreateClause;
@@ -666,6 +669,29 @@ public function union($queryOrCallable, bool $all = false): self
666669
return $this;
667670
}
668671

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

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
@@ -1108,6 +1108,36 @@ public function testAutomaticIdentifierGeneration(): void
11081108
$this->assertInstanceOf(Variable::class, $node->getName());
11091109
}
11101110

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

0 commit comments

Comments
 (0)