I have a MySQL table with id, parent and comment which I managed to order almost-as-it-should from latest to oldest. If a comment has no parent, this is set to 0.
The comments table: Table Image
Hereby my current code:
<?php
$con = mysqli_connect('host','username','password','database');
if (mysqli_connect_errno($con)) {
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$comments = array();
$results = mysqli_query($con,"SELECT * FROM comments ORDER BY id DESC");
while ($row = mysqli_fetch_assoc($results)) {
$parent = $row['parent'];
$comments[$parent ? $parent : 0][] = $row;
}
function thread($parent,$comments) {
foreach ($comments[$parent] as $post) {
echo "<div>";
echo $post['comment'];
$id = $post['id'];
if (isset($comments[$id])) {
thread($id,$comments);
}
echo "</div>";
}
}
thread(0,$comments);
?>
The above results in the following order (turned into a list for readability, you can do this in the code by turning the div tags into li and echoing ul tags around the foreach loop):
Third comment
Second comment
Another reply to the second comment
And another one
Reply to the second comment
Reply to a reply
First comment
Reply to the first comment
Notice that replies are ordered in a descending manner as well, just like the comments. Other than that it's completely fine and working like a charm. But this is what it's supposed to be like:
Third comment
Second comment
Reply to the second comment
Reply to a reply
Another reply to the second comment
And another one
First comment
Reply to the first comment
Shortly said: comments should be in descending order, replies in ascending. That is where I got stuck completely. Thanks a lot for your help!!
You can use the array_reverse function to reverse the order of the items in an array:
$results = mysqli_query($con,"SELECT * FROM comments ORDER BY id DESC");
while ($row = mysqli_fetch_assoc($results)) {
// ...
}
// Important bit: Reverse all threads except the main thread (id=0):
foreach ($comments as $key => $value) {
if ($key !== 0) {
$comments[$key] = array_reverse($value);
}
}
function thread($parent,$comments) {
// And so on...
Related
I am trying to create a post-comment system, with simple php-mysqli, as simple as it is, it does not seem to give me the result in the fashion I want i.e:
---POST MESSAGE----
-----comments-----
Here is the code I used:
<?php
session_start();
include_once('php_includes/db_conx.php');
$user=$_SESSION['user'];
$o =mysqli_query($db_conx, "SELECT post.id,post.post,post.date,post_comments.poster,post_comments.comment,post_comments.date FROM post LEFT JOIN post_comments ON post.id=post_comments.post_id AND post.username='$user' ORDER BY post.date");
while($r=mysqli_fetch_array($o,MYSQLI_ASSOC)){
$status= $r['post'];
$date=$r['date'];
$com=$r['comment'];
$pid=$r['id'];
$poster=$r['poster'];
if(count($pid) > 1){
}
echo $status.'|'.$pid.'|'.$date.'<br>'.$poster.':'.$com.'<hr>';
}
?>
It seems to duplicate the post for each comment for same post.
Not sure am making sense, but i will appreciate an answer.
First, add post id to your query's ORDER BY. That will ensure that your post and all its comments appear together, and only once. (I'd recommend adding post_comments.date as well so your comments will appear in order, but that won't be necessary to get the grouping working.)
... ORDER BY post.date, post.id, post_comments.date
Then keep track of the post id as you go. Echo the post information only when the post id changes.
$id = null; // initialize to null
while ($r = mysqli_fetch_array($o, MYSQLI_ASSOC)) {
$pid = $r['id'];
$status = $r['post'];
$date = $r['date'];
$com = $r['comment'];
$poster = $r['poster'];
if ($pid !== $id) {
echo $status.'|'.$pid.'|'.$date.'<br>'; // new post, so echo post info here
$id = $pid; // $id becomes new post id
}
if ($com) {
echo $poster.':'.$com.'<br>'; // echo comment if present
}
}
One other thing that will probably cause some trouble is that you have selected both post.date and post_comments.date in your query, so I'm not sure which one will be in $r['date']. It would be a good idea to alias at least one of those columns to disambiguate them.
Thank you for reading this and for your help in advance.
I got a simple Book-Catalogue /procedure code/, every visitor can see the book catalogue and click through out the books and check their authors BUT I've got an idea to add a feature that allows the users to write a comment to each book ONLY if they're logged. So I added a session variable $_SESSION['isLogged']. But there is a lot of code blocks that duplicates. What I need is an advice, what to do with this duplicated blocks of code. What the good practice says? And my code below is from one of the 6 files that I got. In everyfile I got this repeating of code.
So here's my code:
if (!isset($_SESSION['isLogged'])) {
echo '<div class="user-navigation">
<a href="register.php" class="user-nav" >Register now</a>
Log in
</div>
<div class="navigation1">
Add book
Add author
</div>';
$booksAndAuthors = mysqli_query($connection, 'SELECT DISTINCT * FROM books LEFT JOIN books_authors ON books.book_id=books_authors.book_id LEFT JOIN authors ON authors.author_id=books_authors.author_id');
$result = array();
while ($resultArr = mysqli_fetch_assoc($booksAndAuthors)) {
$result[$resultArr['book_id']] ['book_name'] = $resultArr['book_title']; // Reodering array
$result[$resultArr['book_id']] ['author'][$resultArr['author_id']] = $resultArr['author_name']; // Reordering array
}
echo '<table class="table"><tr><th>Book name</th><th>Author</th></tr>'; // Open table html table tags
foreach ($result as $k=>$b) { // Foreach the result array to get the book_name
echo '<tr><td>' . $b['book_name'] . '</td><td>';
$data = array(); // Create an empty array in order to fill the data inside
foreach ($b['author'] as $k => $a) { // Foreach the nested array with the authors to get the author_name displayed
$_GET['author_name'] = $a;
$data[] = '' . $a . ''; // Link is chosen by the author_id
}
echo implode(', ', $data); // Add a comma after every record
echo '</td></tr>'; // Close table cell and row
}
exit;
echo '</table>'; // Close html table tag
}
else {
echo '<div class="user-navigation">
My Profile
Log out
</div>
<div class="navigation2">
Add book
Add author
</div>';
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
$booksAndAuthors = mysqli_query($connection, 'SELECT DISTINCT * FROM books LEFT JOIN books_authors ON books.book_id=books_authors.book_id LEFT JOIN authors ON authors.author_id=books_authors.author_id');
$result = array();
while ($resultArr = mysqli_fetch_assoc($booksAndAuthors)) {
$result[$resultArr['book_id']] ['book_name'] = $resultArr['book_title']; // Reodering array
$result[$resultArr['book_id']] ['author'][$resultArr['author_id']] = $resultArr['author_name']; // Reordering array
}
echo '<table class="table"><tr><th>Book name</th><th>Author</th></tr>'; // Open table html table tags
foreach ($result as $k=>$b) { // Foreach the result array to get the book_name
echo '<tr><td>' . $b['book_name'] . '</td><td>';
$data = array(); // Create an empty array in order to fill the data inside
foreach ($b['author'] as $k => $a) { // Foreach the nested array with the authors to get the author_name displayed
$_GET['author_name'] = $a;
$data[] = '' . $a . ''; // Link is chosen by the author_id
}
echo implode(', ', $data); // Add a comma after every record
echo '</td></tr>'; // Close table cell and row
}
echo '</table>'; // Close html table tag
}
*If my question is not properly asked or you have some notices.Please let me know!
* (=
Put this code in a file then include that file at the bottom of all of the pages.
For example put the code in isLogged.php
Then on each page where the code is duplicated replace it with:
include "isLogged.php";
include is what you're looking for.
You should create a file that contains your repeated code. functions.php for example.
Then call it with one of these methods.
include "functions.php"
http://php.net/manual/en/function.include.php
include_once("functions.php")
http://php.net/manual/en/function.include-once.php
require_once("functions.php")
http://php.net/manual/en/function.require-once.php
create a function of repeated block and call when it required or create a file with the set of repeated code and use any of include,include_once,require_once to add the file.for more detail see php manual for these functions.
I'm trying to display a company name from a company table, and then loop out news from another table. The two tables are joined. This means that: I want to just pick out one row from one table in the join, and loop out data from the other table in the join. Is this possible? My code below only display two out of three posts in the news table in my loop.
Thanks!
$sql = "SELECT
newID, newTitle, newSummary, newDate,
comID, comName, comImageThumb
FROM
tblNews a RIGHT JOIN tblCompanies b
ON a.newCompanyID = b.comID
ORDER BY newDate DESC";
// Get company name and display
$companyData = mysql_fetch_assoc($result);
$comName = $companyData['comName'];
echo "<a href='#' class='name'>$comName</a>";
// Looping news
while($news = mysql_fetch_assoc($result)) {
// Display news posts
$newTitle = $news['newTitle'];
echo $newTitle;
}
Put everything inside while loop and try :
while($news = mysql_fetch_assoc($result)) {
// Get company name and display
$comName = $news ['comName'];
echo "<a href='#' class='name'>$comName</a>";
// Looping news
// Display news posts
$newTitle[] = $news['newTitle'];
echo $newTitle;
}
You should look at mysql GROUP_CONCAT for more detail.
Easiest solution probably would be to keep track of whether you are in the first iteration:
// query company name and news entries here
$firstRow = true;
while($news = mysql_fetch_assoc($result)) {
if ($firstRow) { echo $news['comName']; }
$firstRow = false;
echo $news['newTitle'];
}
You could also keep your current code and use mysql_data_seek(0) to reset the cursor after extracting the company name first.
And on a side note: Don't use mysql_* functions anymore! Use PDO instead.
Move the fetch operation to the end of the loop:
$companyData = mysql_fetch_assoc($result);
$comName = $companyData['comName'];
echo "<a href='#' class='name'>$comName</a>";
// you've already fetched a news item....
$news=$companyData;
do {
$newTitle = $news['newTitle'];
echo $newTitle;
} while ($news=mysql_fetch_assoc($result));
I'm making a related stories section which uses the tags in a post to go through and find other stories with similar tags.
I want to make sure that I'm not pulling the same story multiple times if it shares more than one tag with another post.
So it's basically
foreach($tags as $t) {
$getStories = mysql_query("SELECT * FROM `posts` WHERE `tags` LIKE '%$t%' LIMIT 2");
while($related = mysql_fetch_array($getStories)) {
echo $related['title'];
}
So I pull 2 related stories based on the first tag, now, when it goes around through next loop for the second tag, how can I make sure that a story pulled the last time doesn't get picked the second or third time. I do have a unique ID just called 'id', just not sure what to do with it in this situation.
Thanks!
What you could do is fetch all related articles at once:
$tagsClause = '';
foreach ($tags as $t) {
$tagsClause .= " OR tags LIKE '%$t%'";
}
$tagsClause = substr($tagsClause, 4); // Remove first ' OR '
$getStories = mysql_query('SELECT * FROM `posts` WHERE ' . $tagsClause);
while($related = mysql_fetch_array($getStories)) {
echo $related['title'];
}
However, this does not account for your use of LIMIT.
EDIT
Apparently this did not pose a problem, cf. comments.
Use UNIQUE.
SELECT UNIQUE(post_id), [other stuff] FROM posts WHERE [...]
i think you can make an array containing the ids you have selected, when you process a tag, check the id is not in the selected array, but it's not efficient.
just like this:
$ids = array();
foreach($tags as $t) {
$getStories = mysql_query("SELECT * FROM `posts` WHERE `tags` = '$t' LIMIT 2");
//here to check and add id to array ids
while($related = mysql_fetch_array($getStories)) {
echo $related['title'];
}
}
I'm trying to make a simple alphabetical list to order items in my database. The thing I can't figure out is how to actually list it.
I would like it to be the same format as you have on miniclip.com
Here's an image
I looked around, but couldnt find an answer really.
(I would like it to finish even at the end of each vertical column, except the last one for sure)
Any help would be welcome!
In MySQL:
SELECT * FROM table ORDER BY name ASC
In PHP:
$fruits = array("lemon", "orange", "banana", "apple");
sort($fruits);
foreach ($fruits as $key => $val) {
echo "fruits[" . $key . "] = " . $val . "\n";
}
fruits[0] = apple
fruits[1] = banana
fruits[2] = lemon
fruits[3] = orange
Assuming that your result set already is sorted by using the ORDER BY clause, to group the results by their first character you just need to remember the first character of the previous entry and print out the first character of the current entry if they are different. So:
$prevLabel = null;
while ($row = mysql_fetch_assoc($result)) {
$currLabel = strtoupper(substr($row['name'], 0, 1));
if ($currLabel !== $prevLabel) {
echo $currLabel;
$prevLabel = $currLabel;
}
echo $row['name'];
}
This will print the first character as a label for each group that’s members have the same first character.
He doesn't seem to have an issue with the storting, but doing the column format and headers for each new letter.
Suppose $arr contains your alphabetically sorted list with numeric keys. each element has indexes 'name' and 'link'. This should be pretty safe assumption for data from a SQL query.
$firstLetter = -1;
$desiredColumns = 4; //you can change this!
$columnCount = (count($arr)+27)/$desiredColumns+1;
echo "<table><tr><td>";
foreach($arr as $key => $cur)
{
if ($key != 0 && $key % desiredColumns == 0) echo "</td><td>";
if ($cur['name'][0] !== $firstLetter)
{
echo "<strong>$firstLetter</strong> <br />"; $firstLetter = $cur['name'][0];
}
echo "".$cur['name']."<br />";
}
echo "</td><tr></table>";
You'll have to treat numbers as a special case, but this is the idea. If you are using a template engine there are obviously better ways of doing this, but I figure you would have mentioned that. This is a rough sketch, making pretty HTML isn't my thing.
--Query-- get table into $arr. I can't see your tables obviously, Im making assumptions if names nad stuff so you'll need to verify or change them
$sql = "SELECT * FROM table T ORDER BY name";
$conn = //you should have this
$res = mysql_query($sql, $conn);
$arr = array();
while($row = mysql_fetch_assc($res)
$arr[] = $row;
// start above code here. This isn't safe for empty query responses or other error but it works
I presume you're using MySQL (or another SQL) database, in which case you should simply retrieve the data in the required order using a SORT BY clause on the lookup SELECT. (Sorting this PHP is trivial via the sort function, but it makes sense to get the database to do this - that's pretty much what it's for.)
In terms of balancing the output of each of the columns, you could get a COUNT of the required rows in your database (or simply use the count of the resulting PHP array of data) and use this to ensure that the output is balanced.
As a final thought, if this is going to be output on a per-page basis, I'd highly recommend generating it into a static file when the structure changes and simply including this static file as a part of the output - generating this on the fly is needlessly resource inefficient.
The mysql option mentioned above is definitely the best bet. If the data comes out of the DM in order, that's the simplest way to go.
Your next option might be to look at the
asort and ksort functions in PHP to find the exact one you're looking for.
http://www.php.net/manual/en/array.sorting.php
How are you pulling the data?
<?php
$result = mysql_query("SELECT titles FROM gamelist ORDER BY title ASC");
while ($row = mysql_fetch_assoc($result)) {
echo "{$result['title']}<br/>";
}
?>
There are two ways to do it.
You could use your database and use the 'order' clause to pull them by a specific field alphabetically.
You could also use either a key sort or value sort on a PHP array.
The PHP functions are sort($array) and ksort($array).
http://php.net/manual/en/function.sort.php
http://php.net/manual/en/function.ksort.php
<?php
$list = $your_list_array_from_database
//if you need info on how to do this, just let me know
sort($list);
foreach($list as $item) {
echo $item;
}
?>
I found this post and had the same problem. I used the code below to output a list by category name with a header equal to the first letter. In my database table (category) I have name and category_letter. So, name = football and category_list = 'F'.
<section>
<?php
try {
$cats_sql = $dbo->prepare("SELECT name, category_list, FROM category WHERE category_list REGEXP '^[A-Z#]' GROUP BY category_list ASC");
$cats_sql->execute();
$results_cats = $cats_sql->fetchAll();
} catch(PDOException $e) {
include('basehttp/error');
}
$array_cats = $results_cats;
if(is_array($array_cats)) {
foreach($array_cats as $row_cats) {
$cat_var = $row_cats[category_list]; // Each Category list title
?>
<aside>
<h1><a name=""><? echo $cat_var ?></a></h1>
<?php
try {
$search_sql = $dbo->prepare("SELECT name, category_list FROM category WHERE category_list=:cat_var ORDER BY name ASC"); // Pulling a list of names for the category list
$search_sql->bindParam(":cat_var",$cat_var,PDO::PARAM_STR);
$search_sql->execute();
$results_search = $search_sql->fetchAll();
} catch(PDOException $e) {
include('basehttp/error');
}
$array_search = $results_search;
if(is_array($array_search)) { // Output list of names which match category
foreach($array_search as $row_search) {
?>
<h2><?php echo $row_search[name]; ?></h2>
<br class="clear">
<?php
}
}
?>
</aside>
<br class="clear">
<?php
}
}
?>
</section>
Its actually Simple....I did similar thing for my project once. I had to pull out all music albums name and categorize them in alphabetical order.
In my table, "album_name" is the column where names are stored.
$sql= "select * from album_table order by album_name ASC";
$temp_char= ""; // temporary variable, initially blank;
using while loop, iterate through records;
while($row= $rs->fetch_assoc())
{
$album_name= $row['album_name'];
$first_char_of_albm= $album_name[0]; // this will store first alphabet;
$first_char_of_albm= strtoupper($first_char_of_albm); // make uppercase or lower as per your needs
if($temp_char!=$first_char_of_albm)
{
echo $first_char_of_albm;
$temp_char= $first_char_of_albm; // update $temp_char variable
}
}
That's it....
I am posting my answer to this old question for 3 reasons:
You don't always get to write your queries to MySQL or another DBMS, as with a web service / API. None of the other answers address PHP sorting without query manipulation, while also addressing the vertical alphabetical sort
Sometimes you have to deal with associative arrays, and only a couple other answers deal with assoc. arrays. BTW, my answer will work for both associative and indexed arrays.
I didn't want an overly complex solution.
Actually, the solution I came up with was pretty simple--use multiple tags with style="float:left", inside of a giant table. While I was sceptical that having multiple tbody tags in a single table would pass HTML validation, it in fact did pass without errors.
Some things to note:
$numCols is your desired number of columns.
Since we are floating items, you may need to set the width and min-width of parent elements and/or add some <br style="clear: both" />, based on your situation.
for alternative sorting methods, see http://php.net/manual/en/array.sorting.php
Here's my full answer:
function sortVertically( $data = array() )
{
/* PREPARE data for printing */
ksort( $data ); // Sort array by key.
$numCols = 4; // Desired number of columns
$numCells = is_array($data) ? count($data) : 1 ;
$numRows = ceil($numCells / $numCols);
$extraCells = $numCells % $numCols; // Store num of tbody's with extra cell
$i = 0; // iterator
$cCell = 0; // num of Cells printed
$output = NULL; // initialize
/* START table printing */
$output .= '<div>';
$output .= '<table>';
foreach( $data as $key => $value )
{
if( $i % $numRows === 0 ) // Start a new tbody
{
if( $i !== 0 ) // Close prev tbody
{
$extraCells--;
if ($extraCells === 0 )
{
$numRows--; // No more tbody's with an extra cell
$extraCells--; // Avoid re-reducing numRows
}
$output .= '</tbody>';
}
$output .= '<tbody style="float: left;">';
$i = 0; // Reset iterator to 0
}
$output .= '<tr>';
$output .= '<th>'.$key.'</th>';
$output .= '<td>'.$value.'</td>';
$output .= '</tr>';
$cCell++; // increase cells printed count
if($cCell == $numCells){ // last cell, close tbody
$output .= '</tbody>';
}
$i++;
}
$output .= '</table>';
$output .= '</div>';
return $output;
}
I hope that this code will be useful to you all.