Issue with modulo and array index in php
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>';
}
}
精彩评论