Ver Mensaje Individual
  #11 (permalink)  
Antiguo 29/09/2011, 15:49
Avatar de Webstudio
Webstudio
Colaborador
 
Fecha de Ingreso: noviembre-2001
Ubicación: 127.0.0.1
Mensajes: 3.499
Antigüedad: 22 años, 5 meses
Puntos: 69
Respuesta: Call to undefined method de un objeto de sesión

Puedo hacerte una sugerencia respecto a tu implementación? En vez de guardar todos los datos en arrays distintos... das demasiado espacio al error, si los indices de repente no se correspondieran los unos con los otros.

Podrías utilizar este acercamiento a tus objetos, donde dejás reservado a cada uno las competencias que le son necesarias. Fijate como todo el tema de trabajo con datos de un producto quedaron dentro de la clase Producto, mientras que los manejos de distintos productos, quedan dentro del Carrito.
Código PHP:
<?php
class Carrito
{
    protected 
$productos = array();

    
/**
     * Agrega un producto al carrito, ya sea asignando un array
     * o un objeto del tipo Producto ya cargado
     */
    
public function agregaProducto($info)
    {
        if(
is_array($info)) {
            
$product = new Producto();
            
$product->cargaPorArray($info);
        } else if(
$info instanceof Producto) {
            
$product $info;
        } else {
            throw 
Exception('Informacion de Producto invalida');
        }

        
$id $product->getId();

        
/** Si el producto ya existe, incrementa la cantidad */
        
if($this->existeProducto($id))
        {
            
$this->obtieneProducto($id)->incrementaCant$product->getCantidad() );
        } else {
            
$this->productos[$id] = $product;
        }
    }

    
/**
     * Retorna una bandera si el producto ya existe
     */
    
public function existeProducto($id)
    {
        return !empty(
$this->productos[$id]);
    }

    
/**
     * Accessor para un producto específico
     * Devuelve null si no existe.
     */
    
public function obtieneProducto($id)
    {
        if (
$this->existeProducto($id)) {
            return 
$this->productos[$id];
        } else {
            return 
null;
        }
    }

    
/**
     * Si pasamos un Producto como parámentro, usa el id para
     * eliminarlo del carrito
     */
    
public function eliminaProducto($producto)
    {
        if (
$producto instanceof Producto) {
            
$id $producto->getId();
        } else {
            
$id = (int) $producto;
        }

        unset(
$this->productos[$id]);
    }

    
/**
     * Calcula el precio total del carrito, tomando en cuenta
     * el precio de cada producto y la cantidad de elementos
     */
    
public function getPrecio()
    {
        
$total 0;
        foreach(
$this->productos as $producto) {
            
$total += $producto->getPrecio();
        }
        return 
round($total2);
    }

    
/**
     * Utiliza un objeto Renderer para representar el Carrito
     * a través de tags HTML
     */
    
public function imprimeProductos()
    {
        
/* Acá yo no soy muy amigo de mezclar HTML dentro de
           objetos de negocio, asi que voy por una opcion
           que suelo utilizar: Objetos Renderers */
        
return CarritoRenderer::render($this);
    }

    public function 
obtenerProductos()
    {
        return 
$this->productos;
    }
}

class 
Producto
{
    protected 
$id           null;
    protected 
$nombre       '';
    protected 
$descripcion  '';
    protected 
$tamano       null;
    protected 
$precio       null;
    protected 
$cantidad     null;

    
/**
     * Accessors, se puede arreglar con un método __get
     */
    
public function getId()
    {
        return 
$this->id;
    }

    public function 
getNombre()
    {
        return 
$this->nombre;
    }

    public function 
getDescripcion()
    {
        return 
$this->descripcion;
    }

    public function 
getTamano()
    {
        return 
$this->tamano;
    }

    public function 
getCantidad()
    {
        return 
$this->cantidad;
    }

    public function 
incrementaCant$cantidad )
    {
        return 
$this->cantidad+=$cantidad;
    }

    public function 
getPrecio()
    {
        return 
$this->precio $this->cantidad;
    }


    public function 
cargaPorArray($data)
    {
        if(!
$this->id) { // evitamos sobreescribir un producto
            
$this->id           $data['id'];
            
$this->nombre       $data['nombre'];
            
$this->descripcion  $data['descripcion'];
            
$this->tamano       $data['tamano'];
            
$this->precio       $data['precio'];
            
$this->cantidad     $data['cantidad'];
        } else {
            throw new 
Exception('Intentaste cargar un producto que ya estaba cargado.');
        }
    }
}
Es una opinión personal, pero a mi no me gusta mezclar tags de HTML junto a mi lógica de negocios, así puedo mantener la funcionalidad independiente de la presentación. En este caso y para este fin, es que creé dos objetos nuevos, que se encargar de representar en HTML a los Productos y al Carrito. Se llaman Renderers porque se me ocurrió llamarlos asi, pero te separan la lógica de presentación por si después querés cambiar el HTML por GTK o por linea de comandos:

Código PHP:
class CarritoRenderer
{
    static public function 
render(Carrito $carrito)
    {
        
$html '';
        foreach(
$carrito->obtenerProductos() as $producto)
        {
            
$html.= ProductoRenderer::render($producto);
        }
        return 
$html;
    }
}


class 
ProductoRenderer
{
    static public function 
render(Producto $producto)
    {
        
$html '';

        
$id $producto->getId();

        if (
$id) {
            
$html.= "<li id='{$id}'>";
            
$html.= self::renderImagen($producto);
            
$html.= self::renderNombre($producto);
            
$html.= self::renderDescripcion($producto);
            
$html.= self::renderCantidad($producto);
            
$html.= self::renderPrecio($producto);
            
$html.= "<a class='delete' href='eliminar_producto_carrito.php?id={$id}'>Eliminar</a>";
            
$html.= '</li>';
        }

        return 
$html;
    }

    static function 
renderImagen(Producto $producto)
    {
        return 
"<a href='products.php?id={$producto->getId()}&tamano={$producto->getTamano()}'
                class='item' >
                <img src='/images/canastillas/{$producto->getId()}{$producto->getTamano()}.jpg' />
                </a>"
;
    }

    static function 
renderNombre(Producto $producto)
    {
        return 
"<h4>{$producto->getNombre()}</h4>";
    }

    static function 
renderDescripcion(Producto $producto)
    {
        return 
"<p>{$producto->getDescripcion()}</p>";
    }

    static function 
renderCantidad(Producto $producto)
    {
        return 
"<h5>Unidades: {$producto->getCantidad()}</h5>";
    }

    static function 
renderPrecio(Producto $producto)
    {
        return 
"<h3>{$producto->getPrecio()}</h3>";
    }

Si te fijás, estos objetos están preparados para ser dibujados con el método render() o llamando a los renders más específicos para armar la estructura que vos quieras luego en tus vistas.

Si vamos a la utlización de estas nuevas clases, podrías hacerlo asi :
Código PHP:
<?php
require_once ("carrito.php");
require_once (
"operacionesbd.php");
require_once (
'crearconexionbd.php');
crearConexionBD();
session_start();

$id $_GET["id"]?$_GET["id"]:null;
$tipo $_GET["type"]?$_GET["type"]:null;
$carrito = new Carrito();

if (
$id && $tipo) {
    
// Pedis los datos.
    
$elemento mysql_fetch_assoc(seleccionaCanastillaProductoPorID($id$tipo));
    
$elemento['descripcion'] = substr($elemento["descripcion"], 070) . "...";

    
//Dependiendo del tipo ...
    
switch($tipo) {
        case 
'mini':
        case 
'midi':
        case 
'maxi':
            
$precio $elemento[$tipo];
            break;
        default:
            
$precio $elemento['precio'];
            break;
    }

    
/* Acá asegurate que los indices del array son los mismos
       que los índices que necesita el objeto Producto */
    
$carrito->agregaProducto($elemento);

echo 
$c->imprimeProductos();
}
Ahora, cómo hacemos para guardar el carrito de compra entero y persistirlo en la sesión?
Esta parte no es TAN simple. Hay algunos métodos, te voy a contar cuál es el que a mi me parece un poco más complicado pero menos inestable. Siempre es bueno separar competencias, asi que la encargada de guardar el carrito en una sesión, debería ser otra clase. Veamos como la podemos hacer lo más simple posible...
Código PHP:
class CarritoSessionMapper
{
    static public function 
getCarrito()
    {
        if(!
session_id()) {
            
session_start();
        }
        if (!empty(
$_SESSION['carrito']) && is_string($_SESSION['carrito']) )
        {
            return 
unserialize($_SESSION['carrito']);
        } else {
            return 
null;
        }
    }

    static public function 
setCarrito(Carrito $carrito)
    {
        if(!
session_id()) {
            
session_start();
        }
        
$_SESSION['carrito'] = serialize($carrito);
    }

Y la manera de utilizarlo, sería algo asi :
Código PHP:
if( ($c CarritoSessionMapper::getCarrito()) === null) {
    
$c = new Carrito();
}

$c->agregaProducto(array(
    
'id'    => 5,
    
'nombre' => 'Zend Framework',
    
'descripcion' => 'Excelente Framework de Desarrollo',
    
'tamano' => 'maxi',
    
'precio' => 25,
    
'cantidad' =>  '3',
));

CarritoSessionMapper::setCarrito($c);

var_dump($_SESSION); 
Ya saben, este tipo de ejercicios me encantan. Si alguna parte no se entendió o no funcionó, me avisan y lo revisamos.

Saludos !
__________________
Tutoriales Photoshop | Web-Studio.com.ar
Artículos PHP | ZonaPHP.com