Navegando por la web de Symfony en busca de un plugin que implementase las operaciones de un "Carrito de la compra" y que fuese compatible con la versión 1.4 y el ORM Doctrine, no encontré nada interesante. Sin embargo, vi el plugin sfShoppingCartPlugin creado por Fabien Potencier para la versión 1.0 de Symfony y Propel.
Tras echarle un ojo, vi que implementa basicamente lo que necesito para mi carro de la compra y que solo requería de algunas modificaciones para hacerlo compatible con Doctrine, por lo que decidí hacer uso de él.
El plugin en si consta solamente con dos clases:
Básicamente, el uso es el siguiente:
<?php
// Se crea el objeto de la cesta
$carrito = new sfShoppingCart(sfConfig::get('app_tax'));
// Se añade un nuevo Articulo al carrito
$article = ArticlesTable::getInstance()->find($request->getParameter('id'));
$articleItem = new sfShoppingCartItem(get_class($article), $article->getId());
$articleItem->setPrice($article->getPrice());
$articleItem->setQuantity($request->getParameter('quantity'));
$carrito->addItem($articleItem);
// Recorrer el carrito y ver el precio total
foreach ($carrito->getItems() as $item) {
echo sprintf(
'%d: %f x %d = %f',
$item->getId(),
$item->getPrice(),
$item->getQuantity(),
($item->getPrice() * $item->getQuantity())
);
}
echo 'Total: ' . $carrito->getTotal();
Además de estas operaciones la clase sfShoppingCart incluye dos operaciones que devuelven el objeto Item del modelo:
sfShoppingCart::getObjects()
sfShoppingCart::getObject($class, $id)
Estos métodos hacen uso de Propel para obtener el objeto de la clase $class y con id $id.
Como lo que necesito es hacer uso de Doctrine, nada mejor que extender sfShoppingCart a sfDoctrineShoppingCart y sobreescribir ambos métodos:
public function getObject($class, $id)
{
// We must first make sure that the requested object does exist in the shopping cart
$ind = $this->getItemIndice($class, $id);
return (($ind !== null) ? call_user_func(array($class.'Table', 'find'), $id) : null);
}
public function getObjects()
{
$object_ids = array();
foreach ($this->getItems() as $item)
{
if (!array_key_exists($item->getClass(), $object_ids))
{
$object_ids[$item->getClass()] = array();
}
$object_ids[$item->getClass()][] = $item->getId();
}
$objects = array();
foreach ($object_ids as $class => $ids)
{
$objects = array_merge(
$objects,
Doctrine::getTable($class)->createQuery('c')->whereIn('c.id', $ids)->execute()->getData()
);
}
return $objects;
}
Con ésto ya se puede hacer uso de la clase sfDoctrineShoppingCart con todas las funcionalidades que ofrece sfShoppingCart pero adaptado a Doctrine.
Además he implementado algunos métodos extra para hacerlo más cómodo al uso. Por ejemplo sfDoctrineShoppingCart::getInstance() o sfDoctrineShoppingCart::save() para obtener y almacenar el carrito
Me acabo de crear una cuenta twitter con fin de seguir las ponencias de Symfony organizadas por decharlas en Castellón. No me manejo muy bien aun en esto, pero parece que me adapto rápidamente.
Voy a experimentar durante un tiempo a ver qué ocurre...
Pueden seguirme en Alberto Ramirez on Twitter
Bueno, llevo unos 3 meses trabajando con la versión 1.4 de Symfony y el ORM Doctrine. Anteriormente estuve un par de años trabajando con la version 1.0 y Propel.
El cambio a la hora de codificar es significativo, no puedo comentar nada nuevo que no se haya comentado ya al respecto de las novedades de estas versiones sobre la 1.0.
En mi caso, la integración con el subframework de formularios aun continua su proceso, me manejo mucho mejor que al principio, pero aun hay muchos aspectos que desconozco.
El sistema de enrutamiento lo veo muy cómodo, ya que permite establecer en el routing.yml los parámetros del objeto que vas a pasar por URL de modo que a la hora de llamar al helper link_to, solo con pasarle el objeto completo, éste ya se encarga de formatear la URL. Además con los comportamientos (behaviors) de Doctrine, también se pueden definir Slug de forma muy sencilla.
Ejemplo:
routing.yml
watch_show:
url: /catalogo/:brand_url/:slug
class: sfDoctrineRoute
options: { model: Watch, type: object }
param: { module: watch, action: show }
requirements:
id: \d+
sf_method: [get]
showSuccess.php
url_for('watch_show', $principalWatch)
lib/model/doctrine/Watch.class.php
public function getBrandUrl()
{
return $this->getBrand()->getSlug();
}
schema.yml
Watch:
tableName: watch
actAs:
Timestampable: ~
Sluggable:
unique: true
fields: [model_name]
canUpdate: true
apps/frontend/modules/watch/actions/actions.class.php
public function executeShow(sfWebRequest $request)
{
// 'watch_show'
$this->watch = $this->getRoute()->getObject();
}
Otro de los cambios importantes es el paso a Doctrine como ORM en defecto de Propel. Lo noto muy flexible, fácil de aprender pero por completo diferente a Propel. Los retrieves se hacen en base a la clase Doctrine_Query y vas formando query de una forma mucho más gráfica y no tan complejas como con Criteria de Propel.
Ejemplo de Query:
$q = Doctrine::getTable('Articulos')->createQuery('a')
->leftJoin('a.Familias f')
->leftJoin('a.Subfamilias sf')
->innerJoin('a.fotos ft')
->where('a.pagarias = ?',false)
->andWhere('a.agotado = ?', false)
->andWhere('a.familia = f.id')
->orderBy('a.portada DESC')
->limit(23)
;
Doctrine cuenta con una tabla que modela el objeto en si "Articulos" (en este ejemplo) y con una clase destinada a las operaciones de consulta a la tabla "ArticulosTable" (siguiendo con el ejemplo).
Existen muchas más cosas a tener en cuenta. Por lo que es recomendable leer las novedades de cada versión con el fin de tener en mente las nuevas funcionalidades que aporta cada versión de Symfony.
Yo acabo de pedir el libro Más con Symfony que está destinado a los programadores que conocen bien el entorno y quieren ser aun más productivos con Symfony.
Un complemento para Firefox que he usado recientemente y me ha facilitado la vida para debugear peticiones AJAX es FirePHP. FirePHP es un complemento para Firebug y permite hacer debug PHP en la consola de Firebug.
¿Cómo usarlo?
Hay que usar Firefox y tener instalado el complemento Firebug (que seguro que lo tienes ya), luego hay que instalar FirePHP y descargar e instalar la librería PHP.
Una vez obtenido el complemento e instalada la librería se puede usar de esta forma:
require_once('FirePHPCore/FirePHP.class.php');
$firephp = FirePHP::getInstance(true);
$firephp->log('Hello World');
Se pueden loguear variables, costantes, arrays, objetos, etc.
Para Symfony
Si se está usando Symfony como framework para el desarrollo se puede instalar el plugin sfFirePHP e integrarlo en su funcionamiento (sistema de log, barra de depuración web, etc).
Enlaces:
PHP nos brinda la posibilidad de almacenar el contenido de la salida estándar en un buffer y poder operar con él. Una de las posibilidades que tenemos con este buffer es la de simular un efecto de carga e ir mostrando contenido de la web secuencialmente (Simulando un efecto AJAX).
Este sería un pequeño script PHP de ejemplo.
ob_start();
for($x=0; $x<10; $x++){
echo printf('- Línea %d.'."\n", $x);
ob_flush();
flush();
sleep(1);
}
ob_end_flush();
Aunque es muy sencillo, lo que realiza es lo siguiente: