Modelos dinámicos en CakePHP, una mejor forma de hacerlo

Como buen enredador que soy no me quedaba conforme con la anterior solución al problema de los modelos dinámicos. Si bien funcionaba muy bien el tener que declararlos mediante un

App:uses('ModeloDinamicoModel', 'Model');

en lugar del habitual

public $uses = ['ModeloDinamico'];

me molestaba, también había otras cosas, como que se salía bastante de la definición habitual de los modelos cuando querías relacionar entre si dos tablas.

Así que investigando por ahí (vía Google y stackoverflow, no voy a mentir) he llegado a una solución mucho más elegante y que es mucho más clara de entender.

Todo la magia se basa en modificar el AppModel para incluir un nuevo método:

<?php
  App::uses('Model', 'Model');

  class AppModel extends Model {
    public function setDbConfig ($dbconfig) {
      if (is_array($dbconfig)) {
        ConnectionManager::create($dbconfig['name'], $dbconfig);
        $dbconfig = $dbconfig['name'];
      }
      $this->useDbConfig = $dbconfig;
      if (is_array($this->hasMany)) foreach ($this->hasMany as $name => $table) $this->$name->useDbConfig = $dbconfig;
      if (is_array($this->belongsTo)) foreach ($this->belongsTo as $name => $table) $this->$name->useDbConfig = $dbconfig;
      if (is_array($this->hasAndBelongsToMany)) foreach ($this->hasAndBelongsToMany as $name => $table) $this->$name->useDbConfig = $dbconfig;
      $this->cacheQueries = false;
    }
  }

El nuevo método es el setDbConfig, como parámetro obligatorio admite un nombre de conexión de base de datos de la configuración (/Config/database.php) o bien un array con los parámetros de una nueva configuración. Después de configurar la conexión de la base de datos revisa si se ha definido alguna relación hasMany, belongsTo o hasAndBelongsToMany y en ese caso define la base de datos para esas relaciones.

Así pues el modelo que definía en el anterior post queda de la siguiente manera:

<?php
  class EjemploUno extends AppModel {
    public $useDbConfig = 'default';
    public $useTable = 'uno';
    public $primaryKey = 'clave';
    public $name = 'uno';
    public $hasMany = [
      'EjemploDos' => [
        'foreignKey' => 'clave',
        'associationForeignKey' => 'claveuno',
      ],
    ];
  }

En este caso este es el modelo “EjemploUno” y tiene una relación hasMany con “EjemploDos”:

<?php
  class EjemploDos extends AppModel {
    public $useDbConfig = 'default';
    public $useTable = 'dos';
    public $primaryKey = 'clavedos';
    public $name = 'dos';
    public $belongsTo = [
      'EjemploUno' => [
        'foreignKey' => 'claveuno',
        'associationForeignKey' => 'clave',
      ],
    ];
  }

Ahora definimos todos los modelos, también de las tablas relacionadas, y seguimos todas las reglas de CakePHP. Lo único especial es que si las nomenclaturas de las claves y las reglas de los nombres no se cumplen hay que decirle a CakePHP qué claves son las que establecen la relación, lo que es normal si estamos atacando unas tablas de una base de datos que no funciona bajo CakePHP.

En el controlador lo usaríamos como casi cualquier modelo normal:

<?php
  class EjemplosController extends AppController {
    public $uses = ['EjemploUno', 'EjemploDos'];

    public index () {
      $this->EjemploUno->setDbConfig($databaseconfig);
      $this->find('all');
    }
  }

Lo único diferente es el uso de setDbConfig antes de atacar al modelo con un find (o lo que toque). Si el $useDbConfig de la definición de los modelos ya tiene una base válida para ellos entonces no necesitaríamos utilizar el setDbConfig, y lo tendríamos ahí simplemente como forma de cambio de base de datos en caliente.

Lo que he podido ver durante estos días es que hay muchas posibilidades con los modelos dinámicos, yo solo me he centrado en la definición al vuelo de la base de datos con la que trabaja el modelo, pero eso sólo es una de las muchas caras que tiene este mundo tan dinámico.

Leave a Reply

Your email address will not be published. Required fields are marked *