Skip to content

Commit

Permalink
feat: added feature to return instance of models when fetching from a…
Browse files Browse the repository at this point in the history
… model class
  • Loading branch information
mgcostaParedes committed May 17, 2021
1 parent 2ab62c7 commit 87a48db
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 8 deletions.
48 changes: 47 additions & 1 deletion src/Spanner/Builder/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,46 @@ class Builder implements Operator, Aggregator, Fetchable
*/
public $wheres = [];

/**
* The group constraints which query is targeting
*
* @var array
*/
public $groups;

/**
* The having constraints which query is targeting
*
* @var array
*/
public $havings;

/**
* The orders constraints which query is targeting
*
* @var array
*/
public $orders;

/**
* The maximum number of results to return.
*
* @var int
*/
public $limit;

/**
* The number of results to skip.
*
* @var int
*/
public $offset;

/**
* The query union statements.
*
* @var array
*/
public $unions;

public $bindings = [
Expand Down Expand Up @@ -618,9 +648,16 @@ public function first(array $columns = ['*'])

public function get(array $columns = ['*']): Collection
{
return collect($this->onceWithColumns(Arr::wrap($columns), function () {
$result = collect($this->onceWithColumns(Arr::wrap($columns), function () {
return $this->runSelect();
}));

if ($this->model) {
$models = $this->hydrate($result->toArray())->all();
return $this->getModel()->newCollection($models);
}

return $result;
}

public function find($id, $columns = ['*']): array
Expand Down Expand Up @@ -689,6 +726,15 @@ public function toSql(): string
return $this->grammar->compile($this);
}

protected function hydrate(array $items): Collection
{
$instance = $this->model->newInstance();

return $instance->newCollection(array_map(function ($item) use ($instance) {
return $instance->newFromBuilder($item);
}, $items));
}

protected function invalidOperatorOrValue($operator, $value): bool
{
if (is_null($value) && is_null($operator)) {
Expand Down
29 changes: 28 additions & 1 deletion src/Spanner/Model/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ abstract class Model

protected $keyStrategy = 'uuid4';

public $strategies = ['uuid4', 'increment', false];
protected $strategies = ['uuid4', 'increment', false];

public function getTable(): string
{
Expand All @@ -56,6 +56,11 @@ public function getConnection(): Database
return static::$connection;
}

public function setConnection(Database $connection): void
{
static::$connection = $connection;
}

public static function setConnectionDatabase(Database $connection)
{
static::$connection = $connection;
Expand Down Expand Up @@ -118,6 +123,28 @@ public function save(): bool
return true;
}

public function newCollection(array $models = []): Collection
{
return new Collection($models);
}

public function newInstance(): self
{
$model = new static();
$model->setTable($this->getTable());

return $model;
}

public function newFromBuilder($attributes = [], $connection = null): self
{
$model = $this->newInstance();
$model->setRawAttributes((array) $attributes);
$model->setConnection($connection ?: $this->getConnection());

return $model;
}

private function isValidStrategy(): bool
{
if (!in_array($this->keyStrategy, $this->strategies)) {
Expand Down
2 changes: 1 addition & 1 deletion src/Spanner/Model/Strategies/IncrementStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public function generateKey(Model $model): int
// find latest result on database
$lastResult = $model->newQuery()->orderBy($model->getPrimaryKey(), 'desc')->first();
if (!empty($lastResult)) {
return $lastResult[$model->getPrimaryKey()] + 1;
return $lastResult->{$model->getPrimaryKey()} + 1;
}
return 1;
}
Expand Down
14 changes: 14 additions & 0 deletions src/Spanner/Traits/ModelAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,18 @@ public function getAttributes(): array

return $attributes;
}

public function setRawAttributes(array $attributes): void
{
$reflect = new ReflectionObject($this);
$props = $reflect->getProperties(ReflectionProperty::IS_PUBLIC);
foreach ($attributes as $key => $attribute) {
$exists = array_filter($props, function ($prop) use ($key) {
return $prop->name == $key && $prop->class == get_called_class();
});
if (!empty($exists)) {
$this->{$key} = $attribute;
}
}
}
}
24 changes: 24 additions & 0 deletions tests/unit/BuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Google\Cloud\Spanner\Result;
use Google\Cloud\Spanner\Timestamp;
use Google\Cloud\Spanner\Transaction;
use Illuminate\Support\Collection;
use MgCosta\Spanner\Builder\Builder;
use MgCosta\Spanner\Builder\ParamCounter;
use MgCosta\Spanner\Builder\Expression;
Expand Down Expand Up @@ -613,6 +614,8 @@ public function testShouldReturnNullWhenCallingAggregateMethodWithoutResults()
$mockResult = m::mock(Result::class);
$mockResult->shouldReceive('getIterator')->andReturn(new ArrayIterator([]));
$this->mockedModel->shouldReceive('getPrimaryKey')->andReturn('DummyID');
$this->mockedModel->shouldReceive('newInstance')->andReturnSelf();
$this->mockedModel->shouldReceive('newCollection')->andReturn(new Collection([]));
$this->database->shouldReceive('execute')->andReturn($mockResult);
$result = $this->builder->where('age', '>', 35)->count();
$this->assertEquals(null, $result);
Expand All @@ -623,6 +626,9 @@ public function testShouldReturnAnIntegerWhenCallingCountMethod()
$mockResult = m::mock(Result::class);
$mockResult->shouldReceive('getIterator')->andReturn(new ArrayIterator([['aggregate' => 39]]));
$this->mockedModel->shouldReceive('getPrimaryKey')->andReturn('DummyID');
$this->mockedModel->shouldReceive('newInstance')->andReturnSelf();
$this->mockedModel->shouldReceive('newFromBuilder')->andReturnSelf();
$this->mockedModel->shouldReceive('newCollection')->andReturn(new Collection($mockResult));
$this->database->shouldReceive('execute')->andReturn($mockResult);
$result = $this->builder->where('age', '>', 35)->count();
$this->assertEquals(39, $result);
Expand All @@ -633,6 +639,9 @@ public function testShouldReturnAnIntegerWhenCallingMaxMethod()
$mockResult = m::mock(Result::class);
$mockResult->shouldReceive('getIterator')->andReturn(new ArrayIterator([['aggregate' => 200]]));
$this->mockedModel->shouldReceive('getPrimaryKey')->andReturn('DummyID');
$this->mockedModel->shouldReceive('newInstance')->andReturnSelf();
$this->mockedModel->shouldReceive('newFromBuilder')->andReturnSelf();
$this->mockedModel->shouldReceive('newCollection')->andReturn(new Collection($mockResult));
$this->database->shouldReceive('execute')->andReturn($mockResult);
$result = $this->builder->where('age', '>', 35)->max('age');
$this->assertEquals(200, $result);
Expand All @@ -643,6 +652,9 @@ public function testShouldReturnAnIntegerWhenCallingMinMethod()
$mockResult = m::mock(Result::class);
$mockResult->shouldReceive('getIterator')->andReturn(new ArrayIterator([['aggregate' => 20]]));
$this->mockedModel->shouldReceive('getPrimaryKey')->andReturn('DummyID');
$this->mockedModel->shouldReceive('newInstance')->andReturnSelf();
$this->mockedModel->shouldReceive('newFromBuilder')->andReturnSelf();
$this->mockedModel->shouldReceive('newCollection')->andReturn(new Collection($mockResult));
$this->database->shouldReceive('execute')->andReturn($mockResult);
$result = $this->builder->where('age', '>', 35)->min('age');
$this->assertEquals(20, $result);
Expand All @@ -653,6 +665,9 @@ public function testShouldReturnAnIntegerWhenCallingSumMethod()
$mockResult = m::mock(Result::class);
$mockResult->shouldReceive('getIterator')->andReturn(new ArrayIterator([['aggregate' => 333]]));
$this->mockedModel->shouldReceive('getPrimaryKey')->andReturn('DummyID');
$this->mockedModel->shouldReceive('newInstance')->andReturnSelf();
$this->mockedModel->shouldReceive('newFromBuilder')->andReturnSelf();
$this->mockedModel->shouldReceive('newCollection')->andReturn(new Collection($mockResult));
$this->database->shouldReceive('execute')->andReturn($mockResult);
$result = $this->builder->where('age', '>', 35)->sum('age');
$this->assertEquals(333, $result);
Expand All @@ -663,6 +678,9 @@ public function testShouldReturnAFloatWhenCallingAvgMethod()
$mockResult = m::mock(Result::class);
$mockResult->shouldReceive('getIterator')->andReturn(new ArrayIterator([['aggregate' => 333.67]]));
$this->mockedModel->shouldReceive('getPrimaryKey')->andReturn('DummyID');
$this->mockedModel->shouldReceive('newInstance')->andReturnSelf();
$this->mockedModel->shouldReceive('newFromBuilder')->andReturnSelf();
$this->mockedModel->shouldReceive('newCollection')->andReturn(new Collection($mockResult));
$this->database->shouldReceive('execute')->andReturn($mockResult);
$result = $this->builder->where('age', '>', 35)->avg('age');
$this->assertEquals(333.67, $result);
Expand All @@ -673,6 +691,9 @@ public function testShouldReturnAValueWhenCallingValueMethod()
$mockResult = m::mock(Result::class);
$mockResult->shouldReceive('getIterator')->andReturn(new ArrayIterator([['columnA' => 'A', 'columnB' => 'B']]));
$this->mockedModel->shouldReceive('getPrimaryKey')->andReturn('DummyID');
$this->mockedModel->shouldReceive('newInstance')->andReturnSelf();
$this->mockedModel->shouldReceive('newFromBuilder')->andReturnSelf();
$this->mockedModel->shouldReceive('newCollection')->andReturn(new Collection($mockResult));
$this->database->shouldReceive('execute')->andReturn($mockResult);
$result = $this->builder->where('age', '>', 35)->value('columnA');
$this->assertEquals('A', $result);
Expand All @@ -683,6 +704,9 @@ public function testShouldReturnACollectionWhenCallingFindMethodProperly()
$mockResult = m::mock(Result::class);
$mockResult->shouldReceive('getIterator')->andReturn($this->getRandomData(55));
$this->mockedModel->shouldReceive('getPrimaryKey')->andReturn('DummyID');
$this->mockedModel->shouldReceive('newInstance')->andReturnSelf();
$this->mockedModel->shouldReceive('newFromBuilder')->andReturnSelf();
$this->mockedModel->shouldReceive('newCollection')->andReturn(new Collection($mockResult));
$this->database->shouldReceive('execute')->andReturn($mockResult);
$result = $this->builder->find('55');
$this->assertIsArray($result);
Expand Down
23 changes: 22 additions & 1 deletion tests/unit/FacadeSpannerDBTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,46 @@
namespace Tests\unit;

use Google\Cloud\Spanner\Database;
use Google\Cloud\Spanner\Result;
use MgCosta\Spanner\Builder\Builder;
use MgCosta\Spanner\Facade\SpannerDB;
use Codeception\Test\Unit;
use MgCosta\Spanner\Manager\Manager;
use ArrayIterator;
use Mockery as m;

class FacadeSpannerDBTest extends Unit
{
private $facade;
private $connection;

public function setUp(): void
{
parent::setUp();
new Manager(m::mock(Database::class));
$database = m::mock(Database::class);
new Manager($database);
$this->connection = $database;
$this->facade = new SpannerDB();
}

public function testShouldReturnAQueryBuilderInstanceWhenCallingWithinMethodTable()
{
$this->assertInstanceOf(Builder::class, $this->facade->table('test'));
}

public function testShouldReturnACollectionOfArrayWhenCallingMethodToFetchResultFromFacade()
{
$data = new ArrayIterator([
[
'id' => 1,
'name' => 'test'
]
]);
$mockResult = m::mock(Result::class);
$mockResult->shouldReceive('getIterator')->andReturn($data);

$this->connection->shouldReceive('execute')->andReturn($mockResult);
$result = $this->facade->table('test')->where('id', 1)->first();
$this->assertIsArray($result);
}
}
7 changes: 3 additions & 4 deletions tests/unit/IncrementStrategyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Codeception\Test\Unit;
use Mockery as m;
use ArrayIterator;
use Tests\unit\stubs\DummyModel;

class IncrementStrategyTest extends Unit
{
Expand All @@ -18,7 +19,7 @@ class IncrementStrategyTest extends Unit

public function setUp(): void
{
$this->model = $this->getMockForAbstractClass(Model::class);
$this->model = new DummyModel();
$this->connection = m::mock(Database::class);
Model::setConnectionDatabase($this->connection);
$this->strategy = new IncrementStrategy();
Expand All @@ -43,9 +44,7 @@ public function testShouldGetAnIncrementedValueWhenCallingGenerateKeyAndFetchAnV

public function testShouldGetTheStartIncrementValueAs1WhenCallingGenerateKeyAndFetchAnEmptyResultFromDatabase()
{
$data = new ArrayIterator([
[]
]);
$data = new ArrayIterator([]);
$mockResult = m::mock(Result::class);
$mockResult->shouldReceive('getIterator')->andReturn($data);

Expand Down
14 changes: 14 additions & 0 deletions tests/unit/ModelAttributesTraitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,18 @@ public function testShouldGetAllPropertiesAsAnArrayWhenCallingGetAttributesMetho
];
$this->assertEquals($expectedArray, $this->trait->getAttributes());
}

public function testShouldAssignClassPropertiesWhenCallingSetRawAttributesWithinAValidArray()
{
$this->trait->setRawAttributes([
'id' => 1,
'firstName' => 'Michael',
'lastName' => 'Brandon',
'age' => 30
]);
$this->assertEquals(1, $this->trait->id);
$this->assertEquals('Michael', $this->trait->firstName);
$this->assertEquals('Brandon', $this->trait->lastName);
$this->assertEquals(30, $this->trait->age);
}
}
2 changes: 2 additions & 0 deletions tests/unit/stubs/DummyModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@
class DummyModel extends Model
{
protected $keyStrategy = 'increment';

public $id;
}

0 comments on commit 87a48db

Please sign in to comment.