sábado, 14 de febrero de 2009

Carrito de compras con PHP (Orientado a Objetos)


En este tutorial vamos a crear y utilizar las clases necesarias para manejar un carrito de compras en PHP. Las funciones que esta clase "Carrito" tendrá serán:

  • Agregar un producto.
  • Eliminar un producto.
  • Modificar un producto.
  • Imprimir el carrito.
Por fines didácticos quiero utilizar el patrón de diseño Iterator en esta clase "Carrito", ese patrón de diseño nos permitie recorrer los arreglos, collecciones, etc. Sin exponer sus estructuras, es decir, vamos a estandarizar la manejar de recorrer ese tipo de objetos, siempre que queramos recorrer un arreglo, coleccion, etc. Vamos a hacerlo de la misma manera sin importar que clase es.

Tambien vamos a trabajar el carrito creado como una variable de sesion, para que este presente durante toda la aplicación (mientras el usuario realiza sus compras).

Creando la clase Producto

Esta clase se encargará de guardar la informacion relativa a cada producto en especifibo, cada que que queramos un nuevo producto debemos crear una instancia de esta clase y pasarle sus atributos: codigo (para la identificacion unica), cantidad, precio, nombre, etc.

Archivo Producto.php

class Producto
{
var $codigo;
var $nombre;
var $cantidad;
var $precio;
}

Creando la clase Carrito

La clase Carrito no es mas que una collección de objetos Producto y los metodos para manejar esos productos:

Archivo Carrito.php

/**
* Esta clase permite manejar funciones basicas de un carrito de
* compras, esta desarrollada usando el patron de diseño iterator
* para orfecer mayor encapsulamiento y orden en los datos
* para utilizarla deben tener una clase producto que contenga
* el atributo "codigo" representando la identificacion del producto
*
* Cualquier duda pueden escribir a aalejo@gmail.com
* o entrar en la explicacion de esta clase en
* www.internetdeveloping.blogspot.com
*/
require('Collection.php');
require('Producto.php');

Class Carrito
{
/**
* Viene representando la lista de productos del carrito
* @var Collection
*/
var $productos;

function Carrito()
{
$this->productos = new Collection('Producto');
}

/**
* Esta funcion devuelve un producto a partir del codigo
* @param $codigo
* @return Producto
*/
public function getProducto($codigo)
{
$iterator = $this->productos->getIterator();
while($iterator->valid())
{
$prod = $iterator->current();

if($prod->codigo == $codigo)
{
return $prod;
}

$iterator->next();
}

return null;
}

/**
* Permite agregar un producto al carrito de compras
* @param $producto
* @return null
*/
public function agregarProducto($producto)
{
$prod = $this->getProducto($producto->codigo);
if($prod) $prod->cantidad++;
else
$this->productos->add($producto);
}

/**
* Elimina un producto del carrito, a partir de un codigo
* @param $codigo
* @return null
*/
public function eliminarProducto($codigo)
{
$existe = false;
$iterator = $this->productos->getIterator();
while($iterator->valid())
{
$prod = $iterator->current();
if($prod->codigo == $codigo)
{
if($prod->cantidad > 1)
$prod->cantidad--;
else
{
$this->productos->remove($iterator->key());
}
}
$iterator->next();
}
}

/**
* Se utiliza solo con fines de desarrollo, imprime la lista
* de productos del carrito, se puede modificar para imprimir
* toda la informacion de cada producto
* @return String
*/
public function trace()
{
$out = "";
$iterator = $this->productos->getIterator();
while($iterator->valid())
{
$prod = $iterator->current();

$out .= "Producto ".$prod->nombre." -> ".$prod->cantidad."
";

$iterator->next();
}

return $out;
}

}

Seguramente se preguntan porque la variables $productos es de tipo Collection, bueno esto es muy simple, para no trabajar con arreglos tediosos decidi utilizar una clase que encontre en internet llamada Collection.php, lo bueno de esta clase es que implementa la interaz IteratorAggregate que nos permitirá utilizar el patron de diseño Iterator que explique anteriormente, por otro lado, sus metodos son mucho mas intuitivos que los de un arreglo. A continuación les dejo el codigo de la clase Collection.php

Archivo Collection.php

class Collection implements Countable, IteratorAggregate, ArrayAccess
{
protected $_valueType;
protected $_collection = array();

/**
* Construct a new typed collection
* @param string valueType collection value type
*/
public function __construct($valueType)
{
$this->_valueType = $valueType;
}

/**
* Add a value into the collection
* @param string $value
* @throws InvalidArgumentException when wrong type
*/
public function add($value)
{
if(!$this->isValidType($value))
throw new InvalidArgumentException('Trying to add a value of wrong type');

$this->_collection[] = $value;
}

/**
* Set index's value
* @param integer $index
* @param mixed $value
* @throws OutOfRangeException
* @throws InvalidArgumentException
*/
public function set($index, $value)
{
if($index >= $this->count())
throw new OutOfRangeException('Index out of range');

if(!$this->isValidType($value))
throw new InvalidArgumentException('Trying to add a value of wrong type');

$this->_collection[$index] = $value;
}

/**
* Remove a value from the collection
* @param integer $index index to remove
* @throws OutOfRangeException if index is out of range
*/
public function remove($index)
{
if($index >= $this->count())
throw new OutOfRangeException('Index out of range');

array_splice($this->_collection, $index, 1);
}

/**
* Return value at index
* @param integer $index
* @return mixed
* @throws OutOfRangeException
*/
public function get($index)
{
if($index >= $this->count())
throw new OutOfRangeException('Index out of range');

return $this->_collection[$index];
}

/**
* Determine if index exists
* @param integer $index
* @return boolean
*/
public function exists($index)
{
if($index >= $this->count())
return false;

return true;
}
/**
* Return count of items in collection
* Implements countable
* @return integer
*/
public function count()
{
return count($this->_collection);
}

/**
* Determine if this value can be added to this collection
* @param string $value
* @return boolean
*/
public function isValidType($value)
{
$baseType = gettype($value);
if($this->_valueType == $baseType)
return true;

if($baseType == 'object')
{
$class = get_class($value);

if($this->_valueType == $class)
return true;
}

return false;
}

/**
* Return an iterator
* Implements IteratorAggregate
* @return ArrayIterator
*/
public function getIterator()
{
return new ArrayIterator($this->_collection);
}

/**
* Set offset to value
* Implements ArrayAccess
* @see set
* @param integer $offset
* @param mixed $value
*/
public function offsetSet($offset, $value)
{
$this->set($offset, $value);
}

/**
* Unset offset
* Implements ArrayAccess
* @see remove
* @param integer $offset
*/
public function offsetUnset($offset)
{
$this->remove($offset);
}

/**
* get an offset's value
* Implements ArrayAccess
* @see get
* @param integer $offset
* @return mixed
*/
public function offsetGet($offset)
{
return $this->get($offset);
}

/**
* Determine if offset exists
* Implements ArrayAccess
* @see exists
* @param integer $offset
* @return boolean
*/
public function offsetExists($offset)
{
return $this->exists($offset);
}
}


Utilizando del carrito de compras


Bueno ya estamos listos, ahora solo falta probar nuestra clase, para eso vamos a crear un arrchivo llamado VistaCarrito.php donde vamos a crear una instancia de la clase Carrito y vamos a agregarle, eliminarle y modiicarle produtos.

Archivo VistaCarrito.php

session_start();
require("Carrito.php");

$carrito = new Carrito();
$_SESSION["carrito"] = $carrito;
//Creo dos productos para jugar con ellos
$prod = new Producto();
$prod->codigo = 1;
$prod->cantidad = 1;
$prod->nombre = "Cama";
$prod->precio = 50;

$prod2 = new Producto();
$prod2->codigo = 2;
$prod2->cantidad = 1;
$prod2->nombre = "Silla";
$prod2->precio = 23;

echo "Agregando dos productos...
";
$_SESSION["carrito"]->agregarProducto($prod);
$_SESSION["carrito"]->agregarProducto($prod2);

echo $_SESSION["carrito"]->trace();

echo "Modificando el producto de codigo 1...
";
$auxProd = $_SESSION["carrito"]->getProducto("1");
$auxProd->cantidad++;

echo $_SESSION["carrito"]->trace();

echo "Eliminando el producto de codigo 2...
";
$_SESSION["carrito"]->eliminarProducto("2");

echo $_SESSION["carrito"]->trace();

echo "Listo...";

Recuerden que antes de empezar a utilizar las variables de session siempre debemos llamar a la funcion session_start(). Creamos la variable carrito como un objeto de sesion para poder utilizarlo durante toda la paigna web.

Bueno espero que todo haya quedado claro, aqui les dejo el codigo fuente de la clase para los que la quieran utilizar, cualquier duda me pueden escribir a mi correro aalejo@gmail.com.




6 comentarios:

  1. Saludos! He montado un salon recreativo... muchos me direis que en buena hora!!! con esto de la crisis digo jajaja...
    Weno a lo que iba, queria una web del salon para promocion y esas cosas. Buscando encontre este sitio de desarrollo web y tiene bastante buena pinta. La verdad es que de esto entiendo bastante poco y me gustaria a ver si me dariais vuestras opiniones sobre ellos. A ver que os parece... en cuestion de precios he preguntado y estoy contento, por aqui en bilbao parece que es de lo mejor.
    Un saludo y espero vuestras respuestas.

    ResponderEliminar
  2. Me sirvió de mucho :D! y está muy completo, Graciaaas!!!!

    ResponderEliminar
  3. Que tal amigo. Tengo un problema, si ya tengo datos dentro del valor session["carrito"] como puedo obtenerlos de nuevo para hacer un update, porque al de nuevo crear un objeto carrito y pasarlo a la session["carrito"] se desaparece todo lo que se tenia????

    ResponderEliminar
  4. EXCELENTE APORTE MI ESTIMADO....
    Muchas gracias.

    Saludos

    ResponderEliminar
  5. Muy bueno su aporte, sé que estoy empezando a programar recién en php , su aporte en si me sirvió mucho gracias

    ResponderEliminar
  6. Hola colega muy lindo tu código, sin embargo me esta dando problema, cuando añado el segundo producto se esta eliminando el anterior.
    Ejemplo selecciona el primer producto y se añade
    el usuario sigue navegando selecciona otro producto y puff en lugar de añadirlo, y tener los dos, elimina el primero y solo deja el que se añadio resientemete

    ResponderEliminar