-
-
Notifications
You must be signed in to change notification settings - Fork 142
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e07bf8a
commit 1092efd
Showing
6 changed files
with
251 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
<?php namespace Sofa\Eloquence; | ||
|
||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder; | ||
use Sofa\Eloquence\Contracts\Mappable as MappableContract; | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong. |
||
|
||
class Builder extends EloquentBuilder | ||
{ | ||
|
||
/** | ||
* Add where constraint to the query. | ||
* | ||
* @param string $column | ||
* @param string $operator | ||
* @param mixed $value | ||
* @param string $boolean | ||
* @return $this | ||
*/ | ||
public function where($column, $operator = null, $value = null, $boolean = 'and') | ||
{ | ||
// If developer provided column prefixed with table name we will | ||
// not even try to map the column, since obviously the value | ||
// refers to the actual column name on the queried table | ||
if ($this->notPrefixed($column)) { | ||
$column = $this->getColumnMapping($column); | ||
|
||
if ($this->nestedMapping($column)) { | ||
return $this->mappedWhere($column, $operator, $value, $boolean); | ||
} | ||
} | ||
|
||
return parent::where($column, $operator, $value, $boolean); | ||
} | ||
|
||
/** | ||
* Determine whether the column was not passed with table prefix. | ||
* | ||
* @param string $column | ||
* @return boolean | ||
*/ | ||
protected function notPrefixed($column) | ||
{ | ||
return strpos($column, '.') === false; | ||
} | ||
|
||
/** | ||
* Get the mapping for a column if exists or simply return the column. | ||
* | ||
* @param string $column | ||
* @return string | ||
*/ | ||
protected function getColumnMapping($column) | ||
{ | ||
$model = $this->getModel(); | ||
|
||
if (is_string($column) && $model instanceof MappableContract && $model->hasMapping($column)) { | ||
$column = $model->getMappingForAttribute($column); | ||
} | ||
|
||
return $column; | ||
} | ||
|
||
/** | ||
* Determine whether the mapping points to relation. | ||
* | ||
* @param string $mapping | ||
* @return boolean | ||
*/ | ||
protected function nestedMapping($mapping) | ||
{ | ||
return strpos($mapping, '.') !== false; | ||
} | ||
|
||
/** | ||
* Add a relationship count condition to the query with where clauses. | ||
* | ||
* @param string $mapping | ||
* @param string $operator | ||
* @param mixed $value | ||
* @param string $boolean | ||
* @return $this | ||
*/ | ||
protected function mappedWhere($mapping, $operator, $value, $boolean) | ||
{ | ||
list($target, $column) = $this->parseMapping($mapping); | ||
|
||
return $this->has($target, '>=', 1, $boolean, function ($q) use ($column, $operator, $value) { | ||
$q->where($column, $operator, $value); | ||
}); | ||
} | ||
|
||
/** | ||
* Get the target relation and column from the mapping. | ||
* | ||
* @param string $mapping | ||
* @return array | ||
*/ | ||
protected function parseMapping($mapping) | ||
{ | ||
$segments = explode('.', $mapping); | ||
|
||
$column = array_pop($segments); | ||
|
||
$target = implode('.', $segments); | ||
|
||
return [$target, $column]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?php namespace Sofa\Eloquence\Contracts; | ||
|
||
interface Mappable | ||
{ | ||
public function hasMapping($key); | ||
public function mapAttribute($key); | ||
public function getMappingForAttribute($key); | ||
public function getMaps(); | ||
public function setMaps(array $mappings); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
<?php namespace Sofa\Eloquence\Tests; | ||
|
||
use Sofa\Eloquence\Builder; | ||
use Sofa\Eloquence\Mappable; | ||
use Sofa\Eloquence\Contracts\Mappable as MappableContract; | ||
|
||
use Illuminate\Database\Query\Builder as Query; | ||
use Illuminate\Database\Eloquent\Model; | ||
|
||
use Mockery as m; | ||
|
||
class BuilderTest extends \PHPUnit_Framework_TestCase { | ||
|
||
public function setUp() | ||
{ | ||
$this->model = new ModelStub; | ||
} | ||
|
||
/** | ||
* @test | ||
* @covers \Sofa\Eloquence\Builder::where | ||
* @covers \Sofa\Eloquence\Builder::notPrefixed | ||
* @covers \Sofa\Eloquence\Builder::getColumnMapping | ||
* @covers \Sofa\Eloquence\Builder::nestedMapping | ||
*/ | ||
public function it_adds_where_constraint_for_alias_mapping() | ||
{ | ||
$builder = $this->getBuilder(); | ||
|
||
$sql = $builder->where('foo', 'value')->toSql(); | ||
|
||
$this->assertEquals('select * from "table" where "bar" = ?', $sql); | ||
} | ||
|
||
/** | ||
* @test | ||
* @covers \Sofa\Eloquence\Builder::where | ||
* @covers \Sofa\Eloquence\Builder::mappedWhere | ||
* @covers \Sofa\Eloquence\Builder::notPrefixed | ||
* @covers \Sofa\Eloquence\Builder::getColumnMapping | ||
* @covers \Sofa\Eloquence\Builder::nestedMapping | ||
* @covers \Sofa\Eloquence\Builder::parseMapping | ||
*/ | ||
public function it_adds_where_constraint_for_nested_mappings() | ||
{ | ||
$alias = 'aliased_column'; | ||
$target = 'deeply.nested.relation'; | ||
$mapping = $target . '.column'; | ||
|
||
$connection = m::mock('\Illuminate\Database\ConnectionInterface'); | ||
$processor = m::mock('\Illuminate\Database\Query\Processors\Processor'); | ||
$grammar = m::mock('\Illuminate\Database\Query\Grammars\Grammar'); | ||
$query = new Query($connection, $grammar, $processor); | ||
|
||
$model = m::mock('\Sofa\Eloquence\Contracts\Mappable'); | ||
$model->shouldReceive('hasMapping')->with($alias)->andReturn(true); | ||
$model->shouldReceive('getMappingForAttribute')->with($alias)->andReturn($mapping); | ||
|
||
$builder = m::mock('\Sofa\Eloquence\Builder[has,getModel]', [$query]); | ||
$builder->shouldReceive('getModel')->andReturn($model); | ||
$builder->shouldReceive('has')->with($target, '>=', 1, 'and', m::type('callable'))->andReturn($builder); | ||
|
||
$builder->where('aliased_column', 'value'); | ||
} | ||
|
||
protected function getBuilder() | ||
{ | ||
$grammar = new \Illuminate\Database\Query\Grammars\Grammar; | ||
$connection = m::mock('\Illuminate\Database\ConnectionInterface'); | ||
$processor = m::mock('\Illuminate\Database\Query\Processors\Processor'); | ||
$query = new Query($connection, $grammar, $processor); | ||
$builder = new Builder($query); | ||
|
||
$model = new MappableStub; | ||
$builder->setModel($model); | ||
|
||
return $builder; | ||
} | ||
} | ||
|
||
class MappableStub extends Model implements MappableContract { | ||
use Mappable; | ||
|
||
protected $table = 'table'; | ||
protected $maps = [ | ||
'foo' => 'bar', | ||
]; | ||
} |
You didn't implement it.