Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to check exists through tables with "through" key. #30

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 77 additions & 22 deletions src/QueryBuilderParser/JoinSupportingQueryBuilderParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,28 +95,51 @@ private function buildSubclauseQuery($query, $rule, $value, $condition)

$not = array_key_exists('not_exists', $subclause) && $subclause['not_exists'];

// Create a where exists clause to join to the other table, and find results matching the criteria
$query = $query->whereExists(
/**
* @param Builder $query
*/
function(Builder $query) use ($subclause) {

$q = $query->selectRaw(1)
->from($subclause['to_table'])
->whereRaw($subclause['to_table'].'.'.$subclause['to_col']
.' = '
.$subclause['from_table'].'.'.$subclause['from_col']);

if (array_key_exists('to_clause', $subclause)) {
$q->where($subclause['to_clause']);
}

$this->buildSubclauseInnerQuery($subclause, $q);
},
$condition,
$not
);
if ( $not ) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing that I am cautious of is that this code looks like it will change the behavior for other users of this class.

Can we separate out the code inside the if() {...} block into two separate functions to ensure that the length of this function (buildSubclauseQuery) is not too long?

// Create a where exists clause to join to the other table, and find results matching the criteria
$query = $query->whereExists(
/**
* @param Builder $query
*/
function(Builder $query) use ($subclause, $not, $condition) {
$q = $query->selectRaw(1)
->from($subclause['to_table'])
->whereRaw($subclause['to_table'].'.'.$subclause['to_col']
.' = '
.$subclause['from_table'].'.'.$subclause['from_col']);
if ( ! isset( $subclause["through"] ) ) {
if ( array_key_exists( 'to_clause', $subclause ) ) {
$q->where( $subclause['to_clause'] );
}
$this->buildSubclauseInnerQuery( $subclause, $q );
} else {
$this->buildSubclauseThroughQuery( $subclause, $not, $condition, $q );
}

},
$condition,
$not
);
} else {
// Create a join clause to join to the other table, and find results matching the criteria

$query = $query->join( $subclause["to_table"], $subclause['to_table'] . '.' . $subclause['to_col'], '=', $subclause['from_table'] . '.' . $subclause['from_col'] );
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please try to keep lines < 80 characters long. This line of code wraps in my browser.

$first_from_col = $subclause['from_col'];
$first_from = $subclause['from_table'];
//Loop through the 'through' key to access through multiple tables
while ( isset( $subclause['through'] ) ) {
$subclause["through"]["require_array"] = $subclause["require_array"];
$subclause["through"]["operator"] = $subclause["operator"];
$subclause["through"]["value"] = $subclause["value"];
$subclause = $subclause["through"];
$query = $query->join( $subclause["to_table"], $subclause['to_table'] . '.' . $subclause['to_col'], '=', $subclause['from_table'] . '.' . $subclause['from_col'] );
}
if (array_key_exists('to_clause', $subclause)) {
$query->where($subclause['to_clause']);
}
$this->buildSubclauseInnerQuery( $subclause, $query );
$query->groupBy( $first_from.".".$first_from_col );
}

return $query;
}
Expand Down Expand Up @@ -206,4 +229,36 @@ private function buildSubclauseWithNull($subclause, Builder $query, $isNotNull =
return $query->whereNull($subclause['to_value_column']);
}

private function buildSubclauseThroughQuery($subclause, $not, $condition, Builder $q) {
$subclause["through"]["require_array"] = $subclause["require_array"];
$subclause["through"]["operator"] = $subclause["operator"];
$subclause["through"]["value"] = $subclause["value"];
$subclause = $subclause["through"];

$q->whereExists(
function ( \Illuminate\Database\Query\Builder $query ) use ( $subclause, $not, $condition, $q ) {

$q = $query->selectRaw( 1 )
->from( $subclause['to_table'] )
->whereRaw( $subclause['to_table'] . '.' . $subclause['to_col']
. ' = '
. $subclause['from_table'] . '.' . $subclause['from_col'] );



if ( ! isset( $subclause["through"] ) ) {
if ( array_key_exists( 'to_clause', $subclause ) ) {
$q->where( $subclause['to_clause'] );
}
$this->buildSubclauseInnerQuery( $subclause, $q );
} else {
$this->buildSubclauseThroughQuery( $subclause, $not, $condition, $q );
}
},
$condition,
$not
);
return $q;
}

}
28 changes: 28 additions & 0 deletions tests/JoinSupportingQueryBuilderParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@ private function getJoinFields()
'to_value_column' => 's_value',
'to_clause' => array('othercol' => 'value'),
),
'join2through' => array(
'from_table' => 'master2',
'from_col' => 'm2_col',
'to_table' => 'subtable2',
'to_col' => 's2_col',
'to_value_column' => 's2_value',
'not_exists' => true,
'through' => array(
'from_table' => 'subtable2',
'from_col' => 's2_col',
'to_table' => 'subtable3',
'to_col' => 's3_col',
'to_value_column' => 's3_value',
)
),
);
}

Expand Down Expand Up @@ -328,4 +343,17 @@ public function testDateNotBetween()
$this->assertEquals(22, $bindings[0]->day);
$this->assertEquals(28, $bindings[1]->day);
}

public function testJoinNotExistsInThrough()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, with this project, we are trying to stick to PSR-2 styling (https://www.php-fig.org/psr/psr-2/). Could you please ensure that you are using spaces and not tabs to format code?

{
$json = '{"condition":"AND","rules":[{"id":"join2through","field":"join2through","type":"text","input":"select","operator":"in","value":["a","b"]}]}';

$builder = $this->createQueryBuilder();

$parser = $this->getParserUnderTest();
$parser->parse($json, $builder);

$this->assertEquals('select * where not exists (select 1 from `subtable2` where subtable2.s2_col = master2.m2_col and not exists (select 1 from `subtable3` where subtable3.s3_col = subtable2.s2_col and `s3_value` in (?, ?)))',
$builder->toSql());
}
}