Como fazer um Carrinho de Compras utilizando a biblioteca Cart

Update! 18/03/2015

A biblioteca Cart  foi descontinuada na versão 3.0 do CodeIgniter. Apesar de você conseguir encontrá-la na versão nova do CI, ela foi mantida somente por questões de retrocompatibilidade. Então, sugere-se que esta biblioteca não seja mais utilizada.

 

Olá.

Na era das Lojas-Virtuais é bem comum que você tenha que desenvolver algo parecido para algum cliente.

Apesar de já existirem excelentes sistemas prontos de lojas virtuais, é sempre bom saber como implementar a principal funcionalidade de uma Loja-Virtual. O Carrinho de Compras.

Antes de continuar no post, você pode ver este projeto funcionando clicando no link abaixo:

-> Carrinho de Compras do CodeIgniter.

Não é raro clientes desejarem ter suas próprias aplicações para gerenciar a venda de seus produtos, e se você não for usar um sistema de loja-virtual pronto, você vai ter que desenvolver um na unha.

No post de hoje vamos aprender a como fazer um carrinho de compras utilizando a biblioteca cart do CodeIgniter, que nos fornece a solução praticamente completa.

Digo praticamente completa porque esta biblioteca não vai gerenciar checkout ou os meios de pagamento e entrega.

Ela somente fornece a capacidade você criar um sistema de carrinho de compras, onde será possível adicionar ou remover produtos, atualizar suas quantidades, fazer o cálculo automático de preços vezes quantidade, listar quantos itens há no carrinho, listar o total dos produtos no carrinho, etc.

Apesar de ser uma solução excelente, ela tem um pequeno problema para quem usa acentos ou caracteres especiais como nós brasileiros.

Originalmente esta biblioteca não aceita que você insira um produto que tenha acentuação ou cedilha, til, circunflexo, etc, mas existe uma maneira bem tranquila de resolver isto. Lá embaixo no código você vai ver como.

Para começar nosso projeto, eu fiz uma instalação limpa do CI e configurei-o para acessar um Banco de Dados e para carregar automaticamente a biblioteca cart.

A biblioteca cart usa sessions para armazenar os dados do carrinho de compras, porém, se você não está utilizando sessions em outro lugar de sua aplicação, não vai ser preciso carregar esta biblioteca, pois o cart já faz isto automaticamente.

É importante destacar também que você deve configurar a session do CI para armazenar as informações no banco de dados, em vez de cookies.

Para isto, você deve mudar para TRUE a linha abaixo dentro do arquivo config.php

$config['sess_use_database'] = TRUE;

Esta será provavelmente a linha 251.

Depois, você precisa criar a tabela que as sessions irão utilizar. Abaixo estou também colando o código desta tabela. Basta executá-la a aba SQL do seu PHPMyAdmin.

CREATE TABLE IF NOT EXISTS  `ci_sessions` (
	session_id varchar(40) DEFAULT '0' NOT NULL,
	ip_address varchar(45) DEFAULT '0' NOT NULL,
	user_agent varchar(120) NOT NULL,
	last_activity int(10) unsigned DEFAULT 0 NOT NULL,
	user_data text NOT NULL,
	PRIMARY KEY (session_id),
	KEY `last_activity_idx` (`last_activity`)
);

Feito isso, vamos agora começar a configurar nosso carrinho de compras.

Como você viu, neste exemplo eu somente cadastrei alguns produtos em uma tabela produtos e os listei na página inicial do carrinho de compras, sendo que coloquei um botão de Comprar para cada produto, exatamente como seria em uma loja-virtual.

Eu só não deixei visualmente igual, pois não é o objetivo aqui.

Quando o usuário clicar no botão Comprar, o produto é adicionado ao carrinho de compras e em seguida o carrinho de compras é carregado na tela, mostrando o que existe nele.

Você pode clicar no link Continuar Comprando para testar novos produtos.

Para carregar os produtos, eu criei um model chamado m_produtos.php com o seguinte código:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

	class M_produtos extends CI_Model {
		function retorna_produtos($id_produto = null){
			
			if ($id_produto != null) 
				$this->db->where("id", $id_produto);
			
			$this->db->order_by("id", 'asc');
			return $this->db->get("produtos");
			
		}
	}

Este código quando chamado sem a passagem de parâmetros, simplesmente retorna todos os produtos da tabela produtos.

No controller welcome.php eu o configurei para trazer estes produtos do banco de dados e disponibilizá-los para a view, através da variável produtos. Veja:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Welcome extends CI_Controller {

	public function index()
	{
		$this->load->model("m_produtos", "produtos");
		$this->load->helper("funcoes");
		
		$variaveis['produtos'] = $this->produtos->retorna_produtos();
		$this->load->view('v_produtos', $variaveis);
	}
}

Após ele carregar os produtos, eu chamo a view principal, v_produtos, que é responsável pela tela inicial do nosso sistema.

Repare que eu também carreguei um helper chamado funcoes que foi onde eu coloque uma função de formatação de preço que costumo utilizar bastante.

Abaixo segue o código do arquivo funcoes_helper.php que deve ser colocado com este nome dentro da pasta helpers dentro de application.

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

if ( ! function_exists('formata_preco'))
{
    function formata_preco($valor)
	{
		$negativo = false;
		$preco = "";
		$valor = intval(trim($valor));
			if ($valor < 0) {
				$negativo = true;
				$valor = abs($valor);
		}
		$valor = strrev($valor);
		while (strlen($valor) < 3) {
			$valor .= "0";
		}
		for ($i = 0; $i < strlen($valor); $i++)	{
			if ($i == 2)	{
				$preco .= ",";
			}
			if (($i <> 2) AND (($i+1)%3 == 0))	{
				$preco .= ".";
			}
			$preco .= substr($valor, $i , 1);
		}
		$preco = strrev($preco);
		return ($negativo ? "-" : "") . $preco;
	}
}

Para usar esta função, você deve gravar os preços sem pontos ou zeros no banco de dados. Exemplo: se for armazenar R$ 100,00, então grave no banco assim: 10000. Depois passe este valor para a função que ela fará a conversão automática.

Feito isto, vamos agora ver a view v_produtos.php que é responsável por mostrar os produtos que podem ser comprados.

<html>
	<head>
		<title>Produtos</title>
		<style>
			* {
				font-family: Arial;
				font-size: 12px;
			}
			h2 {
				font-size: 18px;
				background-color: #ddd;
				padding: 5px;
			}
			table {
				border: 1px solid;
				padding: 5px;
			}
			table th {
				background-color: #ddd;
				padding: 5px;
			}
			table td {
				padding: 5px;
				background-color: #fff;
				text-align: center;
			}
			table td input {
				text-align: center;
				padding: 3px;
			}
		</style>
	</head>
	<body>
		<div id="container" style="width: 900px; margin: 0 auto;">
			<h2 style="text-align: center;">Produtos da Loja-Virtual para você comprar</h2>
			<p style="text-align: center"><a href="<?= base_url('index.php/carrinho/listar')?>">Ver meu Carrinho de Compras</a></p>
			<table style="width: 100%; " align="center">
				<tr>
					<th>Código</th>
					<th>Produto</th>
					<th>Quantidade</th>
					<th>Preço Unitário</th>
					<th>Ação</th>
				</tr>
				<? foreach($produtos -> result() as $produto): ?>
				<form method="post" action="<?= base_url('index.php/carrinho/inserir') ?>">
					<tr>
						<td><?= $produto->id ?></td>
						<td style="text-align: left;"><?= $produto->descricao ?></td>
						<td><input type="text" name="quantidade" maxlength="2" size="3" value="1" /></td>
						<td>R$ <?= formata_preco($produto->preco) ?></td>
						<td><button type="submit">Comprar</button></td>
					</tr>
					<input type="hidden" name="id_produto" value="<?= $produto->id ?>">
					<input type="hidden" name="descricao" value="<?= $produto->descricao ?>">
					<input type="hidden" name="preco" value="<?= $produto->preco ?>">
				</form>
				<? endforeach; ?>
			</table>
		</div>
	</body>
</html>

Neste código não tem nenhum mistério. É basicamente HTML com um loop onde eu listo os produtos.

Porém, para evitar o uso de AJAX neste post, eu tentei deixar da maneira mais compatível possível.

Quando você clicar no botão Comprar, a página na verdade faz um post dos dados do produto para uma função dentro do controller que é responsável pela inserção do produto no carrinho de compras.

Como cada produto tem seus respectivos dados, eu criei um <form></form> para cada um, e dentro deste form, eu coloquei em campos hidden as informações necessárias para que o produto seja inserido no banco de dados.

Os campos requeridos são:

  • id – Código que identifica cada produto.
  • qty – Quantidade sendo comprada.
  • price – O preço do produto.
  • name – O nome do produto.
  • options – Algum atributo do produto. Exemplo: se for uma camisa vermelha, tamanho P, ou um produto 127V ou 220V, etc. Deve ser passado via array.

Abaixo veja o formato que estas informações devem ser passadas:

$data = array(
               'id'      => 'sku_123ABC',
               'qty'     => 1,
               'price'   => 39.95,
               'name'    => 'T-Shirt',
               'options' => array('Size' => 'L', 'Color' => 'Red')
            );

$this->cart->insert($data);

Com exceção do item ‘options’, todos os outros são obrigatórios, caso contrário, o item não será inserido no carrinho de compras.

Quando um produto é inserido com sucesso, é retornado um código único, chamado rowid que é responsável por cada linha dentro do carrinho de compras.

Isto serve para que seja possível ao usuário inserir um mesmo produto porém com opções diferentes.

Bem, terminada a parte de listagem de produtos, vamos agora trabalhar no controller carrinho.php, que é responsável por gerenciar as funções do carrinho de compras.

Abaixo o código do controller carrinho.php:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

	class Carrinho extends CI_Controller {
			
		public function listar(){
			
			$this->load->helper("funcoes");
			$this->load->view("v_carrinho");
			
		}
		public function atualizar(){
			
			//recebo todo o conteúdo postado do formulário e no loop abaixo recupero o que preciso
			$conteudo_postado = $this->input->post();
			
			foreach($conteudo_postado as $conteudo) {
				
				$dados[] = array(
				
					"rowid" => $conteudo['rowid'],
					"qty" => $conteudo['qty']
				
				);
					
			}
			//com os dados já preparados, basta dar um update no carrinho
			$this->cart->update($dados);
			
			redirect(base_url('index.php/carrinho/listar'));
			
		}
		public function inserir(){
			
			
			$id_produto = $this->input->post("id_produto");
			$descricao = $this->input->post("descricao");
			$quantidade = $this->input->post("quantidade");
			$preco = $this->input->post("preco");
			
			//se o usuário não informar a quantidade do produto, então, coloco 1
			if (empty($quantidade)) 
				$quantidade = 1;
			
			//A biblioteca do carrinho de compras já está carregada lá pelo autoload, então não preciso chamá-la aqui novamente.
			
			//Esta linha serve para permitir que produtos com acentuação no nome sejam aceitos.
			$this->cart->product_name_rules = "'\d\D'";
			 
			$data = array(
               'id'      => $id_produto,
               'qty'     => $quantidade,
               'price'   => $preco,
               'name'    => $descricao
            );

			
			
			
			if ($this->cart->insert($data)) {
				redirect(base_url('index.php/carrinho/listar'));
			} else {
				echo "ERRO. Não foi possível inserir. <pre>";
				print_r($data);
				echo "</pre>";				
			}
			
		}
		//função que limpa o conteúdo do carrinho de compras.
		public function limpar(){
			
			$this->cart->destroy();
			redirect(base_url('index.php/carrinho/listar'));
			
		}
		
	}

A função listar somente carrega uma view que tem o código do Carrinho de Compras, que somente lista o que existe dentro ele.

A segunda função tem como objetivo atualizar os itens do carrinho de compras.

Ela serve para que quando o usuário deseje excluir ou alterar a quantidade de um determinado produto, ele consiga fazer sem maiores problemas.

Nesta função é atribuída na variável $conteudo_postado tudo o que vem via post do formulário que engloba o carrinho de compras.

Para que um produto seja atualizado, 2 parâmetros devem ser passados obrigatoriamente: o rowid e a quantidade a ser alterada.

Se for passada a quantidade zero, então o produto é excluído do carrinho.

Em seguida, eu varro a variável $conteudo_postado e acesso as duas informações que preciso através dos índices rowid e qty.

Tendo isto em mãos, eu crio uma array no formato requerido e passo como parâmetro para a função de atualização do carrinho de compras, deste jeito:

foreach($conteudo_postado as $conteudo) {
				
				$dados[] = array(
				
					"rowid" => $conteudo['rowid'],
					"qty" => $conteudo['qty']
				
				);
					
			}
			//com os dados já preparados, basta dar um update no carrinho
			$this->cart->update($dados);

Em seguida, eu simplesmente recarrego o carrinho de compras com as informações atualizadas.

A função inserir também é bem simples.

Ela basicamente recebe os valores requeridos para a inserção no carrinho de compras, monta um array no formato necessário que é passada como parâmetro para a função de inserção no carrinho de compras:

$this->cart->insert($data)

Como falei lá no começo do post, antes de dar um insert, existe uma linha que devemos adicionar para informar ao CodeIgniter que ele deve aceitar todos os caracteres digitados. Veja abaixo a linha:

$this->cart->product_name_rules = "'\d\D'";

Chame esta linha antes de chamar a função insert do carrinho de compras.

Se você não colocar esta linha, seu carrinho de compras vai funcionar somente com produtos que não tenham acentos ou caracteres especiais escritos.

E por último a função que apenas limpa o carrinho de compras. Ao ser chamada, ela acessa a função da biblioteca cart que faz isto para nós. Veja:

$this->cart->destroy();

Depois de limpar o carrinho, eu o mostro novamente.

Vamos ver agora o código a view do carrinho de compras:

<html>
	<head>
		<title>Carrinho de Compras do CodeIgniter</title>
		<style>
			* {
				font-family: Arial;
				font-size: 12px;
			}
			h2 {
				font-size: 18px;
				background-color: #ddd;
				padding: 5px;
			}
			table {
				border: 1px solid;
				padding: 5px;
			}
			table th {
				background-color: #ddd;
			}
			table td {
				padding: 5px;
				background-color: #fff;
				text-align: center;
			}
			table td input {
				text-align: center;
				padding: 3px;
			}
		</style>		
	</head>
	<body>
		<div id="container" style="width: 900px; margin: 0 auto;">
			

			<h2 style="text-align: center;">Produtos no seu Carrinho de Compras</h2>
			<p style="text-align: center;"><a href="<?= base_url('index.php/welcome')?>">Continuar Comprando</a></p>
			<?php echo form_open('carrinho/atualizar'); ?>
			<p><strong>Total de Itens no Carrinho:</strong> <?= $this->cart->total_items() ?></p>
			<p>Para excluir um produto, informe quantidade 0 e clique no botão Atualizar.</p>
			<table cellpadding="6" cellspacing="1" style="width:100%" border="1" align="center">
			
			<tr>
			  <th>Quantidade</th>
			  <th>Produto</th>
			  <th style="text-align:right">Preço</th>
			  <th style="text-align:right">Sub-Total</th>
			</tr>
			
			<?php $i = 1; ?>
			
			<?php foreach ($this->cart->contents() as $items): ?>
			
				<?php echo form_hidden($i.'[rowid]', $items['rowid']); ?>
			
				<tr>
				  <td><?php echo form_input(array('name' => $i.'[qty]', 'value' => $items['qty'], 'maxlength' => '3', 'size' => '5')); ?></td>
				  <td style="text-align: left;">
					<?php echo $items['name']; ?>
			
						<?php if ($this->cart->has_options($items['rowid']) == TRUE): ?>
			
							<p>
								<?php foreach ($this->cart->product_options($items['rowid']) as $option_name => $option_value): ?>
			
									<strong><?php echo $option_name; ?>:</strong> <?php echo $option_value; ?><br />
			
								<?php endforeach; ?>
							</p>
			
						<?php endif; ?>
			
				  </td>
				  <td style="text-align:right">R$ <?php echo formata_preco($items['price']); ?></td>
				  <td style="text-align:right">R$ <?php echo formata_preco($items['subtotal']); ?></td>
				</tr>
			
			<?php $i++; ?>
			
			<?php endforeach; ?>
			
			<tr>
			  <td colspan="2">&nbsp;</td>
			  <td class="left"><strong>Total</strong></td>
			  <td class="right">R$ <?php echo formata_preco($this->cart->total()); ?></td>
			</tr>
			<tr>
				<td colspan="4">
					<div style="float: left; padding-top: 6px;"><a href="<?= base_url('index.php/carrinho/limpar')?>">Limpar Carrinho</a></div>
					<div style="float: right;"><?php echo form_submit('', 'Atualizar'); ?></div>
				</td>
			</tr>
			</table>
		</div>
	</body>
</html>

Aqui também não há nenhum segredo.

O código eu copiei da ajuda do site do CI, e o que tive que mudar é somente o endereço do action do formulário, indicando qual é a função que irá tratar as atualizações do carrinho de compras.

Repare que antes do carrinho de compras, eu chamei uma função que me traz a quantidade de itens dentro do carrinho. Esta função é:

$this->cart->total_items()

Viu como já está tudo pronto?

Esta biblioteca também faz o cálculo automático dos preços e quantidades dos itens inseridos no carrinho, assim como também calcula o total geral.

O próximo passo agora seria você criar um botão Fechar Compra e proceder para as etapas de cadastro, envio e pagamento, mas isto não será contemplado aqui.

Depois, para acessar o total do carrinho de compras, basta chamar a função:

$this->cart->total()

Esta é a função que retorna o valor total que o comprador deverá pagar no checkout.

Bom, acredito que seja isso. Espero que o post tenha sido útil para você.

Abaixo você pode testar novamente o sistema e logo em seguida, um link para você baixar o projeto para o seu computador.

-> Carrinho de Compras do CodeIgniter.

O SQL que cria as tabelas está dentro do zip.

-> Baixar o projeto

Se tiver alguma dúvida, basta fazer um comentário nos campos abaixo.

Um abraço.

 

Fábio S. Reszko

Sou Programador PHP desde 2006 e eu acredito sinceramente que programar usando um Framework PHP é a solução para os problemas de códigos desorganizados, difíceis de entender e de dar manutenção no futuro. Se você também acredita nisto, então fique à vontade em explorar meu blog.