@@ -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 }
0 commit comments