开发者

Issue with modulo and array index in php

开发者 https://www.devze.com 2023-02-10 15:50 出处:网络
I\'m trying to create an html table from database records. I want the table to be 4 cells wide, so I figured if the array index of the row I\'m looping through is a multiple of 4, then add a tr tag at

I'm trying to create an html table from database records. I want the table to be 4 cells wide, so I figured if the array index of the row I'm looping through is a multiple of 4, then add a tr tag at the beginning. However, it doesn't seem to be working correctly. Can anyone help me out? (This is CodeIgniter, so the echo anchor, etc. just creates an a href tag.)

<table width="80%" border="1">
<tr> <!-- create initial tr tag, since we haven't started the开发者_C百科 loop yet -->
<?php foreach($projects as $index=>$project) : ?>
    <?php echo ((($index + 1) % 4 == 0) ? '<tr>' : ''); ?>
    <td>
    <?php echo anchor('project/view/'.$project->id, $project->project_name, 'title='.$project->project_name); ?>
    </td>
    <?php echo ((($index + 1) % 4 == 0) ? '</tr>' : ''); ?>
<?php endforeach; ?>
 </table>

The $index + 1 is because dividing by 0 (where the array index starts) causes an error. I echoed out the values of $index+1 on each row, and if I have 5 rows in my table I get 1, 2, 3, 4, 5. But the line that should create the tr tag if $index+1 does not divide evenly by 4 isn't evaluating; I'm getting a single row table that just keeps getting wider.

Here's what I'd expect to see as rendered code:

<table width="80%" border="1">
  <tr> 
    <td><a href="http://localhost/ignite/index.php/project/view/1" title=Basil's Beatnik Turtle>Basil's Beatnik Turtle</a></td> 
    <td><a href="http://localhost/ignite/index.php/project/view/2" title=Mr. Werewolf Genes>Mr. Werewolf Genes</a></td> 
    <td><a href="http://localhost/ignite/index.php/project/view/3" title=Romeo+Juliet>Romeo+Juliet</a></td> 
    <td><a href="http://localhost/ignite/index.php/project/view/4" title=Basic Hat>Basic Hat</a></td> 
  </tr>
  <tr>
    <!-- I'm not trying to auto-generate empty cells to fill in a final row that contains fewer than 4 at this point, although it would be nice -->
    <td><a href="http://localhost/ignite/index.php/project/view/5" title=Flutterby Hat>Flutterby Hat</a></td> 
  </tr>
</table> 

Here's what I'm actually getting:

<table width="80%" border="1">
  <tr> 
    <td><a href="http://localhost/ignite/index.php/project/view/1" title=Basil's Beatnik Turtle>Basil's Beatnik Turtle</a></td> 
    <td><a href="http://localhost/ignite/index.php/project/view/2" title=Mr. Werewolf Genes>Mr. Werewolf Genes</a></td> 
    <td><a href="http://localhost/ignite/index.php/project/view/3" title=Romeo+Juliet>Romeo+Juliet</a></td> 
  <!-- note the lack of closing </tr> tag on previous chunk - plus it's only 3 cells, not 4 -->

  <tr>
    <td><a href="http://localhost/ignite/index.php/project/view/4" title=Basic Hat>Basic Hat</a></td>
  </tr>
  <!-- this previous chunk has both <tr> and </tr>, but only contains one <td> -->

    <td><a href="http://localhost/ignite/index.php/project/view/5" title=Flutterby Hat>Flutterby Hat</a></td> 
  <!-- the previous chunk is missing both <tr> and </tr> tags, and is only a single cell -->
</table>     
Basil's Beatnik Turtle Mr. Werewolf Genes Romeo+Juliet tag on previous chunk - plus it's only 3 cells, not 4 --> Basic Hat and , but only contains one --> Flutterby Hat and tags, and is only a single cell -->

Where am I going wrong?


Without knowing what the data that's being returned from the SQL query looks like, it seems like $index is not what you are expecting it to be.

I humbly suggest this alternative:

<?
$count = 1;
foreach($projects as $index=>$project)
{
    if($count % 4 == 0)
    {
         print "</tr><tr>";
    }
    print "<td>" . anchor('project/view/'.$project->id, $project->project_name, 'title='.$project->project_name) . "</td>";
    $count++;
}
?>


I'd suggest switching to using a counter variable, also, you'll need to rethink the last part of it, because unless it ends on a multiple of 4, it's not gonna give you the final /tr either.

Here's how I'd do it, and I'd also get rid of those awful ternary operators:

<table width="80%" border="1">
<?php
$i = 1;
foreach($projects as $index=>$project)
{
    if($i == 1)
    {
          echo '<tr>';
    }

    echo '<td>' . anchor('project/view/'.$project->id, $project->project_name, 'title='.$project->project_name) . '</td>';

    if($i == 4)
    {
          echo '</tr>';
          $i = 0;
    }

    $i++;
} 

if($i != 1) //Catch it if it doesn't end evenly, since $i will == 1 if it ended on a multiple of 4
{
     while($i <= 4) //Create empty cells to even table out, table will quite possibly look funky otherwise 
     {
         echo '<td></td>';
         $i++;
     }
     echo '</tr>'; //End table row
}
?>
 </table>

If you want it to look nice, append PHP_EOL or "\n" to the end of each echo statement. If you want it to look really nice, you could prepend "\t" to certain echo statements to give it the look of a manually indented and hand coded html.


My preferred solution for things like this is breaking it out into a helper function (codeigniter details). It keeps the markup cleaner and makes sure that after you've solved it once you don't have to rethink it.

Here's a crack and what you're after. It will return a multidimensional array of rows that you can iterate over.

function rowify($array, $perRow) {
    $currentRow = 0;
    $counter = 0;
    $new = array();
    foreach ($array as $key => $element) {
        if ($counter % 4 == 0) {
            ++$currentRow;
            $new[$currentRow] = array();
        }
        $new[$currentRow][$key] = $element;
        ++$counter;
    }
    return $new;
}

Using this, your markup will look like:

<table width="80%" border="1">
<?php foreach(rowify($projects,4) as $row) : ?>
  <tr>
    <?php foreach ($row as $index=>$project):?>
      <td>
        <?php echo anchor('project/view/'.$project->id, $project->project_name, 'title='.$project->project_name); ?>
      </td>
    <?php endforeach; ?>
  </tr>
<?php endforeach; ?>
 </table>

which I think is quite elegant - no if statements, no echo ''; stuff, just iteration.


At the moment, you're adding a tr at the beginning of every fourth cell, and a end tr at the end of every fourth cell. The end tr needs to be added after the fourth cell, and the tr should be added before the fifth one.


Think about it - you're using the exact same logic check to place the opening <tr> as the closing </tr>. This means that every 4th element will be surrounded by an opening row tag and a closing row tag.


if it helps - here is some old code I dug out that does it - not codeigniter but should guide you - have taken my db stuff out to clean it up:

define('NUMCOLS', 4);
$count = 0;
$counter = 1;
//create table
echo '<table width = \"100%\" border = 1 cellpadding = 0>';
//create a table row for each book   
foreach ($book_array as $row)
{
  // get your data
  if($count % NUMCOLS == 0) //new row
    echo '<tr>';
    echo'<td>';
    // display data
    echo '</td>';
    $count++;
   $counter++;
  if($count % NUMCOLS ==0)
    echo '</tr>'; //end row
}
echo '</table>';
}
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号