PhpActiveRecord

17 Oct

Cuando creamos una aplicación web hay tareas que debemos repetir una y otra vez, tareas que suelen pasar por un proceso denso, que muchas veces nos hace cometer errores sobre todo de distracción, y por lo general todo esto lleva mucho tiempo, tiempo que no tenemos. Una de estas tareas son las consultas de la base de datos. Una aplicación mediana ya de por sí suele tener muchas tablas, lo que nos obliga a tener que crear consultas por cada una de estas tablas, además testear que éstas funcionen correctamente.

ORM

Un ORM (Object-Relational mapping) es una forma de trabajar con una base de datos, mediante clases y objetos, osea que por cada tabla tendremos una clase, cada registro será un objeto, y para acceder a los datos de esa tabla debemos utilizar métodos de la clase correspondiente.

Googleando encontré este excelente ORM: http://www.phpactiverecord.org/, muy fácil de usar. Así que vamos a empezar conociéndolo un poco.

Antes de comenzar voy a aclarar algo, y es que este Framework funciona a partir de la versión  5.3 de PHP, si no sabés cuál es la versión de tu PHP, tendrás que crear un archivo en tu localhost y escribir la función:

phpinfo();

Y ejecutarlo en tu navegador para verificar la versión.

Primero vamos a descargar la librería desde aquí:

https://github.com/kla/php-activerecord/tree/master

Una vez descargada vamos a descomprimir el archivo.

Ahora podemos crear un nuevo proyecto en nuestro localhost, al que yo en mi caso voy a llamar: php_activerecord.

También vamos a crear una nueva base de datos mysql con el mismo nombre, php_activerecord:

CREATE DATABASE php_activerecord;

Ésta tendrá dos tablas, una llamada cliente y otra llamada pedido. Ambas tablas tendrán una relación de 1 a n, osea, un cliente tendrá muchos pedidos y cada pedido le pertenecerá a un cliente:

Tabla cliente:

CREATE TABLE cliente(
   cliente_id int(10) unsigned not null auto_increment primary key,
   nombre varchar(50) not null,
   apellido varchar(50) not null,
   email varchar(50) not null,
   contrasena varchar(50) not null,
   fecha_creacion datetime not null,
   fecha_modificacion datetime not null
)ENGINE=innoDB;

Tabla pedido:

CREATE TABLE pedido(
   pedido_id int(10) unsigned not null auto_increment primary key,
   asunto varchar(50) not null,
   mensaje text not null,
   fecha_creacion datetime not null,
   fecha_modificacion datetime not null,
   cliente_id int(10) unsigned not null,
   foreign key(cliente_id)
   references cliente(cliente_id)
   on delete cascade
   on update cascade
)ENGINE=innoDB;

Bien, ahora que ya tenemos nuestra base de datos podemos continuar con la conexión y las clases para trabajar con la misma.

Dentro de nuestro proyecto: php_activerecord vamos a crear dos directorios uno llamado libraries y otro llamado models. Dentro de libraries vamos a mover el proyecto descompilado que descargamos, independientemente del nombre que tenga vamos a llamarlo php-activerecord.

Ahora vamos a crear un archivo .php, yo lo voy a creer dentro del directorio raíz, llamado _conexion.php con el siguiente código:

<?php 
 require_once 'libraries/php-activerecord/ActiveRecord.php';
 ActiveRecord\Config::initialize(function($cfg)
 {
    $cfg->set_model_directory('models');
    $cfg->set_connections(array(
    'development' => 'mysql://username:password@localhost/database_name'));
 }); 
?>

Con respecto a la línea:

require_once 'libraries/php-activerecord/ActiveRecord.php';

Debemos asegurarnos que apunte correctamente al archivo ActiviteRecord.php.

Además la línea:

$cfg->set_model_directory('models');

Sirve para importar los modelos que crearemos más adelante.

Y en:

'mysql://username:password@localhost/database_name'

Debemos escribir los datos correctos para la conexión a nuestra base de datos. Por ejemplo en mi caso:

'mysql://root:1234@localhost/php_activerecord'

Bueno, ahora vamos a ir a la carpeta models y vamos a crear tres archivos .php: Cliente.php y Pedido.php.

La clase Cliente.php tendrá el siguiente código:

<?php
 class Cliente extends ActiveRecord\Model {
   public static $table_name = 'cliente';
   public static $primary_key = 'cliente_id';
   public function before_create(){
      $this->fecha_creacion = date('Y-m-d H:i:s');
      $this->fecha_modificacion = date('Y-m-d H:i:s');
   }
   public function before_update(){
      $this->fecha_modificacion = date('Y-m-d H:i:s');
   }
 }
?>

Vamos a explicar este código. Por empezar cada modelo será una clase que extiende de otras dos clases ActiveRecord y Model, para heredar los métodos que nos facilitarán el trabajo con la base de datos.

Además vamos a crear dos propiedades públicas estáticas:

public static $table_name = 'cliente';
public static $primary_key = 'cliente_id';

En la primera definimos el nombre de la tabla y en la segunda la clave primaria, el id.

Y finalmente dos métodos:

public function before_create(){
   $this->fecha_creacion = date('Y-m-d H:i:s');
   $this->fecha_modificacion = date('Y-m-d H:i:s');
}
public function before_update(){
   $this->fecha_modificacion = date('Y-m-d H:i:s');
}

El método before_create() se disparará cuando se guarde un nuevo registro en la base de datos y before_update() cuando se modifique. Por ejemplo en el primero le indicamos que setee la fecha de creación y modificación con la fecha actual y en el segundo sólo la de modificación. Estos métodos no son obligatorios, pero en nuestro caso las creamos para tener un registro de las fechas.

Y la clase Pedido.php:

<?php
 class Pedido extends ActiveRecord\Model {
   public static $table_name = 'pedido';
   public static $primary_key = 'pedido_id';
   public function before_create(){
      $this->fecha_creacion = date('Y-m-d H:i:s');
      $this->fecha_modificacion = date('Y-m-d H:i:s');
   }
   public function before_update(){
      $this->fecha_modificacion = date('Y-m-d H:i:s');
   }
 }
?>

 

Guardar registros

Existen dos formas muy sencillas de guardar registros nuevos en la base de datos, a través de los métodos save() y create().

Vamos a crear cualquier archivo en nuestro directorio raíz y vamos a hacer lo siguiente:

<?php
   require_once '_conexion.php';
   $cliente = new Cliente();
   $cliente->nombre = 'Francisco';
   $cliente->apellido = 'Lema';
   $cliente->email = 'tito@email.com';
   $cliente->contrasena = '1234';
   $cliente->save();
?>

Como se ve en el ejemplo, importamos el archivo ‘_conexion.php’, luego creamos un nuevo objeto Cliente y seteamos las propiedades con los valores, propiedades que tienen el mismo nombre que las columnas de la tabla. Finalmente con el método save() guardamos un nuevo registro.

Otra forma de guardar registros es mediante el método create():

<?php
 require_once '_conexion.php';
 $cliente = Cliente::create(array(
    'nombre' => 'Carlos',
    'apellido' => 'Nano',
    'email' => 'nano@email.com',
    'contrasena' => '1234'
 ));
?>

El método create() recibe un array donde cada índice será el nombre de la tabla igualado al valor correspondiente.

Recuperar todos registros de una tabla

<?php
 require_once '_conexion.php';
 $clientes = Cliente::all();
 foreach($clientes as $cliente){
    echo "{$cliente->nombre} {$cliente->apellido} / {$cliente->email} <br />";
 }
?>

Como se ve, a través del método ::all() recuperamos todos los registros de una tabla, que nos devolverá un array donde cada posición será un objeto con los datos del cliente.

Recuperar registros por su id

La forma más común de recuperar registros de una tabla es a través de su id o primary key, para ello usaremos el método find() y le pasaremos como parámetro su id:

<?php
 require_once '_conexion.php';
 $cliente = Cliente::find(1);
 echo "{$cliente->nombre} {$cliente->apellido}";
?>

Como se ve, a través del método find() recuperamos un objeto con el id del registro.

Consultas SQL

También podemos hacer una consulta normal con código sql mediante el método find_by_sql():

<?php
 require_once '_conexion.php';
 $clientes = Cliente::find_by_sql(
 'SELECT * FROM cliente WHERE nombre = ? AND apellido = ? ORDER BY apellido, nombre;',
 array('Carlos', 'Nano'));
 foreach($clientes as $cliente){
    echo "{$cliente->nombre} {$cliente->apellido} / {$cliente->email} <br />";
 } 
?>

Como se ve, en el primer parámetro escribimos el código SQL, y los valores con el signo ?, y el segundo parámetro un array con los valores en el orden que van.

Modificar registros 

Para modificar registros, simplemente tendremos que recuperar un registro, por ejemplo con el método find(), setearle las propiedades nuevas y usar el método save() para guardarlo. Por ejemplo:

require_once '_conexion.php';
$cliente = Cliente::find(1);
$cliente->nombre = 'Mariano';
$cliente->save();

 

Eliminar registros 

Para eliminar registros también debemos recuperar el registro como en el ejemplo anterior y eliminarlo con el método delete():

<?php
 require_once '_conexion.php';
 $cliente = Cliente::find(2);
 $cliente->delete();
?>

 

Relaciones en los modelos

Como todos saben, las tablas tienen relaciones entre sí, éstas puede ser de tres tipos:

Relaciones de 1 a 1

Estas se dan cuando un registro está ligado a otro. Por ejemplo, nosotros podemos tener un modelo llamado Usuario y un modelo llamado Cuenta. Cada usuario tendrá una cuenta y esa cuenta le pertenecerá a un usuario.

Relaciones de 1 a n

Son las relaciones más comunes. En este caso un registro puede tener relación con otros, pero cada uno de esos otros le pertenecerán sólo a ese registro. Por ejemplo, siguiendo con el modelo Usuario y un modelo Publicacion, cada usuario puede tener varias publicaciones, pero cada publicación le pertenecerá a un usuario.

Relaciones de n a n

Estas relaciones se dan cuando un registro puede tener relaciones con otros registros, pero a su vez estos registros además de tener relación con el primero pueden estar vinculados con otros. Por ejemplo, suponiendo que seguimos con el modelo Usuario, nosotros queremos saber qué bandas de música le gustan a cada usuario, así que también tendremos un modelo Banda. Entonces en ese caso cada a un usuario le pueden gustar varias bandas de música, pero estas bandas también le gustarán a varios usuarios. Las relaciones de n a n siempre tendrán una tabla intermedia y por ende también un modelo.

En nuestro caso nosotros tenemos un modelo Cliente y uno Pedido, y como dijimos antes, un cliente puede tener muchos pedidos y cada uno le pertenecerá a un cliente. Así que simplemente vamos a editar ambos modelos nuevamente y vamos a agregar las relaciones.

En Cliente:

static $has_many = array(
   array('pedido')
);

Y en Pedido:

static $belongs_to = array(
   array('cliente')
);

En ambos ejemplos para indicarle a nuestro modelo que tendrá muchos registros en otro debemos usar la propiedad $has_many (del inglés tiene muchos) y para indicar que cada registro de un modelo le pertenece a uno de otro modelo  $belongs_to (del inglés pertenece a)

En este enlace podemos ver todas las relaciones y sus parámetros opcionales:

http://www.phpactiverecord.org/projects/main/wiki/Associations#has_many

Entonces los modelos finalmente quedarán con las últimas modificaciones:

Cliente:

<?php
 class Cliente extends ActiveRecord\Model {
    public static $table_name = 'cliente';
    public static $primary_key = 'cliente_id';
    static $has_many = array(
       array('pedido')
    );
    public function before_create(){
       $this->fecha_creacion = date('Y-m-d H:i:s');
       $this->fecha_modificacion = date('Y-m-d H:i:s');
    }
    public function before_update(){
       $this->fecha_modificacion = date('Y-m-d H:i:s');
       }
    }
?>

Y Pedido:

<?php
 class Pedido extends ActiveRecord\Model {
    public static $table_name = 'pedido';
    public static $primary_key = 'pedido_id';
    static $belongs_to = array(
       array('cliente')
    );
    public function before_create(){
       $this->fecha_creacion = date('Y-m-d H:i:s');
       $this->fecha_modificacion = date('Y-m-d H:i:s');
    }
    public function before_update(){
       $this->fecha_modificacion = date('Y-m-d H:i:s');
    }
 }
?>

Ahora vamos a crear varios registros con el modelo Pedido:

require_once '_conexion.php';
   $pedido = Pedido::create(array(
      'asunto' => 'Asunto 1',
      'mensaje' => 'Lorem ipsum dolor sit amet.',
      'cliente_id' => 1
   ));
   $pedido = Pedido::create(array(
      'asunto' => 'Asunto 2',
      'mensaje' => 'Lorem ipsum dolor sit amet.',
      'cliente_id' => 1
   ));
   $pedido = Pedido::create(array(
      'asunto' => 'Asunto 3',
      'mensaje' => 'Lorem ipsum dolor sit amet.',
      'cliente_id' => 1
 ));

Y ahora vamos a buscar los registros que acabamos de crear pero desde el objeto padre, osea desde un cliente con el id 1:

<?php
 require_once '_conexion.php';
 $cliente = Cliente::find(1);
 $pedidos = $cliente->pedido;
 foreach($pedidos as $item){
    echo $item->asunto . '<br />';
 }
?>

Como se ve en el ejemplo al llamar a la propiedad $pedido recuperamos un array con los pedidos que le pertenece a ese cliente.

Redes sociables