We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Calling find() only returns top level object/table row

I have a micro application with a few models. These models are mapped out in the database (I use MySQL Workbench). Everything works well except when I call find on my table that has a few HasMany realtionships it only returns the object representing the row for which I have called find. I want it to have collections of the objects that are related as well and I want to return these in a big messy JSON blob. :)

TLDR; I am trying to retrieve related rows and thought it would be done automatically. What am I missing?

Models:

class Budgets extends Model
{
   public $id;  <--do I really need these in here????
   public $name;     
   public function initialize()
   {
       $this->hasMany("id", "Expenses", "budgets_id");  <--why is this required when the database has this metadata???  (oh well)
       $this->hasMany("id", "Bills", "budgets_id");
   }
}
class Expenses extends Model
{
   public $id;
   public $budgets_id;

   public function initialize()
   {
      $this->belongsTo("budgets_id", "Budgets", "id"); 
   }      
}
class Bills extends Model
{
   public $id;
   public $budgets_id;    
   public function initialize()
   {
      $this->belongsTo("budgets_id", "Budgets", "id"); 
   }    
}

Controller

function findAction($id){       
        $budget = Budgets::findFirstById($id);
        $json = json_encode($budget, true);
        echo $json;exit();
    }

Returns:

{"id":"1","name":"TestBudget","users_id":"2","cash":"1900"}

I want it to return the full budget record as defined by the relationship....not just the budget row.

And why isn't the JSON encoded with type names like this (this is what I'm aiming for):

budgets: [
        {
            id: 1,
            name: "TestBudget",
            cash: 1900,
            bills: [
                { id: 1, name: "Car", amount:300.00 }
            ]
            expenses: [
                { id: 1, name: "Food", amount:175.55 }
            ]
        }
]

Any help or insight is appreciated.

edited Mar '14

I tried this and it happily accessed the related data as expected:

    function findAction($id){
        $budget = Budgets::findFirstById($id);
        foreach ($budget->getBills() as $bill){
            echo $bill->name;
        }
        foreach ($budget->getExpenses() as $expense){
            echo $expense->name;
        }
        $json = json_encode($budget, true);
        echo $json;exit();        
    }

I guess my question that remains is: Do I have to build the JSON that I want--as outlined at the end of my original post--manually?



17.9k
Accepted
answer

A thing of beauty:

function findAction($id){
        $budget = Budgets::findFirstById($id);  
        $data = array("budget"=>$budget);

        $bills = array();
        foreach ($budget->getBills() as $bill){
            $bills[] = $bill; 
        }
        $data["bills"] = $bills;

        $expenses = array();
        foreach ($budget->getExpenses() as $expense){
            $expenses[] = $expense;
        }

        $data["expenses"] = $expenses;
        RestUtil::sendResponse(RestUtil::$STATUS_200, $data);
    }
edited Mar '14

Revised to be technically correct:

function findAction($id){
        $object = Budgets::findFirstById($id);
        $budget = get_object_vars($object);

        $bills = array();
        foreach ($object->getBills() as $bill){
            $bills[] = $bill; 
        }

        $budget["bills"] = $bills;

        $expenses = array();
        foreach ($object->getExpenses() as $expense){
            $expenses[] = $expense;
        }       
        $budget["expenses"] = $expenses;

        RestUtil::sendResponse(RestUtil::$STATUS_200, $budget);        
}

Returns:

{
    id: "1"
    users_id: "2"
    name: "TestBudget"
    cash: "1900"
    -bills: [
    -{
        id: "1"
        budgets_id: "1"
        name: "Car"
        amount: "290.25"
    }
    ]
    -expenses: [
    -{
        id: "1"
        budgets_id: "1"
        name: "Food"
        amount: "175"
    }
    ]
}

You can do this too:

function findAction($id){
        $budget = Budgets::findFirstById($id);

        $budgetarray = $budget->toArray();
        $budgetarray["bills"] = $budget->getBills()->toArray();
        $budgetarray["expenses"] = $budget->getExpenses()->toArray();

        RestUtil::sendResponse(RestUtil::$STATUS_200, $budgetarray);        
}

Thanks maxgalbu. I assume this performs the same action I had expressed, but "behind the scenes" and in native C?

it sure is a phalcon class method (https://docs.phalcon.io/en/latest/api/Phalcon_Mvc_Model.html), so my guess is yes :)

This cleaned up my code and enhanced performance.