@@ -144,13 +144,6 @@ class Model {
144144 */
145145 public Cache |null $ cache = null ;
146146
147- /**
148- * @var array $object_cache
149- * An array used internally to cache already loaded ModelSets for any Model class. This prevents redundant loading
150- * of ModelSet objects during operations that may require multiple references to the same ModelSet.
151- */
152- public static array $ object_cache = [];
153-
154147 /**
155148 * @var ModelSet $related_objects
156149 * A ModelSet containing foreign Model objects related to this Model. These are primarily populated by
@@ -375,7 +368,7 @@ class Model {
375368 */
376369 public function __sleep () {
377370 # Variables
378- $ excluded_properties = ['initial_object ' , 'client ' , 'parent_model ' , 'related_objects ' , ' object_cache ' ];
371+ $ excluded_properties = ['initial_object ' , 'client ' , 'parent_model ' , 'related_objects ' ];
379372 $ properties = array_keys (get_object_vars ($ this ));
380373
381374 # Filter out excluded properties from the list of properties to serialize
@@ -409,7 +402,7 @@ class Model {
409402 */
410403 private function set_construct_options (array $ options ): array {
411404 # Set the async flag if given. Ensure this defaults to true.
412- $ this ->async = isset ( $ options ['async ' ]) ? $ options [ ' async ' ] : true ;
405+ $ this ->async = $ options ['async ' ] ?? true ;
413406 unset($ options ['async ' ]);
414407
415408 # Set the placement index if given and is a positive integer
@@ -529,12 +522,20 @@ class Model {
529522 return new $ class ();
530523 }
531524
525+ /**
526+ * Provides access to the ModelCache singleton instance.
527+ * @return ModelCache The ModelCache singleton instance.
528+ */
529+ public static function get_model_cache (): ModelCache {
530+ return ModelCache::get_instance ();
531+ }
532+
532533 /**
533534 * Clears the object cache for this Model class.
534535 */
535- public static function clear_object_cache (): void {
536+ public static function clear_model_cache (): void {
536537 # Clear the object cache
537- self ::$ object_cache = [] ;
538+ self ::get_model_cache ():: clear () ;
538539 }
539540
540541 /**
@@ -655,7 +656,7 @@ class Model {
655656 */
656657 public static function set_config (string $ path , mixed $ value , mixed $ default = null ): mixed {
657658 # Clear the object cache for this Model class since config is being modified
658- self ::clear_object_cache ();
659+ self ::clear_model_cache ();
659660 return config_set_path (self ::normalize_config_path ($ path ), $ value , $ default );
660661 }
661662
@@ -701,7 +702,7 @@ class Model {
701702 */
702703 public static function del_config (string $ path ): mixed {
703704 # Clear the object cache for this Model class since config is being modified
704- self ::clear_object_cache ();
705+ self ::clear_model_cache ();
705706 return config_del_path (self ::normalize_config_path ($ path ));
706707 }
707708
@@ -739,7 +740,7 @@ class Model {
739740 session_destroy ();
740741
741742 # Clear the object cache for this Model class since config has been modified
742- self ::clear_object_cache ();
743+ self ::clear_model_cache ();
743744
744745 # If a subsystem is specified for this Model, mark it as dirty
745746 if ($ this ->subsystem ) {
@@ -770,7 +771,7 @@ class Model {
770771 global $ config ;
771772
772773 # Clear the object cache for all Model classes since config is being reloaded
773- self ::clear_object_cache ();
774+ self ::clear_model_cache ();
774775
775776 $ config = parse_config (parse: $ force_parse );
776777 }
@@ -867,27 +868,34 @@ class Model {
867868 /**
868869 * Recursively obtains all internal objects for this Model from all parent objects in config. This method is only
869870 * applicable to Models with a `parent_model_class` assigned.
870- * @return array An array of all internal objects for this Model from all parent objects in
871+ * @return array An array containing the internal object and its context information needed to reconstruct the
872+ * Model object later. This array will always be structured as the following:
873+ * [
874+ * "_parent_model" => Model, // The parent Model object
875+ * "_id" => string|int, // The ID of the internal object
876+ * "_internal_object" => array, // The internal object data
877+ * ]
871878 */
872879 protected function get_internal_objects_from_all_parents (): array {
873- # Variables
874- $ internal_objects = [];
880+ /**@var Model $parent_model_class The parent Model class assigned to this Model. */
875881 $ parent_model_class = "\\RESTAPI \\Models \\$ this ->parent_model_class " ;
876- $ parent_model = new $ parent_model_class(skip_init: true );
877- $ parent_configs = $ this -> get_config ( $ parent_model -> config_path , []) ;
882+ $ parent_modelset = $ parent_model_class:: read_all ( );
883+ $ internal_objects = [] ;
878884
879885 # Check for internal objects from all parents
880- foreach ($ parent_configs as $ parent_id => $ parent_config ) {
881- # Obtain the list of child objects from this parent config
882- $ child_configs = $ this ->get_config (" $ parent_model -> config_path / $ parent_id / $ this -> config_path " , []);
883- $ parent_model = new $ parent_model_class (id: $ parent_id , skip_init: true );
884- $ parent_model -> from_internal_object ( $ parent_config );
886+ foreach ($ parent_modelset -> model_objects as $ parent_model ) {
887+ # Obtain the internal config for
888+ $ child_configs = $ this ->get_config (
889+ " $ parent_model -> config_path / $ parent_model -> id / $ this -> config_path " , []
890+ );
885891
886892 foreach ($ child_configs as $ child_id => $ child_config ) {
887- $ child_config ['parent_model ' ] = $ parent_model ;
888- $ child_config ['parent_id ' ] = $ parent_id ;
889- $ child_config ['id ' ] = $ child_id ;
890- $ internal_objects [] = $ child_config ;
893+ # Retain internal object context information needed to reconstruct the Model object later
894+ $ internal_objects [] = [
895+ "_parent_model " => $ parent_model ,
896+ "_id " => $ child_id ,
897+ "_internal_object " => $ child_config ,
898+ ];
891899 }
892900 }
893901
@@ -898,11 +906,13 @@ class Model {
898906 * Obtains all internal objects for this Model. When a `config_path` is specified, this method will obtain the
899907 * internal objects directly from config. When an `internal_callable` is assigned, this method will return
900908 * the output of the assigned callable.
909+ * @param bool $from_all_parents When set to `true`, this method will obtain internal objects from all parent
910+ * objects in config. This is only applicable to Models with a `parent_model_class` assigned.
901911 * @throws ServerError When neither a `config_path` nor a `internal_callable` are assigned to this model, OR both a
902912 * `config_path` and a `internal_callable` are assigned to this model
903913 * @return array The array of internal objects without any additional processing performed.
904914 */
905- public function get_internal_objects (): array {
915+ public function get_internal_objects (bool $ from_all_parents = false ): array {
906916 global $ mock_internal_objects ;
907917
908918 # Throw an error if both `config_path` and `internal_callable` are set.
@@ -916,8 +926,8 @@ class Model {
916926 elseif ($ mock_internal_objects ) {
917927 $ internal_objects = $ mock_internal_objects ;
918928 }
919- # Obtain all internal objects from all parents if a parent Model class is assigned
920- elseif ($ this ->parent_model_class ) {
929+ # Obtain all internal objects from all parents if a parent Model class is assigned and all parents is requested
930+ elseif ($ from_all_parents and $ this ->parent_model_class ) {
921931 $ internal_objects = $ this ->get_internal_objects_from_all_parents ();
922932 }
923933 # Obtain the internal objects from the config path if specified
@@ -977,8 +987,10 @@ class Model {
977987
978988 # When `many` is enabled, obtain the object with our ID. Otherwise, just assign the direct object
979989 $ internal_object = $ this ->many ? $ internal_objects [$ this ->id ] : $ internal_objects ;
980-
981990 $ this ->from_internal_object ($ internal_object );
991+
992+ # Re-index the ModelCache for this object since it has been refreshed from internal
993+ self ::get_model_cache ()::cache_model ($ this );
982994 }
983995
984996 /**
@@ -1932,7 +1944,7 @@ class Model {
19321944 */
19331945 public static function read_all (int $ limit = 0 , int $ offset = 0 , bool $ reverse = false ): ModelSet |Model {
19341946 # Variables
1935- $ model_name = get_called_class ();
1947+ $ model_name = self :: get_class_fqn ();
19361948 $ model = new $ model_name ();
19371949 $ model_objects = [];
19381950 $ requests_pagination = ($ limit or $ offset );
@@ -1948,12 +1960,12 @@ class Model {
19481960 }
19491961
19501962 # Load from cache if it is not exempt and cached objects exist
1951- if (!$ cache_exempt and Model:: $ object_cache [ $ model_name] ) {
1952- return Model::$ object_cache [ $ model_name] ;
1963+ if (!$ cache_exempt and self :: get_model_cache ():: has_modelset ( $ model_name) ) {
1964+ return Model::get_model_cache ():: fetch_modelset ( $ model_name) ;
19531965 }
19541966
1955- # Obtain all of this Model's internally stored objects
1956- $ internal_objects = $ model ->get_internal_objects ();
1967+ # Obtain all of this Model's internally stored objects, including those from parent Models if applicable
1968+ $ internal_objects = $ model ->get_internal_objects (from_all_parents: true );
19571969
19581970 # For non `many` Models, wrap the internal object in an array so we can loop
19591971 $ internal_objects = $ model ->many ? $ internal_objects : [$ internal_objects ];
@@ -1964,15 +1976,19 @@ class Model {
19641976
19651977 # Loop through each internal object and create a Model object for it
19661978 foreach ($ internal_objects as $ internal_id => $ internal_object ) {
1967- # Populate the ID and parent ID values where applicable
1968- $ internal_id = array_key_exists ('id ' , $ internal_object ) ? $ internal_object ['id ' ] : $ internal_id ;
1979+ # For Models with parent Models, the internal object always contains extra context data about
1980+ # the parent Model class. See the get_internal_objects_from_all_parents() method for more information.
1981+ $ internal_id = $ model ->parent_model_class ? $ internal_object ["_id " ] : $ internal_id ;
1982+ $ parent_model = $ model ->parent_model_class ? $ internal_object ["_parent_model " ] : null ;
1983+ $ parent_id = $ parent_model ? $ parent_model ->id : null ;
1984+ $ internal_object = $ parent_model ? $ internal_object ["_internal_object " ] : $ internal_object ;
1985+
1986+ # Normalize IDs
19691987 $ internal_id = is_numeric ($ internal_id ) ? (int ) $ internal_id : $ internal_id ;
1970- $ parent_id = array_key_exists ('parent_id ' , $ internal_object ) ? $ internal_object ['parent_id ' ] : null ;
1971- $ parent_id = is_numeric ($ parent_id ) ? (int ) $ parent_id : $ parent_id ;
19721988
19731989 # Create a new Model object for this internal object and assign its ID
19741990 $ model_object = new $ model (id: $ internal_id , parent_id: $ parent_id , skip_init: true );
1975- $ model_object ->parent_model = $ internal_object [ ' parent_model ' ] ?? null ;
1991+ $ model_object ->parent_model = $ parent_model ;
19761992
19771993 # Populate the Model object using its current internal values and add it to the array of all Model objects
19781994 $ model_object ->from_internal_object ($ internal_object );
@@ -1985,7 +2001,7 @@ class Model {
19852001
19862002 # For many models, cache the ModelSet if not exempt
19872003 if ($ model ->many and !$ cache_exempt ) {
1988- Model:: $ object_cache [ $ model_name] = new ModelSet (model_objects: $ model_objects );
2004+ self :: get_model_cache ():: cache_modelset ( $ model_name, $ modelset );
19892005 }
19902006
19912007 # For many enabled Models return a ModelSet, otherwise return a single Model object
@@ -2088,6 +2104,9 @@ class Model {
20882104 # Obtain the requested Model object from config by ID
20892105 $ this ->from_internal ();
20902106
2107+ # Cache the newly read Model object
2108+ self ::get_model_cache ()::cache_model ($ this );
2109+
20912110 return $ this ;
20922111 }
20932112
@@ -2149,7 +2168,7 @@ class Model {
21492168 }
21502169
21512170 # Reset the object cache as the config has changed
2152- Model:: $ object_cache = [] ;
2171+ self :: clear_model_cache () ;
21532172
21542173 # Return the current representation of this object
21552174 return $ this ;
@@ -2242,7 +2261,7 @@ class Model {
22422261 }
22432262
22442263 # Reset the object cache as the config has changed
2245- Model:: $ object_cache = [] ;
2264+ self :: clear_model_cache () ;
22462265
22472266 # Return the current representation of this object
22482267 return $ this ;
@@ -2338,7 +2357,7 @@ class Model {
23382357 }
23392358
23402359 # Reset the object cache as the config has changed
2341- Model:: $ object_cache = [] ;
2360+ self :: clear_model_cache () ;
23422361
23432362 return $ new_objects ;
23442363 }
@@ -2426,7 +2445,7 @@ class Model {
24262445 }
24272446
24282447 # Reset the object cache as the config has changed
2429- Model:: $ object_cache = [] ;
2448+ self :: clear_model_cache () ;
24302449
24312450 # Return the current representation of this object
24322451 return $ this ;
@@ -2470,7 +2489,7 @@ class Model {
24702489 $ model_objects = self ::query (query_params: $ query_params , excluded: $ excluded , limit: $ limit , offset: $ offset );
24712490
24722491 # Reset the object cache as the config has changed
2473- Model::$ object_cache = [] ;
2492+ Model::clear_model_cache () ;
24742493
24752494 # Delete the Model objects that matched the query
24762495 return $ model_objects ->delete ();
@@ -2484,7 +2503,7 @@ class Model {
24842503 $ model_objects = self ::read_all ();
24852504
24862505 # Reset the object cache as the config has changed
2487- Model::$ object_cache = [] ;
2506+ Model::clear_model_cache () ;
24882507
24892508 # Delete all Model objects for this Model
24902509 return $ model_objects ->delete ();
0 commit comments