I am running into a problem that I feel like is somewhat simple in principle, so I may not be going about this the correct way. I essentially am listing "events" on a page that have their own respective "comments". I am joining to two tables and hoping to run a foreach loop to get all of the events along with their comments. Unfortunately, if an event has more than one comment it's reprinting the original event because of the join (i suspect). What is a better way to achieve what I am trying to accomplish?
In my model:
function get_events()
{
$this->db->select('*');
$this->db->from('events');
$this->db->join('comments', 'comments.event_id = events.id', 'left');
$query = $this->db->get();
if ($query->num_rows() > 0)
{
return $query->result();
}
}
And in the view:
<?php foreach($events as $row): ?>
<div class="event">
<?= $row->event; ?>
<?php if($row->comment == null): ?><br>
<i>No comments.</i>
<?php else: ?>
<div class="comment">
<?= $row->comment; ?>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
In the db I basically have 3 events, one with 2 comments, one with just 1, and one with zero comments. The first event prints twice, each with a different comment, the second event prints fine with its own comment and the third one works perfectly, printing "no comments" since there is nothing joined with it.
Hopefully this all makes sense!
MT
You can check wether you already echoed the event:
<?php
$lastEvent = null;
foreach($events as $row) {
if($row->event != $lastEvent) {
echo '<div class="event">' . $row->event;
}
if($row->comment == null) {
echo '<br><i>No comments.</i>';
} else {
echo '<div class="comment">' . $row->comment . '</div>';
}
if($row->event != $lastEvent) {
echo '</div>';
}
$lastEvent = $row->event;
}
Related
My recordset is:
mysqli_select_db($KCC, $database_KCC);
$query_rsOtherServices = "SELECT pagecontent.mainMenuID, mainmenu.mainMenuLabel, pagecontent.subMenuID, submenu.subMenuLabel, pagecontent.contentID, pagecontent.contentTitle FROM submenu RIGHT JOIN (mainmenu RIGHT JOIN pagecontent ON mainmenu.mainMenuID = pagecontent.mainMenuID) ON submenu.subMenuID = pagecontent.subMenuID WHERE pagecontent.mainMenuID = 1 AND pagecontent.subMenuID IS NULL";
$rsOtherServices = mysqli_query($KCC, $query_rsOtherServices) or die(mysqli_error());
$row_rsOtherServices = mysqli_fetch_assoc($rsOtherServices);
$totalRows_rsOtherServices = mysqli_num_rows($rsOtherServices);
and the code I'm using to display the record is:
<?php do { ?>
<li><h4><?php echo $row_rsOtherServices['contentTitle']; ?></h4></li>
<?php } while ($row_rsOtherServices = mysqli_fetch_assoc($rsOtherServices)); ?>
This all works fine if a record exists but if there is no record, a 'link' is available to click even though it's not visible.
I have tried <?php if ($totalRows_rsOtherServices['subMenuID'] === Null) { ?>, <?php if ($totalRows_rsOtherServices['subMenuID'] > 0) { ?>, <?php if ($totalRows_rsOtherServices['subMenuID'] == true) { ?>, <?php if ($totalRows_rsOtherServices['subMenuID'] == false) { ?>but to no avail.
I know nothing about programming and even less about PHP so I'm not even sure is I'm going in the right direction.
I need to get rid of the 'invisible link'.
Because you are using do ... while, use while only.
Try
<?php while (($row = mysqli_fetch_assoc($rsOtherServices))) { ?>
<li>
<a href="familyservices.php?idVal=<?php echo $row['contentID']; ?>">
<h4><?php echo $row['contentTitle']; ?></h4>
</a>
</li>
<? } ?>
OK, I've figured it out.
Instead of using $((totalRows_rsOtherServices['subMenuID']) > 0), I changed it to (($row_rsOtherServices) > 0). This did the trick.
However, if I use the while (($row = mysqli_fetch_assoc($rsOtherServices)))... as suggested, even if there is a record, it doesn't display.
At this moment in time I'm not going to worry to much about that but will investigate it in the future.
I have what is probably a ridiculously simple problem to solve in PHP, but my brain will not engage.
I have products divided into categories. I have performed a successful query which gives me an array containing the products joined to their respective categories. Now I want to display them by category, splitting each category up into its own div. I have the following code:
$current_category = 0;
do
{
if($current_category != $row['category_id']){
echo '<div class="block">
<div class="menu">
<h4>'.$row['category_name'].'</h4>
<ul>';
do
{
echo '<li><a>'.$row['product_name'].'</a></li>';
}
while ($row['category_id'] == $current_category);
$current_category++;
//close list and divs
echo '</ul>
</div>
</div>';
}
}
while ($row = $stmt->fetch(PDO::FETCH_ASSOC));
But this only outputs the first product in each category, rather than outputting them all then moving on to the next. What am I doing wrong?
You need to get the next row in the inner loop, not in the outer one:
do {
// no need to check we're in the right category - we always are
$current_category = $row['category_id'];
echo '<div class="block">
<div class="menu">
<h4>'.$row['category_name'].'</h4>
<ul>';
do {
echo '<li><a>'.$row['product_name'].'</a></li>';
// !!! get the next row here
$row = $stmt->fetch(PDO::FETCH_ASSOC);
}
while ($row['category_id'] == $current_category);
//close list and divs
echo '</ul>
</div>
</div>';
// !!! and don't get it again here
} while ($row);
You need to make sure that your SQL query sorts by category first
I was trying to create a commenting system like that uses facebook. I use php and jquery. My code works perfect. All I want is just to add a reply system in it. Any idea how to do this?
This is my main page: wall.php
<script>
$(document).ready(function(){
$("#comment_process").click(function(){
if($("#comment_text").val() != ""){
$.post("comments.php?action=post", { comment: $("#comment_text").val() }, function(data) {
$(".comments").html(data);
$("#comment_text").val("");
});
}
});
});
</script>
<div class="comment_container">
<div class="comment_form">
<textarea id="comment_text" ></textarea>
<input type="button" id="comment_process" value="Post"/>
</div>
</div>
<div class="comments"> <?php include_once("comments.php");?> </div>
?>
and this is comments.php
<?php
function getComments(){
$comments = "";
// use desc order by date in order to display comments by date
$sql = mysql_query("SELECT * FROM comments ORDER BY comment_date DESC ") or die (mysql_error());
if(mysql_num_rows($sql) == 0){
$comments = " <div class='each_comment'> There are no comments ...</div> ";
}else{
while ($row= mysql_fetch_assoc($sql)){
$comments .= "Says : <div class='each_comment'> <small><em> ".$row['comment_date']." </em></small><br />".$row['comment']."</div> </br>";
}
}
return $comments;
}
function postComments($comment){
$comment = mysql_real_escape_string(strip_tags($comment));
$sql = mysql_query(" INSERT INTO `comments` (comment, comment_date) VALUES ('".$comment."', now()) ");
return true;
}
if((isset($_GET['action'])) && ($_GET['action'] == "post")) {
postComments($_POST['comment']);
}
echo getComments();
?>
Orbling has it right. Let me show you what I did for my blog site that I am working on in ASP.NET MVC and maybe it will others in the future.
I have two classes named Article and Comment and they would have the following properties in php from what I remember. A little rusty on my php
class Article {
public $article_id;
public $user_id;
public $article_date;
//... more properties, ctors, methods
}
class Comment {
public $comment_id;
public $article_id;
public $isRoot;
public $parent_id;
public $comment_date;
public $content;
//... more properties, ctors, methods
}
Then I would user similar to what you constructed:
function getComments($article_id){
$str = "";
// This should put all the comment in order by date but we still have to separate the //comments from the replies so we will add an additional nest foreach loop following an if //statement inside the main foreach loop. What will happen is the newest/oldest comment //will be the first one appended to the string and then it will go to the inner foreach //loop to see if the $parent_id = $comment_id and if it does it will append to string.
//I have a different div class for replies so they are indented. This may not be the most //practical way, but considering that most articles are going to have less that a 1000 //comments if your lucky this wont be too bad.
$q = $this->db->query("SELECT * FROM comments WHERE article_id = $article_id ORDER BY comment_date DESC") or die (mysql_error());
if($q->num_rows() > 0):
foreach($q->result() as $row):
if($row->isRoot && $row->ParentId = null):
// here you will append to the $str where you will create a div container //for each comment which is a root comment. I included a user avatar, user link, date //create, content, and on the bottom of my div I have an <span>expand replies</span> and a //<span>reply<span> where I use JQuery to toggle the replies on and off etc.
// Now I do the same thing here as I will cycle through $q to find the //comments with the parent_id equal to the comment_id of this $row
//they will already be ordered by date. Cool thing is you could create an array of the reply and change the order so maybe you want newest comments on top but oldest replies on //top or I should say below the parent comment
foreach($q->result() as $row2):
if($row->comment_id = $row2->parent_id)
// do the same thing except html format for reply (eg: indent div)
endif;
endforeach;
endforeach;
}
else:
$str = "<div class='each_comment'> There are no comments ...</div> ";
endif;
echo htmlentities($str);
}
Then you will have to use some JQuery/Javascript as Orbling suggests: show(); hide(); replies and use a function such as slideDown(parent_id); once the reply button is hit where a <div> container appears below its parent comment.
I am new to both PHP and the Codeigniter(v2.0) framework. Hoping you can help me find my way through my first webapp. Details below, and thank you in advance!
Goal/Issue Summary: I would like to display within my view all blog_Comments that are attached to each paticular blog_Post.
Details: My blog_Posts table contains all original blog posts [id(int), title, entry, author (all varchar), and date(timestamp)]. My blog_Comments table[id(int), entry_id(int), author, comment(varchar)] contains all comments; they are associated with the original blog_Post via the *entry_id* attribute.
Controller:
$query1 = $this->blog_model->get_posts();
$query2 = $this->blog_model->get_comments();
$data = array();
$data['posts'] = $query1;
$data['comments'] = $query2;
Model:
function get_posts() {
$query = this->db->get('blog_Posts');
return $query->result;
}
function get_comments() {
$query = this->db->get('blog_Comments');
return $query->result;}
View:
<?php if(isset($posts)) : foreach($posts as $posts=>$row) : ?>
<?php $row->title; ?>
<?php $row->author; ?>
<?php $row->entry; ?>
<?php foreach($comments as $comments=>$com) : ?>
<?php if($com->entry_id == $row->id) : ?>
<p>author: <?php echo $com->author; ?></p>
<p>comment: <?php echo $com->comment; ?></p>
<?php endif; ?>
<?php endforeach; ?>
<?php endforeach; ?>
<?php else : ?> <p>no blog_Posts found</p>
<?php endif; ?>
Edit/Update:
My attempt to display the comments is pasted into the view code block above, I've also listed the two issues it is giving me. Right now I have 2 sample original blog posts, and comments associated with each.
Issues:
the first blog entry (first time through the loop) it is displaying ALL comments, including those associated with other entry_id s.
The second blog entry (second time through loop), it throws the following error: "Invalid argument supplied for foreach()", and points to the line in my view containing the foreach for $comments.
In short, I'm trying to loo through only those comments with the entry_id that matches the original posts "id."
Thanks again for your assistance and time.
-AJ
Well since you do not need to display all comments at once, you could just alter your get_comments function like below to return comments for each post and of course display them in your way, order by date created, last entry etc:
So for the model part:
function get_comments($post_id){
$query = $this->db->query("select * from `blog_Comments` where `entry_id` = $post_id ORDER BY `created_on` DESC");
if($query->num_rows() != 0){
return $query->result();
}else{
return false;
}
}
And in your view file:
foreach($posts as $row){
echo 'Title: ' . $row->title .
'Author: ' . $row->author .
'Entry:' . $row->entry;
$comments = $this->blog_model->get_comments($row->id);
if(!$comments){
echo 'No comments';
}else{
foreach($comments as $com){
echo $com->comments . '<br/>' . $com->user . '<br/>' . etc etc
}
}
}
I'm too lazy to test, but i think you are trying to use $row, but you are outside the foreach where its defined. you introduce $row here
<?php if(isset($posts)) : foreach($posts as $posts=>$row) : ?>
and then you close the foreach -- so $row is no longer available down here:
<?php if($com->entry_id == $row->id); ?>
so yeah. but really you should be doing as much logic and checking as you can in the controller and model. verify your data objects - like Posts - THEN the controller determines the correct view. so then if there aren't any posts to show -- you dont have messy 'what if there are no posts' conditionals in your view. you just call the no posts view file.
So basically what I am trying to do is retrieving a list of categories from the database and placing it in a variable called $data['categories'].
In the view I am listing this up by simply using a foreach() statement. So far so good.
Though, some categories have subcategories (not all of them). Now, I added a row in the database saying 'is_direct' (has subcategories or not) which displays whether the category has subcategories or not.
Now, I want to list up those subcategories in case they exist.
Here the method to display the header (where the categories and subcategories will be listed) (Don't mind the private, it should be that).
private function show_header($template='default',$page_name='default')
{
// Get the categories.
$data['categories'] = $this->CI->ws_category->get_categories();
// Display the header.
$this->CI->load->view("templates/".$template."/template/header",$data);
}
This method calls for the method get_categories(), that's the one below (simple Active Record).
public function get_categories()
{
// Prepare the SQL query.
$this->db->select('*');
$this->db->from(APP_PREFIX.'categories');
$this->db->order_by('name_clean');
// Execute the query.
$query = $this->db->get();
// Get the results from the query.
$result = $query->result_array();
// Return the array with data.
return $result;
}
And below the view, you can see where the submenu would go.
<?php
// Loop through all the categories.
foreach ($categories as $item):
?>
<li class='nav_link <?php ($item['is_direct'] == '1' ? 'nav_subcats' : ''); ?>'>
<a href='<?php echo base_url().'category/'.$item['category_id'].'-'.$item['name_clean'].'.html';?>' class='nav_main_link'><?php echo $item['name']; ?></a>
<?php
// In case subcategories are present.
if ($item['is_direct'] != '1')
{
?>
<div class='nav_submenu'>
<ul>
<li><a href='#' class='nav_sub_link'>submenu item</a></li>
</ul>
</div>
<?php
}
?>
</li>
<?php
// End the loop.
endforeach;
?>
Can somebody help me?
Thank you.
The approach I would take would be to build out more complete data in your model (or controller) so that you can simply loop through it in the view (like you are currently doing). Below I have written a possible solution. I have modified your get_categories() method to query for subcategories as well. Then, in the view, we check to see if a category has any subcategories and outputs them as necessary.
public function get_categories_and_subcategories()
{
// Prepare the SQL query.
$this->db->select('*');
$this->db->from(APP_PREFIX.'categories');
$this->db->order_by('name_clean');
// Execute the query.
$query = $this->db->get();
// Get the results from the query.
$result = $query->result_array();
// Get subcategories
for ($i = 0; $i < count($result); $i++) {
// Check if subcategory
if ($result[$i]['is_direct']) {
// Query for subcategories - made up for demonstrational purposes
$query = $this->db->query("SELECT * FROM subcategories WHERE parent_id='".$result[$i]['id']."'");
// Add subcategories to category item
$result[$i]['subcategories'] = $query->result_array();
} else {
$result[$i]['subcategories'] = array();
}
}
// Return the array with data.
return $result;
}
Then in the view:
<li class='nav_link <?php ($item['is_direct'] == '1' ? 'nav_subcats' : ''); ?>'>
<a href='<?php echo base_url().'category/'.$item['category_id'].' '.$item['name_clean'].'.html';?>' class='nav_main_link'><?php echo $item['name']; ?></a>
<?php if (count($item['subcategories']) > 0): ?>
<div class='nav_submenu'>
<ul>
<foreach ($item['subcategories'] as $subcategory): ?>
<li><a href='<?php echo $subcategory['url']; ?>' class='nav_sub_link'><?php echo $subcategory['name']; ?></a></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
</li>
Keep in mind I haven't tested this code but you should be able to adopt the general idea to your application.