Propel

RECU-0259 (Recurso Referencia)

Descripción

Propel es un librería de Mapeo Objeto-Relacional (ORM) de código abierto para PHP5. Le permite acceder a su base de datos mediante un conjunto de objetos, proporcionando una API sencilla para almacenar y recuperar datos. Propel ofrece al desarrollador de aplicaciones web, las herramientas para trabajar con bases de datos de la misma manera se trabaja con otras clases y objetos en PHP.

  • Propel le da a su base de datos de una API bien definida.
  • Propel utiliza los estándares PHP5 OO - Excepciones, carga automática, iteradores y amigos.

Características

A continuación vamos a resumir los principales aspectos funcionales del mapeo ORM utilizando Propel como proveedor del mismo.

Uso de validadores

El uso de validadores permitirá validar una entrada antes de realizar la persistencia a la base de datos. En Propel, validadores son reglas que describen qué tipo de datos son aceptados por una columna. Los validadores se referencian en el archivo schema.xml, utilizando etiquetas <validator>.

Los validadores se aplican en el nivel de PHP, no se crean limitaciones a la propia base de datos. Esto significa que si usted también utiliza otro idioma para trabajar con la base de datos, las reglas de validación no se harán cumplir. También puede aplicar varias entradas por la regla de validación de entrada en el archivo schema.xml.

<table name="user">
  <column name="id" type="INTEGER" primaryKey="true" autoIncrement="true"/>
  <column name="username" type="VARCHAR" size="34" required="true" />
  <validator column="username">
    <rule name="minLength"  value="4"  message="Username must be at least ${value} characters !" />
  </validator>
</table>

En tiempo de ejecución se puede validar una instancia del modelo llamando al método validate() como en el ejemplo siguiente:

<?php
$user = new User();
$user->setUsername("foo"); // only 3 in length, which is too short...
if ($objUser->validate()) {
  // no validation errors, so the data can be persisted
  $user->save();
} else {
  // Something went wrong.
  // Use the validationFailures to check what
  $failures = $objUser->getValidationFailures();
  foreach($failures as $failure) {
    echo $objValidationFailure->getMessage() . "<br />\n";
  }
}

Los validadores que Propel proporciona son los siguientes:

  • MatchValidator, comprueba que se cumple la expresión regular
  • NotMatchValidator, comprueba que no se cumple con la expresión regular
  • MaxLengthValidator, comprueba que no se supera el máximo tamaño de una cadena que se inserta en una columna
  • MinLengthValidator, comprueba que no se supera el máximo tamaño de una cadena que se inserta en una columna
  • MaxValueValidator, comprobar que no se supera un máximo
  • MinValueValidator, comprobar que no se supera un mínimo
  • RequiredValidator, declara el atributo como obligatorio
  • UniqueValidator, comprueba que el valor existe en la tabla
  • ValidValuesValidator,
  • TypeValidator, comprueba que el tipo es aceptado por PHP

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. Propel utiliza internamente las transacciones, y proporciona un API simple para utilizarlos en su propio código. Propel usa PDO como capa de abstracción al acceso a la base de datos. Un ejemplo sería el siguiente:

<?php
public function transferMoney($fromAccountNumber, $toAccountNumber, $amount)
{
  // get the PDO connection object from Propel
  $con = Propel::getConnection(AccountPeer::DATABASE_NAME);

  $fromAccount = AccountPeer::retrieveByPk($fromAccountNumber, $con);
  $toAccount   = AccountPeer::retrieveByPk($toAccountNumber, $con);

  $con->beginTransaction();

  try {
    // remove the amount from $fromAccount
    $fromAccount->setValue($fromAccount->getValue() - $amount);
    $fromAccount->save($con);
    // add the amount to $toAccount
    $toAccount->setValue($toAccount->getValue() + $amount);
    $toAccount->save($con);

    $con->commit();
  } catch (Exception $e) {
    $con->rollback();
    throw $e;
  }
}

Las declaraciones de transacción se realizan mediante los métodos BeginTransaction(), commit() y el rollback(), que son métodos del objeto de conexión PDO. Los métodos de transacciones se utilizan típicamente dentro de un bloque try/catch. La excepción es relanzada después de hacer retroceder la transacción: Eso asegura que el usuario sabe que algo malo pasa.

En este ejemplo, si sucede algo erróneo es lanzada una excepción, y toda la operación se revierte. Esto significa que la transferencia es cancelada, asegurando que el dinero no ha desaparecido . Si ambas modificaciones en cuenta trabajan como se esperaba, toda la transacción se ha completado, lo que significa que los cambios de datos adjuntos en la transacción se conservan en la base de datos.

Comportamientos

Los comportamientos son una buena de formar paquetes de extensiones para el modelo de reutilización. Ellos son poderosos, versátiles, rápidos, y le ayudará a organizar el código de una mejor manera. Existen varios tipos de comportamientos:

  • Los comportamientos pueden modificar su tabla, e incluso añadir otra mesa, mediante la aplicación del método modifyTable. En este método, utiliza $ this-> getTable () para recuperar el modelo de la tabla en tiempo de compilación y manipularla.
<?php
// default parameters value
protected $parameters = array(
  'column_name' => 'foo',
);

public function modifyTable()
{
  $table = $this->getTable();
  $columnName = $this->getParameter('column_name');
  // add the column if not present
  if(!$this->getTable()->containsColumn($columnName)) {
    $column = $this->getTable()->addColumn(array(
      'name'    => $columnName,
      'type'    => 'INTEGER',
    ));
  }
}
  • Comportamientos que pueden agregar código al modelo de objetos generados por la aplicación de uno de los siguientes métodos:
objectAttributes()     // añade atributos al objeto
objectMethods()        // añade métodos al objeto
preInsert()            // añade código que se ejecuta antes de la inserción de un nuevo objeto
postInsert()           // añade código que se ejecuta después de la inserción de un nuevo objeto
preUpdate()            // añade código que se ejecuta antes de actualizar un objeto
postUpdate()           // añade código que se ejecuta después de actualizar un objeto
preSave()              // añade código que se ejecuta antes de salvar un objeto
postSave()             // añade código que se ejecuta después de salvar un objeto
preDelete()            // añade código que se ejecuta antes del borrado de un objeto
postDelete()           // añade código que se ejecuta después del borrado de un objeto
objectCall()           // añade código que se ejecuta inside the object's __call()
objectFilter(&$script) // hace lo que quieras con el código generado pasado por referencia
  • Comportamientos que pueden añadir código a los objetos generados de consultas implementando alguno de estos métodos
queryAttributes()     // añade atributos a la clase Query
queryMethods()        // añade métodos a la clase Query
preSelectQuery()      // añade código que se ejecuta antes de seleccionar un objeto
preUpdateQuery()      // añade código que se ejecuta antes de actualizar un objeto
postUpdateQuery()     // añade código que se ejecuta después de actualizar un objeto
preDeleteQuery()      // añade código que se ejecuta antes del borrado de un objeto
postDeleteQuery()     // añade código que se ejecuta después del borrado de un objeto
queryFilter(&$script) // hace lo que quieras con el código generado pasado por referencia
  • Comportamientos que pueden agregar código a los objetos por pares generados por la aplicación de uno de los siguientes métodos:
staticAttributes()   // añade atributos estáticos a la clase
staticMethods()      // añade métodos estáticos
preSelect()          // añade código antes de cualquier SELECT
peerFilter(&$script) // hace lo que quieras con el código generado pasado por referencia

Log y motor de depuración

Propel proporciona herramientas para controlar y depurar el modelo. Si usted necesita comprobar el código SQL de consultas lentas, o buscar mensajes de error previamente lanzados, Propel facilita la tarea de encontrar y corregir problemas. Propel utiliza la facilidad de logging configurada en runtime-conf.xml para almacenar errores, avisos e información de depuración. El manejador de log se configura en la sección <log> del fichero runtime-conf.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<config>
  <log>
    <type>file</type>
    <name>./propel.log</name>
    <ident>propel</ident>
    <level>7</level> <!-- PEAR_LOG_DEBUG -->
    <conf></conf>
  </log>
  <propel>
    ...
  </propel>
</config>

Los niveles de prioridad que utiliza el log

LogNivelDescripción
PEAR_LOG_EMERG0System is unusable
PEAR_LOG_ALERT1Immediate action required
PEAR_LOG_CRIT2Critical conditions
PEAR_LOG_ERR3Error conditions
PEAR_LOG_WARNING4Warning conditions
PEAR_LOG_NOTICE5Normal but significant
PEAR_LOG_INFO6Informational
PEAR_LOG_DEBUG7Debug-level messages

Ejemplos

A continuación un ejemplo de uso de sentencias de consulta en la nueva API de Propel

/* 
 * Retrieving an article by its primary key
 */ 
$article = ArticleQuery::create()->findPk(123); 

/*
 * Retrieving the comments related to an article
 */ 

$comments = $article->getComments(); // no change 

/*
* Retrieving an article from its title
*/ 
$article = ArticleQuery::create()->findOneByTitle('FooBar'); 
 
/*
* Retrieving articles based on a word appearing in the title
*/ 

$article = ArticleQuery::create() 
 ->filterByTitle('%FooBar%') 
 ->find(); 
/*
* Retrieving articles where the publication date is between last week and today
*/ 
$article = ArticleQuery::create()
  ->filterByPublishedAt(array( 
    'min' => time() - (7 * 24 * 60 * 60),  
    'max' => time(), 
  )) 
  ->find(); 
 
/*
* Retrieving articles based on a custom condition
*/ 

$article = ArticleQuery::create() 
  ->where('UPPER(Article.Title) like ?', '%FooBar%') // binding made by PDO, no injection risk 
  find(); 
   
/*
* Retrieving articles based on a word appearing in the title or the summary
*/ 

$article = ArticleQuery::create() 
  ->where('Article.Title like ?', '%FooBar%') 
  ->orWhere('Article.Summary like ?', '%FooBar%') 
  find(); 
 
/*
* Retrieving articles based on a complex AND/OR clause
* Articles having name or summary like %FooBar% and published between $begin and $end
 */ 

$articles = ArticleQuery::create() 
      ->condition('cond1', 'Title like ?', '%FooBar%') 
      ->condition('cond2', 'Summary' like ?', '%FooBar%') 
      ->combine(array('cond1', 'cond2'), 'or', 'cond3') 
      ->condition('cond4', 'PublishedAt > ?', $begin) 
      ->condition('cond5', 'PublishedAt < ?', $end) 
      ->combine(array('cond4', 'cond5'), 'and', 'cond6') 
      ->combine(array('cond3', 'cond6'), 'and') 
      ->find(); 

/*
* Retrieving the latest 5 articles
*/ 

$articles = ArticleQuery::create() 
  ->orderByPublishedAt('desc') 
  ->limit(5) 
  ->find(); 
/*
* Retrieving the last comment related to an article
*/ 

$comment = CommentQuery::create() 
  ->filterByArticle($article) 
  ->orderByPublishedAt('desc') 
  ->findOne(); 
   
 /*
* Retrieving articles authored by someone
*/ 
 

$articles = ArticleQuery::create() 
  ->useAuthorQuery() 
  ->filterByName('John Doe') 
  ->endUse() 
  ->find() 
 
 
/*
* Retrieving all articles and hydrating their category object in the same query
*/ 

$articles = ArticleQuery::create() 
  ->joinWith('Category') 
  ->find(); 
/*
* Retrieving an article and its category by the article primary key
*/ 

$articles = ArticleQuery::create() 
  ->joinWith('Category') 
  ->findPk(123);

Enlaces externos