HTML table in php foreach loop where the cell data is only populated if an element is equal to the table column head name

Viewed 836

I have created my column headers in my table by using:

if (count($round) > 0): ?>
<table>
  <thead>
    <tr>
<?php foreach ($round as $colhead): array_map('htmlentities', $colhead); ?>
      <th><?php  echo implode('</th><th>',$colhead); ?></th>
    </tr>
<?php endforeach; endif;
?>
  </thead>

$round is:

Array
(
    [0] => Array
        (
            [32] => -7
            [31] => -6
            [29] => -5
            [27] => -4
            [23] => -3
            [19] => -2
            [17] => -1
            [0] => 1
            [2] => 2
            [10] => 3
            [14] => 4
            [16] => 5
            [33] => 6
        )

)

This results in exactly what I want because $round won't always result in the same number of columns.

The data I want in the table row beneath those headers needs to be only those items that contain the column header's name.

Let's say the column headers are: | -7 | -6 | -5 | -4 | -3 | -2 | -1 | 1 | 2 | 3 | 4 | 5 | 6 | based on the foreach loop above.

I used the following to then populate my first column in the next row:

<tbody>
    <tr>
      <td>
  <?php foreach ($matches as $match)  {
$firstCharacter = $match['match']['scores_csv'][0];
$lastCharacter = $match['match']['scores_csv'][-1]; 
$id1 = $match['match']['player1_id'];
$id2 = $match['match']['player2_id'];
    if ($match['match']['round'] === -7) 
      echo  "Table: ".$match['match']['player1_votes']."</br>".$participants[$id1]["name"].$participants[$id1]['misc'].": ".$firstCharacter. "</br>".$participants[$id2]["name"].$participants[$id2]['misc'].":".$lastCharacter. "</br>";
  }
  ?>
    </td>
   </tr>
  </tbody>
</table>

And while it works, it's only because I know the header of my first column in the table is -7. I'm trying to figure out how to only display those items that are equal to my column header to display in the following row based on the column header so that I can then do another foreach loop to display the remainder of my table. So basically, instead of -7 that I have above, what do I need to do? I'm completely drawing a blank.

$match['match']['round'] is what is equal to the column headers. And a small sample $matches is below in case that helps.

 [{"match":{"id":246811883,"tournament_id":10128202,"state":"complete","player1_id":152239163,"player2_id":152239166,"player1_prereq_match_id":null,"player2_prereq_match_id":null,"player1_is_prereq_match_loser":false,"player2_is_prereq_match_loser":false,"winner_id":152239166,"loser_id":152239163,"started_at":"2021-08-07T01: 59: 51.967+02: 00","created_at":"2021-08-07T01: 59: 51.692+02: 00","updated_at":"2021-08-07T02: 22: 29.417+02: 00","identifier":"A","has_attachment":false,"round":1,"player1_votes":1,"player2_votes":null,"group_id":null,"attachment_count":null,"scheduled_time":null,"location":null,"underway_at":"2021-08-07T02: 05: 31.834+02: 00","optional":false,"rushb_id":null,"completed_at":"2021-08-07T02: 22: 29.455+02: 00","suggested_play_order":null,"forfeited":null,"open_graph_image_file_name":null,"open_graph_image_content_type":null,"open_graph_image_file_size":null,"prerequisite_match_ids_csv":"","scores_csv":"0-2"}},{"match":{"id":246811884,"tournament_id":10128202,"state":"complete","player1_id":152239594,"player2_id":152239168,"player1_prereq_match_id":null,"player2_prereq_match_id":null,"player1_is_prereq_match_loser":false,"player2_is_prereq_match_loser":false,"winner_id":152239594,"loser_id":152239168,"started_at":"2021-08-07T01: 59: 51.984+02: 00","created_at":"2021-08-07T01: 59: 51.698+02: 00","updated_at":"2021-08-07T02: 32: 30.655+02: 00","identifier":"B","has_attachment":false,"round":1,"player1_votes":2,"player2_votes":null,"group_id":null,"attachment_count":null,"scheduled_time":null,"location":null,"underway_at":"2021-08-07T02: 05: 23.667+02: 00","optional":false,"rushb_id":null,"completed_at":"2021-08-07T02: 32: 30.690+02: 00","suggested_play_order":null,"forfeited":null,"open_graph_image_file_name":null,"open_graph_image_content_type":null,"open_graph_image_file_size":null,"prerequisite_match_ids_csv":"","scores_csv":"2-1"}}]

I've attempted to reindex the array by using:

$matchesa = array_column($matches, 'match');
$matchesa = array_column($matchesa, null, 'round');

foreach ( $matches as $match )  {
            $id1 = $match['match']['round'];
 {
        echo $matchesa[$id1]['round'].PHP_EOL;
    }
  }

Desired output is like this: enter image description here

3 Answers

You can use a dictionary to store all the matches based on the rounds:

<?php

/*#########SAMPLE DATA########*/

$matches = <<<MATCHES
[{"match":{"id":246811883,"tournament_id":10128202,"state":"complete","player1_id":152239163,"player2_id":152239166,"player1_prereq_match_id":null,"player2_prereq_match_id":null,"player1_is_prereq_match_loser":false,"player2_is_prereq_match_loser":false,"winner_id":152239166,"loser_id":152239163,"started_at":"2021-08-07T01: 59: 51.967+02: 00","created_at":"2021-08-07T01: 59: 51.692+02: 00","updated_at":"2021-08-07T02: 22: 29.417+02: 00","identifier":"A","has_attachment":false,"round":1,"player1_votes":1,"player2_votes":null,"group_id":null,"attachment_count":null,"scheduled_time":null,"location":null,"underway_at":"2021-08-07T02: 05: 31.834+02: 00","optional":false,"rushb_id":null,"completed_at":"2021-08-07T02: 22: 29.455+02: 00","suggested_play_order":null,"forfeited":null,"open_graph_image_file_name":null,"open_graph_image_content_type":null,"open_graph_image_file_size":null,"prerequisite_match_ids_csv":"","scores_csv":"0-2"}},{"match":{"id":246811884,"tournament_id":10128202,"state":"complete","player1_id":152239594,"player2_id":152239168,"player1_prereq_match_id":null,"player2_prereq_match_id":null,"player1_is_prereq_match_loser":false,"player2_is_prereq_match_loser":false,"winner_id":152239594,"loser_id":152239168,"started_at":"2021-08-07T01: 59: 51.984+02: 00","created_at":"2021-08-07T01: 59: 51.698+02: 00","updated_at":"2021-08-07T02: 32: 30.655+02: 00","identifier":"B","has_attachment":false,"round":1,"player1_votes":2,"player2_votes":null,"group_id":null,"attachment_count":null,"scheduled_time":null,"location":null,"underway_at":"2021-08-07T02: 05: 23.667+02: 00","optional":false,"rushb_id":null,"completed_at":"2021-08-07T02: 32: 30.690+02: 00","suggested_play_order":null,"forfeited":null,"open_graph_image_file_name":null,"open_graph_image_content_type":null,"open_graph_image_file_size":null,"prerequisite_match_ids_csv":"","scores_csv":"2-1"}}]
MATCHES;

$round = <<<ROUND
[-7, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6]
ROUND;

$matches = json_decode($matches, true);
$round = json_decode($round);


/*###########################*/

//class to define data when we found a match
class MatchInfo {
    public $firstCharacter;
    public $lastCharacter;
    public $player1Votes;
    public $player1Name;
    public $player1Misc;
    public $player2Name;
    public $player2Misc;
}

$tableHeaders = null;
$matchInfoData = [];
//$participants = [...]

if (!empty($round)) { //if we have round
    //get the html for table headers
    $tableHeaders = array_map(function($colHead){
        return "<th>Round: {$colHead}</th>";
    }, $round);
    $tableHeaders = implode("", $tableHeaders);

    //remove item in matches
    while ($match = array_pop($matches)) {
        $targetRound = $match['match']['round'];
        if (!in_array($targetRound, $round)) continue; //skip item if match round is not in our column

        if (!array_key_exists($targetRound, $matchInfoData)) { //if we never look at this round before
            $matchInfoData[$targetRound] = []; //initialize array to hold other match based on the round
        }


        $id1 = $match['match']['player1_id'];
        $id2 = $match['match']['player2_id'];
        //new up and save off the data
        $matchInfo = new MatchInfo();
        $matchInfo->firstCharacter = $match['match']['scores_csv'][0];
        $matchInfo->lastCharacter = $match['match']['scores_csv'][-1];
        $matchInfo->player1Votes = $match['match']['player1_votes'];
        $matchInfo->player1Name = "Player1 Name"; //$participants[$id1]["name"];
        $matchInfo->player1Misc = "Player1 Misc"; //$participants[$id1]["misc"];
        $matchInfo->player2Name = "Player2 Name"; //$participants[$id2]["name"];
        $matchInfo->player2Misc = "Player2 Misc"; // $participants[$id2]["misc"];

        $matchInfoData[$targetRound][] = $matchInfo; //add data to the round...array_push
    }

}

//if no matches found exit
if (empty($matchInfoData)) {
    echo "No match info";
    exit;
}

$tableData = '';
//now we loop through all the array to be sure its the same order as the ths
foreach ($round as $td) {
    $columnHtml = '';
    if (array_key_exists($td, $matchInfoData)) { //if what we stored contains the targeted column
        //create a td html
        $dataHtmlArray = array_map(function(MatchInfo $curItem){
            return <<<ITEMDATA
    <div class="match-info">
        Table: {$curItem->player1Votes} <br/>
        {$curItem->player1Name} {$curItem->player1Misc} {$curItem->firstCharacter} <br/>
        {$curItem->player2Name} {$curItem->player2Misc} {$curItem->lastCharacter } <br/>
    </div>
ITEMDATA;

        },$matchInfoData[$td]);

        $dataHtml = implode("<br/>", $dataHtmlArray);
        $columnHtml = "<td><div class='match-container'> {$dataHtml} </div></td>";

    }else { //otherwise it needs a blank td to go to next column
        $columnHtml = "<td></td>";
    }
    $tableData .= $columnHtml;
}

?>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">

<table class="table table-bordered">
    <thead>
    <tr><?php echo $tableHeaders; ?></tr>
    </thead>
    <tbody>
    <tr>
        <?php echo $tableData; ?>
    </tr>

    </tbody>
</table>

<style>
    .match-info {
        border: 1px solid #dadde5;
        padding: 24px;
        border-radius: 4px;
    }
</style>

A few comments up front:

Your $round array has one element yet you have a foreach statement iterating "all" of the elements ...

foreach ($round as $colhead): array_map('htmlentities', $colhead);

... which doesn't make much sense semantically because a table should not have multiple headers. Then you invoke array_map on the $colhead list but the results are not saved and used later on. Moreover, as these values are integers, there is no reason to even apply the htmlentities function to these values. Later on, when you are putting out entities such as names, is where this function could prove useful.

The following code builds a map between a round number and the index into the $matches array where that round number can be found. Then the processing becomes greatly simplified. Just for demo purposes:

  1. I am only outputting for each match the player1_id value so that the details of processing do not get lost and for simplicity sake I have set this value to be the same as the round number.
  2. If a matching round is not found, the table column is just filled with a <br>.
  3. Also for simplicity sake I have defined the $round array to contain one element, a list with values [-1, -3, -5] and the $matches array will only have data for two table rows. The first row for round numbers -1 and -5 (-3 will be missing) and the second row will have all three round numbers.
<?php
$round = [[-1, -3, -5]];

$match0 = [];
$match0[] = Array('match' => Array('round' => 2, 'player1_id' => 2));
$match0[] = Array('match' => Array('round' => 1, 'player1_id' => 1));
$match0[] = Array('match' => Array('round' => 3, 'player1_id' => 3));
$match0[] = Array('match' => Array('round' => -1, 'player1_id' => -1));
$match0[] = Array('match' => Array('round' => -4, 'player1_id' => -4));
$match0[] = Array('match' => Array('round' => -5, 'player1_id' => -5));
$match0[] = Array('match' => Array('round' => -6, 'player1_id' => -6));

$match1 = [];
$match1[] = Array('match' => Array('round' => -7, 'player1_id' => -7));
$match1[] = Array('match' => Array('round' => -1, 'player1_id' => -1));
$match1[] = Array('match' => Array('round' => -2, 'player1_id' => -2));
$match1[] = Array('match' => Array('round' => -3, 'player1_id' => -3));
$match1[] = Array('match' => Array('round' => -8, 'player1_id' => -8));
$match1[] = Array('match' => Array('round' => 9, 'player1_id' => 9));
$match1[] = Array('match' => Array('round' => -5, 'player1_id' => -5));

$matches = [$match0, $match1];


if (count($round) > 0):
    $round0 = $round[0];
?>
<table>
  <thead>
    <tr>
<?php
    foreach($round0 as $hdr):
?>
      <th><?= $hdr ?></th>
<?php
    endforeach;
?>
    </tr>
  </thead>
  <tbody>
<?php
    foreach ($matches as $match):
        // create mapping between round number and index into $matches:
        $index_map = [];
        $l = count($match);
        for ($i = 0; $i < $l; $i++):
            $matchRound = $match[$i]['match']['round'];
            $index_map[$matchRound] = $i;
        endfor;
?>
    <tr>
<?php
        foreach($round0 as $matchRound):
            if (!array_key_exists($matchRound, $index_map)):
?>
      <th><br></th>
<?php
            else:
                $theMatch = $match[$index_map[$matchRound]]['match']
?>
      <th><?= $theMatch['player1_id'] ?></th>
<?php
            endif;
        endforeach;
    endforeach;
?>
    </tr>
  </tbody>
</table>
<?php
endif;
?>

Outputs:

<table>
  <thead>
    <tr>
      <th>-1</th>
      <th>-3</th>
      <th>-5</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>-1</th>
      <th><br></th>
      <th>-5</th>
    <tr>
      <th>-1</th>
      <th>-3</th>
      <th>-5</th>
    </tr>
  </tbody>
</table>

You already have all your header columns in $colhead.

So, you can iterate the columns, like this:

foreach ($matches as $match) {
    $colindex = 0;
    for ($colindex = 0; $colindex < count($colhead), $colindex++) {
        if ($match['match']['round'] === $colhead[$colindex]) {
            //your cell template here
        }
    }
}
Related