En este apartado vamos a hablar de como integrar un ORM en el framework gvHIDRA. Actualmente, no se ha desarrollado una clase específica para uso de ORMs, pero es, como veremos en este apartado, sencilla su integración.
Entendemos un ORM (mapeo objeto-relacional) como un sistema o técnica de programación para convertir datos entre el sistema de tipos utilizado en un lenguaje de programación orientado a objetos y el utilizado en una base de datos relacional. En la práctica esto crea una base de datos orientada a objetos virtual, sobre la base de datos relacional. Esto posibilita el uso de las características propias de la orientación a objetos (básicamente herencia y polimorfismo).
Utilizar un ORM tiene una serie de ventajas que nos facilitan enormemente tareas comunes y de mantenimiento:
Reutilización: La principal ventaja que aporta un ORM es la reutilización permitiendo llamar a los métodos de un objeto de datos desde distintas partes de la aplicación e incluso desde diferentes aplicaciones.
Encapsulación: La capa ORM encapsula la lógica de los datos pudiendo hacer cambios que afectan a toda la aplicación únicamente modificando una función.
Portabilidad: Utilizar una capa de abstracción nos permite cambiar en mitad de un proyecto de una base de datos MySQL a una Oracle sin ningún tipo de complicación. Esto es debido a que no utilizamos una sintaxis MySQL, Oracle o SQLite para acceder a nuestro modelo, sino una sintaxis propia del ORM utilizado que es capaz de traducir a diferentes tipos de bases de datos.
Seguridad: Los ORM suelen implementar mecanismos de seguridad que protegen nuestra aplicación de los ataques más comunes como SQL Injections.
Mantenimiento del código: Gracias a la correcta ordenación de la capa de datos, modificar y mantener nuestro código es una tarea sencilla.
Actualmente existen muchas soluciones ORM con PHP en el mercado, tanto libres como propietario, aunque para este ejemplo hemos optado por un enfoque Open-Source con el objetivo de seguir con la misma dirección que gvHidra. Como se ha indicado anteriormente, cualquier otra solución sería válida.
Propel es un ORM basado en PHP. Mediante el mapeo de una BBDD en XML se generan un conjunto de clases para su manejo, simplificando de este modo el acceso a los datos, dando una solución CRUD.
Según indica la propia documentación oficial de Propel, funciona con los siguientes SGBD’s:
PostgreSQL
MySQL
Oracle
Sql Server
Sqlite
El trabajo con las clases generadas es cómodo e intuitivo siempre que se tengan nociones de desarrollo con objetos.En un principio aunque la curva de aprendizaje puede parecer mas compleja de lo esperado, por las pruebas realizadas el resultado mediante este ORM es rápido y eficaz. Realmente no cuesta un tiempo excesivo hacer una consulta o inserción en las tablas de la BBDD. Las opciones que nos brinda este ORM son muchas y variadas; van desde realizar consultas sin necesidad de utilizar SQL, hasta controlar la consulta que se va a realizar contra la BD introduciendo una sentencia SQL mas compleja.
El tratamiento de los datos devueltos en las consultas también es un punto importante, ya que se pueden recupera un solo elemento de una consulta, o en caso de necesitarlo, un array de objetos que contienen esa información. Eso significa que de una sola consulta, el ORM nos permite recuperar un conjunto finito de resultados, un conjunto finito de objetos de una clase.
Propel nos da una enorme variedad de opciones, estas nos cubrirán en la mayoría de los casos aquellas funciones o necesidades que tengamos. Gracias a la herencia, si alguno de los casos no estuviera cubierto como debería, entonces tenemos la opción de crearnos nuestra propia clase de gestión de los datos, con las mejoras que consideremos oportunas. Recordemos que disponemos de todas las ventajas de la programación orientada a objetos, como la sobrecarga o la herencia.
También nos da una gran variedad de operaciones, aparte de las básicas de CRUD. A continuación se detallan algunas de las más importantes:
Inserción de filas relacionadas
Guardado en cascada
Uso de las relaciones en una consulta
Búsqueda de registros relacionados con otro
Consultas embebidas
Relaciones uno a uno
Relaciones muchos a muchos
Reducir al mínimo las consultas
Validaciones
Igual que
No igual que
Tamaño máximo
Tamaño mínimo
Tipo
Valor válido
Personalizadas
Transacciones: Las transacciones de bases de datos son la clave para asegurar la integridad de los datos y el rendimiento de las consultas de base de datos.
Herencia
Igual que
No igual que
La instalación se puede hacer mediante PEAR, es importante tener en cuenta que tendremos que haber instalado previamente también Phing, una vez hecho esto, se accede a los canales correspondientes y se instalan los paquetes de Propel.
La fase previa al uso de Propel en la cual se definen las estructuras de datos y las clases manejadoras, se puede abordar de dos modos:
Se generan a través de funciones nativas de Propel y mas concretamente “propel-gen”, una serie de clases manejadoras de la BBDD, tanto las clases base, como unas clases que heredan de estas primeras
Tendremos entonces que generar el SQL para poder crear las tablas en la BBDD, este proceso también es automatizado mediante la herramienta “propel-gen”, que nos generará un fichero SQL conteniendo toda la estructura de la BBDD, este fichero lo usaremos para crear en la BBDD las tablas correspondientes.
La instalación se puede hacer mediante PEAR, es importante tener en cuenta que tendremos que haber instalado previamente también Phing, una vez hecho esto, se accede a los canales correspondientes y se instalan los paquetes de propel.
Pasos para realizar esta instalación:
Se debe agregar el canal pear.propelorm.org al entorno de PEAR.
Una vez que tenemos el canal, puede instalar el paquete del generador, o el paquete de tiempo de ejecución, o ambas cosas, si es una primera instalación, preferentemente instalar ambas cosas.
Es importante hacer uso de la opción '-a' para permitir que PEAR pueda descargar e instalar las dependencias.
pear channel-discover pear.propelorm.org pear install -a propel/propel_generator pear install -a propel/propel_runtime
El generador de Propel utiliza Phing 2.3.3, si no esta instalado, se debe instalar.
Pasos para realizar esta instalación:
Se debe agregar el canal pear.phing.org al entorno de PEAR.
Posteriormente instalamos el Phing y el Log.
pear channel-discover pear.phing.info pear install phing/phing pear install Log
Una vez realizadas las instalaciones, simplemente tendremos que hacer uso de “propel-gen” para comprobar que todo ha funcionado correctamente, aunque el proceso no realice ninguna función, ya que no habremos creado los ficheros necesarios para ello.
Podemos recuperar la información, los datos, de la base de datos y prepararla en un fichero XML, para ello Propel tiene herramientas que nos lo permitirán, generándonos un fichero XML que contiene todos estos datos.
Posteriormente se explicaran los pasos previos a seguir para hacer uso de Propel. Antes de poder comenzar a usar Propel, tendremos que preparar la capa de comunicación con nuestra BBDD para lo cual nos vamos a encontrar con dos situaciones:
CASO 1: Partimos de una BBDD en formato XML.
Se generan a través de funciones nativas de Propel y mas concretamente “propel-gen”, una serie de clases manejadoras de la BBDD, tanto las clases base, como unas clases que heredan de estas primeras.
Tendremos entonces que generar el SQL para poder crear las tablas en la BBDD, este proceso también es automatizado mediante la herramienta “propel-gen”, que nos generará un fichero SQL conteniendo toda la estructura de la BBDD, este fichero lo usaremos para crear en la BBDD las tablas correspondientes.
CASO 2: Partimos de una BBDD ya creada, usamos ingeniería inversa.
Obtendremos el XML con toda la estructura de las tablas implicadas.
a.Realizamos entonces los mismos pasos que en el primer caso, para poder generar la estructura de las clases manejadoras.
Se detalla a nivel técnico cada uno de los dos casos, con su comportamiento correspondiente.
CASO 1: BBDD en formato XML
Crearemos un nuevo fichero llamado schema.xml que contendrá la estructura de nuestra BBDD siguiendo el siguiente esqueleto:
<?xml version="1.0" encoding="UTF-8"?> <database name="" defaultIdMethod="native"> <table name="" phpName=""> <!-- column and foreign key definitions go here --> <column name="" type="integer" required="true" primaryKey="true" autoIncrement="true"/> ... <foreign-key foreignTable="" phpName="" refPhpName=""> <reference local="" foreign=""/> </foreign-key> … </table> </database>
En este fichero XML vamos a incluir toda la estructura de la BBDD con la que queremos trabajar, las columnas y las claves implicadas. Estos datos serán los que emplearemos para crear las tablas en nuestro SGBD.
Para la construcción final del modelo, necesitamos tanteo el recién creado schema.xml como un nuevo fichero build.properties que contendrá la información de la configuración de la BBDD.
# Driver de la BBDD propel.database = pgsql|mysql|sqlite|mssql|oracle # Nombre del proyecto propel.project = <…>
Se ha de tener en cuenta que para el correcto funcionamiento, el nombre del proyecto debe ser el mismo que el de la BBDD.
Hemos de dejar estos dos ficheros schema.xml y build.properties en el mismo nivel dentro del subdirectorio del proyecto, solo nos queda hacer la llamada a Propel para que nos genere las clases correspondientes, esto es pasándole el parametro “om”, Object Model generador.
Propel-gen om
Por cada tabla en la BBDD, Propel crea tres clases de PHP:
Una clase modelo, lo que representa una fila en la base de datos.
Una clase Peer, ofreciendo constantes estáticas y métodos sobre todo por compatibilidad con versiones anteriores de Propel.
Una clase Consulta que sirve para trabajar sobre una tabla para poder recuperar y actualizar filas.
Propel además también puede generarnos el fichero con el código SQL completo de nuestras tablas, para ello, el parámetro de llamada debe ser “sql”, no es obligatorio para el funcionamiento de Propel pero puede ser interesante para tener el modelo de datos y poder llevarlo a otro SGBD si lo consideramos necesario.
Ajustes de Conexión del Runtime. Ahora se debe agregar un archivo de configuración para que las clases de objetos generadas y el runtime de Propel se puedan conectar a la base de datos, y registrar la actividad de Propel. Para ello crearemos un fichero con el nombre runtime-conf.xml que mantendrá el siguiente esqueleto, el ejemplo define una conexión a MySQL:
<?xml version=”1.0” encoding=”UTF-8”?> <config> <!—Uncomment this if you have PEAR Log installed <log> <type>file</type> <name>/path/to/propel.log</name> <ident>propel-bookstore</ident> <level>7</level> </log> <propel> <datasources default=””> <datasource id=””> <adapter>mysql</adapter> <connection> <dsn>mysql:host=localhost;dbname=</dsn> <user></user> <password></password> </connection> </datasource> </datasources> </propel> </config>
Ahora creamos la configuración para el Runtime mediante la llamada a
propel-gen convert-conf.
Esto nos genera en el directorio \conf de nuestra aplicación el siguiente fichero
<nombre >-conf.php
Donde <nombre> es el nombre del proyecto definido en el fichero build.properties
CASO 2: BBDD ya creada en un SGBD, mediante ingeniería inversa.
Partimos de un punto mas próximo a la realidad como es la incorporación de un ORM a un entorno ya existente, por lo que evidentemente tenemos también la BBDD creada y funcionando. El proceso es bastante similar, solo difiere algunos puntos.
En primer lugar hemos de generar el fichero build.properties el cual tendrá el siguiente código dentro
propel.project = <nombre> # The Propel driver to use for generating SQL, etc. propel.database = <driver> # This must be a PDO DSN propel.database.url = <driver>:dbname=<nombre> propel.database.user = <user> # propel.database.password =<pwd>
Una vez tenemos el fichero build.properties se hace la llamada a Propel para que nos genere el fichero XML con la estructura de la BBDD esta vez haremos uso del parámetro “reverse”.
propel-gen reverse
Con esto obtendremos un fichero schema.xml que contendrá la especificación de la BBDD en XML.
Ahora como en el caso 1, nos queda hacer la llamada a Propel para que nos genere las clases correspondientes, esto es pasandole el parámetro “om”, Object Model generador.
propel-gen om
Recordemos que por cada tabla en la BBDD, Propel crea tres clases de PHP:
una clase modelo.
una clase Peer.
una clase Consulta.
Llegados a este punto, los pasos a seguir son los mismos que las que tenemos en el caso anterior, a partir del punto 1.d.
Llegados a este punto, ya tenemos la estructura de clases para trabajar con Propel en nuestra aplicación, ahora tendremos que incluir la llamada correspondiente.
Mediante esta llamada se incluye la librería de propel, y además inicializamos las clases particulares para que nuestra aplicación haga uso de ella.
// Include the main Propel script require_once 'propel/Propel.php'; // Initialize Propel with the runtime configuration Propel::init("/<proyecto>/build/conf/bookstore-conf.php"); // Add the generated 'classes' directory to the include path set_include_path("/<proyecto>//build/classes" . PATH_SEPARATOR . get_include_path());
Vamos ha realizar un ejemplo.
Disponemos de una tabla, TrecjatReclamante, que tiene la siguiente descripción:
CREATE TABLE trecjat_reclamante ( nif character varying(15) NOT NULL, nombre character varying(25), apellido1 character varying(50), apellido2 character varying(50), direccion character varying(100), poblacion character varying(100), provincia character varying(100), cp character varying(10), email character varying(25), fax character varying(10), telefono character varying(10), movil character varying(10), nombreempresa character varying(50), pais character varying(30) DEFAULT 'España'::character varying, CONSTRAINT pk_reclamante PRIMARY KEY (nif) ) WITH (OIDS=TRUE); ALTER TABLE trecjat_reclamante OWNER TO recjat; GRANT ALL ON TABLE trecjat_reclamante TO recjat; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE trecjat_reclamante TO rrecjat;
Partiendo de esta descripción de la tabla, Propel genera las siguientes clases (sólo mostramos las mas relevantes):
dbgvhidra/build/classes/dbgvhidra/TrecjatReclamante.php<?php /** * Skeleton subclass for representing a row from the 'trecjat_reclamante' table. * * * * You should add additional methods to this class to meet the * application requirements. This class will only be generated as * long as it does not already exist in the output directory. * * @package propel.generator.dbgvhidra */ class TrecjatReclamante extends BaseTrecjatReclamante { } // TrecjatReclamante
<?php /** * Skeleton subclass for performing query and update operations on the 'trecjat_reclamante' table. * * * * You should add additional methods to this class to meet the * application requirements. This class will only be generated as * long as it does not already exist in the output directory. * * @package propel.generator.dbgvhidra */ class TrecjatReclamanteQuery extends BaseTrecjatReclamanteQuery { } // TrecjatReclamanteQuery
<?php /** * Base class that represents a row from the 'trecjat_reclamante' table. * * * * @package propel.generator.dbgvhidra.om */ abstract class BaseTrecjatReclamante extends BaseObject implements Persistent { /** * Peer class name */ const PEER = 'TrecjatReclamantePeer'; /** * The Peer class. * Instance provides a convenient way of calling static methods on a class * that calling code may not be able to identify. * @var TrecjatReclamantePeer */ protected static $peer; /** * The value for the nif field. * @var string */ protected $nif; /** * The value for the nombre field. * @var string */ protected $nombre; /** * The value for the apellido1 field. * @var string */ protected $apellido1; /** * The value for the apellido2 field. * @var string */ protected $apellido2; /** * The value for the direccion field. * @var string */ protected $direccion; /** * The value for the poblacion field. * @var string */ protected $poblacion; ... /** * Flag to prevent endless save loop, if this object is referenced * by another object which falls in this transaction. * @var boolean */ protected $alreadyInSave = false; /** * Flag to prevent endless validation loop, if this object is referenced * by another object which falls in this transaction. * @var boolean */ protected $alreadyInValidation = false; /** * Applies default values to this object. * This method should be called from the object's constructor (or * equivalent initialization method). * @see __construct() */ public function applyDefaultValues() { $this->pais = 'España'; } /** * Initializes internal state of BaseTrecjatReclamante object. * @see applyDefaults() */ public function __construct() { parent::__construct(); $this->applyDefaultValues(); } /*Setters AND Getters*/ ... /*END Setters AND Getters*/ /** * Indicates whether the columns in this object are only set to default values. * * This method can be used in conjunction with isModified() to indicate whether an object is both * modified _and_ has some values set which are non-default. * * @return boolean Whether the columns in this object are only been set with default values. */ public function hasOnlyDefaultValues() { if ($this->pais !== 'España') { return false; } // otherwise, everything was equal, so return TRUE return true; } // hasOnlyDefaultValues() /** * Hydrates (populates) the object variables with values from the database resultset. * * An offset (0-based "start column") is specified so that objects can be hydrated * with a subset of the columns in the resultset rows. This is needed, for example, * for results of JOIN queries where the resultset row includes columns from two or * more tables. * * @param array $row The row returned by PDOStatement->fetch(PDO::FETCH_NUM) * @param int $startcol 0-based offset column which indicates which restultset column to start with. * @param boolean $rehydrate Whether this object is being re-hydrated from the database. * @return int next starting column * @throws PropelException - Any caught Exception will be rewrapped as a PropelException. */ public function hydrate($row, $startcol = 0, $rehydrate = false) { try { $this->nif = ($row[$startcol + 0] !== null) ? (string) $row[$startcol + 0] : null; $this->nombre = ($row[$startcol + 1] !== null) ? (string) $row[$startcol + 1] : null; $this->apellido1 = ($row[$startcol + 2] !== null) ? (string) $row[$startcol + 2] : null; $this->apellido2 = ($row[$startcol + 3] !== null) ? (string) $row[$startcol + 3] : null; $this->direccion = ($row[$startcol + 4] !== null) ? (string) $row[$startcol + 4] : null; $this->poblacion = ($row[$startcol + 5] !== null) ? (string) $row[$startcol + 5] : null; $this->provincia = ($row[$startcol + 6] !== null) ? (string) $row[$startcol + 6] : null; $this->cp = ($row[$startcol + 7] !== null) ? (string) $row[$startcol + 7] : null; $this->email = ($row[$startcol + 8] !== null) ? (string) $row[$startcol + 8] : null; $this->fax = ($row[$startcol + 9] !== null) ? (string) $row[$startcol + 9] : null; $this->telefono = ($row[$startcol + 10] !== null) ? (string) $row[$startcol + 10] : null; $this->movil = ($row[$startcol + 11] !== null) ? (string) $row[$startcol + 11] : null; $this->nombreempresa = ($row[$startcol + 12] !== null) ? (string) $row[$startcol + 12] : null; $this->pais = ($row[$startcol + 13] !== null) ? (string) $row[$startcol + 13] : null; $this->resetModified(); $this->setNew(false); if ($rehydrate) { $this->ensureConsistency(); } return $startcol + 14; // 14 = TrecjatReclamantePeer::NUM_COLUMNS - TrecjatReclamantePeer::NUM_LAZY_LOAD_COLUMNS). } catch (Exception $e) { throw new PropelException("Error populating TrecjatReclamante object", $e); } } /** * Checks and repairs the internal consistency of the object. * * This method is executed after an already-instantiated object is re-hydrated * from the database. It exists to check any foreign keys to make sure that * the objects related to the current object are correct based on foreign key. * * You can override this method in the stub class, but you should always invoke * the base method from the overridden method (i.e. parent::ensureConsistency()), * in case your model changes. * * @throws PropelException */ public function ensureConsistency() { } // ensureConsistency /** * Reloads this object from datastore based on primary key and (optionally) resets all associated objects. * * This will only work if the object has been saved and has a valid primary key set. * * @param boolean $deep (optional) Whether to also de-associated any related objects. * @param PropelPDO $con (optional) The PropelPDO connection to use. * @return void * @throws PropelException - if this object is deleted, unsaved or doesn't have pk match in db */ public function reload($deep = false, PropelPDO $con = null) { if ($this->isDeleted()) { throw new PropelException("Cannot reload a deleted object."); } if ($this->isNew()) { throw new PropelException("Cannot reload an unsaved object."); } if ($con === null) { $con = Propel::getConnection(TrecjatReclamantePeer::DATABASE_NAME, Propel::CONNECTION_READ); } // We don't need to alter the object instance pool; we're just modifying this instance // already in the pool. $stmt = TrecjatReclamantePeer::doSelectStmt($this->buildPkeyCriteria(), $con); $row = $stmt->fetch(PDO::FETCH_NUM); $stmt->closeCursor(); if (!$row) { throw new PropelException('Cannot find matching row in the database to reload object values.'); } $this->hydrate($row, 0, true); // rehydrate if ($deep) { // also de-associate any related objects? } // if (deep) } /** * Removes this object from datastore and sets delete attribute. * * @param PropelPDO $con * @return void * @throws PropelException * @see BaseObject::setDeleted() * @see BaseObject::isDeleted() */ public function delete(PropelPDO $con = null) { if ($this->isDeleted()) { throw new PropelException("This object has already been deleted."); } if ($con === null) { $con = Propel::getConnection(TrecjatReclamantePeer::DATABASE_NAME, Propel::CONNECTION_WRITE); } $con->beginTransaction(); try { $ret = $this->preDelete($con); if ($ret) { TrecjatReclamanteQuery::create() ->filterByPrimaryKey($this->getPrimaryKey()) ->delete($con); $this->postDelete($con); $con->commit(); $this->setDeleted(true); } else { $con->commit(); } } catch (PropelException $e) { $con->rollBack(); throw $e; } } /** * Persists this object to the database. * * If the object is new, it inserts it; otherwise an update is performed. * All modified related objects will also be persisted in the doSave() * method. This method wraps all precipitate database operations in a * single transaction. * * @param PropelPDO $con * @return int The number of rows affected by this insert/update and any referring fk objects' save() operations. * @throws PropelException * @see doSave() */ public function save(PropelPDO $con = null) { if ($this->isDeleted()) { throw new PropelException("You cannot save an object that has been deleted."); } if ($con === null) { $con = Propel::getConnection(TrecjatReclamantePeer::DATABASE_NAME, Propel::CONNECTION_WRITE); } $con->beginTransaction(); $isInsert = $this->isNew(); try { $ret = $this->preSave($con); if ($isInsert) { $ret = $ret AND $this->preInsert($con); } else { $ret = $ret AND $this->preUpdate($con); } if ($ret) { $affectedRows = $this->doSave($con); if ($isInsert) { $this->postInsert($con); } else { $this->postUpdate($con); } $this->postSave($con); TrecjatReclamantePeer::addInstanceToPool($this); } else { $affectedRows = 0; } $con->commit(); return $affectedRows; } catch (PropelException $e) { $con->rollBack(); throw $e; } } ... } // BaseTrecjatReclamante
Ahora vamos a ver como enganchar la clase que implementamos en gvHidra, gesReclamante, con la generada por el Propel, TRectjatReclamante:
<?php /* ORM */ // Include the main Propel script require_once 'C:\wamp\bin\php\php5.2.6\PEAR\propel\Propel.php'; // Initialize Propel with the runtime configuration Propel::init("C:\wamp\www\dbgvhidra\build\conf\dbgvhidra-conf.php"); // Add the generated 'classes' directory to the include path set_include_path("C:\wamp\www\dbgvhidra\build\classes" . PATH_SEPARATOR . get_include_path()); class gesReclamante extends gvHidraForm { public function __construct() { parent::__construct(); }//Fin de constructor public function preBuscar($objDatos) { return 0; } public function postBuscar($objDatos) { $a_reclamantes = TrecjatReclamanteQuery::create()->find(); $res = array(); foreach($a_reclamantes as $reclamante) { $row = array(); $row['NIF'] = $reclamante->getNif(); $row['Nombre'] = $reclamante->getNombre(); $row['Apellido1'] = $reclamante->getApellido1(); $row['Apellido2'] = $reclamante->getApellido2(); array_push($res,$row); } $objDatos->setAllTuplas($res); return 0; } public function preModificar($objDatos) { $data = $objDatos->getAllTuplas(); if(count($data[0])>0) { foreach($data as $row) { $reclamante = TrecjatReclamanteQuery::create()->findPK($row['NIF']); $reclamante->setNombre($row['Nombre']); $reclamante->setApellido1($row['Apellido1']); $reclamante->setApellido2($row['Apellido2']); $reclamante->setProvincia($row['Provincia']); $reclamante->save(); } } return 0; } public function preBorrar($objDatos) { $data = $objDatos->getAllTuplas(); if(count($data[0])>0) { foreach($data as $row) $reclamantes = TrecjatReclamanteQuery::create()->findPK($row['NIF']); $reclamantes -> delete(); } return 0; } public function preEditar($objDatos) { $this->dataSelected = $objDatos->getAllTuplas(); return 0; } public function postEditar($objDatos) { $data = $this->dataSelected; $res = array(); if(count($data[0])>0) { foreach($data as $row) { $reclamante = TrecjatReclamanteQuery::create()->findPK($row['NIF']); $row = array(); $row['NIF'] = $reclamante->getNif(); $row['Nombre'] = $reclamante->getNombre(); $row['Apellido1'] = $reclamante->getApellido1(); $row['Apellido2'] = $reclamante->getApellido2(); $row['Provincia'] = $reclamante->getProvincia(); array_push($res,$row); } } $objDatos->setAllTuplas($res); return 0; } public function preInsertar($objDatos) { $data = $objDatos->getAllTuplas(); if(count($data[0])>0) { foreach($data as $row) { $reclamante = new TrecjatReclamante(); $reclamante->setNif($row['NIF']); $reclamante->setNombre($row['Nombre']); $reclamante->setApellido1($row['Apellido1']); $reclamante->setApellido2($row['Apellido2']); $reclamante->setProvincia($row['Provincia']); $reclamante->save(); } } return 0; } } ?>