Chest Rockwell
Chest Rockwell

Reputation: 177

Appending new data to object

I'm stuck trying to append new data to an stdClass object that I'm creating for an AMchart

I'm returning all the rows I want from the DB, then creating a new object and looping through the returned array, but rather than appending what I want to the end of the existing object, it just gets overwritten. PHP objects dont have an append or push method, so how do you accomplish this?

Here's what my code looks like. Am I missing something simple?

    $sql = 'SELECT 
            count(*) as clients,
            STR_TO_DATE(Appt_date, \'%m/%d/%Y\') AS date,
            SUM(wait_time) as total_wait_time
            FROM tb_by_client
            WHERE status = @qualifier
            GROUP BY Appt_date';

    $rows = $db->fetchAll($sql);
    $chartObject = new stdClass();
    foreach($rows as $row){
        $row->average = round($row->total_wait_time / $row->clients);
        $chartObject->date = $row->date;
        $chartObject->average = $row->average;
    }
    $chartArray[] = $chartObject;
    return json_encode($chartArray);

So instead of getting something that looks like this

[{"date":"2018-10-01","average":12},{"date":"2018-10-02","average":-33},{"date":"2018-10-04","average":23},{"date":"2018-10-05","average":6}]

I get back just a single

[{"date":"2018-10-01","average":12}]

Because each loop overwrites the last key and value

How do you append instead?

Upvotes: 0

Views: 2198

Answers (3)

ArtisticPhoenix
ArtisticPhoenix

Reputation: 21661

Your problem is you overwrite the data without saving it

$chartObject = new stdClass();
foreach($rows as $row){
    $row->average = round($row->total_wait_time / $row->clients);
    $chartObject->date = $row->date;
    $chartObject->average = $row->average;
}
$chartArray[] = $chartObject;

See on each iteration of foreach($rows as $row){ you change the data in $chartObject, but you never save in your $chartArray.

Do this instead

foreach($rows as $row){
    $chartObject = new stdClass(); //new instance of stdClass, obj pass by refrence
    $row->average = round($row->total_wait_time / $row->clients);
    $chartObject->date = $row->date;
    $chartObject->average = $row->average;
    $chartArray[] = $chartObject;
}

Personally I wouldn't even bother with using an object:

foreach($rows as $row){
    $average = round($row->total_wait_time / $row->clients);
    $chartArray[] = ['date'=>$row->date,'average'=>$average];
}

When you JSON Encode an array with string keys, it will make it the correct Javascript Object structure. So there really is no need to keep all those objects in memory and the code is much smaller, cleaner, and easier to read.

One last thing I hinted at in the code, is that objects are pass by reference in PHP (now), and if you don't create a new instance of the object for each iteration, you will actually update all references to the object. This can be illustrated like this:

$obj = new stdClass;

$objects = [];

for($i=0;$i<3;++$i){
    $obj->foo = $i;
    $objects[] = $obj;
    print_r($objects);
}

Output:

Array
(
    [0] => stdClass Object
        (
            [foo] => 0
        )

)
Array
(
    [0] => stdClass Object
        (
            [foo] => 1
        )

    [1] => stdClass Object
        (
            [foo] => 1
        )

)
Array
(
    [0] => stdClass Object
        (
            [foo] => 2
        )

    [1] => stdClass Object
        (
            [foo] => 2
        )

    [2] => stdClass Object
        (
            [foo] => 2
        )

)

Sanbox

Each array is a single iteration of the for loop, this is the same array with another row added after each iteration.

As you can see each copy (its not really a copy) is updated by reference in the array. Basically we have stored the same object (instance ,will call him Bob) 3 times, instead of 3 separate objects (Bob, Alice, John).

If the data you stored was the color of a persons shirt, when Bob puts on a red shirt, he has a red shirt on, but Alice and John don't.

Because of this you need to create a new instance of the object for each iteration and store that.

Hope that helps!

Upvotes: 5

Nigel Ren
Nigel Ren

Reputation: 57121

You can do the maths in the SQL and it cuts out the loop altogether...

   $sql = 'SELECT STR_TO_DATE(Appt_date, \'%m/%d/%Y\') AS date,
            round(SUM(wait_time)/count(*)) as average
            FROM tb_by_client
            WHERE status = @qualifier
            GROUP BY Appt_date';

    return json_encode($db->fetchAll($sql));

Upvotes: 3

Mocca
Mocca

Reputation: 499

You're misunderstanding what should be in the loop and what shouldn't.

This should be fine:

$sql = 'SELECT 
        count(*) as clients,
        STR_TO_DATE(Appt_date, \'%m/%d/%Y\') AS date,
        SUM(wait_time) as total_wait_time
        FROM tb_by_client
        WHERE status = @qualifier
        GROUP BY Appt_date';

$rows = $db->fetchAll($sql);
$chartArray = [];
foreach($rows as $row){
    $row->average = round($row->total_wait_time / $row->clients);
    $chartObject = new stdClass();
    $chartObject->date = $row->date;
    $chartObject->average = $row->average;
    $chartArray[] = $chartObject;
}
return json_encode($chartArray);

Upvotes: 1

Related Questions