Capítulo 13

Criando o Carrinho de Compras

13.1 - O modelo do Carrinho

Ainda não é possível comprar os produtos na nossa loja virtual. Precisamos de algum jeito fazer com que o usuário selecione os produtos que deseja comprar. O jeito mais comum de fazer isso é criar um carrinho de compras, guardando os produtos que foram selecionados.

Vamos criar uma classe que representa o carrinho de compras. Essa classe vai conter uma lista dos produtos escolhidos e o valor total:

public class Carrinho {

  private List<Item> itens = new ArrayList<Item>();
  
  private Double total = 0.0;

  // getters e setters  
}

Além disso precisamos criar uma classe que representa um item do carrinho: um produto com a quantidade selecionada.

public class Item {

  private Produto produto;
  
  private Integer quantidade;

  // getters e setters  
}

Pergunta importante: precisamos guardar esse carrinho no banco? O usuário não comprou ainda os produtos, então não faz muito sentido guardar no banco de dados. Esse carrinho precisa ficar disponível enquanto o usuário estiver navegando no sistema, então podemos apenas colocar o carrinho na sessão. Como vimos antes, para que um componente seja único durante a sessão do usuário basta anotá-lo com @SessionScoped, então basta modificar a classe do carrinho para incluir as anotações:

@Component
@SessionScoped
public class Carrinho {

  private List<Item> itens = new ArrayList<Item>();
  
  private Double total = 0.0;
  
  //getters e setters
}

Agora podemos receber esse carrinho no construtor de algum controller, com a certeza de que ele será único durante a sessão do usuário.

13.2 - Controlando o carrinho de compras

Agora que temos o modelo do Carrinho, vamos criar uma maneira de adicionar produtos. O jeito mais simples é modificar a listagem de produtos adicionando um botão para comprar:

<table>
  <thead>
    <tr>
      ...
      <th>Comprar</th>
      ...
    </tr>
  </thead>
  <tbody>
    <c:forEach items="${produtoList}" var="produto">
      <tr>
        ...
        <td>
          <!-- Adicionando o produto no carrinho de compras -->
          <form action="<c:url value="/carrinho"/>" method="POST">
            <input type="hidden" name="item.produto.id" 
                          value="${produto.id }"/>
            <input class="qtde" name="item.quantidade" value="1"/>
            <button type="submit">Comprar</button>
          </form>
        </td>
        ...
      </tr>          
    </c:forEach>
  </tbody>
</table>
lista-com-compra.png

O novo formulário vai adicionar um item ao carrinho, passando a quantidade e o id do produto. Precisamos agora criar um controlador que trate das operações no carrinho de compras, que seja capaz de adicionar um item ao carrinho. Esse controlador vai se chamar CarrinhoController:

package br.com.caelum.goodbuy.controller;

import br.com.caelum.vraptor.Resource;

@Resource
public class CarrinhoController {

}

Vamos criar o método que responde à URI colocada no formulário de compra:

@Resource
public class CarrinhoController {

  @Post("/carrinho")
  public void adiciona(Item item) {

  }
}

Para adicionar o item ao carrinho, temos que receber um Carrinho no construtor, assim o VRaptor vai passar para o CarrinhoController o carrinho da sessão do usuário:

@Resource
public class CarrinhoController {

  private final Carrinho carrinho;

  public CarrinhoController(Carrinho carrinho) {
    this.carrinho = carrinho;
  }
  
  @Post("/carrinho")
  public void adiciona(Item item) {
    carrinho.adiciona(item);
  }
}

Não existe ainda o método adiciona no Carrinho, então use o Ctrl+1 para criar o método. Ao adicionar um item no carrinho precisamos atualizar o total:

public class Carrinho {

  private List<Item> itens = new ArrayList<Item>();

  private Double total = 0.0;

  public void adiciona(Item item) {
    itens.add(item);
    total += item.getProduto().getPreco() * item.getQuantidade();
  }
  
  //...
}

Repare que estamos usando o preço do produto para atualizar o total do carrinho, mas no formulário só estamos passando o id do produto. Precisamos carregar as outras informações do Produto no banco de dados, usando o ProdutoDao:

@Resource
public class CarrinhoController {

  private final Carrinho carrinho;
  private final ProdutoDao dao;

  public CarrinhoController(Carrinho carrinho, ProdutoDao dao) {
    this.carrinho = carrinho;
    this.dao = dao;
  }

  @Post @Path("/carrinho")
  public void adiciona(Item item) {
    dao.recarrega(item.getProduto());
    carrinho.adiciona(item);
  }
}

Para implementar o método recarrega no ProdutoDao, vamos usar um método da Session que busca as informações no banco e coloca no próprio objeto passado. Esse método se chama refresh.

@Component
public class ProdutoDao {
  //...
  public void recarrega(Produto produto) {
    session.refresh(produto);
  }

}

Por último, precisamos ir para alguma página após adicionar algum item no carrinho. Por ora, vamos voltar pra página de listagem de produtos. Repare que como o redirecionamento é para uma lógica de outro controller, precisamos usar a classe deste controller:

@Resource
public class CarrinhoController {

  private final Carrinho carrinho;
  private final ProdutoDao dao;
  private final Result result;

  public CarrinhoController(Carrinho carrinho, 
      ProdutoDao dao, Result result) {
    this.carrinho = carrinho;
    this.dao = dao;
    this.result = result;
  }

  @Post("/carrinho")
  public void adiciona(Item item) {
    dao.recarrega(item.getProduto());
    carrinho.adiciona(item);
    
    result.redirectTo(ProdutosController.class).lista();
  }
}

Você pode também fazer o curso FJ-28 dessa apostila na Caelum

Querendo aprender ainda mais sobre o framework VRaptor? Esclarecer dúvidas dos exercícios? Ouvir explicações detalhadas com um instrutor?
A Caelum oferece o curso FJ-28 presencial nas cidades de São Paulo, Rio de Janeiro e Brasília, além de turmas incompany.

Consulte as vantagens do curso Web ágil com VRaptor, Hibernate e AJAX.

13.3 - Visualizando os itens do carrinho

Já conseguimos adicionar itens no carrinho, mas não é possível ainda visualizar o que tem dentro dele. Primeiro, vamos adicionar uma lugar em todas as páginas que mostra o estado atual do carrinho: quantos itens ele tem e qual é o valor total. Mas como vamos acessar o carrinho da sessão em todas as nossas páginas? Será que precisamos incluir o carrinho no Result em todas as lógicas? Não, isso não é necessário.

Quando criamos um componente SessionScoped, o VRaptor coloca a instância desse componente como um atributo da sessão, usando a convenção de nomes padrão. Ou seja, para o Carrinho, existirá um atributo da sessão chamado carrinho, então você consegue acessar o carrinho nas suas jsps usando a variável ${carrinho}.

Vamos, então, abrir o arquivo header.jspf e adicionar dentro da div header uma div do carrinho:

<div id="header">
    
  <div id="carrinho">
    <h3>Meu carrinho:</h3>
    <c:if test="${empty carrinho or carrinho.totalDeItens eq 0 }">
      <span>Você não possui itens no seu carrinho</span>
    </c:if>
    <c:if test="${carrinho.totalDeItens > 0 }">
      <ul>
        <li>
          <strong>Itens:</strong> ${carrinho.totalDeItens } </li>
        <li>
          <strong>Total:</strong> 
          <fmt:formatNumber type="currency" 
            value="${carrinho.total }"/>
        </li>
      </ul>
    </c:if>
  </div>
</div>

Estamos usando a expressão ${carrinho.totalDeItens} mas classe Carrinho não possui um getter para esse total de itens. Então vamos adicionar:

public class Carrinho {
  //...
  public Integer getTotalDeItens() {
    return itens.size();
  }
}

Agora ao adicionar produtos ao carrinho, conseguimos ver a quantidade de produtos adicionados e o valor total do carrinho nessa div que acabamos de criar:

div-do-carrinho.png

Ainda não conseguimos visualizar os itens do carrinho, então vamos criar uma lógica que liste os itens, no CarrinhoController.

@Get @Path("/carrinho")
public void visualiza() {

}

Esse método vai atender à mesma URI que o método adiciona, mas com outro verbo HTTP: o GET. Agora podemos criar a página que lista os itens. Crie essa página em /WEB-INF/jsp/carrinho/visualiza.jsp.

<h3>Itens do seu carrinho de compras</h3>

<table>
  <thead>
    <tr>
      <th>Nome</th>
      <th>Descrição</th>
      <th>Preço</th>
      <th>Qtde</th>
      <th>Total</th>
    </tr>
  </thead>
  <tbody>
    <c:forEach items="${carrinho.itens}" var="item">
      <tr>
        <td>${item.produto.nome }</td>
        <td>${item.produto.descricao }</td>
        <td>
          <fmt:formatNumber type="currency" 
            value="${item.produto.preco }"/>
        </td>
        <td>${item.quantidade }</td>
        <td>
          <fmt:formatNumber type="currency" 
            value="${item.quantidade * item.produto.preco }"/>
        </td>
      </tr>          
    </c:forEach>
  </tbody>
  <tfoot>
    <tr>
      <td colspan="2"></td>
      <th colspan="2">Total:</th>
      <th>
        <fmt:formatNumber type="currency" 
          value="${carrinho.total }"/>
      </th>
    </tr>
  </tfoot>
</table>
itens-carrinho.png

Para acessar essa visualização, vamos criar um link na div do carrinho:

<div id="header">
  <div id="carrinho">
    <h3><a href="<c:url value="/carrinho"/>">Meu carrinho:</a></h3>

Agora que temos uma página de visualização, podemos redirecionar para ela quando adicionamos algum produto ao carrinho.

@Resource
public class CarrinhoController {
  //...
  @Post("/carrinho")
  public void adiciona(Item item) {
    dao.recarrega(item.getProduto());
    carrinho.adiciona(item);

    result.redirectTo(this).visualiza();
  }
}

13.4 - Removendo itens do carrinho

Para completar as funcionalidades do carrinho de compras, vamos modificar a visualização do carrrinho para ser possível remover itens:

<table>
...
<tbody>
<c:forEach items="${carrinho.itens}" var="item" varStatus="s">
  <tr>
    ...
    <td>
      <form action="<c:url value="/carrinho/${s.index}"/>" method="POST">
        <button class="link" name="_method" value="DELETE">
          Remover
        </button>
      </form>
    </td>
  </tr>          
</c:forEach>
</tbody>
...
</table>

Vamos criar, também, o método que remove itens do carrinho no controller:

public class CarrinhoController {
  //...
  @Delete("/carrinho/{indiceItem}")
  public void remove(int indiceItem) {
    carrinho.remove(indiceItem);
    result.redirectTo(this).visualiza();
  }
}

E implementar o método que remove o item do carrinho, atualizando o total:

public class Carrinho {
  //...
  public void remove(int indiceItem) {
    Item removido = itens.remove(indiceItem);
    total -= removido.getProduto().getPreco() * removido.getQuantidade();
  }
}

13.5 - Exercícios

  1. Crie os modelos Carrinho e Item:

    package br.com.caelum.goodbuy.modelo;
    
    @Component
    @SessionScoped
    public class Carrinho {
    
      private List<Item> itens = new ArrayList<Item>();
    
      private Double total = 0.0;
    
      //getters e setters
    }
    
    package br.com.caelum.goodbuy.modelo;
    
    public class Item {
    
      private Produto produto;
    
      private Integer quantidade;
      //getters e setters
    }
    
  2. Modifique a listagem de produtos para incluir um botão para comprar o produto:

    <table>
      <thead>
        <tr>
          ...
          <th>Comprar</th>
          ...
        </tr>
      </thead>
      <tbody>
        <c:forEach items="${produtoList}" var="produto">
          <tr>
            ...
            <td>
              <!-- Adicionando o produto no carrinho de compras -->
              <form action="<c:url value="/carrinho"/>" method="POST">
                <input type="hidden" name="item.produto.id" 
                          value="${produto.id }"/>
                <input class="qtde" name="item.quantidade" value="1"/>
                <button type="submit">Comprar</button>
              </form>
            </td>
            ...
          </tr>          
        </c:forEach>
      </tbody>
    </table>
    
  3. Adicione o div do carrinho no cabeçalho da página (arquivo header.jspf)

    <div id="header">
    
      <div id="carrinho">
        <h3><a href="<c:url value="/carrinho"/>">Meu carrinho:</a></h3>
        <c:if test="${empty carrinho or carrinho.totalDeItens eq 0 }">
          <span>Você não possui itens no seu carrinho</span>
        </c:if>
        <c:if test="${carrinho.totalDeItens > 0 }">
          <ul>
            <li><strong>Itens:</strong> ${carrinho.totalDeItens }</li>
            <li><strong>Total:</strong> 
              <fmt:formatNumber type="currency" value="${carrinho.total }"/></li>
          </ul>
        </c:if>
      </div>
    </div>
    
  4. Crie o CarrinhoController, com um método para adicionar itens no carrinho.

    package br.com.caelum.goodbuy.controller;
    
    @Resource
    public class CarrinhoController {
    
      private final Carrinho carrinho;
      private final ProdutoDao dao;
      private final Result result;
    
      public CarrinhoController(Carrinho carrinho, 
          ProdutoDao dao, Result result) {
        this.carrinho = carrinho;
        this.dao = dao;
        this.result = result;
      }
    
      @Post("/carrinho")
      public void adiciona(Item item) {
        dao.recarrega(item.getProduto());
        carrinho.adiciona(item);
    
        result.redirectTo(ProdutosController.class).lista();
      }
    
    }
    
    public class Carrinho {
      //...
      public void adiciona(Item item) {
        itens.add(item);
        total += item.getProduto().getPreco() *
            item.getQuantidade();
      }
      public Integer getTotalDeItens() {
        return itens.size();
      }
    }
    
    public class ProdutoDao {
      //...
      public void recarrega(Produto produto) {
        session.refresh(produto);
      }
    }
    
  5. Compre alguns produtos e veja o carrinho sendo atualizado no header.

    div-do-carrinho.png
  6. Crie a lógica e o jsp que visualiza os itens do carrinho. Mude também o redirecionamento do método adiciona para a nova lógica.

    @Resource
    public class CarrinhoController {
      //...
      @Get("/carrinho")
      public void visualiza() {
      }
      @Post("/carrinho")
      public void adiciona(Item item) {
        dao.recarrega(item.getProduto());
        carrinho.adiciona(item);
    
        result.redirectTo(this).visualiza();
      }
    }
    

    /WEB-INF/jsp/carrinho/visualiza.jsp

    <h3>Itens do seu carrinho de compras</h3>
    
    <table>
      <thead>
        <tr>
          <th>Nome</th>
          <th>Descrição</th>
          <th>Preço</th>
          <th>Qtde</th>
          <th>Total</th>
        </tr>
      </thead>
      <tbody>
        <c:forEach items="${carrinho.itens}" var="item" 
          varStatus="s">
          <tr>
            <td>${item.produto.nome }</td>
            <td>${item.produto.descricao }</td>
            <td>
              <fmt:formatNumber type="currency" 
                value="${item.produto.preco }"/>
            </td>
            <td>${item.quantidade }</td>
            <td>
              <fmt:formatNumber type="currency" 
                value="${item.quantidade * item.produto.preco }"/>
            </td>
          </tr>          
        </c:forEach>
      </tbody>
      <tfoot>
        <tr>
          <td colspan="2"></td>
          <th colspan="2">Total:</th>
          <th>
            <fmt:formatNumber type="currency" 
              value="${carrinho.total }"/>
          </th>
        </tr>
      </tfoot>
    </table>
    
  7. Adicione mais produtos ao carrinho.

    itens-carrinho.png
  8. Crie a lógica de remover produtos do carrinho e adicione o botão de remoção na visualização do carrinho.

    public class CarrinhoController {
      //...
      @Delete("/carrinho/{indiceItem}")
      public void remove(int indiceItem) {
        carrinho.remove(indiceItem);
        result.redirectTo(this).visualiza();
      }
    }
    
    public class Carrinho {
      public void remove(int indiceItem) {
        Item removido = itens.remove(indiceItem);
        total -= removido.getProduto().getPreco() * removido.getQuantidade();
      }
    }
    

    /WEB-INF/jsp/carrinho/visualiza.jsp

    <table>
    ...
    <tbody>
    <c:forEach items="${carrinho.itens}" var="item" varStatus="s">
      <tr>
        ...
        <td>
          <form action="<c:url value="/carrinho/${s.index}"/>" method="POST">
            <button class="link" name="_method" value="DELETE">Remover</button>
          </form>
        </td>
      </tr>          
    </c:forEach>
    </tbody>
    ...
    </table>
    
  9. Adicione e remova itens do carrinho.

Tire suas dúvidas no GUJ Respostas

O GUJ é um dos principais fóruns brasileiros de computação e o maior em português sobre Java. A nova versão do GUJ é baseada em uma ferramenta de perguntas e respostas (QA) e tem uma comunidade muito forte. São mais de 150 mil usuários pra ajudar você a esclarecer suas dúvidas.

Faça sua pergunta.