Skip to content

Commit 3f7a278

Browse files
committed
Completing test coverage for schemamodel with MANY to MANY rel
1 parent e8427d2 commit 3f7a278

File tree

9 files changed

+395
-65
lines changed

9 files changed

+395
-65
lines changed

composer.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Models/SchemaModel.php

Lines changed: 69 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ abstract class SchemaModel extends Model implements SchemaModelInterface {
3333
*
3434
* @var array
3535
*/
36-
private array $relationship_data = [];
36+
private array $relationshipData = [];
3737

3838
/**
3939
* Constructor.
@@ -43,12 +43,6 @@ abstract class SchemaModel extends Model implements SchemaModelInterface {
4343
* @param array<string,mixed> $attributes Attributes.
4444
*/
4545
public function __construct( array $attributes = [] ) {
46-
if ( ! empty( static::getPropertyDefinitions() ) ) {
47-
throw new InvalidArgumentException( 'Schema models do not accept property definitions. Define a schema interface to link with instead.' );
48-
}
49-
50-
unset( static::$cached_definitions[ static::class ] );
51-
5246
$this->propertyCollection = ModelPropertyCollection::fromPropertyDefinitions( $this->getPropertyDefinitionsFromSchema(), $attributes );
5347
}
5448

@@ -167,19 +161,21 @@ public function addToRelationship( string $key, int $id ): void {
167161
throw new InvalidArgumentException( "Relationship {$key} does not exist." );
168162
}
169163

170-
if ( ! isset( $this->relationship_data[ $key ] ) ) {
171-
$this->relationship_data[ $key ] = [];
164+
if ( ! isset( $this->relationshipData[ $key ] ) ) {
165+
$this->relationshipData[ $key ] = [];
172166
}
173167

174-
if ( ! isset( $this->relationship_data[ $key ]['insert'] ) ) {
175-
$this->relationship_data[ $key ]['insert'] = [];
168+
if ( ! isset( $this->relationshipData[ $key ]['insert'] ) ) {
169+
$this->relationshipData[ $key ]['insert'] = [];
176170
}
177171

178-
$this->relationship_data[ $key ]['insert'][] = $id;
172+
$this->relationshipData[ $key ]['insert'][] = $id;
179173

180-
if ( ! empty( $this->relationship_data[ $key ]['delete'] ) ) {
181-
$this->relationship_data[ $key ]['delete'] = array_diff( $this->relationship_data[ $key ]['delete'], [ $id ] );
174+
if ( ! empty( $this->relationshipData[ $key ]['delete'] ) ) {
175+
$this->relationshipData[ $key ]['delete'] = array_diff( $this->relationshipData[ $key ]['delete'], [ $id ] );
182176
}
177+
178+
$this->relationshipData[ $key ]['current'] = array_unique( array_merge( $this->relationshipData[ $key ]['current'] ?? [], [ $id ] ) );
183179
}
184180

185181
/**
@@ -197,19 +193,21 @@ public function removeFromRelationship( string $key, int $id ): void {
197193
throw new InvalidArgumentException( "Relationship {$key} does not exist." );
198194
}
199195

200-
if ( ! isset( $this->relationship_data[ $key ] ) ) {
201-
$this->relationship_data[ $key ] = [];
196+
if ( ! isset( $this->relationshipData[ $key ] ) ) {
197+
$this->relationshipData[ $key ] = [];
202198
}
203199

204-
if ( ! isset( $this->relationship_data[ $key ]['delete'] ) ) {
205-
$this->relationship_data[ $key ]['delete'] = [];
200+
if ( ! isset( $this->relationshipData[ $key ]['delete'] ) ) {
201+
$this->relationshipData[ $key ]['delete'] = [];
206202
}
207203

208-
$this->relationship_data[ $key ]['delete'][] = $id;
204+
$this->relationshipData[ $key ]['delete'][] = $id;
209205

210-
if ( ! empty( $this->relationship_data[ $key ]['insert'] ) ) {
211-
$this->relationship_data[ $key ]['insert'] = array_diff( $this->relationship_data[ $key ]['insert'], [ $id ] );
206+
if ( ! empty( $this->relationshipData[ $key ]['insert'] ) ) {
207+
$this->relationshipData[ $key ]['insert'] = array_diff( $this->relationshipData[ $key ]['insert'], [ $id ] );
212208
}
209+
210+
$this->relationshipData[ $key ]['current'] = array_diff( $this->relationshipData[ $key ]['current'] ?? [], [ $id ] );
213211
}
214212

215213
/**
@@ -257,7 +255,34 @@ private function getPropertyDefinitionsFromSchema(): array {
257255
* @param mixed $value Relationship value.
258256
*/
259257
protected function setRelationship( string $key, $value ): void {
260-
$this->cachedRelations[ $key ] = $value;
258+
$old_value = $this->relationshipData[ $key ]['current'] ?? null;
259+
$this->relationshipData[ $key ]['current'] = $value;
260+
261+
if ( $old_value ) {
262+
if ( is_array( $old_value ) ) {
263+
foreach ( $old_value as $i ) {
264+
$this->removeFromRelationship( $key, $i );
265+
}
266+
} else {
267+
$this->removeFromRelationship( $key, $old_value );
268+
}
269+
}
270+
271+
if ( is_array( $value ) ) {
272+
foreach ( $value as $i ) {
273+
if ( ! is_int( $i ) ) {
274+
throw new InvalidArgumentException( "Relationship {$key} must be an integer." );
275+
}
276+
277+
$this->addToRelationship( $key, $i );
278+
}
279+
} else {
280+
if ( ! is_int( $value ) ) {
281+
throw new InvalidArgumentException( "Relationship {$key} must be an integer." );
282+
}
283+
284+
$this->addToRelationship( $key, $value );
285+
}
261286
}
262287

263288
/**
@@ -275,8 +300,8 @@ protected function getRelationship( string $key ) {
275300
throw new InvalidArgumentException( "Relationship {$key} does not exist." );
276301
}
277302

278-
if ( $this->hasCachedRelationship( $key ) ) {
279-
return $this->cachedRelations[ $key ];
303+
if ( isset( $this->relationshipData[ $key ]['current'] ) ) {
304+
return $this->relationshipData[ $key ]['current'];
280305
}
281306

282307
$relationship = $relationships[ $key ];
@@ -287,24 +312,19 @@ protected function getRelationship( string $key ) {
287312
case Relationship::BELONGS_TO:
288313
case Relationship::HAS_ONE:
289314
if ( 'post' === $relationship['entity'] ) {
290-
return $this->cachedRelations[ $key ] = get_post( $this->getAttribute( $key ) );
315+
return $this->relationshipData[ $key ]['current'] = get_post( $this->getAttribute( $key ) );
291316
}
292317

293318
throw new InvalidArgumentException( "Relationship {$key} is not a post relationship." );
294319
case Relationship::HAS_MANY:
295320
case Relationship::BELONGS_TO_MANY:
296321
case Relationship::MANY_TO_MANY:
297-
return $this->cachedRelations[ $key ] = iterator_to_array(
298-
$relationship['through']::fetch_all_where(
299-
DB::prepare(
300-
'WHERE %i = %d',
301-
$relationship['columns']['this'],
302-
$this->getPrimaryValue()
303-
),
304-
100,
305-
ARRAY_A,
306-
$relationship['columns']['other'] . ' ASC'
307-
)
322+
return $this->relationshipData[ $key ]['current'] = wp_list_pluck(
323+
$relationship['through']::get_all_by(
324+
$relationship['columns']['this'],
325+
$this->getPrimaryValue()
326+
),
327+
$relationship['columns']['other']
308328
);
309329
}
310330

@@ -324,9 +344,9 @@ private function saveRelationshipData(): void {
324344
continue;
325345
}
326346

327-
if ( ! empty( $this->relationship_data[ $key ]['insert'] ) ) {
347+
if ( ! empty( $this->relationshipData[ $key ]['insert'] ) ) {
328348
$insert_data = [];
329-
foreach ( $this->relationship_data[ $key ]['insert'] as $insert_id ) {
349+
foreach ( $this->relationshipData[ $key ]['insert'] as $insert_id ) {
330350
$insert_data[] = [
331351
$this->getRelationships()[ $key ]['columns']['this'] => $this->getPrimaryValue(),
332352
$this->getRelationships()[ $key ]['columns']['other'] => $insert_id,
@@ -335,17 +355,17 @@ private function saveRelationshipData(): void {
335355

336356
// First delete them to avoid duplicates.
337357
$relationship['through']::delete_many(
338-
$this->relationship_data[ $key ]['insert'],
358+
$this->relationshipData[ $key ]['insert'],
339359
$this->getRelationships()[ $key ]['columns']['other'],
340360
DB::prepare( ' AND %i = %d', $this->getRelationships()[ $key ]['columns']['this'], $this->getPrimaryValue() )
341361
);
342362

343363
$relationship['through']::insert_many( $insert_data );
344364
}
345365

346-
if ( ! empty( $this->relationship_data[ $key ]['delete'] ) ) {
366+
if ( ! empty( $this->relationshipData[ $key ]['delete'] ) ) {
347367
$relationship['through']::delete_many(
348-
$this->relationship_data[ $key ]['delete'],
368+
$this->relationshipData[ $key ]['delete'],
349369
$this->get_relationships()[ $key ]['columns']['other'],
350370
DB::prepare( ' AND %i = %d', $this->get_relationships()[ $key ]['columns']['this'], $this->getPrimaryValue() )
351371
);
@@ -483,21 +503,6 @@ public static function fromData($data, $mode = self::BUILD_MODE_IGNORE_EXTRA) {
483503

484504
$model = new static();
485505

486-
// If we're not ignoring extra keys, check for them and throw an exception if any are found.
487-
if ( ! ($mode & self::BUILD_MODE_IGNORE_EXTRA) ) {
488-
$extraKeys = array_diff_key( (array) $data, static::$properties );
489-
if ( ! empty( $extraKeys ) ) {
490-
Config::throwInvalidArgumentException( 'Query data contains extra keys: ' . implode( ', ', array_keys( $extraKeys ) ) );
491-
}
492-
}
493-
494-
if ( ! ($mode & self::BUILD_MODE_IGNORE_MISSING) ) {
495-
$missingKeys = array_diff_key( static::$properties, (array) $data );
496-
if ( ! empty( $missingKeys ) ) {
497-
Config::throwInvalidArgumentException( 'Query data is missing keys: ' . implode( ', ', array_keys( $missingKeys ) ) );
498-
}
499-
}
500-
501506
foreach (static::propertyKeys() as $key) {
502507
$property_definition = static::getPropertyDefinition( $key );
503508
if ( $key !== $model->getPrimaryColumn() && ! array_key_exists( $key, $data ) && ! $property_definition->hasDefault() ) {
@@ -512,6 +517,14 @@ public static function fromData($data, $mode = self::BUILD_MODE_IGNORE_EXTRA) {
512517
$model->setAttribute( $key, static::castValueForProperty( static::getPropertyDefinition( $key ), $data[ $key ], $key ) );
513518
}
514519

520+
foreach ( $model->getRelationships() as $key => $relationship ) {
521+
if ( ! isset( $data[ $key ] ) ) {
522+
continue;
523+
}
524+
525+
$model->setRelationship( $key, $data[ $key ] );
526+
}
527+
515528
if ( $model->getPrimaryValue() ) {
516529
$model->commitChanges();
517530
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace StellarWP\Models\Tests\Schema;
4+
5+
use StellarWP\Models\SchemaModel;
6+
use StellarWP\Schema\Tables\Contracts\Table_Interface;
7+
use StellarWP\Models\ValueObjects\Relationship;
8+
9+
class MockModelSchemaWithRelationship extends SchemaModel {
10+
public function __construct( array $attributes = [] ) {
11+
parent::__construct( $attributes );
12+
13+
$this->defineRelationship( 'posts', Relationship::MANY_TO_MANY, MockRelationshipTable::class );
14+
$this->defineRelationshipColumns( 'posts', 'mock_model_id', 'post_id' );
15+
}
16+
public function getTableInterface(): Table_Interface {
17+
return new MockRelationshipModelTable();
18+
}
19+
}

tests/_support/Helper/Schema/MockModelTable.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use StellarWP\Schema\Columns\Float_Column;
1212
use StellarWP\Schema\Columns\Integer_Column;
1313
use StellarWP\Schema\Columns\DateTime_Column;
14+
use StellarWP\Schema\Columns\PHP_Types;
1415

1516
class MockModelTable extends Table {
1617
const SCHEMA_VERSION = '0.0.1-test';
@@ -21,20 +22,21 @@ class MockModelTable extends Table {
2122

2223
protected static $schema_slug = 'test-repository';
2324

24-
2525
public static function get_schema_history(): array {
2626
$columns = new Column_Collection();
2727

2828
$columns[] = new ID( 'id' );
2929
$columns[] = ( new String_Column( 'firstName' ) )->set_default( 'Michael' );
3030
$columns[] = ( new String_Column( 'lastName' ) );
31-
$columns[] = ( new Text_Column( 'emails' ) )->set_php_type( Text_Column::PHP_TYPE_JSON );
32-
$columns[] = ( new Float_Column( 'microseconds' ) )->set_length( 15 )->set_precision( 4 );
31+
$columns[] = ( new Text_Column( 'emails' ) )->set_php_type( PHP_Types::JSON );
32+
$columns[] = ( new Float_Column( 'microseconds' ) )->set_length( 17 )->set_precision( 6 );
3333
$columns[] = ( new Integer_Column( 'int' ) );
3434
$columns[] = ( new DateTime_Column( 'date' ) );
3535

36+
$table_name = static::table_name( true );
37+
3638
return [
37-
static::SCHEMA_VERSION => new Table_Schema( static::table_name( true ), $columns ),
39+
static::SCHEMA_VERSION => fn() => new Table_Schema( $table_name, $columns ),
3840
];
3941
}
4042

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace StellarWP\Models\Tests\Schema;
4+
5+
use StellarWP\Schema\Tables\Contracts\Table;
6+
use StellarWP\Schema\Tables\Table_Schema;
7+
use StellarWP\Schema\Collections\Column_Collection;
8+
use StellarWP\Schema\Columns\ID;
9+
use StellarWP\Schema\Columns\String_Column;
10+
use StellarWP\Schema\Columns\Text_Column;
11+
use StellarWP\Schema\Columns\Float_Column;
12+
use StellarWP\Schema\Columns\Integer_Column;
13+
use StellarWP\Schema\Columns\DateTime_Column;
14+
use StellarWP\Schema\Columns\PHP_Types;
15+
16+
class MockRelationshipModelTable extends Table {
17+
const SCHEMA_VERSION = '0.0.1-test';
18+
19+
protected static $base_table_name = 'test_repository_table';
20+
21+
protected static $group = 'test_group';
22+
23+
protected static $schema_slug = 'test-repository';
24+
25+
public static function get_schema_history(): array {
26+
$columns = new Column_Collection();
27+
28+
$columns[] = new ID( 'id' );
29+
$columns[] = ( new String_Column( 'firstName' ) )->set_default( 'Michael' );
30+
$columns[] = ( new String_Column( 'lastName' ) );
31+
$columns[] = ( new Text_Column( 'emails' ) )->set_php_type( PHP_Types::JSON );
32+
$columns[] = ( new Float_Column( 'microseconds' ) )->set_length( 17 )->set_precision( 6 );
33+
$columns[] = ( new Integer_Column( 'int' ) );
34+
$columns[] = ( new DateTime_Column( 'date' ) );
35+
36+
$table_name = static::table_name( true );
37+
38+
return [
39+
static::SCHEMA_VERSION => fn() => new Table_Schema( $table_name, $columns ),
40+
];
41+
}
42+
43+
public static function transform_from_array( array $data ): MockModelSchemaWithRelationship {
44+
return MockModelSchemaWithRelationship::fromData( $data );
45+
}
46+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace StellarWP\Models\Tests\Schema;
4+
5+
use StellarWP\Schema\Tables\Contracts\Table;
6+
use StellarWP\Schema\Tables\Table_Schema;
7+
use StellarWP\Schema\Collections\Column_Collection;
8+
use StellarWP\Schema\Collections\Index_Collection;
9+
use StellarWP\Schema\Columns\ID;
10+
use StellarWP\Schema\Columns\Referenced_ID;
11+
use StellarWP\Schema\Indexes\Classic_Index;
12+
use Exception;
13+
14+
class MockRelationshipTable extends Table {
15+
const SCHEMA_VERSION = '0.0.1-test';
16+
17+
protected static $base_table_name = 'test_relationship_table';
18+
19+
protected static $group = 'test_group';
20+
21+
protected static $schema_slug = 'test-relationship';
22+
23+
public static function get_schema_history(): array {
24+
$columns = new Column_Collection();
25+
26+
$columns[] = new ID( 'id' );
27+
$columns[] = new Referenced_ID( 'post_id' );
28+
$columns[] = new Referenced_ID( 'mock_model_id' );
29+
30+
$indexes = new Index_Collection();
31+
$indexes[] = ( new Classic_Index( 'idx_post_id_mock_model_id' ) )->set_columns( 'post_id', 'mock_model_id' );
32+
33+
$table_name = static::table_name( true );
34+
35+
return [
36+
static::SCHEMA_VERSION => fn() => new Table_Schema( $table_name, $columns, $indexes ),
37+
];
38+
}
39+
40+
public static function transform_from_array( array $data ) {
41+
return $data;
42+
}
43+
}

0 commit comments

Comments
 (0)