I have a function inside a PHP class that reads as follows:
//properties
var $title;
var $thePage;
function gBuildTable($theArray){
//given a 2D array, builds an HTML table based on that array
$table = "<table> \n";
foreach ($theArray as $row){
$table .= "<tr> \n";
foreach ($row as $cell){
$table .= " <td>$cell</td> \n";
} // end foreach
$table .= "</tr> \n";
} // end foreach
$table .= "</table> \n";
return $table;
} // end gBuildTable
function buildTable($theArray){
$temp = $this->gBuildTable($theArray);
$this->addText($temp);
} // end buildTable
function addText($content){
//given any text (including HTML markup)
//adds the text to the page
$this->thePage .= $content;
$this->thePage .= "\n";
} // end addText
function gAddText($content){
//given any text (including HTML markup)
//returns the text
$temp= $content;
$temp .= "\n";
return $temp;
} // end addText
So then I have a bunch of data which I build into a 2D array as such (hopefully its correct structure):
$data = array(
array("Name", $name),
array("Email", $email),
array("Hobby", $hobby),
array("", $stamp)
);
$obj = &New sH; //the class name
$obj->buildTable($data);
Am I making some boneheaded oversight or something that I am not seeing? This doesn't produce anything, it LITERALLY comes back empty. No errors, No warning, No notices, NADA.
FYI: The variables such as $name have all been previously initialized and set, they are NOT NULL. Any assistance as to what I am doing wrong, would be greatly appreciated. TIA
PS. I used current() inside the foreach loop to echo out what it is seeing at that time, and it sees the data, so I am at a loss why its not actually doing anything.
Update [SOVLED]
I can't believe I missed it and no one else caught it, but its OK, part of the wonderful world that is coding. I resolved it by noticing that its missing ONE line to grab the internal object that is being built:
echo $obj->$thePage;
You need to echo $table; in your function..!
or store the value the function is returning and echo in your main code.
$data = array(
array("Name", $name),
array("Email", $email),
array("Hobby", $hobby),
array("", $stamp)
);
$obj = &New sH; //the class name
$table = $obj->buildTable($data);
echo $table;
Related
I researched several Stack Overflow posts which seemed good, but didn't offer me the fix to my issue. I'm created a tree walker function with PHP 7 that recursively loops through all nodes until it hits a leaf. I believe hitting the "leaf" qualifies as "stop condition"(?)
Anyway the function processTree() takes three arguments: an array, a start depth level, and finally an options array which is really the only way I could figure to collect output at each depth level.
Here is basically what the function should do. I pass it an associative array with three dimensions. The array consists of bible verses structured like so:
$verses['book_name']['chapter_num']['verse_num'] = $verse_text
Demo $verses array:
$verses['Jeremiah'][29][11] = 'For I know the thoughts that I think toward you, saith the LORD, thoughts of peace, and not of evil, to give you an expected end.';
$verses['John'][3][16] = 'For God so loved the world, that he gave his only begotten Son, that whosoever believeth in him should not perish, but have everlasting life.';
$verses['Romans'][8][28] = 'And we know that all things work together for good to them that love God, to them who are the called according to his purpose.';
$verses['John'][1][1] = "In the beginning was the Word, and the Word was with God, and the Word was God.";
$verses['John'][1][3] = "All things were made by him; and without him was not any thing made that was made.";
Here is what my function looks like now:
Function: processTree():
function processTree($tree, $lev=0, $options=[]) {
$markup = '';
$out_arr = [];
foreach ($tree as $branch => $twig) {
// Debug
// If twig is verse text
if (!is_array($twig)) {
echo '<p><strong>Twig:</strong> ' . $twig . '</p>';
}
$markup .= '<li>';
$cur_depth = $lev+1;
if (is_array($twig)) {
// if node
echo '<p><strong>Branch:</strong> ' . $branch . '</p>';
$options[$cur_depth] = $branch;
$markup .= "<h$cur_depth>" .$branch. "</h$cur_depth>" . processTree($twig, $cur_depth, $options);
} else {
// if leaf
$options[$cur_depth] = $branch;
$markup .= "<h$cur_depth>" .$branch. "</h$cur_depth>" . $twig;
}
$markup .= var_dump($options);
$markup .= '</li>';
} // /END foreach
$markup = '<ul>' . $markup . '</ul>';
$out_arr['markup'] = $markup;
$out_arr['results'] = $options;
return $out_arr;
}
// Since default values are defined for args 2 and 3 they are not needed here
echo processTree($verses);
The function sort of worked when I was outputting a string ($markup), but when I tried to change it to an output an array, it gives me "Array to String conversion error". I suspect it has to do with the processTree() call in the "if (is_array($twig))" section, but I'm not sure how to solve it.
DESIRED OUTPUT:
Given the three variables $book_name, $chapter_num, and $verse_num I should be able to either get/set the $verse_text; this works. But, I'm having trouble storing this info when I'm sequentially looping through the array. Imagine I didn't know what all verses were contained in the array. I want to loop through the array tree and in the end print something like this:
$result = processTree($verses);
var_dump($result);
// Output:
// Here are the rows/verses stored in array $verses:
Jeremiah 29:11
John 1:1
John 1:3
John 3:16
Romans 8:28
In the function I am able to store the three individual values to build that list of strings, but I can't seem to figure out how to build them into strings, and keep the three from overwriting the previous three. I think the problem is with maintaining state (or "statefulness"?). Looking for the most elegant way to solve this. I hope this makes sense, if not, ask and I will try to clarify.
RESOURCES:
Here are the URLS that I researched before posting:
How to return array from recursive function
PHP: Return an array from recursive function
PHP: recursively convert array to string if key has a certain value
I'm not sure I'm understanding your issue correctly, but if you know that you only ever have exactly three dimensions to your array then you don't need recursion, just nested iteration:
foreach ($data as $book => $chapters) {
foreach ($chapters as $chapter => $verses) {
foreach ($verses as $verse => $text) {
printf("%s %d:%d\n", $book, $chapter, $verse);
}
}
}
Output:
Jeremiah 29:11
John 3:16
John 1:1
John 1:3
Romans 8:28
If you want that as an array:
$list = [];
foreach ($data as $book => $chapters) {
foreach ($chapters as $chapter => $verses) {
foreach ($verses as $verse => $text) {
$list[] = sprintf('%s %d:%d', $book, $chapter, $verse);
}
}
}
print_r($list);
Output:
Array
(
[0] => Jeremiah 29:11
[1] => John 3:16
[2] => John 1:1
[3] => John 1:3
[4] => Romans 8:28
)
At the last line you are returning empty array which you are appending in the recursive call. You should either make the function return void, return the whole text or call the function by itself since it is echoing the text itself and not appending it as a text.
//edit: like this:
function processTree($tree, $lev=0, $options=[]) {
$markup = '';
$out_arr = [];
foreach ($tree as $branch => $twig) {
// Debug
// If twig is verse text
if (!is_array($twig)) {
echo '<p><strong>Twig:</strong> ' . $twig . '</p>';
}
$markup .= '<li>';
$cur_depth = $lev+1;
if (is_array($twig)) {
// if node
echo '<p><strong>Branch:</strong> ' . $branch . '</p>';
$options[$cur_depth] = $branch;
$markup .= "<h$cur_depth>" .$branch. "</h$cur_depth>" . processTree($twig, $cur_depth, $options);
} else {
// if leaf
$options[$cur_depth] = $branch;
$markup .= "<h$cur_depth>" .$branch. "</h$cur_depth>" . $twig;
}
$markup .= var_dump($options);
$markup .= '</li>';
} // /END foreach
$markup = '<ul>' . $markup . '</ul>';
}
// Since default values are defined for args 2 and 3 they are not needed here
processTree($verses);
function get_galleryxml_row($table_data)
{
$xml_output = array();
if ($table_data)
{
foreach($table_data as $key => $row)
{
$xml_output[] .= $this->exporter->get_property_gallery_data($key['id']);
}
return implode(" ", $xml_output);
}
}
get_property_gallery_data Returns area of images and urls which does contain data and I have checked but some reason i am getting the follow error.
Array to string conversion and it states this line as the error
$xml_output[] .= $this->exporter->get_property_gallery_data($key['id']);
No need of . -
$xml_output[] = $this->exporter->get_property_gallery_data($row['id']); // It should be only $key or $row['id']
It will store the value with new index. . is used to concatenate strings.
Try this...
$xml_output[] .= $this->exporter->get_property_gallery_data($key['id']);
to
$xml_output[] = $this->exporter->get_property_gallery_data($row['id']);
Hi, this is my first time trying to create some code in PHP and it took me a long time but I'm able to convert data to xml. Now I need create a JSON object and it's not going well. The biggest problem is trying to create a new class in PHP (I don't know if what I did is ok or not) and if this is the correct way to attach a list. I think some of it's good but to me since I only use java and c# it seems a little crazy. I think I'm doing something wrong. The line that's showing me an error is $array['data']->attach( new Cake($name,$ingredients,$prepare,$image)); but i don't know what I'm doing wrong.
I haven't yet written the line that includes and transforms the array into json
Thanks
//opens the file, if it doesn't exist, it creates
$pointer = fopen($file, "w");
// writes into json
$cake_list['data'] = new SplObjectStorage();
for ($i = 0; $i < $row; $i++) {
// Takes the SQL data
$name = mysql_result($sql, $i, "B.nome");
$ingredients = mysql_result($sql, $i, "B.ingredientes");
$prepare = mysql_result($sql, $i, "B.preparo");
$image = mysql_result($sql, $i, "B.imagem");
// assembles the xml tags
// $content = "{";
// $content .= "}";
$array['data']->attach( new Cake($name,$ingredients,$prepare,$image));
// $content .= ",";
// Writes in file
// echo $content;
$content = json_encode($content);
fwrite($pointer, $content);
// echo $content;
} // close FOR
echo cake_list;
// close the file
fclose($pointer);
// message
// echo "The file <b> ".$file."</b> was created successfully !";
// closes IF($row)
class Cake {
var $name;
var $ingredients;
var $prepare;
var $image;
public function __construct($name, $ingredients, $prepare, $image)
{
$this->name = $name;
$this->ingredients = $ingredients;
$this->prepare = $prepare;
$this->image = $image;
}
}
function create_instance($class, $arg1, $arg2, $arg3, $arg4)
{
$reflection_class = new ReflectionClass($class);
return $reflection_class->newInstanceArgs($arg1, $arg2,$arg3, $arg4);
}
The error you are experiencing is because you are doing $array['data'] when you mean $cake_list['data'] so change the error line to:
$cake_list['data']->attach(new Cake($name, $ingredients, $prepare, $image));
Also a simple way to simple way to create a JSON object (or more accurately a string representation of a JSON object) is to do this:
$array = array(
'name' => $name,
'ingredients' => $ingredients,
'prepare' => $prepare,
'image' => $image
);
$json = json_encode($array);
You can also create simple easy to use objects like this:
$myObject = new stdClass(); // stdClass() is generic class in PHP
$myObject->name = $name;
$myObject->ingredients = $ingredients;
$myObject->prepare = $prepare;
$myObject->image = $image;
Update for CodingBiz:
I'm putting this in my code:
for($i=1;$i<=$numRows;$i++) {
$output .= '<tr>';
$row = $this->fetchAssoc($result);
$colRow = $this->fetchAssoc($colResult);
foreach($colRow as $colName) {
$output .= "<td>".$row[$colName]."</td>";
}
$output .= '</tr>';
}
in place of
for($i=1;$i<=$numRows;$i++) {
$output .= '<tr>';
$row = $this->fetchAssoc($result);
for($j=1;$j<=$colNumRows;$j++) {
$colRow = $this->fetchAssoc($colResult);
$output .= "<td>".$row[$colRow["COLUMN_NAME"]]."</td>";
}
$output .= '</tr>';
}
Is there anything wrong with this?
Original Post:
I'm writing a function in a PHP class to display the results of a query in a table. I'm not structuring any of the table myself, I want it everything to be done using PHP. Here is my code so far:
function allResults($table,$cols) {
if(isset($cols)) {
$query = "SELECT $cols FROM $table";
}
else {
$query = "SELECT * FROM $table";
}
$result = $this->query($query);
$numRows = $this->numRows($result);
$colQuery ="SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='shareride' AND TABLE_NAME='$table'";
$colResult = $this->query($colQuery);
$colNumRows = $this->numRows($colResult);
$output = '<table class="allResults">';
$output .= '<tr>';
for($i=1;$i<=$colNumRows;$i++) {
$colRow = $this->fetchAssoc($colResult);
$output .= "<td>".$colRow["COLUMN_NAME"]."</td>";
}
$output .= '</tr>';
for($i=1;$i<=$numRows;$i++) {
$output .= '<tr>';
$row = $this->fetchAssoc($result);
for($j=1;$j<=$colNumRows;$j++) {
$colRow = $this->fetchAssoc($colResult);
$output .= "<td>".$row[$colRow["COLUMN_NAME"]]."</td>";
}
$output .= '</tr>';
}
$output .= '</table>';
return $output;
}
In case it is unclear, query refers to mysqli_query, numRows refers to mysqli_num_rows, and fetchAssoc refers to mysqli_fetch_assoc. The database name is "shareride."
I know I am missing something in this line:
$output .= "<td>".$row[$colRow["COLUMN_NAME"]]."</td>";
but I just don't know what it is. Right now, I get all the table column titles displayed correctly, and I get the correct number of content rows, but I just can't populate those rows with the actual data from the database.
What am I missing? Any help would be GREATLY appreciated!
Get the data and column names from the same result set
<?php
$i = 0;
$colNames = array();
$data = array();
while($row = ***_fetch_assoc($res)) //where $res is from the main query result not schema information
{
//get the column names into an array $colNames
if($i == 0) //make sure this is done once
{
foreach($row as $colname => $val)
$colNames[] = $colname;
}
//get the data into an array
$data[] = $row;
$i++;
}
?>
UPDATE: Suggested by #YourCommonSense to replace the above code and it worked, simple and shorter - A WAY TO GET THE COLUMN NAMES/ARRAY KEYS WITHOUT LOOPING THROUGH LIKE I DID
$data = array();
while($row = mysql_fetch_assoc($res))
{
$data[] = $row;
}
$colNames = array_keys(reset($data))
Continued as before: Print the table
<table border="1">
<tr>
<?php
//print the header
foreach($colNames as $colName)
{
echo "<th>$colName</th>";
}
?>
</tr>
<?php
//print the rows
foreach($data as $row)
{
echo "<tr>";
foreach($colNames as $colName)
{
echo "<td>".$row[$colName]."</td>";
}
echo "</tr>";
}
?>
</table>
Test Result
You can see how I separated the data retrieval from table generation. They are dependent of each other now and you can test your table generation without the database by populating the arrays with static data
You can also make them into separate functions.
Never mix your database handling code with HTML output code. These are 2 totally different matters. Make your allResults function only return array with data, and then you can make another function to print in fancy way (and not in the database handler class).
You don't need information schema to get column name - you already have it in the returned array keys.
You don't need numRows either - use while() and foreach()
NEVER insert anything into query directly like you do with $cols - eventually it will lead to errors and injections.
Such a function, without accepting some parameters for the query, makes absolutely no sense especially in the context of migrating from mysql to mysqli - you are going yo use it as an old school mysql query inserting variables, not placeholders. So, it makes migration totally useless.
To know "Is there anything wrong with code", one have to run it, not watch. Run and debug your code, outputting key variables and making sure you can see errors occurred.
i'd try replacing the data part with something like:
while($row = $this->fetchAssoc($colResult))
{
echo "<tr>";
foreach($row as $value)
{
echo sprintf("<td>%s</td>",$value)
}
echo "</tr>";
}
i know it's not a proper answer, but it's really hard to read that code imho
I have a php method that creates an HTML table with data it retrieves from a property.
My biggest concern is the performance of my application because I deal with a large amount of data.
public function getHTML() {
$phpObj = json_decode($this->data); // array(object, object, object, ....);
$table = "<table><tbody>\n";
if (count($phpObj->query->results->row) > 0) {
$row = $phpObj->query->results->row;
foreach ($row as $value) {
$table .= "<tr>\n";
foreach ($value as $key => $val) { // concerned about loop inside loop
$table .= "<td>" . $value->$key . "</td>";
}
$table .= "\n</tr>\n";
}
$table .= "</tbody></table>";
return $table;
}
else {
return 'HTML table not created.';
}
}
Is there a more efficient way of traversing through the array and objects without creating a loop inside a loop?
Don't concatenate and return the value, echo it immediately instead. Less clean but the performance will be much more interesting since the strings are immediately outputed to the output buffer which is managed more efficiently.
A loop inside a loop is often the best way to traverse a two-dimensional array.
String concatenation is cost-intensive. You could reduce the number of repetitive string concatenations by using arrays:
public function getHTML() {
$phpObj = json_decode($this->data);
if (count($phpObj->query->results->row) > 0) {
$rows = array();
foreach ($phpObj->query->results->row as $row) {
$cells = array();
$rows[] = "<td>" . implode("</td><td>", $row) . "</td>";
}
return "<table><tbody>\n<tr>\n" .
implode("\n<tr>\n<tr>\n", $rows) .
"\n</tr>\n</tbody></table>";
} else {
return 'HTML table not created.';
}
}
You could also use anonymous functions (available since PHP 5.3):
public function getHTML() {
$phpObj = json_decode($this->data);
if (count($phpObj->query->results->row) > 0) {
return "<table><tbody>\n<tr>\n" .
implode("\n<tr>\n<tr>\n", array_map(function($cells) { return "<td>".implode("</td><td>", $cells)."</td>"; }, $phpObj->query->results->row)) .
"\n</tr>\n</tbody></table>";
} else {
return 'HTML table not created.';
}
}
UPDATE
Col. Shrapnel correctly stated that, oddly, string concatenation is actually relatively fast in php.
As Vincent said, don't run a bunch of concatenations, that's killing you. You have two options to speed up your script:
Echo immediately.
Store your lines in a an array, and join the array at the end.
Example of two:
<?php
$mylines = array();
foreach ($row as $value) {
$mylines[] = "<tr>\n";
foreach ($value as $key => $val) { // concerned about loop inside loop
$mylines[] = "<td>" . $value->$key . "</td>";
}
$mylines[] = "\n</tr>\n";
}
return implode('', $mylines);
You could move the HTML building into the frontend and ship the JSON data to the user via AJAX and javascript.
This could also allow your to only ship pieces of the data at a time (depending on your html layout), so they could be dynamically polled when needed (like google/bing image search).
I know I didn't answer the question directly. That is because the code you have is probably the fastest it can be done (in PHP) w/o doing silly little optimizations that would only make the code harder to read/maintain (probably only save a few % anyway).
EDIT: after looking at it again, I bet your code is actually just polling the data from an external source in JSON. You could probably remove this code completely by having the frontend javascript do the HTTP hit and deal with the data there. This removes the need for this PHP code to run at all.
EDIT 2: after reading your comment about this being a fall back for javascript being disabled, I looked at the code your currently doing. It appears to be translatable into implode.
//declared this functions somewhere
function tr_build( $row_value )
{
$tablerow .= "<tr>\n";
if( $row_value ) {
$tablerow .= "<td>".implode( "</td><td>", $row_value )."</td>";
}
$tablerow .= "\n</tr>\n";
return $tablerow;
}
//this replaces the double loop
if( $row ) {
$tablerows = "<tr>\n".implode( "\n</tr>\n<tr>\n", array_map( "tr_build", $row ) )."\n</tr>\n"
} else {
$tablerows = "";
}
Why are you bothering with this?
$table .= "<tr>\n";
foreach ($value as $key => $val) { // concerned about loop inside loop
$table .= "<td>" . $value->$key . "</td>";
}
$table .= "\n</tr>\n";
You never actually use $val so why have this loop at all?
$table .= "<table><td>";
$table .= implode("</td><td>", array_keys($value));
$table .= "</td></table>";