Skip to content

Commit

Permalink
[ENHANCEMENT] - Allow additional conditions on joins (#28)
Browse files Browse the repository at this point in the history
* Implemented NOT and XOR operations

* Implemented tests for supported operators

* Fixed NOT operator

* Adjusted tests

* Finalized tests

* Fixed table name escaping

* Fixed further tests

* Implemented join conditionals

* Added support for IN conditions

* Fixed bound params

* Updated README.md
  • Loading branch information
joshmcrae authored Dec 2, 2024
1 parent 854bf43 commit 0ca9b95
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 9 deletions.
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ Returns true if a record exists otherwise false.
$db->table('mytable')->eq('column1', 12)->exists();
```

### Left joins
### Joins

```php
// SELECT * FROM mytable LEFT JOIN my_other_table AS t1 ON t1.id=mytable.foreign_key
Expand All @@ -346,6 +346,28 @@ or
$db->table('mytable')->join('my_other_table', 'id', 'foreign_key')->findAll();
```

or

```php
// SELECT * FROM mytable LEFT JOIN my_other_table AS t1 ON t1.id=mytable.foreign_key
$db->table('mytable')->inner('my_other_table', 't1', 'id', 'mytable', 'foreign_key')->findAll();
```

Additional equality conditions can be added to a left or inner join:

```php
// SELECT * FROM mytable LEFT JOIN my_other_table AS t1 ON t1.id=mytable.foreign_key and t1.status="active"
$db->table('mytable')->left('my_other_table', 't1', 'id', 'mytable', 'foreign_key', ['status' => 'active'])->findAll();
```

or

```php
// SELECT * FROM mytable LEFT JOIN my_other_table AS t1 ON t1.id=mytable.foreign_key and t1.status IN ("archived", "disabled")
$db->table('mytable')->left('my_other_table', 't1', 'id', 'mytable', 'foreign_key', ['status' => ['archived', 'disabled']])->findAll();
```


### Equals condition

```php
Expand Down
42 changes: 34 additions & 8 deletions lib/PicoDb/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -559,16 +559,29 @@ public function join($table, $foreign_column, $local_column, $local_table = '',
* @param string $column1
* @param string $table2
* @param string $column2
* @param array $conditions
* @return $this
*/
public function left($table1, $alias1, $column1, $table2, $column2)
{
public function left($table1, $alias1, $column1, $table2, $column2, $conditions = [])
{
$where = '';
foreach ($conditions as $column => $value) {
if (is_array($value)) {
$where .= ' AND ' . $this->db->escapeIdentifier($alias1) . '.' . $this->db->escapeIdentifier($column) . ' IN (' . implode(',', array_fill(0, count($value), '?')) . ')';
$this->joinValues = array_merge($this->joinValues, $value);
} else {
$where .= ' AND ' . $this->db->escapeIdentifier($alias1) . '.' . $this->db->escapeIdentifier($column) . ' = ?';
$this->joinValues[] = $value;
}
}

$this->joins[] = sprintf(
'LEFT JOIN %s AS %s ON %s=%s',
'LEFT JOIN %s AS %s ON %s=%s%s',
$this->db->escapeIdentifier($table1),
$this->db->escapeIdentifier($alias1),
$this->db->escapeIdentifier($alias1).'.'.$this->db->escapeIdentifier($column1),
$this->db->escapeIdentifier($table2).'.'.$this->db->escapeIdentifier($column2)
$this->db->escapeIdentifier($table2).'.'.$this->db->escapeIdentifier($column2),
$where
);

return $this;
Expand All @@ -583,16 +596,29 @@ public function left($table1, $alias1, $column1, $table2, $column2)
* @param string $column1
* @param string $table2
* @param string $column2
* @param array $conditions
* @return $this
*/
public function inner($table1, $alias1, $column1, $table2, $column2)
{
public function inner($table1, $alias1, $column1, $table2, $column2, array $conditions = [])
{
$where = '';
foreach ($conditions as $column => $value) {
if (is_array($value)) {
$where .= ' AND ' . $this->db->escapeIdentifier($alias1) . '.' . $this->db->escapeIdentifier($column) . ' IN (' . implode(',', array_fill(0, count($value), '?')) . ')';
$this->joinValues = array_merge($this->joinValues, $value);
} else {
$where .= ' AND ' . $this->db->escapeIdentifier($alias1) . '.' . $this->db->escapeIdentifier($column) . ' = ?';
$this->joinValues[] = $value;
}
}

$this->joins[] = sprintf(
'JOIN %s AS %s ON %s=%s',
'JOIN %s AS %s ON %s=%s%s',
$this->db->escapeIdentifier($table1),
$this->db->escapeIdentifier($alias1),
$this->db->escapeIdentifier($alias1).'.'.$this->db->escapeIdentifier($column1),
$this->db->escapeIdentifier($table2).'.'.$this->db->escapeIdentifier($column2)
$this->db->escapeIdentifier($table2).'.'.$this->db->escapeIdentifier($column2),
$where
);

return $this;
Expand Down
24 changes: 24 additions & 0 deletions tests/MysqlTableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,30 @@ public function testLeftJoin()
);
}

public function testLeftJoinConditions()
{
$this->assertNotFalse($this->db->execute('CREATE TABLE test1 (a INTEGER NOT NULL, foreign_key INTEGER NOT NULL)'));
$this->assertNotFalse($this->db->execute('CREATE TABLE test2 (id INTEGER NOT NULL, b INTEGER NOT NULL)'));

$this->assertTrue($this->db->table('test2')->insert(array('id' => 42, 'b' => 2)));
$this->assertTrue($this->db->table('test1')->insert(array('a' => 18, 'foreign_key' => 42)));

$this->assertEquals(
array('a' => 18, 'b' => 2),
$this->db->table('test2')->columns('a', 'b')->eq('a', 18)->left('test1', 't1', 'foreign_key', 'test2', 'id', ['a' => 18])->findOne()
);

$this->assertEquals(
array('a' => 18, 'b' => 2),
$this->db->table('test2')->columns('a', 'b')->eq('a', 18)->left('test1', 't1', 'foreign_key', 'test2', 'id', ['a' => [18, 19]])->findOne()
);


$this->assertNull(
$this->db->table('test2')->columns('a', 'b')->eq('a', 18)->left('test1', 't1', 'foreign_key', 'test2', 'id', ['a' => 19])->findOne()
);
}

public function testJoinSubquery()
{
$this->assertNotFalse($this->db->execute('CREATE TABLE test1 (id INTEGER NOT NULL, a INTEGER NOT NULL)'));
Expand Down
23 changes: 23 additions & 0 deletions tests/PostgresTableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,29 @@ public function testLeftJoin()
);
}

public function testLeftJoinConditions()
{
$this->assertNotFalse($this->db->execute('CREATE TABLE test1 (a INTEGER NOT NULL, foreign_key INTEGER NOT NULL)'));
$this->assertNotFalse($this->db->execute('CREATE TABLE test2 (id INTEGER NOT NULL, b INTEGER NOT NULL)'));

$this->assertTrue($this->db->table('test2')->insert(array('id' => 42, 'b' => 2)));
$this->assertTrue($this->db->table('test1')->insert(array('a' => 18, 'foreign_key' => 42)));

$this->assertEquals(
array('a' => 18, 'b' => 2),
$this->db->table('test2')->columns('a', 'b')->eq('a', 18)->left('test1', 't1', 'foreign_key', 'test2', 'id', ['a' => 18])->findOne()
);

$this->assertEquals(
array('a' => 18, 'b' => 2),
$this->db->table('test2')->columns('a', 'b')->eq('a', 18)->left('test1', 't1', 'foreign_key', 'test2', 'id', ['a' => [18, 19]])->findOne()
);

$this->assertNull(
$this->db->table('test2')->columns('a', 'b')->eq('a', 18)->left('test1', 't1', 'foreign_key', 'test2', 'id', ['a' => 19])->findOne()
);
}

public function testJoinSubquery()
{
$this->assertNotFalse($this->db->execute('CREATE TABLE test1 (id INTEGER NOT NULL, a INTEGER NOT NULL)'));
Expand Down
23 changes: 23 additions & 0 deletions tests/SqliteTableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,29 @@ public function testLeftJoin()
);
}

public function testLeftJoinConditions()
{
$this->assertNotFalse($this->db->execute('CREATE TABLE test1 (a INTEGER NOT NULL, foreign_key INTEGER NOT NULL)'));
$this->assertNotFalse($this->db->execute('CREATE TABLE test2 (id INTEGER NOT NULL, b INTEGER NOT NULL)'));

$this->assertTrue($this->db->table('test2')->insert(array('id' => 42, 'b' => 2)));
$this->assertTrue($this->db->table('test1')->insert(array('a' => 18, 'foreign_key' => 42)));

$this->assertEquals(
array('a' => 18, 'b' => 2),
$this->db->table('test2')->columns('a', 'b')->eq('a', 18)->left('test1', 't1', 'foreign_key', 'test2', 'id', ['a' => 18])->findOne()
);

$this->assertEquals(
array('a' => 18, 'b' => 2),
$this->db->table('test2')->columns('a', 'b')->eq('a', 18)->left('test1', 't1', 'foreign_key', 'test2', 'id', ['a' => [18, 19]])->findOne()
);

$this->assertNull(
$this->db->table('test2')->columns('a', 'b')->eq('a', 18)->left('test1', 't1', 'foreign_key', 'test2', 'id', ['a' => 19])->findOne()
);
}

public function testJoinSubquery()
{
$this->assertNotFalse($this->db->execute('CREATE TABLE test1 (id INTEGER NOT NULL, a INTEGER NOT NULL)'));
Expand Down

0 comments on commit 0ca9b95

Please sign in to comment.