Массив перезаписывается после цикла foreach

Я пытаюсь поместить объекты, содержащие элементы из набора результатов sql, в массив. Мой код выглядит так:

$data = array();
$sql = "SELECT id,type,name,username FROM users";
foreach ($conn->query($sql) as $row) { 
    $this->set_id($row['id']);
    $this->set_type($row['type']);
    $this->set_username($row['username']);
    $this->set_password($row['password']);

    $data[] = $this;
}

Мой набор результатов правильный, но после завершения цикла foreach я перезаписываю ячейки массива значениями из последнего набора записей. Например, если у меня есть эти результаты {1,'type1','user','pass'}, {2,'type2','foo','bar'}, когда я печатаю свой массив $data вне цикла, я получаю только второй набор результатов, повторяющийся дважды. Что я делаю неправильно?


person stebcom    schedule 28.02.2013    source источник
comment
Вы обновляете один экземпляр $this каждый раз, когда выполняете цикл, а затем указываете каждый элемент массива на один и тот же $this.... если $this является вашей моделью, вам нужен новый экземпляр для каждого элемента массива   -  person Mark Baker    schedule 28.02.2013


Ответы (4)


Внутри метода экземпляра $this всегда относится к текущему экземпляру класса; на каждой итерации цикла вы изменяете сам экземпляр, а затем добавляете его в $data; но в момент присваивания копия не делается, а вместо этого добавляется ссылка на тот же экземпляр.

В конце у вас есть массив с одним и тем же объектом по каждому индексу.

Вам нужно будет создать новый экземпляр класса, в котором вы находитесь, на каждой итерации цикла:

foreach ($conn->query($sql) as $row) { 
    $obj = new self; // create new instance of ourselves

    $obj->set_id($row['id']);
    $obj->set_type($row['type']);
    $obj->set_username($row['username']);
    $obj->set_password($row['password']);

    $data[] = $obj;
}

Это личное предложение, но я бы либо переместил этот код в статический метод, либо вообще в отдельный класс.

person Ja͢ck    schedule 28.02.2013

Объекты так не работают, $this всегда один и тот же объект, и вы изменяете его на каждой итерации. Вы могли использовать $data[] = clone $this; для создания нового экземпляра каждый раз, но это плохой дизайн класса, и вам было бы лучше разбить свой класс на два отдельных класса.

У класса не должно быть более одной ответственности, но ваша обязанность действует как шлюз базы данных и модель предметной области одновременно.

person Fabian Schmengler    schedule 28.02.2013
comment
Спасибо потрясающе, я решил эту задачу по разделению модели и базы данных! - person stebcom; 28.02.2013

Что бы ни было $this, это ссылка на какой-то объект. Итак, когда вы обновляете этот объект, все ссылки, сохраненные в массиве, по-прежнему указывают на этот же объект. Вы должны определить новый объект в каждом цикле.

person yourdeveloperfriend    schedule 28.02.2013

Вы не создаете новый объект $this в начале итерации. В обеих итерациях вы изменяете один и тот же объект и дважды добавляете его в массив. При работе с объектами они передаются по ссылке, что означает, что вы не добавляете копию в массив, а добавляете ссылку на переменную $this. Возможно, вам следует вместо этого добавить $row в массив, но вы все равно перезапишете данные, сохраненные в $this.

Вы также можете использовать ключевое слово clone (см. руководство), чтобы сохранить копию объекта.

Дополнительная информация: http://php.net/manual/en/language.references.php< /а>

person leafnode    schedule 28.02.2013
comment
Оказал вам такую ​​же услугу. Я думаю, кто-то просто -1 всем нам. - person yourdeveloperfriend; 28.02.2013