0

I have an entity Villa, and I want this Entity to contain other Villas which have the same 'complex' (Varchar(255)).

class VillasTable extends Table
{

    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     */
    public function initialize(array $config)
    {
        $this->table('villas');
        $this->displayField('name');
        $this->primaryKey('id');

        $this->hasMany('Complexs', [
            'className' => 'Villas',
            'foreignKey' => false,
            'propertyName' => 'complexs',
            'conditions' => ['Complexs.complex' => 'Villas.complex']
        ]);
    }
}
?>

I don't know if it's possible. I don't want to add a find in each function who need those entity. Also I would like to make a function in the Entity which uses this new field. ``

John Conde
  • 217,595
  • 99
  • 455
  • 496
Ludo
  • 157
  • 13
  • So what's the problem? – ndm May 16 '15 at 01:16
  • In cakePHP 3 , we can't use false for the foreign key :'( – Ludo May 16 '15 at 02:35
  • Ah, on a second look, it's a `hasMany` association, right, they don't support disabling foreign keys, as the ORM wouldn't know how to collect the keys and stitch the results together. – ndm May 16 '15 at 12:36
  • I've updated my answer, it was missing a comparision for the primary key in order to avoid the main record to be included in the associated records. – ndm May 17 '15 at 14:49

1 Answers1

1

Despite the fact that using a VARCHAR(255) as a foreign key is probably highly inefficient and/or will require huge indices, I guess that generally an option that defines the foreign key of the other table would come in handy here, similar to targetForeignKey for belongsToMany associations. You may to want to suggest that as an enhancement.

Result formatters

Currently this doesn't seem to be possible out of the box using associations, so you'd probably have to select and inject the associated records yourself, for example in a result formatter.

$Villas
    ->find()
    ->formatResults(function($results) {
        /* @var $results \Cake\Datasource\ResultSetInterface|\Cake\Collection\Collection */

        // extract the custom foreign keys from the results
        $keys = array_unique($results->extract('complex')->toArray());

        // find the associated rows using the extracted foreign keys
        $villas = \Cake\ORM\TableRegistry::get('Villas')
            ->find()
            ->where(['complex IN' => $keys])
            ->all();

        // inject the associated records into the results
        return $results->map(function ($row) use ($villas) {
            if (isset($row['complex'])) {
                // filters the associated records based on the custom foreign keys
                // and adds them to a property on the row/entity
                $row['complexs'] = $villas->filter(function ($value, $key) use ($row) {
                    return
                        $value['complex'] === $row['complex'] &&
                        $value['id'] !== $row['id'];
                })
                ->toArray(false);
            }
            return $row;
        });
    });

This would fetch the associated rows afterwards using the custom foreign keys, and inject the results, so that you'd end up with the associated records on the entities.

See also Cookbook > Database Access & ORM > Query Builder > Adding Calculated Fields

There might be other options, like for example using a custom eager loader that collects the necessary keys, combined with a custom association class that uses the proper key for stitching the results together, see

ndm
  • 59,784
  • 9
  • 71
  • 110