Desafio PHP
Relatório com fetch modes e transação
Enunciado
Crie um script que gerencie pedidos. Crie tabelas clientes (id, nome) e pedidos (id, cliente_id, valor). Insira 3 clientes e vários pedidos. Usando uma transação, insira um novo pedido para um cliente existente. Em seguida, gere um relatório que liste cada cliente e o total de seus pedidos, usando PDO::FETCH_GROUP e PDO::FETCH_ASSOC para agrupar os resultados. Exiba o total geral de pedidos.
Requisitos
- Usar
beginTransactionecommitpara inserir o novo pedido. - Usar prepared statements para todas as inserções e consultas.
- Utilizar
fetchAll(PDO::FETCH_GROUP | PDO::FETCH_ASSOC)para agrupar pedidos por cliente. - Exibir o nome do cliente, a lista de valores de seus pedidos e o total geral.
Código inicial
<?php
// Seu código aqui
Saída esperada
Ana: 100, 200
Bruno: 150
Carla: 300
Total geral: 750
Ver dica
Para agrupar, faça um JOIN e use fetchAll(PDO::FETCH_GROUP | PDO::FETCH_ASSOC). A chave do array será o cliente_id ou nome. Ajuste a consulta para que a primeira coluna seja o agrupador.
Mostrar solução
<?php
$pdo = new PDO('sqlite::memory:');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Cria tabelas
$pdo->exec('CREATE TABLE clientes (id INTEGER PRIMARY KEY, nome TEXT)');
$pdo->exec('CREATE TABLE pedidos (id INTEGER PRIMARY KEY, cliente_id INTEGER, valor REAL)');
// Insere clientes
$stmt = $pdo->prepare('INSERT INTO clientes (nome) VALUES (:nome)');
$stmt->execute([':nome' => 'Ana']);
$stmt->execute([':nome' => 'Bruno']);
$stmt->execute([':nome' => 'Carla']);
// Insere pedidos iniciais
$stmt = $pdo->prepare('INSERT INTO pedidos (cliente_id, valor) VALUES (:cliente_id, :valor)');
$stmt->execute([':cliente_id' => 1, ':valor' => 100]);
$stmt->execute([':cliente_id' => 1, ':valor' => 200]);
$stmt->execute([':cliente_id' => 2, ':valor' => 150]);
$stmt->execute([':cliente_id' => 3, ':valor' => 300]);
// Transação para inserir novo pedido
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare('INSERT INTO pedidos (cliente_id, valor) VALUES (:cliente_id, :valor)');
$stmt->execute([':cliente_id' => 3, ':valor' => 50]);
$pdo->commit();
} catch (PDOException $e) {
$pdo->rollBack();
echo "Erro ao inserir pedido: " . $e->getMessage() . "\n";
}
// Relatório agrupado por cliente
$sql = 'SELECT c.nome, p.valor
FROM clientes c
JOIN pedidos p ON p.cliente_id = c.id
ORDER BY c.nome, p.valor';
$stmt = $pdo->query($sql);
$dados = $stmt->fetchAll(PDO::FETCH_GROUP | PDO::FETCH_ASSOC);
$totalGeral = 0;
foreach ($dados as $nome => $pedidos) {
$valores = array_column($pedidos, 'valor');
$totalCliente = array_sum($valores);
$totalGeral += $totalCliente;
echo $nome . ': ' . implode(', ', $valores) . "\n";
}
echo "Total geral: $totalGeral\n";
Passo a passo
- Conectamos ao SQLite em memória e configuramos exceções.
- Criamos as tabelas
clientesepedidos. - Inserimos três clientes com prepared statement.
- Inserimos quatro pedidos iniciais distribuídos entre os clientes.
- Iniciamos uma transação, inserimos um novo pedido para a Carla (id 3) e commitamos.
- Executamos uma consulta com JOIN que retorna
nomeevalor, ordenada por nome. - Usamos
fetchAll(PDO::FETCH_GROUP | PDO::FETCH_ASSOC): o PDO agrupa os resultados pela primeira coluna (nome), e cada grupo é um array associativo com a colunavalor. - Iteramos sobre o array agrupado, extraímos os valores com
array_column, somamos por cliente e acumulamos o total geral. - Exibimos cada cliente com seus valores e o total geral.
Por que funciona
O FETCH_GROUP agrupa as linhas pela primeira coluna do resultado, criando um array multidimensional onde a chave é o valor dessa coluna. Combinado com FETCH_ASSOC, cada linha interna é um array associativo. A transação garante que a inserção do novo pedido seja atômica. O prepared statement protege contra injeção.
Erros comuns
- Não ordenar por
nomeantes de agrupar: o agrupamento funciona, mas a ordem dos grupos pode ser imprevisível. UseORDER BY. - Esquecer de usar
array_columnpara extrair os valores: se tentararray_sum($pedidos)diretamente, dará erro porque$pedidosé um array de arrays. - Usar
FETCH_GROUPsem a coluna de agrupamento como primeira na SELECT: o agrupamento será feito pela coluna errada.
Outra forma de resolver
Poderia usar GROUP_CONCAT no SQL para concatenar os valores em uma string, mas perderia a flexibilidade de manipular os dados em PHP.
Saída esperada
Ana: 100, 200
Bruno: 150
Carla: 300, 50
Total geral: 800