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
Related
I have searched for about 4 hours today on how to do this.
I want to pull all post titles and categories from the posts table and
essentially want to list the "Funny" category and then list all post that have the funny category under that category. Right now I am getting the following:
Funny
-post title
Funny
-post title
I want to output
Funny
-post title
-post title
<?php
$query = "SELECT post_category,post_title FROM posts ";
$select_categories_sidebars = mysqli_query($connection, $query);
?>
<h4>Blog Categories</h4>
<div class="row">
<div class="col-lg-12">
<ul class="list-group">
<?php
while($row = mysqli_fetch_assoc($select_categories_sidebars)) {
$post_title = $row['post_title'];
$post_tags = $row['post_category'];
echo "<li class='list-group-item'>$post_tags</li><ul>";
echo "<li class='list-group-item'><a href='category.php?category=$post_title'> {$post_title}</a></li></ul>";
}
?>
</ul>
That should do the trick:
First you should sort your query by category:
$query = "SELECT post_category,post_title FROM posts ORDER BY post_category";
Then do this:
<?php
while($row = mysqli_fetch_assoc($select_categories_sidebars)) {
$post_title = $row['post_title'];
$post_tags = $row['post_category'];
$used_tag = null,
// check if you already posted that category/tag. If not, show it:
if($used_tag!=$post_tags) {
echo "<li class='list-group-item'>$post_tags</li>"; // I removed a '</ul>' here
// set this title as 'used'
$used_tag=$post_tags;
}
echo "<li class='list-group-item'><a href='category.php?category=$post_title'> {$post_title}</a></li>"; // I removed a '</ul>' here too
}
?>
BUT
In an ideal world you'd have two tables to accomplish this.
One for the categories, one for the posts. Each table with id's to work with
This way you produce a lot of redundant data, it's more complicated to filter, and so on...
You might want to have a look at relational database design.
Well I basically got it working how I want I just need to format it correctly now, but it is working with some help from this question. PHP Group sql query under the same title
<?php
$query = "SELECT post_category,post_title FROM posts ORDER BY post_tags";
$select_categories_sidebars = mysqli_query($connection, $query);
?>
<h4>Blog Categories</h4>
<div class="row">
<div class="col-lg-12">
<ul class="list-group">
<?php
$title = "";
while ($row = mysqli_fetch_array($select_categories_sidebars)) {
if ($row['post_category'] != $title) {
echo '<li class="list-group-item">'.$row['post_category'].'</li><br>';
$title = $row['post_category'];
}
echo '<li class="list-group-item">'.$row['post_title'];
}
?>
I'm wondering what is the appropriate syntax is to echo a row from MySQLi code in a block of HTML text. I'm working on a page that uses PHP code at the start to determine if a session is started and to post comments that user has posted, after pulling said comments from a MySQLi database. The interesting and confusing part is, I've accomplished what I'm trying to do in one HTML div, but I can't seem to get it to work in the next.
<?php
$page_title = "store";
require_once('connectvars.php');
require_once('startsession.php');
require_once('header.php');
// Connect to the DB
$dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
// Grab location data from the DB
// Grab post data
$query2 = "SELECT sp.post_id, sp.admin_id, sp.post, sp.date, sa.admin_id, s.store_name, s.store_city, s.store_state, s.store_zip,s.store_phone, s.store_email, s.store_address FROM store_posts sp, store_admin sa, stores s WHERE sp.admin_id='$admin_id' and sa.admin_id='$admin_id' and s.admin_id='$admin_id'ORDER BY date DESC";
$data = mysqli_query($dbc, $query2);
if (mysqli_num_rows($data) == 0) {
$blankwall = "Empty feed? '<a href=manage.php><u>Click here</u></a> to manage posts and communicate with your customers!";
}
?>
<body>
<div id="content">
<!-- BANNER & CONTENT-->
<h2>Recent Posts</h2>
<div id="store_wrap">
<div id="left_col">
<br />
<?php
**// Loop through posts and show them
if (mysqli_num_rows($data) == 0) {
echo $blankwall;
}
else {
while ($row = mysqli_fetch_array($data)) {
// Show the posts
echo '' . $row['post'] . ' | ';
echo date('M j, Y g:i A', strtotime($row['date']));
echo '<br /><hr />';
}**
}
?>
</div><!-- closes left_col -->
So all the above code is there to query the DB to grab the correct array and then show $row['posts'] in the HTML div below the PHP code, titled left_col. I am trying to do the exact same thing in the next div but instead of echoing $row['posts'], I want to echo rows such as $row['store_city'] to have the page display the store's location after pulling it out of the previously selected array. Here's my non-functioning code for that part:
<div id="right_col">
<div id="store_picture">
<img src="images/store.jpg" style="width:325px;"/>
</div><!-- closes picture --><br />
<div id="store_address">
<br /><br /><h2>Location Info</h2>
<?php
if (mysqli_num_rows($data) == 1) {
while ($row = mysqli_fetch_array($data)) {
echo '<p>' . $row['store_city']. '</p>';
}
}
mysqli_close($dbc);
?>
</div><!-- closes store_address -->
<div id="store_info">
<p></p>
</div><!-- closes store_info -->
</div><!-- closes right_col -->
<div class="clear"></div>
</div><!-- closes store_wrap -->
</div><!-- closes content -->
For whatever reason, the second time, when I try to echo data from that array, I just have empty space within that div. I don't get any errors. I just don't get...anything. Therefore, I think this is a syntax issue. I've tried exactly the same thing I did with the section where I echo $row['post'] and it isn't working.
The chief issue you're facing is that you make a second attempt at fetching rows from the $data MySQLi result resource object after already having fetched them once. This won't work as intended, as MySQL keeps an internal recordset pointer which advances every time mysqli_fetch_array() is called. So when the end of the first loop is reached, the pointer is at the end of the recordset and a subsequent call will return FALSE.
Therefore, your second loop gets nowhere because its first call to mysqli_fetch_array() returns FALSE, exiting the loop. You have two options.
The quickest fix is to just rewind the record pointer using mysqli_data_seek(). Called right before the second loop, it will set the pointer back to the first record, allowing you to fetch them all again.
if (mysqli_num_rows($data) == 1) {
// Rewind the record pointer back to the first record
mysqli_data_seek($data, 0);
while ($row = mysqli_fetch_array($data)) {
// note addition of htmlspecialchars() to escape the string for HTML!
echo '<p>' .htmlspecialchars($row['store_city']). '</p>';
}
}
Perhaps a better option if your recordset is small is to fetch all rows into an array once, then use that array with a foreach loop for both your subsequent output loops:
// To hold all rows
$rows = array();
// Do the query
$data = mysqli_query($dbc, $query2);
// (don't forget to check for errors)
if ($data) {
while ($row = mysqli_fetch_array($data)) {
// Append the row onto the $rows array
$rows[] = $row;
}
}
else{
// handle the error somehow...
}
Later, instead of using $data again, you will loop over $rows with a foreach loop.
// use count($rows)
if (count($rows) == 0) {
echo $blankwall;
}
else {
foreach ($rows as $row) {
// Show the posts
echo '' . $row['post'] . ' | ';
echo date('M j, Y g:i A', strtotime($row['date']));
echo '<br /><hr />';
}
}
...And do the same thing again for your second loop later in the code. It's recommended to use htmlspecialchars() on the string fields output from the query where appropriate.
About this code:
<?php
$result = mysqli_query($connect,"SELECT subcategories.subcat_name, subsubcategories.subsubcat_name FROM subcategories INNER JOIN subsubcategories ON subcategories.subcat_ID=subsubcategories.subcat_ID WHERE subsubcategories.subcat_ID = 1");
$subcat_name = mysqli_fetch_array($result);
?>
<div class="grid_5 alpha omega" id="titlescontent"><p class="titlebar"><?php echo $subcat_name['subcat_name'];?></p></div>
<div class="clear"></div>
<div class="grid_5 alpha omega" id="content"><ul class="subcat">
<?php
while ($row=mysqli_fetch_array($result)){
?><li><?php echo $row['subsubcat_name'];?></li><?php
}
?>
</ul></div>
<div class="clear"></div>
For some reason it starts displaying the subsubcat_name from the 2nd result and not the first one which I also want to be displayed. Any idea how come and what I need to change in this code?
Actually, I see you need that first one for the title? In that case, try this alternate approach:
Replace your while loop with:
$row = $subcat_name;
do {
echo "<li>".$row['subsubcat_name']."</li>";
} while($row = mysqli_fetch_array($result));
What does this change? It basically means the loop body will run for your initial row, then for the rest.
you have extra mysqli_fetch_array call
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;
}
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.