I am working on an homework assignment, and I am having a hard time with it. Any help is appreciated!
For the Module 2 Exercise, students will write a PHP program named "moduletwo.php" that generates an HTML table with the following requirements:
A function called "makeTable($headerArray, $numRows)" that creates a table with headers as they appear in the array and however many rows are specified.
If a number of rows is not specified, use a default of 5
Even number rows should use the class "dark"
Odd number rows should use the class "light"
There should be a column that has only the row number
Create an array of table headers (these can be whatever you want, but be creative here, and do not just use "Heading 1" or generic term)
This function will be used in the body of the HTML document to create an empty table with the array of headers
The HTML document should be complete, including DOCTYPE and all necessary elements.
Write a CSS file named "moduletwo.css" to go along with this that creates the "dark" and "light" classes with background colors of your choosing, but that must be readable and reflect the "dark" and "light" names. Link to this in the head section of the page."
this is what i have so far:
<?php
function makeTable($headerarray, $numrows)
{
echo "<table>"; # echo the table begining
foreach ($headerarray as $header) { # loop through the erray to print the th tag
echo "<th>" . $header . "</th>";
}
foreach ($numRows as $row) { #loop $numrows times to create that number of empty rows
echo "<tr>";
echo "<td>";
echo "</td>";
echo "</tr>";
}
echo "</table>"; # echo table ending;
}
$headers = array(
"Book Title",
"Book Author",
"Publication Date",
"Book Pages",
"Finished Book"
);
makeTable($headers, 5); #create a table with 5 empty rows under the header
?>
I'm struggling with creating the rows.
First, I want to say that I do not recommend copying this exactly and using it for your homework. You should read the comments and attempt to understand why it does what it does. I've done my best to comment the code so you can learn it.
Newbie Friendly
//define default `$numrows` in function declaration
function makeTable($headerarray, $numrows = 5) {
//create table beginning
echo "<table>";
//create first row, which is your header
//this needs to be done OUTSIDE of the header loop, as there is only 1 set of headers.
echo "<tr>";
//loop through headers using FOREACH
foreach($headerarray as $header) {
//add <th> element for each header
echo "<th>" . $header . "</th>";
}
//end table row (header)
echo "</tr>";
//define ODD class. The first loop is ALWAYS ODD.
$class = 'light';
//get a count of columns
$numcolumns = count($headerarray);
//loop a specific number of times (number of rows), using a FOR loop
for ($row = 1; $row <= $numrows; $row++) {
//create table row inside FIRST FOR LOOP. This creates a row each time.
//also add `$class` to row.
echo "<tr class=\"" . $class . "\">";
//loop a specific number of times (number of columns). This creates a column each time.
for ($column = 1; $column <= $numcolumns; $column++) {
//create a <td> element (cell)
echo "<td>";
if($column == 1) { //if column is 1
//use row number as cell content
echo "Row " . $row;
} else { //if column IS NOT 1
//use column number as content
echo "Column " . $column; #show column number as content
}
//end <td> element (cell)
echo "</td>"; #end row
}
//end table row
echo "</tr>";
//AT THE END OF ROW LOOP, switch `$class` to 'dark' if it is currently 'light'
//or switch it to 'light' if it is currently 'dark'
//since this is at the end of the row loop, it takes effect on the next loop. That means it should alternate between the 2
/***
This solution has several benefits. First, you don't need to know if the current row is even or odd.
second, you could use this to check if the row is even/odd IF YOU NEED TO
(if `$class` is 'light', you know the row is ODD)
**/
if($class == 'light') {
$class = 'dark';
} else {
$class = 'light';
}
}
//end table
echo "</table>";
}
Which results in this HTML using your example data.
<table>
<tr>
<th>Book Title</th>
<th>Book Author</th>
<th>Publication Date</th>
<th>Book Pages</th>
<th>Finished Book</th>
</tr>
<tr class="light"><td>Row 1</td>
<td>Column 2</td>
<td>Column 3</td>
<td>Column 4</td>
<td>Column 5</td>
</tr>
<tr class="dark"><td>Row 2</td>
<td>Column 2</td>
<td>Column 3</td>
<td>Column 4</td>
<td>Column 5</td>
</tr>
<tr class="light"><td>Row 3</td>
<td>Column 2</td>
<td>Column 3</td>
<td>Column 4</td>
<td>Column 5</td>
</tr>
<tr class="dark"><td>Row 4</td>
<td>Column 2</td>
<td>Column 3</td>
<td>Column 4</td>
<td>Column 5</td>
</tr>
<tr class="light"><td>Row 5</td>
<td>Column 2</td>
<td>Column 3</td>
<td>Column 4</td>
<td>Column 5</td>
</tr>
</table>
The CSS part is pretty simple, you just need to define a dark and light rule.
tr.light {
background-color: LIGHT_COLOR;
}
tr.dark {
background-color: DARK_COLOR;
}
A more advanced approach
If I had to do this, I'd probably do something like this:
function makeTable($headerarray, $numrows = 5) {
$headers = "";
foreach($headerarray as $head) {
$headers .= "<th>{$head}</th>";
}
$body = "";
$numcolumns = count($headerarray);
for ($row = 1; $row <= $numrows; $row++) {
$tmp = "<tr>";
for ($col = 1; $col <= $numcolumns; $col++) {
$content = $col == 1 ? "Row {$row}" : "Column {$col}";
$tmp .= "<td>{$content}</td>";
}
$body .= $tmp . "</tr>";
}
$html = "
<table>
<thead>
<tr>
{$headers}
</tr>
</thead>
<tbody>
{$body}
</tbody>
</table>
";
return $html;
}
This function returns the table instead of directly echoing it. This allows you to do more stuff with the HTML at the very end if you need to. You would use this function like this:
$headers = array(
"Book Title",
"Book Author",
"Publication Date",
"Book Pages",
"Finished Book"
);
echo makeTable($headers, 5);
I also completely removed the classes from the rows, because it's easier to do with CSS alone, which will react better to rows being removed by javascript or whatever. It can be applied like this:
tbody > tr:nth-child(even) {
background-color: DARK_COLOR;
}
tbody > tr:nth-child(odd) {
background-color: LIGHT_COLOR;
}
Related
I have this async code , but when i try to render inside a table , the table become a mess (image) but after a page refresh the table is perfctly fine and i dont know why , how can i fix this problem ?
and if is possible to have a better async code , i know i need to async the
$api->getMatchTimeline($match->gameId); but i don't know how can i do it.
<table class="table table table-bordered" style="width:100%">
<tr>
<thead>
<th>Items</th>
</thead>
</tr>
<tbody>
<?php
$onSuccess = function (Objects\MatchDto $match) use (&$api, $champ) {
echo "<tr>";
echo "<td>";
foreach ($match->participants as $partId) {
if ($partId->championId == $champ) {
$participant_id = $partId->stats->participantId;
$pp = $api->getMatchTimeline($match->gameId);
foreach ($pp->frames as $p) {
foreach ($p->events as $t) {
if ($t->type == "ITEM_PURCHASED" and $t->participantId == $participant_id) {
$item_id = $t->itemId;
$d = $api->getStaticItem($item_id);
$depth = $d->depth;
if (($depth == 3 or $depth == 2)) {
echo "<a href = https://lolprofile.net/match/kr/$match->gameId#Match%20History >";
echo "<img src =" . DataDragonAPI::getItemIconUrl($item_id) . " />" . '</a>';
}
}
}
}
}
}
echo "</td>";
echo "</tr>";
};
$onFailure = function ($ex) {
echo $ex;
};
foreach ($database as $game) {
$api->nextAsync($onSuccess);
$match = $api->getMatch($game->match_ids);
}
$api->commitAsync();
?>
</tbody>
</table>
render outside the table
The problem isn't to do with your "async" PHP code, but because your <table> markup is incorrect.
HTML's <table> element has two different forms. The first is the "implicit <tbody> form, like this:
<table>
<tr>
<td>col 1</td>
<td>col 2</td>
<td>col 3</td>
</tr>
</table>
The other has an explicit <tbody> element, which can also optionally have a <thead> and <tfoot> (you can also have multiple <tbody> but only a single <thead>. You can use a <thead> and <tfoot> with an implicit <tbody> but this is not recommended - I recommend everyone use the explicit syntax, like so:
<table>
<tbody>
<tr>
<td>col 1</td>
<td>col 2</td>
<td>col 3</td>
</tr>
</tbody>
</table>
Note that the actual DOM representation of both tables is the same: in the DOM a <table> never has <tr> as immediate children.
Secondarily, you can also make your code a LOT easier to read and follow if you separate your application logic from your rendering logic by doing all of your API calls and whatnot at the start of your PHP script and populate a "view model" object and then the rendering logic is greatly simplfied, like so:
<?php
// This PHP block should be before *any* HTML is written:
class MatchAndItems {
public Objects\MatchDto $match;
public Array $items;
}
$allMatches = Array(); # Array of `MatchAndItems`.
$onFailure = function ($ex) {
echo $ex;
};
$gotMatch = function (Objects\MatchDto $match) use (&$api, $champ, $allMatches) {
$e = new MatchAndItems();
$e->match = $match;
$e->items = array();
foreach ($match->participants as $partId) {
if ($partId->championId == $champ) {
$participant_id = $partId->stats->participantId;
$pp = $api->getMatchTimeline($match->gameId);
foreach ($pp->frames as $p) {
foreach ($p->events as $t) {
if ($t->type == "ITEM_PURCHASED" and $t->participantId == $participant_id) {
$item_id = $t->itemId;
$d = $api->getStaticItem($item_id);
array_push( $e->items, $d );
}
}
}
}
}
array_push( $allMatches, $e );
};
# Set-up web-service HTTP request batches:
foreach( $database as $game ) {
$api->nextAsync( $gotMatch )->getMatch( $game->match_ids );
}
# Invoke the batch:
$api->commitAsync();
# The below code uses https://www.php.net/manual/en/control-structures.alternative-syntax.php
?>
<!-- etc -->
<table class="table table table-bordered" style="width: 100%">
<thead>
<tr>
<th>Items</th>
</tr>
</thead>
<tbody>
<?php foreach( $allMatches as $match ): ?>
<tr>
<td>
<?php
foreach( $match->items as $item ):
if( $item->depth == 2 or $item->depth == 3 ):
echo "<a href = https://lolprofile.net/match/kr/$match->gameId#Match%20History >";
echo "<img src =" . DataDragonAPI::getItemIconUrl($item_id) . " />" . '</a>';
endif;
endforeach;
?>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
I am trying to get data to show on two table rows instead of just one row. Here is the code I have currently:
<tr>
<th>Title</th>
<th>Body</th>
<th>Date Posted</th>
</tr>
<tr>
<?php
for ($i=0; $i<count($entries); $i++) {
echo "<td>" . $entries[$i] . "</td>";
}
?>
</tr>
If I try to echo a table row tag in the echo statement it doesn't match the table header values.
Thanks
(screenshot of my issue http://imgur.com/O15fqHd)
per entries is PER row in an array. Each item can contain those fields. So, you can do it like this:
// process each row
foreach ($entries as $row) {
echo '<tr>';
// now process each column
foreach ($row as $fieldname => $value) {
echo '<td>'.$value.'</td>';
}
echo '</tr>';
}
The result would look like this:
<tr>
<td>value row 1 col 1</td>
<td>value row 1 col 2</td>
<td>value row 1 col 3</td>
</tr>
<tr>
<td>value row 2 col 1</td>
<td>value row 2 col 2</td>
<td>value row 2 col 3</td>
</tr>
<tr>
<td>value row 3 col 1</td>
<td>value row 3 col 2</td>
<td>value row 3 col 3</td>
</tr>
<tr>
<td>value row 4 col 1</td>
<td>value row 4 col 2</td>
<td>value row 4 col 3</td>
</tr>
....
If this is not working for you, perhaps you can include a sample data from your $entries variable.
EDIT 2:
Your data from your comment looks like this:
array(6) {
[0]=> string(9) "Test Post"
[1]=> string(59) "Test post to see if this will work in the edit post section"
[2]=> string(10) "2014-07-03"
[3]=> string(11) "Test Post 2"
[4]=> string(81) "I sure hope this array return displays all instead of just one, we will find out." [5]=> string(10) "2014-07-03"
}
It should be like this:
Array
(
[0] => Array
(
[0] => "Test Post"
[1] => "Test post to see if this will work in the edit post section"
[2] => "2014-07-03"
)
[1] => Array
(
[0] => "Test Post 2"
[1] => "I sure hope this array return displays all instead of just one, we will find out."
[2] => "2014-07-03"
)
)
Now, examine the way you are getting your data and build something like that. If you are using MySQL, try my wrapper class: https://github.com/lodev09/php-mysqli-wrapper-class
If you want to use only one loop (despite all the bad programming practices it would entail), you can try this:
$numberOfTableColumns = 3;
$entries = ...;
$j = 0;
for ($i=1; $i<=count($entries); $i++) {
if ($i % $numberOfTableColumns != 0){
if ($i == 1){
echo "<tr>";
}
if ($j == 1){
$j = 0;
echo "<tr>";
}
}
echo "<td>" . $entries[$i-1] . "</td>";
if ($i % $numberOfTableColumns == 0){
$j = 1;
echo "</tr>";
}
}
However, one has to admit that lodev09's solution is far more elegant.
My database colleague built a couple of views and SP's for me so I can focus on html/php.
One is a query that shows a "total" row at the bottom of the table.
I want to:
Exclude this last row on my <tbody>
Show this single row in my <tfoot>
Why don't I just show everything in <tbody> since the totals row will always show at the bottom anyway? Because I want my <tfoot>'s <td>'s colspan to be different, for presentation purposes.
How can I do this using php alone?
I'm using basic PDO:
foreach($table as $row) {
echo '<tr>
<td>'.$row['Item'].'</td>
<td>'.$row['Amount'].'</td>
</tr>';
}
This should work as long as the last row is not exactly the same as any other row.
$last_row = end($table);
echo '<tbody>';
foreach ($table as $row) {
if ($row === $last_row) continue;
echo '<tr>
<td>'.$row['Item'].'</td>
<td>'.$row['Amount'].'</td>
</tr>';
}
echo '</tbody>';
echo '<tfoot>
<tr>
<td>'.$last_row['Item'].'</td>
<td>'.$last_row['Amount'].'</td>
</tr>
</tfoot>';
If it's possible that the last row is not unique you can use the key instead:
$last_key = count($table) - 1;
echo '<tbody>';
foreach ($table as $key => $row) {
if ($key === $last_key) continue;
echo '<tr>
<td>'.$row['Item'].'</td>
<td>'.$row['Amount'].'</td>
</tr>';
}
echo '</tbody>';
echo '<tfoot>
<tr>
<td>'.$table[$last_key]['Item'].'</td>
<td>'.$table[$last_key]['Amount'].'</td>
</tr>
</tfoot>';
$total = 0;
foreach($table as $row) {
$total += $row['Amount'];
echo '<tr>
<td>'.$row['Item'].'</td>
<td>'.$row['Amount'].'</td>
</tr>';
}
echo '<tr>
<td>Total:</td>
<td>'.$total.'</td>
</tr>';
You don't need to use thead or tfoot if you do not intend to use them to style your content or in some cases SEO purposes.
I have a table with trs that look like this.
<tr>
<td>Link</td>
<td>value 2</td>
<td>value 3</td>
<td>value 4</td>
<td>value 5</td>
</tr>
I'm using xPath to select all <tr> and looping through them.
foreach($xpathResult as $item){
//print the href of the first td
//print the nodeValue of the second td
//print the nodeValue of the 3rd td
//print the nodeValue of the 4th td
//print the nodeValue of the 5th td
}
How do I print the href of the first td and the nodeValue of each td thereafter? There's firstChild and lastChild, but how do I get to the middle s? I could use firstChild->nextSibling and firstChild->nextSibling->nextSibling but isn't there an easier way?
Just use a loop on childNodes, like so:
foreach( $trs as $tr) {
echo $tr->firstChild->firstChild->attributes->getNamedItem( 'href')->value;
foreach( $tr->childNodes as $k => $td) {
if( $k == 0) continue; // Skip the first one
echo $td->nodeValue . "\n";
}
}
Why not just select the td's directly?
$results = $dom->xpath('//tr/td');
foreach($results as $td) {
echo $td->nodeValue;
}
I've recently posted a similar question but that was to create one row of cells across and after it reaches 3 columns, it creates a new row, so I can have 3 columns of infinite rows. That was solved.
Now what I need is this (GIVEN A and B should be records from the database using $row = mysql_fetch_array($results) so basically it would be something like $row['username']; for A and B ).
<tr>
<td><img src="images/ava/A.png" /></td>
<td>A</td>
<td width="2px" rowspan="3"></td>
<td><img src="images/ava/B.png" /></td>
<td>B</td>
</tr>
<tr>
<td><div class="gauge"><div class="innergauge"></div>A</div></td>
<td><div class="gauge"><div class="innergauge"></div>B</div></td>
</tr>
<tr>
<td>A</td>
<td>B</td>
</tr>
As what you can see, the record I got from my database has to fill in to this format, where A is one record and B is another, then if there is record C, this whole thing should repeat again to form a second row.
Build each section as you go, combine them at the end. Here's how you do sections 1 and 3, the middle section is left as an exercise:
$section1 = '<tr>';
$section2 = // start $section2
$section3 = '<tr>';
$i = 0;
while($row = mysql_fetch_array(...)){
if($i > 0){
// won't happen the first time through the loop
$section1.= '<td width="2px" rowspan="3"></td>';
}
$section1 .= '<td><img src="images/ava/'.$row['username'].'"/></td>';
$section1 .= '<td>'.$row['username'].'</td>';
// now do $section2
$section3 .= '<td>'.$row['username'].'</td>';
$i++;
}
$section1 .= '</tr>';
// finish $section 2
$section3 .= '</tr>';
// now output
echo $section1 . $section2 . $section3;