Vous pouvez conserver un état pendant l'itération qui permet de savoir si la ligne actuelle est un nouveau nom/payable :
$last_name_seen = NULL;
while ($row = mysql_fetch_array($result)) {
$firstname = "";
$payable = "";
if ($last_name_seen === NULL || $row['firstname'] != $last_name_seen) {
$last_name_seen = $row['firstname'];
$firstname = $row['firstname'];
$payable = $row['payable'];
}
echo '<tr>';
echo '<td style="border: 1px solid black;">'.$firstname.'</td>';
echo '<td>'.$payable.'</td>';
echo '<td>'.$row['product'].'</td>';
echo '<td>'.$row['qty'].'</td>';
echo '</tr>';
}
Notez que votre requête MySQL doit avoir un certain ORDER BY
clause, pour générer l'ordre que vous voulez, par exemple
ORDER BY Name, Product;
Comme le suggèrent également les commentaires ci-dessus, si vous utilisez une API PHP obsolète, vous devriez envisager de passer à quelque chose de plus moderne. Mais la logique utilisée dans la boucle ci-dessus ne changerait pas beaucoup avec la modification de l'API MySQL.