Nesting Foreach Loops in PHP - php

I have a class that has this type of structure:
Class League
Array Teams
Class Teams
Array Players
Class Players
String name
However, if I want to get a list of all players in the league, this doesn't seem to work:
foreach ($league->teams->players as $player) {
echo $player->name;
}
What am I missing? Do you have to use two foreach loops?

See this example:
<?php
//Create your players
$player1 = new stdClass;
$player2 = new stdClass;
$player3 = new stdClass;
$player1->name = 'Mike';
$player2->name = 'Luke';
$player3->name = 'Smith';
//Create your teams
$team1 = new stdClass;
$team2 = new stdClass;
//Adding the players to their teams
$team1->Players = array($player1, $player2);
$team2->Players = array($player3);
//Create the league
$league = new stdClass;
//Adding the teams to the league
$league->Teams = array($team1, $team2);
//For each element in the Teams array get the team in $team
foreach ($league->Teams as $teams) {
//For each element in the Players array get the player in $player
foreach($teams->Players as $player) {
//Print the name
echo $player->name . "<br>\n";
}
}
?>
Output:
Mike
Luke
Smith

So those are three separate classes, not a single class. You have shown nothing about how you relate those classes together and how you actually create the data structure. I don't see how you think you will be able to magically be able to list all players with a call such as $league->teams->players without having specific methods within each of the class to deal with aggregation from data stored in nested objects.
Without defining these relationships in your classes, you would need to do nested loops like this:
foreach ($league->Teams as $team) {
foreach($team->Players as $player) {
echo $player->name;
}
}
If you want methods to, for example list all players at the league level, you would need to define a methods to do this. Perhaps something like:
In team class:
public function function get_all_players() {
$return = array();
if(count($this->Players) > 0) {
$return = $this->Players;
}
return $return;
}
In league class:
public function get_all_players() {
$return = array();
if(count($this->Teams) > 0) {
foreach($this->Teams as $team) {
$return = array_merge($return, $team->get_all_players());
}
}
return $return;
}
Usage would be:
foreach($league->get_all_players() as $player) {
echo $player->name;
}

You may need two nested foreach
foreach ($league->teams as $team;){
foreach ($team->players as $player){
$list[] = $player->name;
}
}

Related

Returns multiple values from a PHP function

Apologize for the repeated question. Return multiple values from database with function. I tried executing code, the function returns one value, where I want all the values of id and name.
Database: id and name has 9 rows. Is there anything I was missing in my code.
function readdata() {
$sth = $db->execute('SELECT * FROM mynumbers m WHERE m.id>1 ORDER BY m.id ASC');
foreach ($sth as $s) {
$object = new stdClass();
$object->id = $s->id;
$object->name = $s->name;
return $object;
}
}
$rd = readdata();
echo $rd->id;
echo $rd->name;
May be something like this:
function readdata() {
$sth = $db->execute('SELECT * FROM mynumbers m WHERE m.id>1 ORDER BY m.id ASC');
$out = [];
foreach ($sth as $s) {
$object = new stdClass();
$object->id = $s->id;
$object->name = $s->name;
$out[] = $object;
}
return $out;
}
$rd = readdata();
//and here
foreach($rd as $obj){
echo $obj->id;
echo $obj->name;
}
This is more a suggestion than an answer. Why re-inventing the wheel? PDO already is capable of returning classes and also fetching all results into an array.
function readdata(PDO $db): array
{
$sth = $db->prepare('SELECT * FROM mynumbers m WHERE m.id>1 ORDER BY m.id ASC');
$sth->execute();
return $sth->fetchAll(PDO::FETCH_CLASS);
}
$objects = readdata($db);
$objects is now an array. Each element contains a stdClass object with each column name as property.
foreach($objects as $object) {
echo $object->id, PHP_EOL;
echo $object->name, PHP_EOL;
}
Your foreach loop intends to run through all values of the array $sth, but returns only with the FIRST one.
You can just return $sth to get the whole array, or build a new array and append to it:
$ret = array();
foreach ($sth as $s) {
...
$ret[] = $object;
}
and then
return $ret;

Recursive Mysql Query using Joomla Database Object

Hello im trying to fectch all childs and sub childs of a record in db
so far i've got this
function fetchNetChildren($parent, $network) {
$db = JFactory::getDBO();
$db->setQuery('SELECT id FROM #__sometable_clients WHERE network_referal = '.$parent.' AND networks = '.$network.' AND `status` > 0');
$list = array();
while (true){
$row = $db->loadAssocList();
$list[] = $row['id'];
$list = array_merge($list, fetchNetChildren($row['id'], $network));
}
return $list;
}
when i run this ive got a 500 eror and have no idea why, any tips?
Ive achieved this using a different aproach, ive discarded the initial funcion and created 2 others that returns what i want.
1 - I've created a mysql query that returns in a multidimensional array of all childrens, grandchildrens etc of the provided id.
function getChilds($parentid, $network) {
// query linda e maravilhosa
$query = '
select #pv:=id as id, network_referal from #__mytable
join
(select #pv:='.$parentid.')tmp
where network_referal=#pv and networks = '.$network.' ';
$campos = carregarcampos($query,'list');
return $campos;
}
2 - WIth the data $elements now i can manipulate and create a php multidimentional array organizing the provided data
function buildTree(array $elements, $parentId = 0) {
$branch = array();
foreach ($elements as $element) {
if ($element['parent_id'] == $parentId) {
$children = buildTree($elements, $element['id']);
if ($children) {
$element['children'] = $children;
}
$branch[] = $element;
}
}
return $branch;
}
so to use i must run
buildTree(getChilds($parentid, $network), $parentid)

group array of php objects by object property

I have a php object (book) with 3 properties: name, category, description
I then have a list of these book objects in an array.
I want to create a new associative array for these objects grouped by category.
Say I have 4 book objects in an array called $books
name category description
==================================
book1 cat1 this is book 1
book2 cat1 this is book 2
book3 cat2 this is book 3
book4 cat3 this is book 4
how can I create an associative array called $categories
$categories['cat1'] = array(book1, book2)
$categories['cat2'] = array(book2)
$categories['cat3'] = array(book3)
where book? is the book object and not the word
Like this:
foreach($books as $book)
{
$categories[$book->category][] = $book;
}
Simply loop the array of objects into a new array with the key being the category:
$newArray = array();
foreach($array as $entity)
{
if(!isset($newArray[$entity->category]))
{
$newArray[$entity->category] = array();
}
$newArray[$entity->category][] = $entity;
}
Is this what you was looking for ?
Explanation of the code:
/*
* Create a new blank array, to store the organized data in.
*/
$newArray = array();
/*
* Start looping your original dataset
*/
foreach($array as $entity)
{
/*
* If the key in the new array does not exists, set it to a blank array.
*/
if(!isset($newArray[$entity->category]))
{
$newArray[$entity->category] = array();
}
/*
* Add a new item to the array, making shore it falls into the correct category / key
*/
$newArray[$entity->category][] = $entity;
}
You can do it with ouzo goodies:
$categories = Arrays::groupBy($books, Functions::extractField('category'));
See: http://ouzo.readthedocs.org/en/latest/utils/arrays.html#groupby
$categories = array();
for ($i = 0; $i < count($books); $i++){
if (isset($categories[$books[$i]->category]) == false)
$categories[$books[$i]->category] = array();
$categories[$books[$i]->category][] = $books[$i]
}
cheers
Try this:
$categories = array();
foreach ($books as $book){
if (!array_key_exists($book->category , $categories))
$categories[$book->category] = array();
$categories[$book->category][] = $book;
}
This should works:
$categories = array();
foreach ($books as $book) {
$categories[$book['category']][] = $book;
}
I had a similar problem, but a bit more complicated with wordpress and metavalues/metakeys (where $results was an array of associative arrays fetched from a $wpdb->get_results() query.
This was my solution adapted to your problem:
$categories = array();
foreach ($results as $row) {
$id = $row['category'];
$description = $row['category'];
$name = $row['name']
if (!isset($categories[$id])) {
$categories[$id] = array();
}
$categories[$id] = array_merge($categories[$id], 'description'=>$description , 'name'=>$name);
}
Then you can run another for loop to get each array from the categories array:
foreach ($categories as $category) {
var_dump($category);
}
If you want to group objects by getting key from specific method with or without additional arguments, you can use this library method:
Arr::groupObjects(array $objects, string $method, ...$args): array

Building a multidimensional associative array using PHP and MYSQL

I'm trying to build a multidimensional associative array while fetching the results from a MySQL query.
But i can't figure out how to achieve this.
I want to build an array like this one:
array ("cat_name" => "category1",
"id_cat" => "1",
"sub_cat" => array ("name_sub_cat" => "subCategory",
"id_sub_cat" => "4",
"ss_cat" =>array ("ss_cat_name" =>"ss_cat1",
"id_ss_cat" => "4"
)
)
);
Here's where i'm building the array:
Edit : I've ended up with something like that, not very sexy, but if think i'm almost there
while($row = mysql_fetch_assoc($resultQueryCat)){
$menuArray[$row["id_cat"]]["cat_name"] = $row["cat_name"];
$menuArray[$row["id_cat"]][$row["id_sub_cat"]]["id_sub_cat"] = $row["id_sub_cat"];
$menuArray[$row["id_cat"]][$row["id_sub_cat"]]["name_sub_cat"] = $row["name_sub_cat"];
$menuArray[$row["id_cat"]][$row["id_sub_cat"]][$row["ss_cat_name"]]["ss_cat_name"] = $row["ss_cat_name"];
$menuArray[$row["id_cat"]][$row["id_sub_cat"]][$row["ss_cat_name"]]["id_ss_cat"] = $row["id_ss_cat"];
}
Edit2: The code to display the array
$menu.='<ul>';
foreach ($menuArray as $key) {
$compteur_cat++;
$menu.= '<div id="collapsiblePanelCol'.$compteur_cat.'" class="collapsiblePanelCol floatleft">
<li class="categorie"> '.$key["cat_name"].'
<ul>';
foreach ($key as $key1) {
if (is_array($key1){/* One of the thing i forgot which totally screwed up my results*/
$menu.= '<ul>
<li class="ss_categorie">'.$key1["name_sub_cat"].'<ul>';
foreach ($key1 as $key2) {
if (is_array($key2)){
$menu.= '<li class="element">'.$key2["ss_cat_name"].'</li>'; }
}
$menu.= '</ul></li></ul>';
}
}
$menu.='</ul>
</li>
</div>';
}
$menu.= '</ul>';
Thanks.
Final Edit: My code is working :) i edited the previous code to make it correct
It's not an easy task, as data from DB might contain loops :-)
I'd better save it in id->data hashmap so that you can build the structure easier.
Not sure why you need multidimensions.
The way I build multidimensional arrays is through classes and their objects.
Example:
class Categories
{
public $cat_name;
public $id_cat;
function __construct($cat_name, $id_cat)
{
$this->id = $id_cat;
$this->name = $cat_name;
$this->sub_cat = array();
}
}
class SubCategories
{
public $name_sub_cat;
public $id_sub_cat;
function __construct($name_sub_cat, $id_sub_cat)
{
$this->id = $id_sub_cat;
$this->name = $name_sub_cat;
$this->ss_cat = array();
}
}
class SSCategories
{
public $ss_cat_name;
public $id_ss_cat;
function __construct($ss_cat_name, $id_ss_cat)
{
$this->id = $id_ss_cat;
$this->name = $ss_cat_name;
}
}
Then to write to those classes (after fetching MySQL DB Data [assuming OOP mysql]):
if($result)
{
$array = array();
while($row = $result->fetch_array())
{
$array[] = new Categories($row["cat_name"], $row["id_cat"]);
}
foreach($array as $main)
{
... (fetching sub category info)
if($sub_result)
{
$sub_array = array();
while($sub_row = $sub_result->fetch_array())
{
$sub_array[] = new SubCategories($sub_row["name_sub_cat"], $sub_row["id_sub_cat"]);
}
$main->sub_cat = $sub_array;
You would repeat for each within original if loop for as many sub arrays as you have. This took a while :).
NOTE: Close if loops (heirarchally) if there are no more sub arrays.
echo '<pre>';
print_r($array);
echo '</pre>';
The above code will help show the levels of your array!

how to loop over looped elements in Php?

I'm creating a tree-structure of categories with parentid's which can be called from children in such a way:
ID | Name | ParentID
1 1 0
2 2 1
3 3 2
4 4 1
Resulting in this:
1 = 1
2 = 1 -> 2
3 = 1 -> 2 -> 3
4 = 1 -> 4
which means 3 is a child of 2, which is a child of 1.
when trying to get this idea (with the -> to show what relations are set) I only get to the second grade (1 -> 2) but not to the third (1->2->3) because of the looping function I use for it.
//put all ID's in an array
while ($row2 = $connector->fetchArray($result2)){
$id = $row2['ID'];
$parents[$id] = $row2['name'];
}
// show the tree-structure
while ($row = $connector->fetchArray($result)){
if($row['parentid']!=0)echo $parents[$row['parentid']].' -> ';
echo $row['name'].' - ';
echo '<br>';
}
I'd like two things to change:
have the code automatically generate a tree sized as necessary.
in the while-loops i have to select the $result twice (once as $result, once as $result2) to make it work. these $result's have exactly the same database-query:SELECT ID,name,parentid FROM categories
to fetch results from. I'd like to only declare this once.
Thanks for all the good answers. I've gone with the easiest, less-code-to-implement approach:
$result = $connector->query('SELECT ID,name,parentid FROM categories');
// Get an array containing the results.
$parents = array();
while ($row = $connector->fetchArray($result)){
$id = $row['ID'];
$parents[$id] = array('ID' => $row['ID'],'name' => $row['name'],'parentid' => $row['parentid']);
}
foreach ($parents as $id => $row){
$pid=$id;
$arrTmp= array();
do { // iterate through all parents until top is reached
$arrTmp[]=$pid;
$pid = $parents[$pid]['parentid'];
}while ($pid != 0);
$arrTmp = array_reverse($arrTmp);
foreach($arrTmp as $id){
echo $parents[$id]['name'].' -> ';
}
echo '<br>';
}
Rather than have PHP organize the items into a tree, why not ask the database to do it for you? I found this article on hierarchical data to be very good and the examples are almost identical to yours.
EDIT
The SQL for getting the full tree using the Adjacency Model is not ideal. As the article explains it requires rather a lot of joins for even a small hierarchy. Is it not possible for you to use the Nested Set approach? The SQL stays the same regardless of the size of the hierarchy and INSERT and DELETE shouldn't be very difficult either.
If you really want to do hierachies with parent ids(suitable only for small number of items/hierachies)
I modified your code a little bit(I did not test it so there may be some syntax errors):
//put all recordsets in an array to save second query
while ($row2 = $connector->fetchArray($result2)){
$id = $row2['ID'];
$parents[$id] = array('name' => $row2['name'],'parent' => $row2['parentid']);
}
// show the tree-structure
foreach ($parents as $id => $row){
$pid = $row['parentid'];
while ($pid != 0){ // iterate through all parents until top is reached
echo $parents[$pid]['name'].' -> ';
$pid = $parents[$pid]['parentid'];
}
echo $parents[$id]['name'].' - ';
echo '<br>';
}
To answer your comment:
$parents = array();
$parents[2] = array('ID'=>2,'name'=>'General','parentid'=>0);
$parents[3] = array('ID'=>3,'name'=>'Gadgets','parentid'=>2);
$parents[4] = array('ID'=>4,'name'=>'iPhone','parentid'=>3);
foreach ($parents as $id => $row){
$pid=$id;
$arrTmp= array();
do { // iterate through all parents until top is reached
$arrTmp[]=$pid;
$pid = $parents[$pid]['parentid'];
}while ($pid != 0);
$arrTmp = array_reverse($arrTmp);
foreach($arrTmp as $id){
echo $parents[$id]['name'].' -> ';
}
echo '<br>';
}
Prints out:
General ->
General -> Gadgets ->
General -> Gadgets -> iPhone ->
Maybe easier with OOP. Just sort the query by parentId
Note: The listChildren method and the printout at the bottom is just there to show it is listed correctly. I did not interpret the question that the display was important.
class Element {
public $id;
public $name;
public $parent = null;
public $children = array();
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
public function addChild($element)
{
$this->children[$element->id] = $element;
$element->setParent($this);
}
public function setParent($element)
{
$this->parent = $element;
}
public function hasChildren()
{
return !empty($this->children);
}
public function listChildren()
{
if (empty($this->children)) {
return null;
}
$out = array();
foreach ($this->children as $child) {
$data = $child->id . ':' . $child->name;
$subChildren = $child->listChildren();
if ($subChildren !== null) {
$data .= '[' . $subChildren . ']';
}
$out[] = $data;
}
return implode(',', $out);
}
}
$elements = array();
$noParents = array();
while ($row = $connector->fetchArray($result)) {
$elements[$row['id']] = $element = new Element($row['id'], $row['name']);
if (isset($elements[$row['parent']])) {
$elements[$row['parent']]->addChild($element);
} else {
$noParents[] = $element;
}
}
foreach ($noParents as $element) {
if ($element->hasChildren()) {
echo "Element {$element->id} has children {$element->listChildren()}.\n";
} else {
echo "Element {$element->id} has no children.\n";
}
}
If you are using PostgreSQL as the database, you can use the connectby() function to create the record set:
SELECT *
FROM connectby('tableName', 'id', 'parent_id')
AS t(keyid text, parent_keyid text, level int);
I love this function, and use all the time in my code. It can do some very powerful things, very quickly, and you don't have maintain the left/right values like the (adjacency model).

Categories