Desafio PHP

Sistema de carrinho de compras

Enunciado

Crie uma classe Produto com propriedades nome (string) e preco (float) usando promoção de construtor. Crie uma classe Carrinho que mantém um array de produtos. O Carrinho deve ter métodos adicionarProduto(Produto $produto), removerProduto(string $nome) e total(): float. O método removerProduto deve remover o primeiro produto com o nome especificado. Exiba o total antes e depois de remover um produto.

Requisitos

  • A classe Produto deve usar promoção de construtor para nome e preco.
  • A classe Carrinho deve ter uma propriedade privada $produtos inicializada como array vazio.
  • adicionarProduto adiciona um objeto Produto ao array.
  • removerProduto remove o primeiro produto com o nome fornecido, se existir.
  • total retorna a soma dos preços de todos os produtos no carrinho.

Código inicial

<?php

// Defina a classe Produto com promoção de construtor

// Defina a classe Carrinho

// Crie produtos, adicione ao carrinho, remova um e mostre os totais

Saída esperada

Total antes: 45.90
Total depois: 30.00
Ver dica

Para remover, percorra o array com foreach e use unset($this->produtos[$indice]) ou array_splice. Lembre-se de reindexar se necessário.

Mostrar solução
<?php

class Produto {
    public function __construct(
        private string $nome,
        private float $preco
    ) {}

    public function getNome(): string {
        return $this->nome;
    }

    public function getPreco(): float {
        return $this->preco;
    }
}

class Carrinho {
    private array $produtos = [];

    public function adicionarProduto(Produto $produto): void {
        $this->produtos[] = $produto;
    }

    public function removerProduto(string $nome): void {
        foreach ($this->produtos as $indice => $produto) {
            if ($produto->getNome() === $nome) {
                unset($this->produtos[$indice]);
                // Reindexar o array para evitar lacunas
                $this->produtos = array_values($this->produtos);
                break; // remove apenas o primeiro
            }
        }
    }

    public function total(): float {
        $soma = 0.0;
        foreach ($this->produtos as $produto) {
            $soma += $produto->getPreco();
        }
        return $soma;
    }
}

// Criando produtos
$p1 = new Produto("Camiseta", 29.90);
$p2 = new Produto("Caneta", 5.00);
$p3 = new Produto("Caderno", 11.00);

$carrinho = new Carrinho();
$carrinho->adicionarProduto($p1);
$carrinho->adicionarProduto($p2);
$carrinho->adicionarProduto($p3);

echo "Total antes: " . number_format($carrinho->total(), 2, '.', '') . PHP_EOL;

$carrinho->removerProduto("Caneta");
echo "Total depois: " . number_format($carrinho->total(), 2, '.', '') . PHP_EOL;

Passo a passo

  1. A classe Produto usa promoção de construtor para criar as propriedades privadas $nome e $preco, e fornece getters públicos.
  2. A classe Carrinho tem a propriedade $produtos inicializada como array vazio.
  3. adicionarProduto insere o objeto Produto no final do array com [].
  4. removerProduto percorre o array; ao encontrar o primeiro produto com o nome desejado, remove com unset e reindexa com array_values.
  5. total percorre os produtos e soma os preços usando o getter getPreco().
  6. No script principal, criamos três produtos, adicionamos ao carrinho, mostramos o total, removemos a caneta e mostramos o novo total.

Por que funciona

A composição (Carrinho contém Produtos) demonstra como objetos podem interagir. O uso de $this->produtos mantém o estado do carrinho. A reindexação após unset evita problemas em iterações futuras e mantém o array como lista.

Erros comuns

  • Não reindexar após unset: o array fica com lacunas, mas foreach ainda funciona; porém, se usar for com índices, pode falhar.
  • Comparar nomes com == em vez de ===: pode causar coerção de tipos indesejada.
  • Esquecer de inicializar $produtos como array: private array $produtos; sem valor padrão causaria erro ao adicionar.
  • Remover todos os produtos com o mesmo nome em vez do primeiro: o break garante que apenas o primeiro seja removido.

Outra forma de resolver

Usar array_filter para remover por nome, mas isso removeria todas as ocorrências. Para remover apenas a primeira, pode-se usar array_search com array_splice:

$nomes = array_map(fn($p) => $p->getNome(), $this->produtos);
$indice = array_search($nome, $nomes, true);
if ($indice !== false) {
    array_splice($this->produtos, $indice, 1);
}

Essa abordagem é mais concisa e evita reindexação manual.

Saída esperada

Total antes: 45.90
Total depois: 30.00