Skip to content

Commit

Permalink
Merge pull request #78 from KnightAR/WhereTables
Browse files Browse the repository at this point in the history
Add support for WhereHas/WhereDoesntHave functionality
  • Loading branch information
spiritix authored Nov 22, 2019
2 parents 2320216 + 89659cd commit c851771
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 0 deletions.
31 changes: 31 additions & 0 deletions src/Spiritix/LadaCache/Reflector.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,40 @@ public function getTables()
}
}

$this->getTablesFromWhere($this->queryBuilder, $tables);

return $tables;
}

/**
* Get Table Names From Where Exists, Not Exists (whereHas/whereDoesnthave builder syntax)
*
* @param QueryBuilder $queryBuilder
*/
private function getTablesFromWhere(QueryBuilder $queryBuilder, &$tables) {
if (!isset($queryBuilder->wheres)) {
return;
}
$wheres = $queryBuilder->wheres ?: [];
foreach ($wheres as $where) {
if ($where['type'] == 'Exists' || $where['type'] == 'NotExists') {
$tables[] = $where['query']->from;

// Add possible join tables
$joins = $where['query']->joins ?: [];
foreach ($joins as $join) {

if (!in_array($join->table, $tables) && is_string($join->table)) {
$tables[] = $join->table;
}
}
}
if (isset($where['query'])) {
$this->getTablesFromWhere($where['query'], $tables);
}
}
}

/**
* Returns all affected rows as a multidimensional array, split up by table.
*
Expand Down
116 changes: 116 additions & 0 deletions tests/TaggerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
use Spiritix\LadaCache\Reflector;
use Spiritix\LadaCache\Tagger;
use Spiritix\LadaCache\Tests\Database\Models\Car;
use Spiritix\LadaCache\Tests\Database\Models\CarMaterial;
use Spiritix\LadaCache\Tests\Database\Models\Engine;
use Spiritix\LadaCache\Tests\Database\Models\Material;

class TaggerTest extends TestCase
{
Expand Down Expand Up @@ -312,6 +314,120 @@ public function testSelectWithCount()
$this->assertCountEquals($expectedTags, $generatedTags);
}

/**
* Testing that
*
* select cars.id from cars where exists (select * from engines where cars.id = engines.car_id)
* Generates tags like: table_unspecific_cars, table_unspecific_engines
*/
public function testSelectWhereHas()
{
$this->factory->times(5)->create(Car::class)
->each(function ($car) {
$engine = app(Engine::class);
$engine->name = 'XX';
$car->engine()->save($engine);
});

/** @var Car $car */
$car = app(Car::class);
/** @var Engine $engine */
$engine = app(Engine::class);

$sqlBuilder = $car->whereHas('engine')->select('cars.id');
$sqlBuilder->get();

$expectedTags = [
$this->getUnspecificTableTag($car->getTable()),
$this->getUnspecificTableTag($engine->getTable()),
];

$generatedTags = $this->redis->keys($this->redis->prefix('') . 'tags:*');

$this->assertCacheHasTags($expectedTags);
$this->assertCountEquals($expectedTags, $generatedTags);
}

/**
* Testing that
*
* select cars.id from cars where not exists (select * from engines where cars.id = engines.car_id)
* Generates tags like: table_unspecific_cars, table_unspecific_engines
*/
public function testSelectWhereDoesntHave()
{
$this->factory->times(5)->create(Car::class)
->each(function ($car) {
$engine = app(Engine::class);
$engine->name = 'XX';
$car->engine()->save($engine);
});

/** @var Car $car */
$car = app(Car::class);
/** @var Engine $engine */
$engine = app(Engine::class);

$sqlBuilder = $car->whereDoesntHave('engine')->select('cars.id');
$sqlBuilder->get();

$expectedTags = [
$this->getUnspecificTableTag($car->getTable()),
$this->getUnspecificTableTag($engine->getTable()),
];

$generatedTags = $this->redis->keys($this->redis->prefix('') . 'tags:*');

$this->assertCacheHasTags($expectedTags);
$this->assertCountEquals($expectedTags, $generatedTags);
}

/**
* Testing that
*
* select materials.id from materials where (exists (select * from cars inner join car_material on cars.id = car_material.car_id where materials.id = car_material.material_id and not exists (select * from engines where cars.id = engines.car_id)))
* Generates tags like: table_unspecific_materials, table_unspecific_car_material, table_unspecific_cars, table_unspecific_engines
*
* This will test nested joins to verify that nesting works properly
*/
public function testSelectWhereNested()
{
$this->factory->times(5)->create(Car::class)
->each(function ($car) {
$engine = app(Engine::class);
$engine->name = 'XX';
$car->engine()->save($engine);
});

/** @var Material $material */
$material = app(Material::class);
/** @var CarMaterial $car_material */
$car_material = app(CarMaterial::class);
/** @var Car $car */
$car = app(Car::class);
/** @var Engine $engine */
$engine = app(Engine::class);

$sqlBuilder = $material->where(function($query) {
$query->whereHas('cars', function ($query) {
$query->whereDoesntHave('engine');
});
})->select('materials.id');
$sqlBuilder->get();

$expectedTags = [
$this->getUnspecificTableTag($material->getTable()),
$this->getUnspecificTableTag($car_material->getTable()),
$this->getUnspecificTableTag($car->getTable()),
$this->getUnspecificTableTag($engine->getTable()),
];

$generatedTags = $this->redis->keys($this->redis->prefix('') . 'tags:*');

$this->assertCacheHasTags($expectedTags);
$this->assertCountEquals($expectedTags, $generatedTags);
}

/**
* Testing that
*
Expand Down

0 comments on commit c851771

Please sign in to comment.