简体   繁体   中英

Get all rows inside a foreach loop and echo an array result?

I'm trying to get all rows inside a foreach loop but it's not working as it should.

<?php 
foreach ($locations_loop as $row):

    $lr_id  = $row["id"];
    $stmtlr = $pdo->prepare("SELECT * FROM locations_rating WHERE l_id = {$lr_id}");
    $stmtlr->execute();
    $stlr_loop = $stmtlr->fetchAll(PDO::FETCH_ASSOC);
    if (empty($stlr_loop)) {
        $loc_rate[] = "0";
    } else {
        foreach($stlr_loop as $rowlr):
            $loc_rate[] = $rowlr["stars"];
        endforeach;
    }
    
    $rating_array = array_values($loc_rate);
    $rating_avg   = array_sum($rating_array) / count($rating_array);
?>      
<?=round($rating_avg, 1);?>    
<?php endforeach; ?>

$rating_avg outputs something else every time the script runs. It works fine outside a foreach loop tho. I tried to join the two table but no luck since it only outputs only one row.

I might be thinking too far out of the box, but this is just one technique that occurred to me which will ensure that all location ids will receive an average value in the result set.

Assuming $locations_loop (a poor name for a variable containing array type data, tbh) has the following data:

$locations_loop = [
    ['id' => 1],
    ['id' => 2],
    ['id' => 3],
    ['id' => 4],
];

And you have a database table with the following schema: ( db-fiddle demo )

CREATE TABLE `locations_rating` (
  `id` int(11) NOT NULL,
  `l_id` int(11) NOT NULL,
  `stars` int(11) NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `locations_rating` (`id`, `l_id`, `stars`) VALUES
(1, 3, 4),
(2, 2, 2),
(3, 1, 0),
(4, 2, 5),
(5, 3, 2),
(6, 1, 10);

Then you can get all of your data in one trip to the database by creating a "derived table" from your column of id values, then joining the database data to them. Something like this:

SELECT def.l_id,
       ROUND(AVG(COALESCE(stars, 0)), 1) avg
FROM (
  (SELECT 1 AS l_id)
  UNION (SELECT 2)
  UNION (SELECT 3)
  UNION (SELECT 4)
 ) AS def
LEFT JOIN locations_rating AS loc ON def.l_id = loc.l_id
GROUP BY def.l_id

To do this with a prepared statement and bound parameters:

$locationIds = array_column($locations_loop, 'id');
$countIds = count($locationIds);

$fabricatedRows = implode(' UNION ', array_fill(0, $countIds, '(SELECT ? AS l_id)'));

$sql = "SELECT derived.l_id,
               ROUND(AVG(COALESCE(stars, 0)), 1) avg
        ($fabricatedRows) AS derived
        LEFT JOIN locations_rating as loc ON derived.l_id = loc.l_id
        GROUP BY def.l_id";
$stmt = $pdo->prepare($sql);
$stmt->execute($locationIds);
var_export($stmt->fetchAll(PDO::FETCH_ASSOC));

Should output: (I tested this technique to be successful in my local environment)

[
    ['l_id' => 1, 'avg' => 5.0],
    ['l_id' => 2, 'avg' => 3.5],
    ['l_id' => 3, 'avg' => 3.0],
    ['l_id' => 4, 'avg' => 0.0],
]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM