@@ -864,6 +864,33 @@ class Model {
864864 return $ internal_object ;
865865 }
866866
867+ /**
868+ * Recursively obtains all internal objects for this Model from all parent objects in config. This method is only
869+ * 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+ */
872+ protected function get_internal_objects_from_all_parents (): array {
873+ # Variables
874+ $ internal_objects = [];
875+ $ 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 , []);
878+
879+ # 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+
884+ foreach ($ child_configs as $ child_id => $ child_config ) {
885+ $ child_config ["parent_id " ] = $ parent_id ;
886+ $ child_config ["id " ] = $ child_id ;
887+ $ internal_objects [] = $ child_config ;
888+ }
889+ }
890+
891+ return $ internal_objects ;
892+ }
893+
867894 /**
868895 * Obtains all internal objects for this Model. When a `config_path` is specified, this method will obtain the
869896 * internal objects directly from config. When an `internal_callable` is assigned, this method will return
@@ -886,6 +913,10 @@ class Model {
886913 elseif ($ mock_internal_objects ) {
887914 $ internal_objects = $ mock_internal_objects ;
888915 }
916+ # Obtain all internal objects from all parents if a parent Model class is assigned
917+ elseif ($ this ->parent_model_class ) {
918+ $ internal_objects = $ this ->get_internal_objects_from_all_parents ();
919+ }
889920 # Obtain the internal objects from the config path if specified
890921 elseif ($ this ->config_path ) {
891922 $ internal_objects = $ this ->get_config ($ this ->get_config_path (), []);
@@ -913,7 +944,7 @@ class Model {
913944 * @throws ServerError When this Model does not have a `config_path` set.
914945 * @throws NotFoundError When an object with the specified $id does not exist.
915946 */
916- public function from_internal () {
947+ public function from_internal (): void {
917948 # Require a `parent_id` if a `many` parent Model is assigned
918949 if ($ this ->parent_model_class and $ this ->parent_model ->many and !isset ($ this ->parent_id )) {
919950 throw new ServerError (
@@ -1319,7 +1350,7 @@ class Model {
13191350 }
13201351
13211352 # Use the $modelset if provided, otherwise obtain a ModelSet of all existing objects for this Model
1322- $ modelset = $ modelset ?: $ this ->read_all (parent_id: $ this ->parent_id );
1353+ $ modelset = $ modelset ?: $ this ->query (parent_id: $ this ->parent_id );
13231354
13241355 # Use this variable to keep track of query parameters to use when checking uniqueness
13251356 $ query_params = [];
@@ -1415,7 +1446,7 @@ class Model {
14151446
14161447 # For 'many' Models, capture all current Models in a ModelSet if one was not already given
14171448 if ($ this ->many and !$ modelset ) {
1418- $ modelset = $ this ->read_all (parent_id: $ this ->parent_id );
1449+ $ modelset = $ this ->query (parent_id: $ this ->parent_id );
14191450 }
14201451
14211452 # Loop through each of this object's assigned Fields and validate them.
@@ -1887,8 +1918,6 @@ class Model {
18871918 * Fetches Model objects for all objects stored in the internal pfSense values. If `config_path` is set, this will
18881919 * load Model objects for each object stored at the config path. If `internal_callable` is set, this will create
18891920 * Model objects for each object returned by the specified callable.
1890- * @param mixed|null $parent_id Specifies the ID of the parent Model to read all objects from. This is required for
1891- * $many Models with a $parent_model_class. This value has no affect otherwise.
18921921 * @param int $offset The starting point in the dataset to be used with $limit. This is only applicable to $many
18931922 * enabled Models.
18941923 * @param int $limit The maximum number of Model objects to retrieve. This is only applicable to $many
@@ -1899,26 +1928,16 @@ class Model {
18991928 * not enabled.
19001929 */
19011930 public static function read_all (
1902- mixed $ parent_id = null ,
19031931 int $ limit = 0 ,
19041932 int $ offset = 0 ,
19051933 bool $ reverse = false ,
19061934 ): ModelSet |Model {
19071935 # Variables
19081936 $ model_name = get_called_class ();
1909- $ model = new $ model_name (parent_id: $ parent_id );
1937+ $ model = new $ model_name ();
19101938 $ model_objects = [];
1911- $ is_parent_model_many = $ model ->is_parent_model_many ();
19121939 $ requests_pagination = ($ limit or $ offset );
1913- $ cache_exempt = ($ requests_pagination or $ reverse or isset ($ parent_id ));
1914-
1915- # Throw an error if this Model has a $many parent Model, but no parent Model ID was given
1916- if ($ is_parent_model_many and !isset ($ parent_id )) {
1917- throw new ValidationError (
1918- message: 'Field `parent_id` is required to read all. ' ,
1919- response_id: 'MODEL_PARENT_ID_REQUIRED ' ,
1920- );
1921- }
1940+ $ cache_exempt = ($ requests_pagination or $ reverse );
19221941
19231942 # Throw an error if pagination was requested on a Model without $many enabled
19241943 if (!$ model ->many and $ requests_pagination ) {
@@ -1946,8 +1965,11 @@ class Model {
19461965
19471966 # Loop through each internal object and create a Model object for it
19481967 foreach ($ internal_objects as $ internal_id => $ internal_object ) {
1949- # Ensure numeric IDs are converted to integers
1968+ # Populate the ID and parent ID values where applicable
1969+ $ internal_id = array_key_exists ('id ' , $ internal_object ) ? $ internal_object ['id ' ] : $ internal_id ;
19501970 $ internal_id = is_numeric ($ internal_id ) ? (int ) $ internal_id : $ internal_id ;
1971+ $ parent_id = array_key_exists ('parent_id ' , $ internal_object ) ? $ internal_object ['parent_id ' ] : null ;
1972+ $ parent_id = is_numeric ($ parent_id ) ? (int ) $ parent_id : $ parent_id ;
19511973
19521974 # Create a new Model object for this internal object and assign its ID
19531975 $ model_object = new $ model (id: $ internal_id , parent_id: $ parent_id , skip_init: true );
@@ -1978,8 +2000,6 @@ class Model {
19782000 * @param array $query_params An array of query parameters.
19792001 * @param array $excluded An array of field names to exclude from the query. This is helpful when
19802002 * query data may have extra values that you do not want to include in the query.
1981- * @param mixed|null $parent_id Specifies the ID of the parent Model to read all objects from. This is required for
1982- * $many Models with a $parent_model_class. This value has no affect otherwise.
19832003 * @param int $offset The starting point in the dataset to be used with $limit. This is only applicable to $many
19842004 * enabled Models.
19852005 * @param int $limit The maximum number of Model objects to retrieve. This is only applicable to $many
@@ -1993,7 +2013,6 @@ class Model {
19932013 public static function query (
19942014 array $ query_params = [],
19952015 array $ excluded = [],
1996- mixed $ parent_id = null ,
19972016 int $ limit = 0 ,
19982017 int $ offset = 0 ,
19992018 bool $ reverse = false ,
@@ -2007,11 +2026,11 @@ class Model {
20072026
20082027 # If no query or sort parameters were provided, just run read_all() with pagination for optimal performance
20092028 if (!$ query_params and $ sort_by === null ) {
2010- return self ::read_all (parent_id: $ parent_id , limit: $ limit , offset: $ offset , reverse: $ reverse );
2029+ return self ::read_all (limit: $ limit , offset: $ offset , reverse: $ reverse );
20112030 }
20122031
20132032 # Perform the query against all Model objects for this Model first
2014- $ modelset = self ::read_all (parent_id: $ parent_id )->query (query_params: $ query_params , excluded: $ excluded );
2033+ $ modelset = self ::read_all ()->query (query_params: $ query_params , excluded: $ excluded );
20152034
20162035 # Sort the set if a sort field was provided
20172036 if ($ sort_by ) {
@@ -2277,7 +2296,7 @@ class Model {
22772296 # Keep track of all existing objects for this Model before anything is changed. This will be passed back
22782297 # into `apply_replace_all()` so that method can gracefully bring down these objects before replacing them
22792298 # if needed.
2280- $ initial_objects = $ this ->read_all (parent_id: $ this ->parent_id );
2299+ $ initial_objects = $ this ->query (parent_id: $ this ->parent_id );
22812300
22822301 # Obtain any Models that are deemed protected to ensure they do not removed in the next step.
22832302 $ new_objects = new ModelSet ();
@@ -2453,7 +2472,6 @@ class Model {
24532472 $ model_objects = self ::query (
24542473 query_params: $ query_params ,
24552474 excluded: $ excluded ,
2456- parent_id: $ parent_id ,
24572475 limit: $ limit ,
24582476 offset: $ offset ,
24592477 );
0 commit comments