Working solution:
$input = RestUtil::decodeJSON($json);
$budget_data = new Budgets();
$budget_data->promote($input->data);
$json looks like this:
{
id: "1"
users_id: "2"
name: "TestBudget"
cash: "1985.23"
-Bills: [
-{
id: "1"
budgets_id: "1"
name: "Car"
amount: "350.25"
}
]
-Expenses: [
-{
id: "1"
budgets_id: "1"
name: "Food"
amount: "600"
}
-{
id: "2"
budgets_id: "1"
name: "Gas"
amount: "250"
}
]
SharedWith: [ ]
}
$input->data looks like this:
stdClass Object (
[id] => 1 [users_id] => 2 [name] => TestBudget [cash] => 1985.23
[Bills] => Array (
[0] => stdClass Object ( [id] => 1 [budgets_id] => 1 [name] => Car [amount] => 350.25 )
)
[Expenses] => Array (
[0] => stdClass Object ( [id] => 1 [budgets_id]=> 1 [name] => Food [amount] => 600 )
[1] => stdClass Object ( [id] => 2 [budgets_id] => 1 [name] => Gas [amount] => 250 )
)
[SharedWith] => Array ( )
)
Budgets extends EnhancedModel which extends Phalcon\Mvc\Model (see promote()):
<?php
use Phalcon\Mvc\Model;
abstract class EnhancedModel extends Model
{
public function promote($std_class, $target_class=null){
try{
/*
* << Establish the parent Model type >>
* Allow the user to specify the target_class by providing a string or object.
* Assume extending class when null.
*/
if (is_null($target_class)){
$object = $this;
}elseif (is_object($target_class)){
$object = $target_class;
}elseif (is_string($target_class)){
$object = new $target_class(); //New up a Model from a user-supplied class name--usually during recursion
}else {
return $std_class; //Not playing nice? Return the data passed in.
}
/*
* Prepare this array in case we encounter an array of related records nested in our stdClass
* Each element will be an array of one or more...the children being instances of the related Model (related to the parent).
*/
$related_entities = array(); //eventual array of arrays
/* Loop through the stdClass, accessing properties like an array. */
foreach ($std_class as $property => $value) {
/*
* If an array is found as the value of a property we assume it is full of realted entities;
* with the property name being the Model type (case sensitive)
*
*/
if (is_array($value)){ //all of these are stdClass as well, so we recurse to handle each one
/*
* $property should be named to fit the model of the entities in the array
* This is dependent on the user building the JSON object correctly upstream.
*
*/
$related_entities[$property] = array();
foreach($value as $entity){ //Get each array element and treat it as an entity
/*
* For thought-simplicity sake, let's assume this promote() call doesn't find related entities inside this related entity (Yo Dawg...).
* This adds the related entity to an array named for its Model: $related_entities['related_model_name'] = $object_returned_from_promote().
* This WILL, of course, recurse to infinity building out the complete data model.
*/
$related_entities[$property] = $this->promote($entity, $property);
}
}else {
/* Just add the value found to the property of the Model object */
$object->{$property} = $value;
}
}
/*
* Add each array of whatever related entities were found, to the parent object/table
* This depends on the Phalcon ORM Model convention: $MyTableObject->relatedSomthings = array_of_related_somethings
*/
foreach($related_entities as $related_model => $entity_data){
$object->{$related_model} = $entity_data;
}
}catch(Exception $e){
/*
* If the user supplied data (decoded JSON) that does not match the Model we are going to experience an exception
* when trying to access a property that doesn't exist.
*
*/
throw $e;
}
return $object; /* Usually only important when we are using recursion. */
}
}
I have not considered many-to-many, and I have not tested this with a one-to-one relationship. Working great for one-to-many. Anything stand out?