I want to have my products sorted by two parameters: first by "sort order" that every product has; and then by quantity.
Here is what I have in file /catalog/controller/product/category.php (Opencart 1.5.6.4) and I believe here is what should be somehow changed:
if (isset($this->request->get['sort'])) {
$sort = $this->request->get['sort'];
} else {
$sort = 'p.sort_order';
}
Anyone has any ideas? Thanks in advance
By default Opencart only uses a single field at a time for sorting. To accomplish what you described you'll need to modify the query for the getProducts() method which is built in /catalog/model/catalog/product.php. Find line 196 where it reads:
$sql .= " ORDER BY p.sort_order";
This is the default sort order when none is specified. To add a secondary sort criteria you can change it to:
$sql .= " ORDER BY p.sort_order, p.quantity";
If you always want to use quantity as a secondary sort field, you can add after the entire block like this:
} else {
$sql .= " ORDER BY p.sort_order";
}
$sql .= ", p.quantity";
This would sort by quantity as a secondary field regardless of what primary sort field was specified.
Related
I have an itemid and a category id that are both conditional. If none is given then all items are shown newest fist. If itemid is given then only items with an id lower than given id are shown (for paging). If category id is given than only items in a certain category are shown and if both are given than only items from a certain category where item id smaller than itemid are shown (items by category next page).
Because the parameters are conditional you get a lot of if statements depending on the params when building a SQL string (pseudo code I'm wearing out my dollar sign with php stuff):
if itemid ' where i.iid < :itemid '
if catid
if itemid
' and c.id = :catid'
else
' where c.id = :catid'
end if
end if
If more optional parameters are given this will turn very messy so I thought I'd give the createQueryBuilder a try. Was hoping for something like this:
if($itemId!==false){
$qb->where("i.id < :id");
}
if($categoryId!==false){
$qb->where("c.id = :catID");
}
This is sadly not so and the last where will overwrite the first one
What I came up with is this (in Symfony2):
private function getItems($itemId,$categoryId){
$qb=$this->getDoctrine()->getRepository('mrBundle:Item')
->createQueryBuilder('i');
$arr=array();
$qb->innerJoin('i.categories', 'c', null, null);
$itemIdWhere=null;
$categoryIdWhere=null;
if($itemId!==false){
$itemIdWhere=("i.id < :id");
}
if($categoryId!==false){
$categoryIdWhere=("c.id = :catID");
}
if($itemId!==false||$categoryId!==false){
$qb->where($itemIdWhere,$categoryIdWhere);
}
if($itemId!==false){
$qb->setParameter(':id', $itemId);
}
if($categoryId!==false){
$arr[]=("c.id = :catID");
$qb->setParameter(':catID', $categoryId);
}
$qb->add("orderBy", "i.id DESC")
->setFirstResult( 0 )
->setMaxResults( 31 );
I'm not fully trusting the $qb->where(null,null) although it is currently not creating errors or unexpected results. It looks like these parameters are ignored. Could not find anything in the documentation but an empty string would generate an error $qb->where('','').
It also looks a bit clunky to me still, if I could use multiple $qb->where(condition) then only one if statement per optional would be needed $qb->where(condition)->setParameter(':name', $val);
So the question is: Is there a better way?
I guess if doctrine had a function to escape strings I can get rid of the second if statement round (not sure if malicious user could POST something in a different character set that would allow sql injection):
private function getItems($itemId,$categoryId){
$qb=$this->getDoctrine()->getRepository('mrBundle:Item')
->createQueryBuilder('i');
$arr=array();
$qb->innerJoin('i.categories', 'c', null, null);
$itemIdWhere=null;
$categoryIdWhere=null;
if($itemId!==false){
$itemIdWhere=("i.id < ".
someDoctrineEscapeFunction($id));
}
Thank you for reading this far and hope you can enlighten me.
[UPDATE]
I am currently using a dummy where statement so any additional conditional statements can be added with andWhere:
$qb->where('1=1');// adding a dummy where
if($itemId!==false){
$qb->andWhere("i.id < :id")
->setParameter(':id',$itemId);
}
if($categoryId!==false){
$qb->andWhere("c.id = :catID")
->setParameter(':catID',$categoryId);
}
You can create filters if you want to use more generic approach of handling this.
Doctrine 2.2 features a filter system that allows the developer to add SQL to the conditional clauses of queries
Read more about filters however, I'm handling this in similar manner as you showed
I have a custom post type created in my WordPress blog called 'reviews'. Inside this I have a custom field with a number out of 5.
I am trying to add all of these numbers together and then divide by the amount of posts to get an aggregate score.
I am attempting to use this code...
<?php
$total_score=0;
$meta_key = 'target';//set this to your custom field meta key
$allscores=$wpdb->get_col($wpdb->prepare("SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = %s", $meta_key));
foreach ($allscores as $score) {
$total_score = $total_score + $score;
}
echo 'Total score is '.$total_score;
?>
But I am getting a result of 0, can anyone see where I am going wrong?
Turns out it was me all along, the numbers entered in the database were spelt out alphabetically rather than numerical so the 0 result I was receiving was technically correct :)
This one is a little tough,
I have imported a list of products from ACTINIC to a mysql database. I need to be able to collect all products from a top level menu item and put all of the child menu items in an array so i can look up which products belong to said top level item.
All the menu items have id's that are sequential starting from 1.
i need a way of looping through an undetermined amount of child items based on their parent id's and to collect all of there id's into an array to be used to loop up the products.
I think i have explained that well enough, any help or ideas would be great. Just need a starting point then i can go from here.
// EDIT
Thanks johan for pointing me in the right direction
// $all is a mysql query
loopit($all);
function loopit($array){
while($forone = mysql_fetch_array($array)){
echo "id=" . $forone['nSectionID'] . " name=" . $forone['Section text'] . "<br />";
$seltwo = mysql_query("SELECT * FROM [table] WHERE nParentSectionID='" . $forone['nSectionID'] . "'");
loopit($seltwo);
}
}
You will need a recursive function for that. Something like this:
function import ( $items ) {
foreach ( $items as $item ) {
// handle item import
// .....
if ( $item->children ) {
import( $item->children ); // Notice the function calling itself here.
}
}
}
Ahoy all! Long story short with this one if you don't mind lending a hand to this novice PHPer. :)
I have a database field called "Categories" that has this stored:
Fruit, People, Place, Animals, Landscape
I also have a separate table in the DB that has items with these category names in the fields for each item. Right now, the script (i am trying to fork it a bit) uses:
SELECT DISTINCT(type), type FROM the_categories ORDER BY type ASC
in order to display a list of all categories available. Simple enough right?
Welllllll..... I don't want to sort by ASC, I want to sort by the list of items in the first Categories field I mentioned. Whatever order those are in is the order I want to display the "types" above.
Obviously I will have to do an explode on the commas, and maybe give them a 1 to whatever order....but even then.... how do I do an "orderby" using data stored in another folder?
Is this even possible? lol Thanks again!
... ORDER BY FIELD(type,"Fruit","People","Place","Animals","Landscape")
http://www.cfdan.com/posts/Handy_MySQL_-_ORDER_BY_FIELD.cfm
And just so future onlookers have it .... here is the explode code
$query2 = mysql_query("SELECT * FROM categorytable");
while($row = mysql_fetch_array($query2)){
$categories = html_entity_decode($row['categories']);
$thelist = explode(',', $categories);
foreach($thelist as $order){
if(trim($order) != ''){
$order = trim($order);
$order = ", '".$order."'";
$theorder .= $order;
}
}
and then for the query, just put in the the order variable
SELECT DISTINCT(type), type FROM the_categories ORDER BY FIELD(type" . $theorder .")")
For several days now I am trying to cope with the algorithm implementation at the online shop which I am writing in PHP. I do not know whether the problem is only the implementation, or perhaps bad algorithm design. Hovewer, for me it seems fine. I only haven`t checked its complexity of it, but it's such a problem.
After a long deliberation on the same algorithm, without thinking on implementation I came up with the use of binary search tree (bst) with additional data inserted into list consist of user defined info (later about it). The whole orders list would be displayed, or returned using inorder method.
I write it like that:
If the input object date is greater than current object go right
If the input object date is less than current object go left
If the dates are the same stay at place
If the field is blank check if the product is in stock
If it is put into place and finish
If there is not do nothing and exit
If the field is full
{Check if on the list is this user id
If yes than check order priority
If no do nothing and exit
Check if there is product on stock
If yes replace record and exit
If no do nothing and exit
}
{If there is not user id on the list check if product is on stock
If yes then put element on the end
If no do nothing and exit
}
Maybe it looks a little bad, but I was not able to do indentation.
Data is transferred into algorithm in a loop until the end of orders list. The list is unordered.
This is my implementation:
class BinaryTree {
private $predescor = array(
'd'=>array('data'=>0),
'p'=>null,
'r'=>null,
'l'=>null
);
private $ancestor = array(
'd'=>array('data'=>0),
'p'=>null,
'r'=>null,
'l'=>null
);
private $i = 0;
public function insertIntoTree(&$root,$element)
{
$this->predescor = $root;
$this->predescor;
while($this->predescor)
{
if($element['d']['data']==$this->predescor['d']['data'])
{
$this->inertIntoList($element,$this->predescor['d']);
return true;
}
$this->predescor = $this->predescor;
if($element['d']['data']<$this->predescor['d']['data'])
{
$this->predescor = $this->predescor['l'];
}
else
{
$this->predescor = $this->predescor['r'];
}
}
$element['p'] = $this->predescor;
if(!$this->predescor)
{
$root = $element;
}
else if($element['d']['data']<$this->predescor['d']['data'])
{
$this->predescor['l'] = $element;
}
else
{
$this->predescor['r'] = $element;
}
return true;
}
public function putList(&$list,$root)
{
if($root!=null)
{
$this->putList($list, $root['l']);
$lista[$this->i] = $root;
$this->i++;
$this->putList($list, $root['r']);
}
return;
}
private function insertIntoList($element,&$position)
{
if($position == null)
{
$position = $element;
return true;
}
foreach($position['user'] as &$key)
{
if($key == $element['d']['user'])
{
if($key['priority']<$element['d']['user']['priority'])
{
return false;
}
else if($key['priority']==$element['d']['user']['priority'])
{
return false;
}
else
{
if(Orders::checkOrder($element['d']['user']['order']))
{
$key['order'] = $element['d']['user']['order'];
return true;
}
else
{
return false;
}
}
}
}
//#todo add at the end
return true;
}
}
I would like to advise whether there is a simpler way than using bst consisting of a quite complex arrays, which would also be easier to implement? Because now I can not inplement it in PHP.
Thank you in advance.
I wouldn't start by coding this in php at all.
I'd start by building this into the database. ("Orders" implies a database.) I'd start by clarifying a couple of points. Assuming that one order can have many line items . . .
The number of days since the last order seems to clearly apply to the order,
not to individual products.
The user can have "only one request carried at a time". Request for
what? Doesn't seem to make sense for this to apply either to an
order or to an order's line item.
The order priority seems to clearly apply to the order, not to line
items. But a line-item priority might make more sense. (What products does the customer need first?)
Whether the product is in stock seems to apply to the line items, not
to the order as a whole.
I'd start by creating two views. (Not because you'll eventually need two views, but because some things are still unclear.)
One view, which has to do with "ranking" as applied to an order, would calculate or display three things.
Number of days since the last order.
Is this order the "one request carried at a time"?
The order priority.
If the numbers assigned to these three things are consistent in scale, you can just sort on those three columns. But that's not likely. You'll probably need to weight each factor, possibly by multiplying by a "weighting" factor. A calculation on the result should let you put these in a useful order. It's not yet clear whether the calculation is best done in the view or in a stored procedure.
The other view would have to do with whether a line item is in stock. It's not clear whether one line item out of stock means the whole order is incomplete, or whether one line item out of stock changes the calculation of a weighted number that scales along with the others above. (You can make a good argument for each of those approaches.)