I very recently (as in, this week) started learning PHP and am doing a practice task.
(Therefore, apologies in advance, if I sound like I don't know what I'm talking about, that is true)
Here is a crude schematic of my tables:
I have a main table products, and there are 3 planned product types (book, disc, furniture), and each type has a special attribute, with their own tables. (size for discs, weight for books etc.)
So far through testing, I've managed to query out the object from my products table each into their separate bootstrap card components. What I am trying to accomplish now is to add the special attributes to their respective object cards.
This is where i've reached a dead end. The idea I have is to first create a seperate array $printtocard, then call all the products table records by type, then run a foreach through all the found records, and then run a switch inside the foreach where i check the product types and run cases based on type, which create new objects based on the linked table data, and add each of them to the $printtocard array.
Afterwards the plan is to call the array in the code that later outputs the objects.
Here is my code so far:
Main php code
class products {
var $arrayy;
var $sql;
var $typepr;
protected function get_prod($conn1){
$printtocard = array();
$call_type = 'SELECT type FROM products ORDER BY id';
$type_res = mysqli_query($conn1, $call_type);
$type_arr = mysqli_fetch_all($type_res, MYSQLI_ASSOC);
$this->typepr = $type_arr;
return $this->typepr;
foreach($typepr as $xxx){
switch($xxx){
case "disc":
/* $disc_ */$sql = 'SELECT p.sku, p.name, p.price, a.value FROM products AS p INNER JOIN attr_size AS a ON p.id = a.product_id;';
$val_res = mysqli_query($conn1, $sql);
$val_arr = mysqli_fetch_all($val_res, MYSQLI_ASSOC);
$printtocard[] = $val_arr;
return $this->printtocard;
break;
case "book":
/* $book_ */$sql = 'SELECT p.sku, p.name, p.price, a.value FROM products AS p INNER JOIN attr_weight AS a ON p.id = a.product_id;';
$val_res = mysqli_query($conn1, $sql);
$val_arr = mysqli_fetch_all($val_res, MYSQLI_ASSOC);
$printtocard[] = $val_arr;
return $this->printtocard;
break;
case "furniture":
/* $furniture_ */$sql = 'SELECT p.sku, p.name, p.price, a.height, a.width, a.length, FROM products AS p INNER JOIN attr_dims AS a ON p.id = a.product_id';
$val_res = mysqli_query($conn1, $sql);
$val_arr = mysqli_fetch_all($val_res, MYSQLI_ASSOC);
$printtocard[] = $val_arr;
return $this->printtocard;
break;
}
}
//$sql = 'SELECT p.sku, p.name, p.price, a.value FROM products AS p INNER JOIN attr_size AS a ON p.id = a.product_id;';
//$call2 = 'SELECT sku, name, price FROM products ORDER BY id';
//$val_res = mysqli_query($conn1, $sql);
//$val_arr = mysqli_fetch_all($val_res, MYSQLI_ASSOC);
$this->arrayy = $printtocard;
return $this->arrayy;
}
public function get_prod2($conn1)
{
$this->get_prod($conn1);
}
}
$fff= new products;
$fff->get_prod2($conn);
$sss= $fff->arrayy;
Snippet of the webpage code where I autogenerate the cards:
<div class="container">
<div class="row">
<?php
foreach($sss as $value){ ?>
<div class="col s6 md2">
<div class="card cardstyle z-depth-0">
<div class="card-content center">
<?php echo htmlspecialchars($value['sku']); ?>
<div>
<?php echo htmlspecialchars($value['name']); ?>
</div>
<div>
<?php echo htmlspecialchars($value['price']); ?>
</div>
<div>
<?php echo htmlspecialchars($value['value']); ?>
</div>
</div>
<div class="card-action right-align">
<a class="brand-text" href="#"> more info </a>
</div>
</div>
</div>
<?php } ?>
</div>
</div>
The error message I currently get:
Warning
: Invalid argument supplied for foreach() in
C:\xampp\htdocs\uzdevumi\scandi_uzdevumi_kristianskonters\productlist\productlist.php
on line
108
A number of pointers
1. Its better to define your variables at the top depending on your intentions later (protected, private or public)
protected $arrayy;
protected $sql;
protected $typepr;
The return just before your "foreach" loop renders your loop pointless because it stops your function there and returns what you have.
return $this->typepr;
In connection to my previous point, you seem to think that "return" does some concatenation for you as shown by each "case" you have in your switch. No it does not it exits your function back to the calling scope.
return $this->printtocard;
The warning you have at the end means what it says, you are providing the "foreach" loop with an invalid argument. In other words it does not exist, thankfully its php (dynamically typed). But the problem is its undefined (has not value). Maybe its a typo and you wanted to loop through the array variable you got from sql calls.
foreach($typepr as $xxx)
Cross check typos and I did not get to check your html.
Related
I am making a sort of forum for myself to see how everything works. Its build in PHP (PDO) and now I got the problem. I can't figure out how I make sure when someone clicks on a record out of the board that it will only show the items with the topic_id that is set to that board
This is the topic database
This is the boards database
The code too show the board(s):
<section class="col-md-8 connectedSortable">
<div class="box box-info">
<div class="panel panel-default">
<div class="panel-heading main-color-bg">
<h3 class="panel-title">Boards</h3>
</div>
<div class="panel-body">
<?php
$boardss = $app->get_boards();
foreach($boardss as $board){
echo '<div class="col-md-6">';
echo '<div class="well dash-box">';
echo '<h3>'.$board['topic'].'</h3><br>';
echo '' .$board['omschrijving'].'';
echo '</div>';
echo '</div>';
}
?>
</div>
</div>
</div><!-- /.box -->
</section><!-- right col -->
The functions I use:
public function get_boards(){
$getBoards = $this->database->query("SELECT * FROM boards ORDER BY id DESC");
$boards = $this->database->resultset();
return $boards;
}
public function get_topics(){
$getTopic = $this->database->query("
SELECT topics.*, klanten.foto, klanten.voornaam, klanten.achternaam FROM topics
LEFT JOIN klanten ON topics.klant_id=klanten.id
ORDER BY id ASC");
$topics = $this->database->resultset();
return $topics;
}
When generating the link to the board page, you need to specify which board is actually called. Your current code looks like this:
echo '' . $board['omschrijving'] . '
As you can see, the call itself, which is in the href attribute, does not contain any information. Instead, you should add the primary key of the board there:
echo '' . $board['omschrijving'].'
If that is done, you can get the current board on the boards page via $_GET:
$currentBoard = $_GET['board'];
if (!is_numeric($currentBoard)) {
die('Not a valid board id');
}
$currentBoard = (int)$currentBoard;
With that information, you can then can specify the topic for the board, by adding it to your query in get_topics() and adding the board is as parameter to the function. (get_topics($currentBoard))
SELECT
topics.*,
klanten.foto,
klanten.voornaam,
klanten.achternaam
FROM
topics
LEFT JOIN klanten ON topics.klant_id=klanten.id
WHERE
topics.board_id = :board
ORDER BY id ASC
That statement uses the parameter :board, which you can replace in a prepared statement with the variable $currentBoard. I would highly encourage you to use prepared statments in that case.
Add parameter $board_id to get_topics(), then add a WHERE clause for the passed $board_id, so that only topics of that board will be fetched.
Updated Function:
public function get_topics($board_id){
$getTopic = $this->database->query("
SELECT topics.*, klanten.foto, klanten.voornaam, klanten.achternaam FROM topics
LEFT JOIN klanten ON topics.klant_id=klanten.id
WHERE topics.board_id = '$board_id'
ORDER BY id ASC");
$topics = $this->database->resultset();
return $topics;
}
I recently asked a similar question to this about two weeks ago, but I have had no look in getting my problem working the way I need it to.
I have a query that is selecting from 2 tables; tickets and replies. I'm selecting the information from the ticket table, and I'm selecting the information from the replies table which has the ticket id stored in there. Which now comes to my problem.
My query is only displaying tickets that have more than 0 replies, but I need it to display the ticket information even if it doesn't have any replies.
I would like to know (if possible) if there is any way to fix my problem, and if there is a way to make it simpler than I currently have it.
It's a bit messy right now, but here is my is code to query and display the tickets and replies.
if(isset($_GET['id']) && is_numeric($_GET['id'])) {
$id = trim($_GET['id']);
$i = "";
$ticket = $db->conn->query("
SELECT * FROM tickets
INNER JOIN replies ON tickets.id = '$id'") or die(mysqli_error($db->conn));
while($rows = $ticket->fetch_assoc()) {
$i++;
if($_SESSION['ticket_username'] == $rows['client']) {
if($i <= 1) {
$status = $rows['status'];
echo '
<h3>'.$rows['subject'].'</h3><hr>
<div class="panel panel-danger">
<div class="panel-heading">
<h3 class="panel-title"><small>Created by '.$rows['client'].', '.$timeAgo->inWords($rows['created_at']).'</small></h3>
</div>
<div class="panel-body">'.nl2br($rows['message']).'</div>
</div>
';
}
echo '
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title"><small>Reply from '.$rows['reply_username'].', '.$timeAgo->inWords($rows['reply_time']).'</small></h3>
</div>
<div class="panel-body">'.nl2br($rows['reply_message']).'</div>
</div>
';
} else {
header("Location: index");
}
}
} else {
header("Location: index");
}
Change the query.
Use a LEFT JOIN instead of an INNER JOIN
$ticket = $db->conn->query("SELECT * FROM tickets
LEFT JOIN replies ON tickets.id = '$id'") or die(mysqli_error($db->conn));
Explanation:
Inner Join returns only those values for both the tables get the
match. This is a kind of intersection of two circles thing.
Left Join returns all the rows from left table irrespective
of whether it has any match on the right table.
In your case, if you use inner join, it was returning only those tickets who have replies.
Image Referred from here
i was given help previously on how to join multiple tables together to get this navigation list to work, as you can see i have done this, but now i am trying to output the navigation in my list, but it is duplicating the top and bottom categories based on how many products are in those categories: this is previous link that shows my table setup:
Joining 2 tables with foreign key id
Here is my code trying to echo out the navigation correctly.
try
{
$result = $pdo->query('SELECT product.*, bottom_category.bottom_name, top_category.top_name
FROM product
INNER JOIN bottom_category ON bottom_category.id = product.bottom_category_id
INNER JOIN top_category ON top_category.id = bottom_category.top_category_id
ORDER BY top_category.id,bottom_category.id');
} // end try
catch (PDOException $e)
{
echo 'There was a error fetching the products.' . $e->getMessage();
exit();
} // end catch
$products = array();
foreach ($result as $row)
{
$products[] = array('top_name' => $row['top_name'],
'bottom_name' => $row['bottom_name']);
}
?>
<div class="sidebar">
<h4 class="sidebar-header">Select Products</h4>
<form class="nav-search-form">
<input type="search" name="search" placeholder="Search products">
</form>
<nav class="sidebar-links">
<ul>
<li><a id="red" href="/semtronics/index.php">New Products</a></li>
<?php
foreach ($products as $product):
?>
<li><?php echo htmlspecialchars($product['top_name']);?>
<ul>
<li><?php echo htmlspecialchars($product['bottom_name']);?></li>
</ul>
</li>
<?php endforeach; ?>
</ul>
</nav>
</div><!-- sidebar -->
Now it all works the only problem is it is duplicating the navigation list based on how many products are linked to that category.
I think you need another solution for this task.
You don't need product here
Query what you need is:
select bottom_category.name as bottom_name, top_category.name as top_name
from bottom_category
inner join top_category on top_category.id = bottom_category.top_category_id
order by top_category.id, bottom_category.id
I was thinking that you need something from product table in your code, but if no - use SQL query above.
Or you need data from product table ?
If you need from product you can run
select bottom_category.name as bottom_name, top_category.name as top_name
from product
inner join bottom_category on bottom_category.id = product.bottom_category_id
inner join top_category on top_category.id = bottom_category.top_category_id
group by product.bottom_category_id
order by top_category.id, bottom_category.id
But be careful, you don't know which row from product would be used in this case
I'm building a simple web app at the moment that I'll one day open source. As it stands at the moment, the nav is generated on every page load (which will change to be cached one day) but for the moment, it's being made with the code below. Using PHP 5.2.6 and MySQLi 5.0.7.7, how more efficient can the code below be? I think joins might help, but I'm after advice. Any tips would be greatly appreciated.
<?php
$navQuery = $mysqli->query("SELECT id,slug,name FROM categories WHERE live=1 ORDER BY name ASC") or die(mysqli_error($mysqli));
while($nav = $navQuery->fetch_object()) {
echo '<li>';
echo ''. $nav->name .'';
echo '<ul>';
$subNavQuery = $mysqli->query("SELECT id,name FROM snippets WHERE category='$nav->id' ORDER BY name ASC") or die(mysqli_error($mysqli));
while($subNav = $subNavQuery->fetch_object()) {
echo '<li>';
echo ''. $subNav->name .'';
echo '</li>';
}
echo '</ul>';
echo '</li>';
}
?>
You can run this query:
SELECT c.id AS cid, c.slug AS cslug, c.name AS cname,
s.id AS sid, s.name AS sname
FROM categories AS c
LEFT JOIN snippets AS s ON s.category = c.id
WHERE c.live=1
ORDER BY c.name, s.name
Then iterate thru the results to create the proper heading like:
// last category ID
$lastcid = 0;
while ($r = $navQuery->fetch_object ()) {
if ($r->cid != $lastcid) {
// new category
// let's close the last open category (if any)
if ($lastcid)
printf ('</li></ul>');
// save current category
$lastcid = $r->cid;
// display category
printf ('<li>%s', $r->cslug, $r->cname);
// display first snippet
printf ('<li>%s</li>', $r->cslug, $r->sname, $r->sname);
} else {
// category already processed, just display snippet
// display snippet
printf ('<li>%s</a>', $r->cslug, $r->sname, $r->sname);
}
}
// let's close the last open category (if any)
if ($lastcid)
printf ('</li></ul>');
Note that I used printf but you should use your own function instead which wraps around printf, but runs htmlspecialchars thru the parameters (except the first of course).
Disclaimer: I do not necessarily encourage such use of <ul>s.
This code is just here to show the basic idea of processing hierarchical data got with one query.
First off, you shouldn't query your database in your view. That would be mixing your business logic and your presentation logic. Just assign the query results to a variable in your controller and iterate through it.
As for the query, yup a join can do that in 1 query.
SELECT * -- Make sure you only select the fields you want. Might need to use aliases to avoid conflict
FROM snippets S LEFT JOIN categiries C ON S.category = C.id
WHERE live = 1
ORDER BY S.category, C.name
This will get you an initial result set. But this won't give you the data nicely ordered like you expect. You'll need to use a bit of PHP to group it into some arrays that you can use in your loops.
Something along the lines of
$categories = array();
foreach ($results as $result) {
$snippet = array();
//assign all the snippet related data into this var
if (isset($categories[$result['snippets.category']])) {
$categories[$result['snippets.category']]['snippet'][] = $snippet;
} else {
$category = array();
//assign all the category related data into this var;
$categories[$result['snippets.category']]['snippet'] = array($snippet);
$categories[$result['snippets.category']]['category'] = $category;
}
}
This should give you an array of categories which have all the related snippets in an array. You can simply loop through this array to reproduce your list.
I'd try this one:
SELECT
c.slug,c.name,s.name
FROM
categories c
LEFT JOIN snippets s
ON s.category = c.id
WHERE live=1 ORDER BY c.name, s.name
I didnt test it, though. Also check the indexes using the EXPLAIN statement so MySQL doesnt do a full scan of the table.
With these results, you can loop the results in PHP and check when the category name changes, and build your output as you wish.
Besides a single combined query you can use two separate ones.
You have a basic tree-structure here with branch elements (categories table) and leaf elements (snippets table). The shortcoming of the single-query solution is that you get owner brach-element repeatedly for every single leaf element. This is redundant information and depending on the number of leafs and the amount of information you query from each branch element can produce large amount of additional traffic.
The two-query solution looks like:
$navQuery = $mysqli->query ("SELECT id, slug, name FROM categories WHERE live=1 ORDER BY name")
or die (mysqli_error ($mysqli));
$subNavQuery = $mysqli->query ("SELECT c.id AS cid, s.id, s.name FROM categories AS c LEFT JOIN snippets AS s ON s.category=c.id WHERE c.live=1 ORDER BY c.name, s.name")
or die (mysqli_error ($mysqli));
$sub = $subNavQuery->fetch_object (); // pre-reading one record
while ($nav = $navQuery->fetch_object ()) {
echo '<li>';
echo ''. $nav->name .'';
echo '<ul>';
while ($sub->cid == $nav->id) {
echo '<li>';
echo ''. $sub->name .'';
echo '</li>';
$sub = $subNavQuery->fetch_object ();
}
echo '</ul>';
}
It should print completely the same code as your example
$navQuery = $mysqli->query("SELECT t1.id AS cat_id,t1.slug,t1.name AS cat_name,t2.id,t2.name
FROM categories AS t1
LEFT JOIN snippets AS t2 ON t1.id = t2.category
WHERE t1.live=1
ORDER BY t1.name ASC, t2.name ASC") or die(mysqli_error($mysqli));
$current = false;
while($nav = $navQuery->fetch_object()) {
if ($current != $nav->cat_id) {
if ($current) echo '</ul>';
echo ''. $nav->cat_name .'<ul>';
$current = $nav->cat_id;
}
if ($nav->id) { //check for empty category
echo '<li>'. $nav->name .'</li>';
}
}
//last category
if ($current) echo '</ul>';
Ok so i am having a problem with figuring this problem out. I have a dynamic page that has records pulled out of a mysql database. The php code works great. The code is below
$catagory = $_GET['type'];
$query = "SELECT p.name, p.price, pc.quantity, p.image, p.descr FROM products ... where category_name = $catagory ";
$result = mysql_query($query);
$related = array();
while($row_r = mysql_fetch_assoc($result)){
$related[] = $row_r;
}
later on down the page I loop through them
<div class="main-blue">
<div class="blue-items">
<?php foreach ($related as $row_r) { ?>
<span class="digit"><span class="digit-r"><?php print $row_r["quantity"] ?></span></span>
<h3><?php print $row_r['name'] ?></h3>
.....
The problem is I have a
<div class="dropdown">
<ul>
<li>Grocery</li>
<li>Pizza</li>
<li>Quick Service</li>
<li>Retail</li>
<li>Salon</li>
<li>Bar</li>
</ul>
I need to be able to select one of the dropdowns like for example Bar I need to regrab the record from mysql and make the page load with the records for Bar without refreshing .
like
$catagory = "Bar";
$query = "SELECT p.name, p.price, pc.quantity, p.image, p.descr FROM products ... where category_name = $catagory ";
You need to use some sort of AJAX implementation to get this to work.
It will allow Js to query your PHP script to get a table or new JSON data to repopulate the table.
It doesn't look like you have any jQuery at the moment, give it a go to start with!
This question is very similar