Skip to content

Commit

Permalink
Allow array-sourced models to provide records using generators
Browse files Browse the repository at this point in the history
This removes the hard "array" return type for getRecords, and allows people to use generators (or other iterable types) to provide records. This should still be backwards compatible with other uses of the ArraySource trait that may already have a "getRecords" method with an array return type, due to covariance rules in PHP.

It should be noted that each record must still be an array, it's only the "container" that can be traversable.
  • Loading branch information
bennothommo committed Jul 22, 2024
1 parent 9d73e77 commit a5820c3
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 13 deletions.
51 changes: 39 additions & 12 deletions src/Database/Traits/ArraySource.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<?php namespace Winter\Storm\Database\Traits;
<?php

namespace Winter\Storm\Database\Traits;

use ReflectionClass;
use Illuminate\Database\QueryException;
Expand Down Expand Up @@ -54,8 +56,10 @@ public static function bootArraySource(): void
*
* This method may be overwritten to specify a custom data provider. It should always return an array of
* associative arrays, with column names for keys and a singular value for each column.
*
* @return \Traversable|array
*/
public function getRecords(): array
public function getRecords()
{
if ($this->propertyExists('records')) {
if (!is_array($this->records)) {
Expand Down Expand Up @@ -108,12 +112,21 @@ protected function arraySourceCreateDb(): void
File::put($this->arraySourceGetDbPath(), '');
}

$records = $this->getRecords();

$this->arraySourceCreateTable();

foreach (array_chunk($records, $this->arraySourceGetChunkSize()) as $inserts) {
static::insert($inserts);
$chunk = [];

foreach ($this->getRecords() as $insert) {
$chunk[] = $insert;

if (count($chunk) >= $this->arraySourceGetChunkSize()) {
static::insert($chunk);
$chunk = [];
}
}

if (count($chunk)) {
static::insert($chunk);
}
}

Expand All @@ -130,12 +143,22 @@ protected function arraySourceCreateTable(): void
$schema = ($this->propertyExists('recordSchema'))
? $this->recordSchema
: [];
$firstRecord = $this->getRecords()[0] ?? [];

if (empty($schema) && empty($firstRecord)) {
throw new ApplicationException(
'A model using the ArraySource trait must either provide "$records" or "$recordSchema" as an array.'
);
$firstRecord = [];

// Otherwise, detect the schema from the first record
if (empty($schema)) {
foreach ($this->getRecords() as $record) {
$firstRecord = $record;
break;
}

if (empty($schema) && empty($firstRecord)) {
throw new ApplicationException(
'A model using the ArraySource trait must either provide "$records" or "$recordSchema"
as an array.'
);
}
}

// Add incrementing field based on the primary key if the key is not found in the first record or schema
Expand Down Expand Up @@ -222,6 +245,10 @@ protected function arraySourceResolveDatatype($value): string
return 'dateTime';
}

if (is_array($value) || is_object($value)) {
return 'text';
}

return 'string';
}

Expand Down Expand Up @@ -303,6 +330,6 @@ protected function arraySourceDbNeedsUpdate(): bool
*/
protected function arraySourceGetChunkSize(): int
{
return 100;
return 50;
}
}
35 changes: 34 additions & 1 deletion tests/Database/Traits/ArraySourceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,16 @@ public function testRelations(): void
$this->assertEquals(18, $records->last()->states()->get()->last()->id);
$this->assertEquals('Newfoundland and Labrador', $records->last()->states()->get()->last()->name);
}

public function testGeneratorRecords(): void
{
$records = Random::get();

$this->assertEquals(120, $records->count());

$record = Random::find(10);
$this->assertEquals('Record 10', $record->name);
}
}

class ArrayModel extends \Winter\Storm\Database\Model
Expand Down Expand Up @@ -125,7 +135,10 @@ class ArrayModel extends \Winter\Storm\Database\Model
],
];

public $arraySchema = [
public $recordSchema = [
'id' => 'integer',
'name' => 'string',
'role' => 'string',
'start_year' => 'integer',
];

Expand Down Expand Up @@ -248,3 +261,23 @@ protected function arraySourceGetDbDir(): string|false
return dirname(dirname(__DIR__)) . '/tmp';
}
}

class Random extends \Winter\Storm\Database\Model
{
use \Winter\Storm\Database\Traits\ArraySource;

public $schema = [
'name' => 'string',
'random_int' => 'integer',
];

public function getRecords()
{
for ($i = 1; $i <= 120; ++$i) {
yield [
'name' => 'Record ' . $i,
'random_int' => random_int(1, 120),
];
}
}
}

0 comments on commit a5820c3

Please sign in to comment.