Prestashop product features PHP SQL - php

We are wanting to do a quick compare on the product page that compares the current product and then the two top sellers in that category after that. I have all the SQL to get the current product and the two that are top sellers, but am having trouble adding the features to those. This is what I am facing.
This is just as an example. This SQL would pull the product features within a category.
SELECT ps_product.id_product, ps_feature_lang.name, ps_feature_value_lang.value FROM ps_product LEFT JOIN ps_feature_product ON ps_feature_product.id_product = ps_product.id_product LEFT JOIN ps_feature_lang ON ps_feature_lang.id_feature = ps_feature_product.id_feature LEFT JOIN ps_feature_value_lang ON ps_feature_value_lang.id_feature_value = ps_feature_product.id_feature_value WHERE ps_product.id_category_default = '6' AND ps_feature_lang.id_feature IS NOT NULL
It returns
id_product | name | value
9 Height 5
9 Width 5
9 Depth 5
9 Weight 5
10 Height 10
I have tried to group by id_product, but of course that will only show on of the values from the features list for that product. I have also tried putting them into arrays by products, but can't get them to line up correctly on the front end.
I wasn't sure if prestashop already had a built in function to pull product features

With MySQL you can try to use GROUP_CONCAT() that could help for this query. However, we wrote a quick code snippet to give you an overview of what you can do.
This code:
Retrieves the two best sellers of the current product's category
Retrieves the features list for the 3 products (Current product + 2 best sellers)
Group these and display them into an HTML table
Here is the final result:
Product comparison
Code:
<?php
include(dirname(__FILE__).'/config/config.inc.php');
include(dirname(__FILE__).'/init.php');
ini_set('display_errors', 'On');
$id_product = 1; /* Current Product ID */
$id_category = 2; /* Current Category ID */
$id_lang = 1; /* Current Language ID */
/* Retrieve the two Best sellers for this category, excluding the current product */
$best_sellers = Db::getInstance()->ExecuteS('
SELECT ps.id_product, pl.name product_name
FROM '._DB_PREFIX_.'product_sale ps
LEFT JOIN '._DB_PREFIX_.'category_product cp ON (cp.id_product = ps.id_product)
LEFT JOIN '._DB_PREFIX_.'product p ON (p.id_product = cp.id_product)
LEFT JOIN '._DB_PREFIX_.'product_lang pl ON (pl.id_product = p.id_product)
WHERE cp.id_category = '.(int)$id_category.' AND cp.id_product != '.(int)$id_product.' AND p.active = 1 AND pl.id_lang = '.(int)$id_lang.'
ORDER BY ps.sale_nbr DESC
LIMIT 2');
if (count($best_sellers))
{
$products_to_compare = array();
$products_names = array();
foreach ($best_sellers as $best_seller)
{
$products_to_compare[] = (int)$best_seller['id_product'];
$products_names[(int)$best_seller['id_product']] = $best_seller['product_name'];
}
$products_to_compare[] = (int)$id_product;
$products_names[(int)$id_product] = 'Current product'; /* Replace by the current product name */
$features = Db::getInstance()->ExecuteS('
SELECT fp.id_product, fp.id_feature, fl.name feature_name, fvl.value feature_value
FROM '._DB_PREFIX_.'feature_product fp
LEFT JOIN '._DB_PREFIX_.'feature_lang fl ON (fl.id_feature = fp.id_feature)
LEFT JOIN '._DB_PREFIX_.'feature_value_lang fvl ON (fvl.id_feature_value = fp.id_feature_value)
WHERE fp.id_product IN ('.pSQL(implode(',', $products_to_compare)).')');
$features_to_display = array();
foreach ($features as $feature)
{
if (!isset($features_to_display[(int)$feature['id_feature']]))
{
$features_to_display[(int)$feature['id_feature']] = array();
$features_to_display[(int)$feature['id_feature']]['name'] = $feature['feature_name'];
}
$features_to_display[(int)$feature['id_feature']][(int)$feature['id_product']] = $feature['feature_value'];
}
echo '
<table cellpadding="5" cellspacing="0" border="1">
<tr>
<td></td>';
foreach ($products_to_compare as $product_to_compare)
echo '<td>'.($product_to_compare == $id_product ? '<b>' : '').Tools::safeOutput($products_names[(int)$product_to_compare]).($product_to_compare == $id_product ? '</b>' : '').'</td>';
echo '</tr>';
foreach ($features_to_display as $feature_to_display)
{
echo '
<tr>
<td>'.Tools::safeOutput($feature_to_display['name']).'</td>';
foreach ($products_to_compare as $product_to_compare)
echo '<td>'.($product_to_compare == $id_product ? '<b>' : '').Tools::safeOutput($feature_to_display[(int)$product_to_compare]).($product_to_compare == $id_product ? '</b>' : '').'</td>';
echo '
</tr>';
}
echo '
</table>';
}
else
die('Sorry, no best sellers for this category');
This code can be improved to save memory and transmit the results to Smarty (instead of displaying these directly in the controller).

Related

How to display data one to many relationship using php and mysql

I have a lot of products and each product has many flavour. How can i display one product name with multiple flavor. below is the script that i have tried.
It doesn't display anything. I'm really new to php.
$res = $conn->query("SELECT as_product.p_id,GROUP_CONCAT(as_product_flavour.pfl_prod_id) as flavours FROM as_product
LEFT JOIN as_product_flavour ON as_product.p_id = as_product_flavour.pfl_prod_id
WHERE as_product.p_id = 28 GROUP BY as_product.p_id");
$currGroup = -1;
while($row = $res->fetch_assoc())
{
echo 'Product Name: '.$row['product_name'];
if($row['flavours'] != $currGroup)
{
$currGroup = $row['p_id'];
echo 'Flavour Id: ' . $row['pfl_id'] . '\n';
echo 'Falvour Name: ' . $row['pfl_flavour'] . '\n';
}
}
I would start with a simpler query...
SELECT p.p_id
, f.pfl_prod_id
FROM as_product p
LEFT
JOIN as_product_flavour f
ON p.p_id = f.pfl_prod_id
WHERE p.p_id = 28
...and handle any remaining display issues in the application code, so a simple transformation of your resulting array.

MySQL group result [duplicate]

in order to keep as few SQL statements as possible, I want to do select set from MySQL:
SELECT * FROM products WHERE category IN (10,120,150,500) ORDER BY category,id;
Now, I have list of products in following manner:
CATEGORY
- product 1
- product 2
CATEGORY 2
- product 37
...
What's the best and most efficent way to process MySQL result?
I thought something like (pseudo PHP)
foreach ($product = fetch__assoc($result)){
$products[$category][] = $product;
}
and then when outputting it, do foreach loop:
foreach($categories as $category){
foreach($products[$category] as $product){
$output;
}
}
Is this the best, or is something magical like mysql_use_groupby or something?
Like mluebke commented, using GROUP means that you only get one result for each category. Based on the list you gave as an example, I think you want something like this:
$sql = "SELECT * FROM products WHERE category IN (10,120,150,500) GROUP BY category ORDER BY category, id";
$res = mysql_query($sql);
$list = array();
while ($r = mysql_fetch_object($res)) {
$list[$r->category][$r->id]['name'] = $r->name;
$list[$r->category][$r->id]['whatever'] = $r->whatever;
// etc
}
And then loop through the array. Example:
foreach ($list as $category => $products) {
echo '<h1>' . $category . '</h1>';
foreach ($products as $productId => $productInfo) {
echo 'Product ' . $productId . ': ' . $productInfo['name'];
// etc
}
}
Nope, I think your solution is the best for this problem. It seems that what's important for you is the output later on, so you should stick with your approach.
Do you want to get a list of categories or actually get all products grouped into categories?
If it's the latter, best to do:
SELECT
p.product_id,
p.name,
p.category_id,
c.name AS category
FROM products p
JOIN categories c ON (c.category_id = p.category_id AND p.category_id IN (x,y,z))
Then in PHP you can go through the array (psuedo code):
$cats = array();
foreach($products as $product) {
if(!in_array($product['category'], $cats)) {
$cats[$product['category_id']] = $product['category'];
}
$cats[$product['category_id']][$product['product_id']] = $product['name'];
}
Which will leave you with $cats as an array with products sorted into it.

Building a Mysql table-tree using query instead of loops in PHP

I have a category tree that contains up to 3 levels of children-categories, like this:
houseproducts->livingroom->sofas->twoseats
houseproducts->livingroom->sofas->threeseats
houseproducts->livingroom->sofas->fourseats
So for each sublevel, I do a SELECT based on the mothers category-id. This is done in a PHP-loop like the code below, but I guess it could be done in one single Mysql-query, for better performance. I have tried different JOINS but find it really difficualt. Any advice will be highly appreciated.
function build_category_tree()
{
$cat = array();
// main category loop
$r1 = mysql_query("SELECT cat_id,cat_name FROM categories WHERE cat_mother=0 OR cat_mother='' ORDER BY cat_name");
while ($row=mysql_fetch_assoc($r1))
{
$cat[$row['cat_id']] = $row['cat_name'];
// check for subcategories
$r2 = mysql_query("SELECT cat_id,cat_name FROM categories WHERE cat_mother='".$row['cat_id']."'");
while ($subrow=mysql_fetch_assoc($r2))
{
$cat[$subrow['cat_id']] = ' - '.$subrow['cat_name'];
// check if there is subcats for the current subcategory
$r3 = mysql_query("SELECT cat_id,cat_name FROM categories WHERE cat_mother='".$subrow['cat_id']."'");
while ($subrow2=mysql_fetch_assoc($r3))
{
$cat[$subrow2['cat_id']] = ' -- '.$subrow2['cat_name'];
// check if there is subcats for the current subcategory
$r4 = mysql_query("SELECT cat_id,cat_name FROM categories WHERE cat_mother='".$subrow2['cat_id']."'");
while ($subrow3=mysql_fetch_assoc($r4))
{
$cat[$subrow3['cat_id']] = ' --- '.$subrow3['cat_name'];
}
}
}
}
return $cat;
}
I would read your entire table into an array and segment that array by the mother's keys
Try this:
SELECT l1.cat_id AS l1_cat_id
,l1.cat_name AS l1_cat_name
,l2.cat_id AS l2_cat_id
,l2.cat_name AS l2_cat_name
,l3.cat_id AS l3_cat_id
,l3.cat_name AS l3_cat_name
,l4.cat_id AS l4_cat_id
,l4.cat_name AS l4_cat_name
FROM categories AS l1
JOIN categories AS l2
ON l2.cat_mother = l1.cat_id
JOIN categories AS l3
ON l3.cat_mother = l2.cat_id
JOIN categories AS l4
ON l4.cat_mother = l3.cat_id
WHERE l1.cat_mother=0 OR l1.cat_mother=''
ORDER BY l1_cat_name, l2_cat_name, l3_cat_name, l4_cat_name

How do i create a list(array) of values with the number of times they appear in another array

I am trying to generate a list of "Products" that details the amount of times each "Product appears in a mysql query.
Outline:
1. Get the last 30 days of orders from the database.
2. Get the products that appear in those orders.
3. Get a count of the times a product appears in the orders.
4. Display a list with "Product Name" and the "Qty Sold"
What i have so far (i am 1 week into php, so please excuse me if it's a mess)
$orderid = mysql_query("SELECT orderid FROM isc_orders WHERE orddate >= 1334266286 ");
while ($row = mysql_fetch_array($orderid)) {
$pslm = mysql_query("SELECT * FROM isc_order_products WHERE orderorderid = '$row[0]' GROUP BY ordprodid");
while ($row = mysql_fetch_array($pslm)) {
echo $row[2] . "</br>";
So this gives me a list of the products sold, but not grouped, and no qty shown of course.
I have gone through a number of posts here, and spent about 3 hours trying to do this. Hopefully someone here can help.
Thanks
Jason
Why not use a join and then group the results:
SELECT *, COUNT(1) AS quantity
FROM isc_orders AS o
LEFT JOIN isc_order_products AS p ON o.orderid = p.orderorderid
WHERE orddate >= 1334266286
GROUP BY p.productid
Or something like that. It's hard to guess exactly how it should be written without knowing how your database is structured.
Well, Elijah Madden already posted almost the same thing, but since I had already written it... here it goes:
SELECT
product.name,
COUNT(*)
FROM isc_orders order
INNER JOIN isc_order_products product
ON product.orderorderid = order.orderid
WHERE order.orddate >= 1334266286
GROUP BY order.ordprodid
Not the prettiest thing... you would probably want to get the product info (ie. name) from a product table, but here ya go:
$order_query = mysql_query("
SELECT orderid
FROM isc_orders
WHERE orddate >= " . (time() - (30 * 86400)));
if (mysql_num_rows($order_query) > 0)
{
$orders = array();
while ($order = mysql_fetch_assoc($order_query))
$orders[] = mysql_real_escape_string($order['orderid']);
$product_query = mysql_query("
SELECT *
FROM isc_order_products
WHERE orderorderid IN ('" . implode("', '", $orders) . "')");
if (mysql_num_rows($product_query) > 0)
{
$products = array();
$times_ordered = array();
while ($product = mysql_fetch_assoc($product_query))
{
if (!isset($times_ordered[$product['ordprodid']]))
$times_ordered[$product['ordprodid']] = 0;
$times_ordered[$product['ordprodid']]++;
if (!isset($products[$product['ordprodid']]))
$products[$product['ordprodid']] = $product;
}
foreach ($products as $id => $product)
$products[$id]['times_ordered'] = $times_ordered[$id];
}
mysql_free_result($product_query);
}
mysql_free_result($order_query);
This is what actually worked.
SELECT ordprodname, SUM(ordprodqty), ordprodid
FROM isc_orders, isc_order_products
WHERE orderid = orderorderid AND orddate >= UNIX_TIMESTAMP() - ('$days' * 86400)
GROUP BY ordprodname
ORDER BY SUM(ordprodqty) DESC");

PHP/MySQL group results by column

in order to keep as few SQL statements as possible, I want to do select set from MySQL:
SELECT * FROM products WHERE category IN (10,120,150,500) ORDER BY category,id;
Now, I have list of products in following manner:
CATEGORY
- product 1
- product 2
CATEGORY 2
- product 37
...
What's the best and most efficent way to process MySQL result?
I thought something like (pseudo PHP)
foreach ($product = fetch__assoc($result)){
$products[$category][] = $product;
}
and then when outputting it, do foreach loop:
foreach($categories as $category){
foreach($products[$category] as $product){
$output;
}
}
Is this the best, or is something magical like mysql_use_groupby or something?
Like mluebke commented, using GROUP means that you only get one result for each category. Based on the list you gave as an example, I think you want something like this:
$sql = "SELECT * FROM products WHERE category IN (10,120,150,500) GROUP BY category ORDER BY category, id";
$res = mysql_query($sql);
$list = array();
while ($r = mysql_fetch_object($res)) {
$list[$r->category][$r->id]['name'] = $r->name;
$list[$r->category][$r->id]['whatever'] = $r->whatever;
// etc
}
And then loop through the array. Example:
foreach ($list as $category => $products) {
echo '<h1>' . $category . '</h1>';
foreach ($products as $productId => $productInfo) {
echo 'Product ' . $productId . ': ' . $productInfo['name'];
// etc
}
}
Nope, I think your solution is the best for this problem. It seems that what's important for you is the output later on, so you should stick with your approach.
Do you want to get a list of categories or actually get all products grouped into categories?
If it's the latter, best to do:
SELECT
p.product_id,
p.name,
p.category_id,
c.name AS category
FROM products p
JOIN categories c ON (c.category_id = p.category_id AND p.category_id IN (x,y,z))
Then in PHP you can go through the array (psuedo code):
$cats = array();
foreach($products as $product) {
if(!in_array($product['category'], $cats)) {
$cats[$product['category_id']] = $product['category'];
}
$cats[$product['category_id']][$product['product_id']] = $product['name'];
}
Which will leave you with $cats as an array with products sorted into it.

Categories