Creating virtual field using associated data

I’m trying to create a virtual field for an entity using attributes from associated entities. Creating the virtual field works fine but I don’t know how to get hold of the associated entity attributes.

The website is for a house builder. They have a number of housing developments each of which has a number of plots for sale. Each of those plots has a house of a specific design on it and each of these houses is of a specific type that has a certain number of bedrooms.

What I’m trying to create is a virtual field in the development that’s a simple array of all the different numbers of bedrooms on that development. So, for example, if there are houses with one bedroom, houses with two bedrooms and houses with four bedrooms, the array would be [1,2,4].

In the table definition for the developments I have the following association -

        $this->hasMany('Plots', [
            'foreignKey' => 'development_id',
        ]);

In the table defintion for the plots I have -

        $this->belongsTo('Houses', [
            'foreignKey' => 'house_id',
            'joinType' => 'INNER',
        ]);

In the table definition for the houses I have -

        $this->belongsTo('HouseTypes', [
            'foreignKey' => 'house_type_id',
            'joinType' => 'INNER',
        ]);

Then the housetype entity has an attribute called ‘rooms’.

What I want to do when I create the virtual field is cycle through all the plots in the development to find the number of rooms in each plot like this -

    protected function _getRoomsArray()
    {
        $rooms = [];
        foreach($this->available_plots as $plot)
        {
            $rooms[] = $plot->house->house_type->rooms;
        }
        $rooms = array_unique($rooms);
        sort($rooms);
        return $rooms;
    }

Obviously the $rooms[] = $plot->house->house_type->rooms; line doesn’t work.

Anybody got any idea how to do something like this?

Can you share the code where you read the Development entity that this operates on?

The code in the controller looks like this -

        $developments = $this->Developments->
                            find('all')->
                            where(['live' => '1', 'type' => '1'])->
                            order(['location' => 'ASC']);
        $this->set(compact('developments'));

Is this what you were meaning?

Yes, exactly. Unless you’re using something like this lazy loading plugin, it will only be retrieving the Development record here, none of the associations that you’ve defined. If you add contain(['Plots' => ['Houses' => ['HouseTypes']]]) into your call, it’ll pull in all the other data, and your virtual function should work fine.

@Zuluru Brilliant. That works perfectly. Many thanks

Debugging tip: if you had examined the contents of your $developments or $development variables, you’d have quickly spotted that none of the expected associated data was loaded, which might have given you a better search term to use to find the issue.

1 Like

Thanks. To be honest I didn’t even know if what I was trying to do made sense using Cake. I’m still very new to it. I’d done similar things in the past using Yii but I wouldn’t have been surprised if someone had told me I needed to take a completely different approach.