Recipes book - PHP confusion JOINING 3 tables, not displaying what expected - php

I am doing a recipe book. My database tables look like this:
Table "recipes" has columns: id, name
Table "ingredients" has columns: id, name, measurement_id
Table "recipes_have_ingredients" has columns: id, recipe_id, ingredient_id, ingredient_amount, unit_id
Table "units" has columns: id, name, measurement_id
The idea is that the user will be able to insert an ingredient for a recipe selecting how it is measured (volume, mass, length...), and then, it will display a list of the units for that measurement; for example, if I insert chicken, and I select it as "mass", next time the ingredient chicken is introduced, it will display a list of the units for measuring mass.
Anyway..... at the moment, I am trying to display a table with the recipe name, and the ingredients list, with the ingredient name, amount and unit name.
If I do the query in mysql as
SELECT recipes_have_ingredients.recipe_id
, ingredients.name
, recipes_have_ingredients.ingredient_amount
, units.name
FROM recipes_have_ingredients
JOIN ingredients
ON recipes_have_ingredients.ingredient_id = ingredients.id
JOIN units
ON recipes_have_ingredients.unit_id = units.id
WHERE recipe_id = 2
it displays:
+-----------+-------+-------------------+----------+
| recipe_id | name | ingredient_amount | name |
+-----------+-------+-------------------+----------+
| 2 | onion | 1 | kilogram |
| 2 | milk | 30 | litre |
+-----------+-------+-------------------+----------+
Which is what I want, but, if I try to create a function of it as:
public function display_ingredients_for_recipe($recipe_id = ':recipe_id') {
include 'includes/db_connection.php';
try {
$sql = "SELECT recipes_have_ingredients.recipe_id, ingredients.name, recipes_have_ingredients.ingredient_amount, units.name FROM recipes_have_ingredients JOIN ingredients ON recipes_have_ingredients.ingredient_id=ingredients.id JOIN units ON recipes_have_ingredients.unit_id=units.id WHERE recipe_id=:recipe_id";
$results = $conn->prepare($sql);
$results->bindParam(':recipe_id', $recipe_id, PDO::PARAM_INT);
$results->execute();
} catch(PDOException $e) {
echo 'Error: ' . $e->getMessage() . '<br />';
return array();
}
return $results->fetchAll(PDO::FETCH_ASSOC);
}
And I try to use it (recipe['id']) is coming from the display_recipes method:
public function display_recipes() {
include 'includes/db_connection.php';
try {
$sql = "SELECT id, name FROM recipes";
$results = $conn->prepare($sql);
$results->execute();
} catch (PDOException $e) {
echo 'Error: ' . $e->getMessage() . '<br />';
return array();
}
return $results->fetchAll(PDO::FETCH_ASSOC);
}
$ingredients = $recipeObj->display_ingredients_for_recipe($recipe['id']);
echo '<pre>';
print_r($ingredients);
echo '</pre>'; die();
It is displaying:
Array
(
[0] => Array
(
[recipe_id] => 1
[name] => gram
[ingredient_amount] => 350
)
)
And I don't know what I am doing wrong.... why if I do the query on mysql it selects what I want but then, if I try to debug the ingredients I can't make the ingredient name and the unit name to appear.
Any help will be appreciated.

Because you have two fields with the same name (which, ironically, also happens to be "name"). PHP can't have two of the same key in an array, so they overwrite. If you rename one (or both) of the fields, it should work.
SELECT recipes_have_ingredients.recipe_id, ingredients.name AS ingredient_name, recipes_have_ingredients.ingredient_amount, units.name AS unit_name ...

Related

SELECT results from another table AS column array

I can't figure out how to get results from 2 tables, in 1 query result (can't simple JOIN)
I have these 2 tables in my MySQL database:
Table 1: sales
id
name
info
Table 2: users
sale_id
user_id
Now, every sale have different number of assigned users. Some sale have 2 users, some sale have 10 users.
In single row, I need to have columns from sale table, and all assigned users to it (connected with same Sale_id)
I need result, something like this:
enter image description here
Try this :
SELECT s.*,
(SELECT GROUP_CONCAT(u.user_id SEPARATOR ', ')
FROM users u
WHERE u.sale_id = s.id) AS users
FROM sales s
Some insight on your programming language would have been nice.
And yes, as suggested by wogsland and icoder, one typically use joins and loop through results to build en array. But the use of GROUP_CONCAT, as Yoleth pointed out, is what you need. I don’t know if it was the goal here, but it can reduce memory used in the result because there is no row repetition.
SELECT info FROM Sales AS s,
(
SELECT sale_id, GROUP_CONCAT(user_id) AS assigned_users
FROM Users
GROUP BY sale_id) AS u
WHERE s.id=u.sale_id;
In a single query, with a fancy JOIN:
SELECT s.info AS info, u.sale_id AS sale_id, GROUP_CONCAT(u.user_id) AS assigned_users
FROM Sales AS s LEFT JOIN Users AS u
ON s.id=u.sale_id
WHERE sale_id IS NOT NULL GROUP BY u.sale_id;
You can simply join two tables and get query result set like this:
saleID | saleName | userID | userName
1 | Oct Sale | 5 | Tim
1 | Oct Sale | 6 | Nik
2 | Nov Sale | 7 | Bill
Then you can walk each row and build associative array from that data:
$sales = array();
while( $row = mysqli_fetch_assoc($result)) {
if (!array_key_exists($row['saleID'], $sales)) {
$sales[$row['saleID']] = array(
'saleID' => $row['saleID'],
'saleName' => $row['saleName'],
'users' => array()
);
}
array_push($sales[$row['saleID']]['users'], array(
'userID' => $row['userID'],
'userName' => $row['userName']
));
}
Well, MySQL isn't going to return you a nice nested array like that. But you can create it by looping through the result. Assuming your MySQL connection is named $mysqli then try something like
$sales = array();
$result = $mysqli->query("SELECT sales.*, users.user_id FROM sales, users WHERE sales.id = users.sales_id");
while ($row = $result->fetch_assoc()) {
$sales[$row->id]['sales_id'] = $row->id;
$sales[$row->id]['name'] = $row->name;
$sales[$row->id]['info'] = $row->info;
$sales[$row->id]['assigned_users'][] = $row->user_id;
}

Dynamically selecting tables in mySQL

I have a query in mySQL
SELECT id FROM admin_products;
which return a list of ids, like so
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
+------+
And I was using PHP to dynamically generate tables like
vendor_1, vendor_2, vendor_3, vendor_4, vendor_5
Now I want to write a query to retrieve the price and quantity from the table id
For example
"ENTER QUERY HERE"
Should retrieve
+-----------------------------+
| id | price | quantity |
+-----------------------------+
| 1 | 23| 13| // price and quantity retrieved from table vendor_1 since id=1
| 2 | 158| 85| // price and quantity retrieved from table vendor_2 since id=2
| 3 | 15| 7| // price and quantity retrieved from table vendor_3 since id=3
| 4 | 112| 9| // price and quantity retrieved from table vendor_4 since id=4
| 5 | 123| 199| // price and quantity retrieved from table vendor_5 since id=5
+-----------------------------+
What I'm doing now in PHP is:
$conn = mysqli_connect($server,$user,$pwd,$db);
$sql = "SELECT id FROM admin_products";
$res = mysqli_query($conn,$sql);
if(mysqli_num_rows($res)>0){
while($row = mysqli_fetch_assoc($res)){
$product = array();
$innerSQL = "SELECT price,quantity FROM vendor_".$row['id'];
$innerRes = mysqli_query($conn,$innerSQL);
if(mysqli_num_rows($innerRes)>0){
while($innerRow = mysqli_fetch_assoc($innerRes)){
array_push($product,$row['id']);
array_push($product,$innerRow['price']);
array_push($product,$innerRow['quantity']);
}
}
}
}
But it takes two hits to the mySQL database. Can't it be reduced to one?
EDIT
I have later on realized that my database structure was incorrect and dynamically creating tables is a very bad idea and could spell disaster later on
-Solution 1:
Note: This will only work if you have in your vendor_x tables id for the vendor id to match them with. (As Strawberry said, this is a terrible idea to dynamically generate tables).
After selecting the correct id you can do something like this:
connect to the MySql Server
Then you can create the table name and store it in a variable.
$tableName = 'vendor_' . $id;
I would suggest after that to have a check if the table exists with a simple query:
$sql = "SHOW TABLES LIKE '$tableName'";
If this returns empty result you can throw an exception that the table does not exist or handle it whatsoever way you would like.
After checking every table, to be sure it exists, you can create your query.
$joins = "";
$sql = "
SELECT
v.id,
price,
quantity
FROM
vendors AS v
";
foreach ($ids as $id) {
$tableName = "vendor_" . $id;
$tableAlias = "v".$id;
$joins .= " LEFT JOIN " . $tableName . " AS ". $tableAlias ."
ON (v.id = ". $tableAlias .".vendor_id) ";
}
$sql .= $joins;
Then execute the query.
-Solution 2:
Create only one table to manage your vendors. It should have a structure like this :
`id` // AI value
`vendor_id` // The id of the vendor to easily join it afterwards
`price`
`quantity`
You can name it something like vendor_product or whatsoever
And now you have only one simple query:
$sql = "
SELECT
v.id,
vp.quantity,
vp.price
FROM
vendors AS v
LEFT JOIN vendor_product AS vp
ON (vp.vendor_id = v.id)
";
EDIT for the comment about the structure:
You will need one table for the vendors, such so:
`vendor`:
`id`, //AI value
`username`,
`password` // I suggest to you not to keep it in plain text.
`vendor_product` :
`id`, //AI value
`vendor_id`,
`price`,
`quantity`
I don't know here if you are going to store more information about each product, but this should do the trick.
How to show the product with least price ?
You need to match them by somehow and group by that selecting minimum price.
Try this if it suits
$table = "vendor"."_".$id; // this will create table name if $id = 1 then $table = vendor_1;
mysqli_query($connect , "SELECT * FROM $table");
2nd
If you want to fetch data of all table at once then
1) fetch id from admin_products and store in an array like
$ids = array(1,2,3,4,5);
2) Now loop throw array and create sql;
$sql = "SELECT * FROM ";
$ids = array(1,2,3,4,5);
foreach($ids as $id){
$table = "vendor"."_".$id; // this will create table name if $id = 1 then $table = vendor_1;
$sql .=" $table,";
}
$sql = rtrim($sql,",");// this will trim the last comma
echo $sql;
// output SELECT * FROM vendor_1, vendor_2, vendor_3, vendor_4, vendor_5

MySQL SELECT statement where value is in array

I need some help with an MySQL statement that I cannot really make to work.
I have a table like this:
+---+-------------------------------+
|id | fruit |
+---+-------------------------------+
| 1 | apple,orange,pear,grape,lemon |
| 2 | pear,melon,apple,kiwi,lemon |
| 3 | orange,kiwi,pear,apple,cherry |
| 4 | grape,lemon,cherry,apple,melon|
+---+-------------------------------+
What I need to do is to SELECT all rows where the column fruit contains the word melon. The word in question might be at any position in the array.
I tired with the below query but for some reason I only get 3-4 rows, definitely not all of them:
$fruit = $_GET['fruit'];
$query1= "SELECT * FROM tbl_fruits WHERE ".$fruit." IN (fruit)";
Any comments will be appreciated.
You can use FIND_IN_SET
SELECT * FROM tbl_fruits
WHERE find_in_set('$fruit', fruit)
But you actually should rather change your table design.
Never store multiple values in a single column!
A better table design would be
fruits table
------------
id name
1 melon
2 orange
3 apple
...
products table
-------------------
id name price
1 P1 1.50
2 P2 2.99
3 P3 0.99
product_fruits table
--------------------
product_id fruit_id
1 1
1 2
2 2
3 1
That is a classic many to many relation (m to n).
$fruit = $_GET['fruit'];
$query1= sprintf("SELECT * FROM tbl_fruits WHERE fruit LIKE '%%s%'",
mysql_real_escape_string($fruit ));
Try below sql
$fruit = $_GET['fruit'];
$query1= "SELECT * FROM tbl_fruits WHERE fruit LIKE '%".$fruit."%'";
Use a LIKE clause:
SELECT * FROM tbl_fruits
WHERE fruit LIKE '%apple%';
select * from tbl_fruits WHERE fruit like '%melon%'
As mentioned before you can/should build your table structure differently.
It's the "relational" in "relational database". see http://en.wikipedia.org/wiki/Database_normalization
As always: not a silver bullet. There are other kinds of daabases and there can be rasons for not normalizing (parts of) tables. But anyway, here's an sscce using PDO:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'localonly', 'localonly');
//echo 'client version: ', $pdo->getAttribute(PDO::ATTR_CLIENT_VERSION), "\n";
//echo 'server version: ', $pdo->getAttribute(PDO::ATTR_SERVER_VERSION), "\n";
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
setup($pdo);
$stmt = $pdo->prepare("
SELECT
m.name
FROM
soFruits as f
JOIN
soMixXFruits as x
ON
f.id=x.idFruit
JOIN
soFruitMix as m
ON
x.idMix=m.id
WHERE
f.name=?
");
$stmt->setFetchMode(PDO::FETCH_ASSOC);
foo($stmt, 'apple');
foo($stmt, 'melon');
function foo(PDOStatement $stmt, $param) {
echo "----- $param -----\r\n";
$stmt->execute( array($param) );
foreach($stmt as $r) {
echo join(', ', $r), "\r\n";
}
}
function setup($pdo) {
$queries = array(
'
CREATE TEMPORARY TABLE soFruits (
id INT auto_increment,
name varchar(64),
primary key(id)
)
',
'
CREATE TEMPORARY TABLE soFruitMix (
id INT auto_increment,
name varchar(32),
primary key(id)
)
',
'
CREATE TEMPORARY TABLE soMixXFruits (
idMix int,
idFruit int,
primary key(idMix, idFruit)
)
',
"INSERT INTO soFruits (id,name) VALUES (1,'apple'),(2,'cherry'),(3,'grape'),(4,'kiwi'),(5,'lemon'),(6,'melon'),(7,'orange'),(8,'pear')",
"INSERT INTO soFruitMix (id,name) VALUES (1, 'mix1'),(2,'mix2'),(3,'mix3'),(4,'mix4')",
);
foreach( $queries as $q ) {
$pdo->exec($q);
}
$data = [
'1'=>['apple','orange','pear','grape','lemon'],
'2'=>['pear','melon','apple','kiwi','lemon'],
'3'=>['orange','kiwi','pear','apple','cherry'],
'4'=>['grape','lemon','cherry','apple','melon']
];
$stmt = $pdo->prepare('
INSERT INTO soMixXFruits (idMix, idFruit)
SELECT
:idMix, id
FROM
soFruits
WHERE
name=:nameFruit
');
$stmt->bindParam('idMix', $idMix);
$stmt->bindParam('nameFruit', $nameFruit);
foreach($data as $idMix=>$mix) {
foreach($mix as $nameFruit) {
$stmt->execute();
}
}
}
Take a look at the table definitions in function setup($pdo). You have entities like fruits and mixes and then there's a table that represents their relationship.
side-note: I didn't pay attention to the indicies. But for performance that is usually crucial, see http://www.sitepoint.com/using-explain-to-write-better-mysql-queries/

MODx Retrieve data from database and sort into a table

I'm trying to set up a table with scores from the MODX database, this data has been entered in via tv.variables in the MODX admin panel.
My SQL code retrieves my desired data with:
SELECT sc.pagetitle, cv.value, t.name
FROM pphc_site_tmplvar_contentvalues cv, pphc_site_content sc, pphc_site_tmplvars t
WHERE sc.id = cv.contentid
AND cv.tmplvarid = t.id
ORDER BY cv.value * 1 DESC
My SQL retrieves the team name, range of team scores (plays, wins, losses, draws, total points) and if they won/lost/drew.
Below is a screenshot of what I'm after (with the SQL I can get at the moment)
My code at the moment for dealing with the SQL/loop is:
//$output = $x; //get draws, losses, played, points and wins
$id = $x; //id of current page
function sort_by_value($a, $b) {
return $b["value"] - $a["value"];
}
$sql = "SELECT sc.pagetitle, cv.value, t.name
FROM pphc_site_tmplvar_contentvalues cv, pphc_site_content sc, pphc_site_tmplvars t
WHERE sc.id = cv.contentid
AND cv.tmplvarid = t.id
AND sc.id = $id
ORDER BY cv.value * 1 DESC";
$result = $modx->query($sql)->fetchAll();
usort($result, "sort_by_value");
//print_r($result);die();
$html = '';
foreach ($result as $row) {
$html .= ''. $row['value'] .'<br>';
}
return $html;
At the moment all this code does is retrieve the results of each team - Great!
I can't seem to figure out how to lay out the data in table form AND include the team names
Go back to your SQL & rewrite the query so that you get your results like this:
pagetitle | wins | losses | played | draws | points
Bath HC | 5 | 4 | 2 | 5 | 234
then you will be able to:
foreach ($result as $row) {
$html.= $modx->getChunk($rowTpl,$row);
}
return $html;
where $rowTpl is a chunk you have defined in modx ~ something like:
<tr>
<td>[[+pagetitle]]</td>
<td>[[+wins]]</td>
<td>[[+losses]]</td>
<td>[[+played]]</td>
<td>[[+draws]]</td>
<td>[[+points]]</td>
</tr>
It's just a matter of getting the query returning the data formatted in a more useful way.

sql join repeating results

I need to make a list printing all the clients ordered by the gym name, but it's repeating the gym name the same number of gym's clients. If gym1 have 4 clients, the echo is printed 4 times.
The tables/columns are:
members (id, gym, name, etc...)
and
gym (gymID, gym_name, etc...).
member.gym is to know to what gym the client belongs (gym.gymID)
if ($stmt = $mysqli->prepare(" SELECT DISTINCT g.*, m.*
FROM gym g
INNER JOIN members m ON m.gym = g.gymID")) {
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_array()) {
echo 'Sport center: ' . $row['gym_name'] . '<br>';
// here print the gym's clients list
}
}
DISTINCT is not working... What is the problem??
That's the normal behavior.
Example.
Consider the following tables
Table "gym"
-----------
gym_id | gym_name
-------+----------
1 | Gym A
2 | Gym B
Table "members"
---------------
member_id | gym_id | member_name
----------+--------+------------
1 | 1 | Bob
2 | 1 | Jeff
And now, execute this query:
select g.gym_id, g.gim_name, m.member_id, m.member_name
from gym as g
inner join members as m on g.gym_id = m.gym_id;
Result:
gym_id | gym_name | member_id | member_name
-------+----------+-----------+-------------
1 | Gym A | 1 | Bob
1 | Gym B | 2 | Jeff
That happens because each row in the gym table is matched with a row in the members name. Even if you use select distinct, the result would be the same, because every row is different.
I think what you want is an output like this:
Gym A
Bob
Jeff
Although that can be done directly in SQL, it's easier to handle it directly with PHP, because doing it in SQL would be a real pain in the neck would require writing quite a complex query. I'm not quite good with PHP, but it could be something like this:
/*
You don't need "DISTINCT", but you need "ORDER BY" to make this work
*/
if ($stmt = $mysqli->prepare(" SELECT g.*, m.*
FROM gym g
INNER JOIN members m ON m.gym = g.gymID
ORDER BY g.gymID")) {
$stmt->execute();
$result = $stmt->get_result();
$gym = "";
while ($row = $result->fetch_array()) {
if($row['gym_name'] != $gym)
echo 'Sport center: ' . $row['gym_name'] . '<br>';
echo ' Member: ' . $row['member_name'] . '<br>';
$gym = $row['gym_name'];
}
}
First, drop the DISTINCT and slap in an ORDER BY:
SELECT g.*, m.*
FROM gym g
INNER JOIN members m ON m.gym = g.gymID
ORDER BY g.name;
Now, adjust your PHP code to only print the gym name if it's different from the last gym you printed.
SELECT g.*, m.*
FROM gym g
INNER JOIN members m ON m.gym = g.gymID
ORDER BY g.name,gym_clients;
I dont know the name of Gym Name column & gym Client Column So, Please Change the name if they are not correctly spelled.
I hope this will work for you
This should work:
SELECT *
FROM gym g
LEFT JOIN members m
ON g.gym_ID=m.gym_id
GROUP BY g.gym_name

Categories