Solucionado (ver solução)
Solucionado
(ver solução)
4
respostas

Dúvida: Atualização do carrinho e finalização da compra

Olá a todos,

em um Post anterior aqui no Forum deste curso eu argumentei quanto a possibilidade de alterar diretamente as quantidades de compra diretamente na página do carrinho. Nessa ocasião o Paulo Alves Junior deu várias sujestões e então eu implementei esta funcionalidade no carrinho de compras e tentei deixar o mais próximo possível do carrinho de compras da casa do código, implementei as funções do botão atualizar e substitui o button de remoção de produto por um link, exatamente como na casa do código, segue o código abaixo`do meu carrinho de compras:

 <section class="infoSection container">
        <h2 class="infoSection-titulo">Seu carrinho
            (${carrinhoCompras.quantidade})</h2>

<form action="${spring:mvcUrl('CCC#update').build()}">
        <table class="formularioDoCarrinho-tabela">
            <thead class="formularioDoCarrinho-cabecalho">
                <tr>
                    <th></th>
                    <th class="formularioDoCarrinho-cabecalho-item">Item</th>
                    <th
                        class="formularioDoCarrinho-cabecalho-item formularioDoCarrinho-cabecalho-preco">Preço</th>
                    <th class="formularioDoCarrinho-cabecalho-item">Qtd</th>
                    <th class="formularioDoCarrinho-cabecalho-item">Total</th>
                    <th></th>
                </tr>
            </thead>
            <tbody>
                <c:forEach items="${carrinhoCompras.itens}" var="item">
                    <tr>
                        <td class="formularioDoCarrinho-item"><a
                            href="/products/livro-apis-nodejs"> <img
                                class="formularioDoCarrinho-item-imagem"
                                src="//cdn.shopify.com/s/files/1/0155/7645/products/zGb75MbYhQ0_Rc954-y3OZOq9O169BSqY0WslUDTmBk_size_mode_3_size_1024x768_small.jpeg?v=1453497826" />
                        </a></td>
                        <td class="formularioDoCarrinho-item">
                            <h2 class="formularioDoCarrinho-item-titulo">${item.produto.titulo }</h2>
                        </td>
                        <td
                            class="formularioDoCarrinho-item formularioDoCarrinho-item-preco">${item.preco}</td>
                        <td class="formularioDoCarrinho-item"><input
                            class="formularioDoCarrinho-item-quantidade" type="number"
                            min="0" id="quantidade" name="quantidade"
                            value="${carrinhoCompras.getQuantidade(item)}"></td>
                        <td
                            class="formularioDoCarrinho-item formularioDoCarrinho-item-precoTotal">${carrinhoCompras.getTotal(item)}</td>
                        <td class="formularioDoCarrinho-item">
                             <a href="${spring:mvcUrl('CCC#remover').arg(0,item.produto.id).arg(1,item.tipoPreco).build()}">
                        <img class="formularioDoCarrinho-item-remover-imagem"  src="//cdn.shopify.com/s/files/1/0155/7645/t/196/assets/trash.png?10584220066651581615" alt="X" title="Remover">
                    </a>
                        </td>
                    </tr>
                </c:forEach>
            </tbody>
            <tfoot class="formularioDoCarrinho-rodape">
                <tr>
                    <td
                        class="formularioDoCarrinho-rodape-item formularioDoCarrinho-finalizar"
                        colspan="3">
                    </td>
                    <td class="formularioDoCarrinho-rodape-item">
                        <button class="formularioDoCarrinho-atualizar" type="submit"
                         class="update-cart" name="update">Atualizar</button> 
                    </td>
                    <td class="formularioDoCarrinho-rodape-item">
                        ${carrinhoCompras.total}</td>
                    <td></td>
                </tr>
            </tfoot>
        </table>
</form>
    <form action="${spring:mvcUrl('PC#finalizar').build()}"
                            method="post">
                            <button class="formularioDoCarrinho-finalizar-botao"
                                type="submit" name="checkout">Finalizar<span
                                    class="formularioDoCarrinho-finalizar-botao-texto"
                                    role="presentation"> compra</span>
                            </button>
                        </form> 
    </section>
 @Controller
@RequestMapping("/carrinho")
@Scope(value=WebApplicationContext.SCOPE_SESSION)
public class CarrinhoCompraController implements Serializable{


    private static final long serialVersionUID = 1L;

    @Autowired
    private ProdutoDAO produtoDAO;
    @Autowired
    private CarrinhoCompras carrinho;
    @Autowired
    private HttpServletRequest request;

    @RequestMapping("/add")
    public ModelAndView add(Integer produtoId,TipoPreco tipoPreco){
        CarrinhoItem carrinhoItem = criaItem(produtoId,tipoPreco);
        carrinho.add(carrinhoItem);
        ModelAndView modelAndView = new ModelAndView("redirect:/carrinho");
        return modelAndView;        
    }

    @RequestMapping(method=RequestMethod.GET)
    public ModelAndView itens(){
        ModelAndView modelAndView = new ModelAndView("/carrinho/itens");
        return modelAndView;
    }

    @RequestMapping("/remover")
    public ModelAndView remover(Integer produtoId,TipoPreco tipoPreco){
        carrinho.remover(produtoId,tipoPreco);
        return new ModelAndView("redirect:/carrinho");
    }

    //Método Novo
    @RequestMapping("/update")
    public ModelAndView update(){
        String[] quantidades = request.getParameterValues("quantidade");
        Iterator<CarrinhoItem> it = carrinho.getItens().iterator();
        int i = 0;
        while(it.hasNext()){    
            CarrinhoItem item =(CarrinhoItem) it.next();
            carrinho.remover(item.getProduto().getId(),item.getTipoPreco());
            carrinho.add(item, Integer.parseInt(quantidades[i]));
            i++;
        }
        return new ModelAndView("redirect:/carrinho");
    }

    private CarrinhoItem criaItem(Integer produtoId, TipoPreco tipoPreco) {
        Produto produto = produtoDAO.find(produtoId);
        CarrinhoItem carrinhoItem = new CarrinhoItem(produto,tipoPreco);
        return carrinhoItem;
    }

}
 @Component
@Scope(value=WebApplicationContext.SCOPE_SESSION)
public class CarrinhoCompras implements Serializable{

    private static final long serialVersionUID = 1L;
    private Map<CarrinhoItem,Integer> itens = new LinkedHashMap<CarrinhoItem,Integer>();

    public void add(CarrinhoItem item) {
        itens.put(item, getQuantidade(item) + 1);
    }

    //Método Novo
    public void add(CarrinhoItem item,Integer quantidade) {
        itens.put(item, getQuantidade(item) + quantidade);
    }

    public Integer getQuantidade(CarrinhoItem item) {
        if(!itens.containsKey(item)){
            itens.put(item, 0);
        }
        return itens.get(item);
    }

    public BigDecimal getTotal(CarrinhoItem item){
        return item.getTotal(getQuantidade(item));
    }


    public Integer getQuantidade(){
        return itens.values().stream().reduce(0,(proximo,acumulador)-> proximo + acumulador);
    }

    public Collection<CarrinhoItem> getItens(){
        return  itens.keySet();
    }

    public BigDecimal getTotal(){
        BigDecimal total= BigDecimal.ZERO;
        for(CarrinhoItem item : itens.keySet() ){
            total = total.add(getTotal(item));
        }
        return total;
    }

    public void remover(Integer produtoId, TipoPreco tipoPreco) {
        Produto produto = new Produto();
        produto.setId(produtoId);
        itens.remove(new CarrinhoItem(produto, tipoPreco));
    }
}

Porém ainda existe uma coisa que me incomoda. Se você for no site da casa do código, escolher comprar um livro, alterar a quantidade e logo em seguida clicar em "Finalizar Compra" você será redirecionado para a página de checkout com as quantidades e preços corretos, coisa que não acontece na minha implementação de carrinho, no meu carrinho antes de finalizar a compra, se você alterou a quantidade de produtos, você deve clicar no botão atualizar para que então a quantidade do produto esteja atualizada no carrinho. A pergunta é como automatizar este processo igual ao site da casa do código e eu não precisar atualizar o carrinho antes de prosseguir com a compra?

Outra dúvida, observando a página do carrinho na casa do código, observei que os botões(ambos submit) de "Finalizar Compra" e "Atualizar" estão dentro de um mesmo form, como o Spring vai direfir a action, visto que cada submit vai realizar uma tarefa diferente?

//código do carrinho na casa do código

 <section class="infoSection container">
        <h2 class="infoSection-titulo">Seu carrinho</h2>

        <form class="formularioDoCarrinho" action="/cart" method="post">
            <table class="formularioDoCarrinho-tabela">
                <thead class="formularioDoCarrinho-cabecalho">
                    <tr>
                        <th></th>
                        <th class="formularioDoCarrinho-cabecalho-item">Item</th>
                        <th
                            class="formularioDoCarrinho-cabecalho-item formularioDoCarrinho-cabecalho-preco">Preço</th>
                        <th class="formularioDoCarrinho-cabecalho-item">Qtd</th>
                        <th class="formularioDoCarrinho-cabecalho-item">Total</th>
                        <th></th>
                    </tr>
                </thead>
                <tbody>

                    <tr>
                        <td class="formularioDoCarrinho-item"><a
                            href="/products/livro-spring-mvc"> <img
                                class="formularioDoCarrinho-item-imagem"
                                src="//cdn.shopify.com/s/files/1/0155/7645/products/spring-mvc-featured_small.png?v=1430247080">
                        </a></td>
                        <td class="formularioDoCarrinho-item">
                            <h2 class="formularioDoCarrinho-item-titulo">Spring MVC:
                                Domine o principal framework web Java - Impresso</h2>
                        </td>
                        <td
                            class="formularioDoCarrinho-item formularioDoCarrinho-item-preco">R$69,90</td>
                        <td class="formularioDoCarrinho-item"><input
                            class="formularioDoCarrinho-item-quantidade" type="number"
                            min="0" id="updates_1291146305" name="updates[1291146305]"
                            value="1"></td>
                        <td
                            class="formularioDoCarrinho-item formularioDoCarrinho-item-precoTotal"
                            title="Preço unitário: R$69,90">R$69,90</td>
                        <td class="formularioDoCarrinho-item"><a
                            href="/cart/change?id=1291146305&amp;quantity=0"> <img
                                class="formularioDoCarrinho-item-remover-imagem"
                                src="//cdn.shopify.com/s/files/1/0155/7645/t/192/assets/trash.png?13499226283805943877"
                                alt="X" title="Remover">
                        </a></td>
                    </tr>
                </tbody>
                <tfoot class="formularioDoCarrinho-rodape">
                    <tr>
                        <td
                            class="formularioDoCarrinho-rodape-item formularioDoCarrinho-finalizar"
                            colspan="3">
                            <button class="formularioDoCarrinho-finalizar-botao"
                                type="submit" name="checkout">
                                Finalizar<span
                                    class="formularioDoCarrinho-finalizar-botao-texto"
                                    role="presentation"> compra</span>
                            </button>
                        </td>
                        <td class="formularioDoCarrinho-rodape-item">
                            <button class="formularioDoCarrinho-atualizar" type="submit"
                                name="update">Atualizar</button>
                        </td>
                        <td class="formularioDoCarrinho-rodape-item">R$69,90</td>
                        <td></td>
                    </tr>
                </tfoot>
            </table>
        </form>

    </section>
4 respostas

Oi Ricardo, estou preparando sua resposta, ok?!

Só para lhe dar um feedback, pois ainda não concluí, já que ela exige que eu faça uns testes antes de responder rsrs.

Até amanhã lhe respondo com detalhes de implementação, ok?!

Abraço

tudo bem Paulo, te agradeço pela atenção.

solução!

Oi Ricardo, tudo bem?

Desculpe a demora, mas sua resposta será longa e precisava fazer uns testes antes. Além de que, o tempo estava bem disputado semana passada :)

Que bom ver você indo além do ensinado e evoluindo nos estudos. Parabéns!!!

É um prazer ajudar. E para sua dúvida, existem várias opções de implementação rsrs. Vamos para uma bem simples na view, mas exige lógica de tela no controller. Que é enviar todos os dados para a mesma action. No seu caso, PC#finalizar. Com isso, as quantidades irão atualizadas para a finalização da compra.

O problema seria saber quando o usuário deseja apenas atualizar a quantidade e quando dele deseja realmente finalizar a compra não é?

É possível fazer isso via JavaScript (alguns acham meio sujo, eu não acho, mas não gosto muito). E é possível fazer, verificando os parâmetros recebidos. Optei pela segunda opção, pois só exigirá de você, conhecimento visto no curso.

Primeiro, é preciso saber exatamente que linha está sendo modificada. Para isso, o Spring pode fazer automaticamente (vi que você fez na mão. mas assim é difícil controlar). A listagem ficaria assim agora:

<c:forEach items="${carrinhoCompras.itens}" var="item"
        varStatus="cont">
    <tr>
        <td class="formularioDoCarrinho-item"><a
            href="/products/livro-apis-nodejs"> <img
                class="formularioDoCarrinho-item-imagem"
                src="//cdn.shopify.com/s/files/1/0155/7645/products/zGb75MbYhQ0_Rc954-y3OZOq9O169BSqY0WslUDTmBk_size_mode_3_size_1024x768_small.jpeg?v=1453497826" />
        </a></td>
        <td class="formularioDoCarrinho-item">
            <h2 class="formularioDoCarrinho-item-titulo">${item.produto.titulo }</h2>
        </td>
        <td
            class="formularioDoCarrinho-item formularioDoCarrinho-item-preco">${item.preco}</td>
        <td class="formularioDoCarrinho-item">
        <input type="hidden" 
            name="itens[${cont.index }].produto.id" 
            value="${item.produto.id }">
        <input
            class="formularioDoCarrinho-item-quantidade" type="number"
            min="0" id="quantidade"
            name="itens[${cont.index }].quantidade"
            value="${carrinhoCompras.getQuantidade(item)}"></td>
        <td
            class="formularioDoCarrinho-item formularioDoCarrinho-item-precoTotal">${carrinhoCompras.getTotal(item)}</td>
        <td class="formularioDoCarrinho-item">
             <a href="${spring:mvcUrl('CCC#remover').arg(0,item.produto.id).arg(1,item.tipoPreco).build()}">
        <img class="formularioDoCarrinho-item-remover-imagem"  src="//cdn.shopify.com/s/files/1/0155/7645/t/196/assets/trash.png?10584220066651581615" alt="X" title="Remover">
    </a>
        </td>
    </tr>
</c:forEach>

O importante no código acima é: no forEach o atributo varStatus que possui um índice da linha atual em que o for está percorrendo.

O input hidden que leva o ID do item que está sendo alterada a quantidade e perceba que o name do input agora está como array itens[${cont.index }]..... Isso é necessário para saber qual item o usuário está alterando.

E o inputda própria quantidade que o name também ficou como array.

Agora, no controller, precisamos pegar essa informação. Para isso, o Srping tem uma restrição, ele não consegue pegar um array direto, você precisará criar uma classe que tenha a lista dentro, apenas para servir de representação para o Spring passar. Ficando assim:

public class ItensCarrinho {

    private List<CarrinhoItem> items = new ArrayList<>();

    public List<CarrinhoItem> getItems() {
        return items;
    }

    public void setItems(List<CarrinhoItem> items) {
        this.items = items;
    }

}

E agora, você precisará deixar apenas 1 form. Esse form pode ser que iria para a finalização do carrinho:

<form action="${spring:mvcUrl('PC#finalizar').build()}"
                            method="post">

No ProdutosController, no método finalizar faremos:

public ModelAndView finalizar(@AuthenticationPrincipal Usuario usuario, ItensCarrinho itens, HttpServletRequest request) {
  for (CarrinhoItem item : itens.getItems()) {
    System.out.println("==========>>>>>>>>>>>>>" + item.toString());
    // Aqui você atualiza o CarrinhoCompras com as quantidades e retira os de quantidade Zero. Enfim, uma lógica de tratamento das quatidades.
  }

  // Aqui, antes de fazer a requisição REST, vamos verificar se o botão que o cara clicou foi atualizar ou finalizar
  if (request.getParameter("atualizar") != null) {
    System.out.println("Clicou em " + request.getParameter("atualizar"));
    // Sabendo que é para atualizar, você manda ele de volta para o carrinho. Se ele não entrar aqui, é porque ele clicou em Finalizar.
  }
}

Agora os botões ficariam assim:

<td class="formularioDoCarrinho-rodape-item">
  <button class="formularioDoCarrinho-atualizar" type="submit"
    class="update-cart" name="atualizar">Atualizar</button> 
</td>

O name do botão é o parâmetro que verificaremos no request.

Essa solução, não é lá a coisa mais linda do mundo, pois você verifica se o parâmetro do botão que o cara clicou foi enviado, uma vez que o formulário não envia o valor dos botões que o cara não clicou.

Porém, é única forma que conheço para o mesmo método, pegar duas ações para o mesmo form.

Avalie se essa solução se parece com o que buscava.

Abraço :)

Obrigado pela atenção Paulo, gostei muito dos cursos de Spring MVC, uma aplicação real e que vai certamente ajudar a muitos desenvolvedores. Obrigado pela atenção.