Craft calculator loop - php

I'm trying to do a craft calculator for a game.
I have a database who looks like this :
Crafts :
id item_id item_name amount
3 1895 a 5
8 2486 c 1
Craft_materials :
id craft_id item_id item_name amount
1 3 2486 c 15
2 3 5302 d 23
3 3 5698 e 2
4 8 2014 f 3
And here is my query to retrieve the data :
$craftProduct = $db->query("SELECT * FROM crafts WHERE item_id=$item");
$craftProduct->setFetchMode(PDO::FETCH_OBJ);
$product = $craftProduct->fetch();
$craftID = $product->id;
$craftMaterial = $db->query("SELECT * FROM craft_materials WHERE craft_id=$craftID");
$craftMaterial->setFetchMode(PDO::FETCH_OBJ);
echo '<ul>';
echo '<li>'.$product->item_name.' ('.$product->amount.')</li>';
echo '<ul>';
while($material = $craftMaterial->fetch()){
echo '<li>'.$material->item_name.' ('.$material->amount.')</li>';
}
echo '</ul>';
echo '</ul>';
What I want to do is take the material id and check if it corresponds with a craft. If it does, I want to show the material needed to craft it.
I want something which looks like this :
- a (5)
- c (15)
- f (3)
- d (23)
- e (2)
However, I don't know how to do the loop. Can anyone help me?

Here is a basic recursion setup:
function render_item($item_id) {
$product = fetch_item($item_id); // select from crafts where item_id = $item_id
$children = "";
foreach (fetch_children($product->id) as $child_id) { // select from craft_mats where craft_id = $product->id
$children .= render_item($child_id);
}
echo "<li>" . $product->item_name . "<ul>" . $children . "</ul></li>";
}
echo "<ul>" . render_item(1895) . "</ul>"

Related

PHP Join multiple tables not working as expected

I'll explain briefly what I've done so far-
First of all I have multiple of stores and each store has its own table in which it contains id,item,qty and price and there is a main table called all_items that contains the total qty taken from each store table.
Now each store table either have same item number or not. Here you can see what the tables looks like :
Store tables called : S1, S2, S3
S1
id item qty price
1 x1 10 12
2 x2 10 15
3 x3 5 5
S2
id item qty price
1 x1 10 12
2 x4 6 6
S3
id item qty price
1 x3 1 5
2 x6 5 5
3 x7 5 12
all_items - Contains original total qty
id item qty price
1 x1 20 12
2 x2 10 15
3 x3 6 5
4 x4 6 6
5 x6 5 5
6 x7 5 12
Okay now, I've decided to find out the qty available for each item in all stores for comparing purposes- As follows :
item price S1 S2 S3
x1 12 10 10 -
x2 15 10 - -
x3 5 5 - 5
x4 6 - 6 -
x6 5 - - 5
x7 12 - - 5
I hope its clear for you now. I've created a form with checkbox input type ( for each store ) to allow user select which stores he wants to compare, so after submitting the form - a PHP page contains the following code :
$allstore = $_POST['store']; //Collects name from checkbox ticks under form
function createSelect($allstore)
{
if (empty($allstore))
return "";
$querySelect = "";
$queryJoin = "";
$baseTable = "";
foreach ($allstore as $store => $value) {
if (!$querySelect) {
$baseTable = "all_items";
$querySelect = "SELECT " . $store . ".item_no, " . $store . ".actual_price, " . $store . ".selling_price, " . $store . ".qty as " . $store;
} else {
$querySelect .= ", " . $store . ".qty as " . $store;
$queryJoin .= "
INNER JOIN " . $store . " ON " . $baseTable . ".item_no = " . $store . ".item_no";
}
}
$querySelect .= " FROM " . $baseTable;
$query = $querySelect . $queryJoin;
return $query;
}
$allstore = array(); // The below code allows function to know how many stores selected in $allstore
if (!empty($_POST['store'])) {
foreach ($_POST['store'] as $value) {
$allstore["s_".$value] = 0; // or 1, it doesn't matter because your function adds all the keys
}
}
var_dump(createSelect($allstore)); // Output SQL
$query = (createSelect($allstore));
$result = mysql_query($query);
//Rest of the code .....
Now if you notice $baseTable = "all_items"; makes the whole query fail. However if I change it value to $baseTable = $store; it works and shows an output but not as expected because it turns out S1 is the main now and the result will be totally different because it relays on the S1 items only.
If you can provide any helpful solution will be appreciated.
Why not use UNION?
function createSelect($stores)
{
$query = "";
$baseTable = "all_items";
foreach($stores as $i => $store)
{
$query .= "(SELECT {$store}.id AS {$store}_id, {$store}.item AS {$store}_item, {$store}.price AS {$store}_price, {$store}.qty AS {$store}_qty, '{$store}' as Source FROM {$store}) UNION ";
}
$query .= "(SELECT all_items.id AS {$baseTable}_id, {$baseTable}.item AS {$baseTable}_item, {$baseTable}.price AS {$baseTable}_price, {$baseTable}.qty AS {$baseTable}_qty, '{$baseTable}' as Source FROM {$baseTable})";
return $query;
}
$result = mysql_query(createSelect($allStore));
//Rest of code
//if my tables are S1, S1, and my $baseTable is bs
//echo createSelect(array('S1', 'S2'); will output (SELECT S1.id AS S1_id, S1.item AS S1_item, S1.price AS S1_price, S1.qty AS S1_qty, 'S1' as Source FROM S1) UNION (SELECT S2.id AS S2_id, S2.item AS S2_item, S2.price AS S2_price, S2.qty AS S2_qty, 'S2' as Source FROM S2) UNION (SELECT all_items.id AS all_items_id, all_items.item AS all_items_item, all_items.price AS all_items_price, all_items.qty AS all_items_qty, 'all_items' as Source FROM all_items)

Displaying only 1 column in last row

I've checked the queries in phpMyAdmin a lot of times and is dead sure they are absolutely correct. Also, if I manually write the loop 3 times, setting 2,3,4 instead of incrementing counter it still displays ONLY one column in last row. First two rows result is accurate.
foreach($row as $rec) is basically running 17 times from another query which is printing table headers.
$by_type1 = array("First","Second+","Final");
$counter = 2; //this counter represents type of interview (2-First, 3-Second+, 4-Final)
foreach ($by_type1 as $type1)
{
$table_row = '<tr><td class="rborder">'.$type1.'</td>';
foreach ($row as $rec)
{
$id=$rec['id'];
$qry2 = "SELECT
CONCAT( r.fname, ' ', r.lname ) AS rname,
ch.status_id as Type,
count(ch.status_id) as number
FROM candidateJoborderHistory ch
LEFT JOIN candidates_info c ON ch.candidate_id = c.candidate_id
LEFT JOIN recruiters r ON c.recruiter_id=r.recruiter_id
LEFT JOIN interviewtypes i ON ch.interview_id = i.interview_id
WHERE c.recruiter_id = $id AND UNIX_TIMESTAMP(ch.date_interview) BETWEEN 1401667200 AND
1402099200 AND ch.status_id = $counter
group by ch.status_id";
global $conn;
$conn->open();
$stmt2 = $conn->prepare($qry2);
$stmt2->execute();
$row2 = $stmt2->fetchAll();
foreach($row2 as $row_x)
{
$table_row .= '<td>'.$row_x['number'].'</td>';
}
}
$table_row .='</tr>';
echo $table_row . "\n";
$counter++;
}
What I want is:
First 6 6 4 4 11 6 12 3
Second+ 3 1 2 1 3
Final 3 2 1 4 1
But what I am getting is:
First 6 6 4 4 11 6 12 3
Second+ 3 1 2 1 3
Final 3
Well I think that is a MySQL approach, I've made some changes to your code and explained them:
$by_type1 = array(2=>"First", 3=>"Second+", 4=>"Final");
$counter = 2; //this counter represents type of interview (2-First, 3-Second+, 4-Final)
// Open connection first.
global $conn;
$conn->open();
// Using key => value array gets code simple
foreach ($by_type1 as $counter=>$type1)
{
$table_row = '<tr><td class="rborder">'.$type1.'</td>';
foreach ($row as $rec)
{
$id=$rec['id'];
$qry2 = "SELECT
CONCAT( r.fname, ' ', r.lname ) AS rname,
ch.status_id as Type,
count(ch.status_id) as number
FROM candidateJoborderHistory ch
LEFT JOIN candidates_info c ON ch.candidate_id = c.candidate_id
LEFT JOIN recruiters r ON c.recruiter_id=r.recruiter_id
LEFT JOIN interviewtypes i ON ch.interview_id = i.interview_id
WHERE c.recruiter_id = $id
AND UNIX_TIMESTAMP(ch.date_interview)
BETWEEN 1401667200 AND 1402099200
AND ch.status_id = $counter
GROUP BY ch.status_id";
$res = $conn->prepare($qry2);
$res->execute();
// Loop to get data...
while($row_x = $res->fetch(PDO::FETCH_ASSOC))
{
$table_row .= '<td>'.$row_x['number'].'</td>';
}
}
$table_row .='</tr>';
echo $table_row . "\n";
}
$conn->close();
Code isn't tested but if you have any doubt just ask. Hope it helps!
References:
Prepared Statements
Executing Statements

How to retrieve liker id based on ques_id?

Here is my database:
id | liker | ques_id
1 | 15 | 2342
2 | 22 | 2342
3 | 22 | 2311
4 | 15 | 2389
What I need to get is all the liker's who have liked ques_id. So the result should look something like this:
Question 2342 has been liked by 15 and 22.
Question 2311 has been liked by 22 and so on
My current code produces separate row for each liker and ques_id:
$sqlq=mysql_query("SELECT * FROM likes");
while($rowq=mysql_fetch_array($sqlq)){
$qid=$rowq['ques_id'];
$sql=mysql_query("SELECT * FROM likes where ques_id='$qid'");
$num=mysql_num_rows($sql);
$cont='';
while($row=mysql_fetch_array($sql)){
$liker=$row['liker'];
$cont .="$qid being liked by $liker<br>";
}
echo $cont;
}
I haven't tested this but it should get you started:
$sqlq=mysql_query("SELECT DISTINCT(ques_id) FROM likes");
$cont='';
while($rowq=mysql_fetch_array($sqlq)){
$qid=$rowq['ques_id'];
$sql=mysql_query("SELECT * FROM likes where ques_id='$qid'");
$num=mysql_num_rows($sql);
$row=mysql_fetch_array($sql)
$cont .= "$qid being liked by $liker ";
while($row=mysql_fetch_array($sql)){
$liker=$row['liker'];
$cont .= " and $liker";
}
$cont .= ".<br>";
}
echo $cont;
You need no second query to DB, it enough iterate only first result.
For example, you may collect all the likers, that match a specific ques_id in a assoc array with keys are ques_id, like this:
$mathces = array();
$sqlq=mysql_query("SELECT * FROM likes");
while($rowq=mysql_fetch_array($sqlq)){
$qid=$rowq['ques_id'];
$liker=$rowq['liker'];
$matches[ $qid ][] = $liker;
}
Then, you may foreach $mathces array and build you string.
foreach ($matches as $qid => $likers) {
$cont .= "$qid being liked by " . implode(' and ', $likers );
if (count($likers) == 1)
$cont .= ' and so on';
echo "$cont<br>";
}
I didn't test my code. It need to additional validations (e.g. for $linkers in second loop)
SELECT GROUP_CONCAT(liker), ques_id FROM likes GROUP BY ques_id
That will pull each liker grouped together for each ques_id.
You then only have to process the rows returned.
You should steer clear of the mysql extension as it is deprecated; use PDO or mysqli instead.
$sql = 'SELECT GROUP_CONCAT(liker) as likes, ques_id FROM likes GROUP BY ques_id';
foreach ($conn->query($sql) as $row) {
printf('Question %s is liked by %s', $row['ques_id'], $row['likes']);
}

Group by common id

I am trying to fetch data from my database and would like to group common values in the column called order_ids by that id.
This is the state I currently get my data in
Order_Id | Product Name
-------------------------------
10001 | iPhone 5
10001 | Blackberry 9900
10002 | Galaxy S
10003 | Rhyme
10004 | Google Nexus
10005 | Razr
10006 | iPad Air
And this is the state I want to get it in
Order_Id | Product Name
-------------------------------
10001 | iPhone 5
Blackberry 9900
10002 | Galaxy S
10003 | Rhyme
10004 | Google Nexus
10005 | Razr
10006 | iPad Air
Here is how I get the result in my controller file
foreach($results_query as $results_custom) {
$this->data['result_custom'][] = array(
'model' => $results_custom['product_name'],
'order_number' => $results_custom['order_id']
);
}
Here is how I display it in my view file
<?php foreach ($results_custom as $result) { ?>
<li><?php echo $result['model']; ?></li> <br />
<li><?php echo $result['order_number']; ?></li><br />
<?php } ?>
Is it possible to get my data to display like that or in that state by using SQL or PHP? Please let me know if you want to see my query as well.
In php would be easier to do it. As I don't have PHP enviroment to test it I will show you some logic to do it. Not necessarily working code. Thats because you didn't provide what you did
<?
$sql = "select order_id, product name from ..... order by order_id"// rest of sql code ....
//here you iterate your results
$previousId = ""; //var to store previous id
while( $fetch... ){
if ( $fetchedID != $previousId ){
echo $fetchedId . "-" . $fetchedProductName;
}else{
echo $fetchedProductName;
}
$previousId = $fetchedID;
}
?>
This should do.
As you updated your code this is a solution for you:
<?php
$lines = ""; //to make code cleaner;
$previousModel = "";
foreach ($results_custom as $result) {
if ( $previousModel != $result['model'] ){
$line .= "<li>" . $result['model'] . "</li>";
}else{
$line .= "<li></li>";
}
$line .= "<li>" . $result['model'] . "</li><br />";
$previousModel = $result['model'];
}
echo $line;
<?php } ?>
I suggest you to use GROUP_CONCAT for getting the result
You try as follows
SELECT order_id,GROUP_CONCAT(product_name) as product_name FROM your_table GROUP BY order_id
Just look at this http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat
Note : GROUP_CONCAT has a size limit. Check this link for more MySQL and GROUP_CONCAT() maximum length
you might be able to accomplish this in just MySQL, but it might be easier if you just create a php loop. most people prefer a foreach loop, but I like while loops:
$orderid = "number";
$order_query = mysql_query("SELECT * FROM ordertable WHERE Order_Id = '$orderid'");
while($order_data = mysql_fetch_array($order_query)){
$ordername = stripslashes(mysql_real_escape_string($order_data['Product Name']));
echo $ordername.'<br />';
}
if you need this for ALL orders you could remove searching for a specific order:
$order_query = mysql_query("SELECT * FROM ordertable ORDER BY Order_Id ASC");
while($order_data = mysql_fetch_array($order_query)){
$orderid = mysql_real_escape_string($order_data['Order_Id']);
$ordername = stripslashes(mysql_real_escape_string($order_data['Product Name']));
echo 'ID#: '.$orderid.' - '.$ordername.'<br />';
}

Iterate list values from table

I have two tables, who are joined and the ID of each table and element underneath are similar.
parentID | objectName | subID ID| className| subName |
_____________________________ ________________________
84 | Test | 14 14| BOM | Test
84 | More | 16 14| PDF | Test
84 | Sub | 15 15| Schematics | Test2
I want to list the categoryname and the subID of the related elements. Several ObjectNames will have several related classes.
PHP code:
$objects = mysqli_query($con,"SELECT * from subobject");
$join = mysqli_query($con, "SELECT * FROM subrelation AS subrelation INNER JOIN subobject AS subobject ON subobject.subId = subrelation.ID;");
echo "<ul>";
while($obj = mysqli_fetch_array($objects) and $row = mysqli_fetch_array($join))
{
echo "<li>". $obj['objectName'];
echo "<ul>";
//ITERATION GOES HERE
if($obj['objectName'] == $row['subName'])
echo "<li>". "$row[className]" . "</li>";
//END OF ITTERATION
echo "</ul>";
echo "</li>";
}
echo "</ul>";
?>
and output list:
-Test
-BOM
-Sub
-Schematics
-More
under each field there are supposed to be more listed values.
It looks like you need to simplify your code a bit. My guess is that your problem is occurring because you have different amounts of rows in each result set. This makes your while loop exit when it finishes going through the smaller result set (probably $objects), even though there's still more elements in the larger set.
A solution is to sort the results of your query, use just one condition in your while loop, and keep track of which objectName you're currently on using a string $curr_objectName:
$join = mysqli_query($con, 'SELECT * FROM subrelation AS subrelation INNER JOIN subobject AS subobject ON subobject.subId = subrelation.ID ORDER BY subobject.objectName;');
$curr_objectName = '';
echo '<ul>';
while($row = mysqli_fetch_array($join)) {
$subName = $row['subName'];
if($subName != $curr_objectName)) {
if($curr_objectName != '') {
#close the previous list
#will be skipped on the first loop iteration
echo '</ul>';
echo '</li>';
}
#start a new list
$curr_objectName = $subName;
echo '<li>'. $obj['objectName'];
echo '<ul>';
} else {
echo '<li>'. $row['className'] . '</li>';
}
}
echo '</ul>';

Categories