@@ -17,11 +17,10 @@ use RESTAPI\Responses\ServerError;
1717 * route to its parent Gateway model object.
1818 */
1919class ForeignModelField extends Field {
20+ /**
21+ * @var Model $model Contains the Model object of the foreign Model this Field's value relates to.
22+ */
2023 public Model $ model ;
21- public string $ model_name ;
22- public array $ model_query ;
23- public string $ model_field ;
24- public string $ model_field_internal ;
2524
2625 /**
2726 * Defines the ForeignModelField object and sets its options.
@@ -39,6 +38,9 @@ class ForeignModelField extends Field {
3938 * referenced by this field. For example, say this field's value must be set to an existing FirewallAlias object's
4039 * `name`, but you only want to allow `port` type aliases; you would set $model_name to `FirewallAlias`, $model_field to
4140 * `name`, and $model_query to `["type" => "port"]`. Defaults to all existing $model objects.
41+ * @param array $parent_model_query When the assigned $model_name class has a parent Model, use this variable to
42+ * limit the scope of which parent Model objects can have their child Models included. By default, the child
43+ * Models of ALL parent Models are included.
4244 * @param bool $required If `true`, this field is required to have a value at all times.
4345 * @param bool $unique If `true`, this field must be unique from all other parent model objects. Enabling this
4446 * option requires the Model $context to be set AND the Model $context must have a `config_path` set.
@@ -90,10 +92,11 @@ class ForeignModelField extends Field {
9092 * @param string $help_text Set a description for this field. This description will be used in API documentation.
9193 */
9294 public function __construct (
93- string $ model_name ,
94- string $ model_field ,
95- string $ model_field_internal = "" ,
96- array $ model_query = [],
95+ public string $ model_name ,
96+ public string $ model_field ,
97+ public string $ model_field_internal = "" ,
98+ public array $ model_query = [],
99+ public array $ parent_model_query = [],
97100 bool $ required = false ,
98101 bool $ unique = false ,
99102 mixed $ default = null ,
@@ -121,8 +124,6 @@ class ForeignModelField extends Field {
121124 {
122125 # Assign properties unique to this Field object
123126 $ model_name = "\\RESTAPI \\Models \\$ model_name " ;
124- $ this ->model_query = $ model_query ;
125- $ this ->model_field = $ model_field ;
126127 $ this ->model_field_internal = ($ model_field_internal ) ?: $ model_field ;
127128
128129 # Ensure the properties assigned are allowed and that the assigned $model_name can be constructed
@@ -161,7 +162,7 @@ class ForeignModelField extends Field {
161162 * Checks that the object to be constructed has no conflicts.
162163 * @param string $model_name The foreign Model class's FQN.
163164 */
164- private function check_construct (string $ model_name ) {
165+ private function check_construct (string $ model_name ): void {
165166 # Ensure the assigned $model_name is an existing Model class
166167 if (!class_exists ($ model_name )) {
167168 throw new ServerError (
@@ -226,11 +227,8 @@ class ForeignModelField extends Field {
226227 $ internal_value = $ this ->model ->{$ this ->model_field_internal }->_from_internal ($ internal_value );
227228 }
228229
229- # Add the query parameters needed to locate the related Model object
230- $ query_params = array_merge ($ this ->model_query , [$ this ->model_field_internal => $ internal_value ]);
231-
232230 # Query for the Model object this value relates to.
233- $ query_modelset = $ this ->model -> query ( $ query_params );
231+ $ query_modelset = $ this ->__get_matches ( $ this -> model_field_internal , $ internal_value );
234232
235233 # If the model object exists, return the `model_field` value.
236234 if ($ query_modelset ->exists ()) {
@@ -247,11 +245,8 @@ class ForeignModelField extends Field {
247245 * @return array|string|null The internal value(s) suitable for writing to the pfSense configuration.
248246 */
249247 protected function _to_internal (mixed $ representation_value ) : array |string |null {
250- # Add the query parameters needed to locate the related Model object
251- $ query_params = array_merge ($ this ->model_query , [$ this ->model_field => $ representation_value ]);
252-
253- # Query for the Model object this value relates to.
254- $ query_modelset = $ this ->model ->query ($ query_params );
248+ # Obtain Model objects that matches this field's criteria
249+ $ query_modelset = $ this ->__get_matches ($ this ->model_field , $ representation_value );
255250
256251 # If the model object exists, return the existing `model_field_internal` value.
257252 if ($ query_modelset ->exists ()) {
@@ -267,12 +262,9 @@ class ForeignModelField extends Field {
267262 * @param mixed $value The value being validated. In the event that this is a `many` field, this method will
268263 * receive each value of the array individually, not the array value itself.
269264 */
270- public function validate_extra (mixed $ value ) {
271- # Add the query parameters needed to locate the related Model object
272- $ query_params = array_merge ($ this ->model_query , [$ this ->model_field => $ value ]);
273-
274- # Query for the Model object this value relates to.
275- $ query_modelset = $ this ->model ->query ($ query_params );
265+ public function validate_extra (mixed $ value ): void {
266+ # Obtain Models that match this Field's criteria
267+ $ query_modelset = $ this ->__get_matches ($ this ->model_field , $ value );
276268
277269 # If the model object exists, return the existing `model_field_internal` value.
278270 if (!$ query_modelset ->exists ()) {
@@ -284,17 +276,55 @@ class ForeignModelField extends Field {
284276 }
285277 }
286278
279+ /**
280+ * Obtains the ModelSet of Model objects that are in-scope for this field using the $model_query and
281+ * $parent_model_query properties.
282+ * @return ModelSet A ModelSet of Model objects that are in-scope for this field
283+ */
284+ public function get_in_scope_models (): ModelSet {
285+ # Variables
286+ $ models = new ModelSet ();
287+
288+ # Obtain the parent Models if the assigned $model has a $parent_model_class assigned to it.
289+ if ($ this ->model ->parent_model_class ) {
290+ $ parent_model_class = $ this ->model ->get_parent_model ();
291+ $ parent_model = new $ parent_model_class ();
292+ $ parent_models = $ parent_model ->query ($ this ->parent_model_query );
293+
294+ # Loop through each identified parent and add its children into the ModelSet
295+ foreach ($ parent_models ->model_objects as $ parent ) {
296+ $ parent_children = $ this ->model ->query (parent_id: $ parent ->id );
297+ $ models ->model_objects = array_merge ($ models ->model_objects , $ parent_children ->model_objects );
298+ }
299+ }
300+ # Otherwise, just use all of this $model's current objects
301+ else {
302+ $ models = $ this ->model ->read_all ();
303+ }
304+
305+ # Query for the Model object this value relates to.
306+ return $ models ->query ($ this ->model_query );
307+ }
308+
309+ /**
310+ * Obtains a ModelSet of the Model(s) that match this field's criteria.
311+ * @param string $field_name The name of the field used to check for matching values. This is typically set to the
312+ * same value as $this->field_name.
313+ * @param mixed $field_value The value of the $field_name that indicates there is a match. This is typically set
314+ * to the same value as $this->value.
315+ */
316+ private function __get_matches (string $ field_name , mixed $ field_value ): ModelSet {
317+ return $ this ->get_in_scope_models ()->query (query_params: [$ field_name => $ field_value ]);
318+ }
319+
287320 /**
288321 * Obtains the Model object associated with this field's current value. This is only applicable when this is not
289322 * a $many enabled field.
290323 * @returns Model|null Returns the Model object associated with this Field's current value.
291324 */
292325 public function get_related_model () : Model |null {
293- # Add the query parameters needed to locate the related Model object
294- $ query_params = array_merge ($ this ->model_query , [$ this ->model_field => $ this ->value ]);
295-
296- # Query for the Model object this value relates to.
297- $ query_modelset = $ this ->model ->query ($ query_params );
326+ # Get the Model objects that match this field's criteria
327+ $ query_modelset = $ this ->__get_matches ($ this ->model_field , $ this ->value );
298328
299329 # Return the related model object if it exists
300330 if ($ query_modelset ->exists ()) {
@@ -317,11 +347,8 @@ class ForeignModelField extends Field {
317347
318348 # Loop through each current value and query for model objects that match them
319349 foreach ($ this ->value as $ value ) {
320- # Add the query parameters needed to locate the related Model object
321- $ query_params = array_merge ($ this ->model_query , [$ this ->model_field => $ value ]);
322-
323- # Query for the Model object this value relates to.
324- $ query_modelset = $ this ->model ->query ($ query_params );
350+ # Obtain Model objects that match this value
351+ $ query_modelset = $ this ->__get_matches ($ this ->model_field , $ value );
325352
326353 # Only add the Model object if it exists
327354 if ($ query_modelset ->exists ()) {
0 commit comments