I am trying to reorganize a PHP array (on a WordPress page). My site was coded by someone else, and I'm trying to make things better. I think they did things in a confusing way (poor use of the categories). Rather than recode the entire site, I wanted to see if someone could help me work around a fix.
So, basically, I have an array inside an array. A list of categories, and each category has specific details. I'm calling the categories, based on it's parent. I need to reorganize the list/array of categories, so they can print out in the order that I need them to.
$cat_tree = get_category_parents(wt_get_category_ID(), FALSE, ':', TRUE);
$top_cat = split(':',$cat_tree);
$parent = $top_cat[0];
$idObj = get_category_by_slug($parent);
$args = array(
'child_of' => $idObj->term_id,
'orderby' => 'name',
'order' => 'ASC',
'hide_empty' => 0);
$counter_c=1;
$categories = get_categories( $args );
if($counter_c == 1) $counter_c = '';
foreach ($categories as $category) {
?>
<div> this is where each category array info gets printed... information goes:<br />
Name: <?=$category->cat_name ?> (<?=$category->cat_ID ?>)
</div>
<?php if($counter_c == '') $counter_c = 1; $counter_c++; } // Reset Query
wp_reset_query();
?>
On one page, $cat_tree outputs "entertainment:actors:".
$parent or $topcat[0] outputs "entertainment"
$topcat[1] outputs "actors"
When the "foreach" runs, I get an list sorted by the ID ...
Actors (13)
Artists (14)
Directors (25)
Writers (26)
DeeJays (35)
I need to reorganize this list into a specific order. Each page is different. So I may have to reorganize a list differently for each page. I may need...
Writers (26)
Actors (13)
DeeJays (35)
Artists (14)
Directors (25)
So, I need a way to do this. I thought that maybe I could say "if" on the "actor page", slide "Writers" to the end of the array (I know there is some way to do this), then slide Actors to the end, then DeeJays, then Artists, then Directors. Then run the foreach.
Or, if page is something else, I can slide other elements to the end of the array. That seems a bit much, but it's the only work around that I could think of. Of course, my php knowledge is limited, so any help is appreciated.
You could try a custom sort:
$order = array('Writers', 'Actors', 'DeeJays', 'Artists', 'Directors');
function catsort($a, $b) {
global $order;
$ak = array_search($a->cat_name, $order);
$bk = array_search($b->cat_name, $order);
if($ak === false) $ak = 0;
if($bk === false) $bk = 0;
return $ak - $bk;
}
usort($categories, 'catsort');
Each page would just need a different order array.
Related
I switched over to Opencart 3.0.. Not to fond of the .tpl to .twig changes.
Anyways, I have been trying, to no avail, to display all products from a particular category on another category page.
I found this foreach loop:
{% for product in products %}
Which I imagine reads
<?php foreach ($products as $product) { //do something } ?>
I tried adding the Loop to the path:
catalog/view/theme/*/template/product/category.twig
but it only shows the products from the current category page id I am currently on.
Any help would be greatly appreciated.
It's generally bad practise on OC to edit the files directly, rather look at OCMOD or VQMOD to make runtime changes but not edit the core file. Granted this may be an additional complication right now.
If you look at the category.php file in the /catalog/controller/product/ folder around line 150, you'll see these lines of code:
$data['products'] = array();
$filter_data = array(
'filter_category_id' => $category_id,
'filter_filter' => $filter,
'sort' => $sort,
'order' => $order,
'start' => ($page - 1) * $limit,
'limit' => $limit
);
$product_total = $this->model_catalog_product->getTotalProducts($filter_data);
$results = $this->model_catalog_product->getProducts($filter_data);
What you need to do is create a new $filter_data variable with your requisite filters, you can just have the category ID if that's all you need.
Look at the line below:
$results = $this->model_catalog_product->getProducts($filter_data);
It's calling the method getProducts which is located in the model CatalogCategory (/catalog/model/product.php) this method will build a SQL query based on the filters passed to the method and return an associative array with the results (hence the aptly named $results variable).
The controller file we first looked at then iterates through these $results and stores the values in $data['products'].
Putting this together, you can try the following:
$data['products2'] = array();
$new_filter_data = array(
'filter_category_id' => 13 // or any relevant ID
);
$results2 = $this->model_catalog_product->getProducts($new_filter_data);
This isn't a complete solution as the controller file continues to resize images, get reviews etc. So you'll need to adjust this according to what your needs are and play around a little.
Just make sure you're working on a local copy of the site!
I'm trying to merge 2 arrays in PHP and then sort them.
I'm pulling data from WordPress database ( tags and categories ) and I need to merge them and sort them properly.
Code looks similar to this:
$categories = [
'<a>WordPress Tips</a>',
'<a>SEO Tips'</a>,
'<a>Development Tips</a>'
];
$tags = [
'<a>WordPress</a>',
'<a>SEO'</a>,
'<a>Development</a>'
];
$taxonomies = array_merge($categories, $tags);
sort($taxonomies);
Since I need to loop through $taxonomies array and print them with anchors included ( every one of those must have a valid URL to itself ), I don't get a proper sorting result. However, as soon as I 'strip out' all the tags around these items, sorting works as it should, but I don't get URLs that I need, only string / text.
Could someone suggest a better sorting algorithm which would sort these items properly with html elements included around them? Thanks!
You need to do custom comaprision to make it work using usort() and strip_tags()
<?php
$taxonomies = array_merge($categories, $tags);
print_r($taxonomies);
function cmp($a, $b){
$data1 = strip_tags($a);
$data2 = strip_tags($b);
return ($data1 < $data2) ? -1 : 1;
}
usort($taxonomies, "cmp");
print_r($taxonomies);
Output:-https://eval.in/934071
Since you mentioned that the tags stripped version works, a simple way might be to use them as the keys in the sort. Something like this, for instance :
$taxonomies = array_merge( $categories, $tags );
$toSort = [];
foreach($taxonomies as $tax) {
$toSort[strip_tags($tax)] = $tax;
}
ksort( $toSort );
print_r( $toSort );
Which will give the sorted array and you can just use the values in there. Maybe there's better options but this comes to mind off the top of my head..
EDIT : uasort with a comparision function that actually compares the stripped versions might be better, after looking at other answers here.
So I've been dealing with this problem for quite some time and can't find a clear solution. So basically I'm adding new products directly into db using Product object. For now it went well but I can't manage to link new products with a category.
$cat_ids = [];
foreach ($value['kat_naziv'] as $cat_val) {
$cat_ids[] = (int)$luceed->selectCategoryIds($cat_val)[$cat_val]['id_category'];
}
$product->id_category = 3;
$product->id_category_default = 3;
$product->save();
$product->addToCategories($cat_ids);
So basically $cat_ids is an array of integers that i'm getting from db where name is something i pass as a parameter to selectCategoryIds;
What is the problem here why it wont associate newly created product with categories i give to it
After creating your new product ( i.e $product = new Product() ). You can assign categories to product using.
$product->updateCategories($category_array);
where
$category_array = array("0" => "2", "1" => "3", "4" => "6"...... );
#FMEModule That's exactly what i did there but i've filled the array with the id's of categories from the database
Anyways I ended up writing my own queries for associatting products with categories
Version (1.6)
I found the following bug in Product.php, in addToCategories, search for if (!in_array($new_id_categ, $current_categories)) (line 964),
notice that the if is missing {} - add them, and the problem is solved:
foreach ($categories as $new_id_categ) {
if (!in_array($new_id_categ, $current_categories)) {
if ($position == null) {
$position = (int)$new_categ_pos[$new_id_categ];
}
$product_cats[] = array(
'id_category' => (int)$new_id_categ,
'id_product' => (int)$this->id,
'position' => $position,
);
}
}
Prestashop developer LOVEs omitting {} after if and foreach - this is supper annoying and bug prone.
This issue is fixed in the repo:
https://github.com/PrestaShop/PrestaShop/blob/1.6.1.x/classes/Product.php
NOTE: this solution solves the bug in the following scenario - a product that is already link to a category is link to another category (while keeping the original category)
although I'm not sure if this is the scenario in the question.
Im looking for a way to extract the data from the mysql database to create an unlimited subcategry menu.
The menu has about 1500 categories in it and they are stored into the database table (menu) in the following format :
category_id , category title , parent_id
i want to do 1 mysql query to put all the data into an array.
$menu_list =array();
$query = mysql_query("SELECT * FROM menu ORDER BY category_id ASC");
if(mysql_num_rows($query) != 0) {
while($row = mysql_fetch_array($query)){
$category_id = $row['category_id'];
$title = $row['category_title'];
$parent_id = $row[parent_id'];
$menu_list[] = array($category_id,$title,$parent_id);
}
}
That is the code that i am currently using to turn the data into the array.
I then need to loop through the array to build a menu up.
I will then hide all subcategories and expand with jquery ( I can do this bit no problem )
The problem i have is going through the array and displaying the data in the right order.
The menu has unlimited subcategories so it will probably need to be a recursive function to retrieve the data.
I have folllowed alot of the examples on here but then i get lost how to display the data..
i now need to end up with :
main item
sub cat
sub cat
sub cat
sub cat
main item
sub cat
main item
main item
sub cat
sub cat
sub cat
sub cat
sub cat
etc...
with each category in its own div
I will then need to be able to edit each category (ie title name and position if need be)
then if i change where a sub category is based a quick page refresh will reload the menu in the new order..
eventually i would like to make this into a drag and drop but that will be at a later date.
I hope this has explained it enough..
Thanks in advance..
So the problem has come back to haunt me again...
My method of ajax calling another mysql select was ok for the CMS section as it was only used from time to time.
But now we have got to the front end.
Each time the page is viewed the first 3 levels of the menu are pulled using numerous mysql requests.
As the categories are getting bigger this is now resulting in 1600+ categories being pulled numerous times each, and even at 1000 visits a day will result in more than a 1000000 sql requests a day.
So i think i will definately need to pull the categories into an array, and then recursively go through the array.
I have looked at the solutions above and they seem to work on paper but not in practise.
If anyone can attempt to give me another solution it would be appreciated..
just to recap in my db i have : id , category_name , parent_id
I am now using PDO with mysql and prepared statements for added security..
Thanks in advance..
Here is the code what you need
category_id AS id , category title AS kategori and parent_id AS kid
function countsubcat($pid)
{
$r=mysql_query("select count(kid) AS say from meskat where kid='$pid' limit 1");
$rw=mysql_fetch_array($r);
return $rw['say'];
}
function listmenu($pid = 0)
{
$res = mysql_query("select id,kategori,kid from meskat where kid='$pid'");
while($cat=mysql_fetch_array($res))
{
echo '<li>';
print''.$cat['kategori'].'';
if(countsubcat($cat['id'])>0)
{
print'<ul>';
listmenu($cat['id']);
print'</ul>';
}
echo '</li>';
}
}
echo '<ul>';
listmenu(0); //starting from base category
echo '</ul>';`
In MySQL, this will require many queries, one for each level plus one at a minimum.
you grab all the top-level categories, use them as root nodes for a forest (a group of trees).
You grab all of the categories that are children of any of them.
Add each of these categories as children to the parent node
If there are still children, go to step two, otherwise stop.
What you end up with a tree that you do a depth-first walk through to convert to some (presumably) html representation (e.g. nested lists).
There are some optimisations you can perform. If you sort the child levels by parent ID, then you can assume continuous blocks of children, meaning you don't have to do as many lookups to find the correct parent, just check for a parent id change.
If you maintain a list of the current lowest level of categories, indexed by ID, you can speed up forest creation.
If you overlap steps 2 and 4, then you only do n+1 queries (where n is the number of levels), so checking for more children is also grabbing the next level of children.
In terms of memory, this will use the memory required for the tree, plus lowest level lookup table and a current parent id.
The building algorithm it fairly fast, scaling linearly with the number of categories.
This method also successfully avoids corrupted sections of data, because it won't grab data that have cycles (no root node).
I also suggest using a prepared statement for the children query, since MySQL will compile and cache the query, it will speed up the operation by only sending new data across the wire.
If you don't understand some of the concepts, I suggest hitting up Wikipedia as I have tried to use standard terms for these.
Can you alter the table structure? In that case you could take a look at the Nested Set Model (link includes description and implementation details, scroll down to The Nested Set Model). Node insertion and removal becomes more complex, but allows you to retrieve the whole tree in just one query.
If you can assume that for any category category_id > parent_id is true, the following recursive functions to represent the menu as a multi-level array and render it as a nested HTML list would work:
$menu = array();
$query = mysql_query("SELECT category_id, category_title, parent_id FROM menu ORDER BY category_id ASC");
if(mysql_num_rows($query) != 0) {
while($row = mysql_fetch_assoc($query)) {
if(is_null($row['parent_id']))
$menu['children'][] = $row;
else
add_to_menu(&$menu,$row);
}
}
function add_to_menu($menu,$item) {
if(isset($menu['children'])) {
foreach($menu['children'] as &$child) {
if($item['parent_id'] == $child['category_id']) {
$child['children'][] = $item;
} else {
add_to_menu(&$child,$item);
}
}
}
}
function render_menu($menu) {
if(isset($menu['children'])) {
echo '<ul>';
foreach($menu['children'] as &$child) {
echo "<li>";
echo $child['category_id']." : ".$child['category_title'];
if(isset($child['children'])) {
render_menu(&$child);
}
echo "</li>";
}
echo '</ul>';
}
}
render_menu($menu);
So after various attempts at different solutions i actually have used jquery and a ajax load to retreive the next level.
With mine main categories all have a parent of 1 - shop i do a mysql scan for all categories with 1 as the parent id.
This will then display the main categories. Attached to each main category i have added a folder icon if they have any sub categories.
I have also inserted a blank div with an id of that category.
When the folder is clicked it does an ajax call to a php script to find all categories with that parent. These are appended to the parents_id div.
This inturn adds the category (with a css padding to indicate a sub category) and again a folder sign if it has sub categories as well as another blank div with the category_id.
You can continue this for as many categories and sub categories as the client requires.
I took it a stage further and added an edit icon to category/subcategory so categories can be moved or changed.
If you would like some sample code for this let me know...
Actually, you need very little code to build a tree.
You also only need one query.
Here is what I came up with (I use Drupal's database but it is explicit enough to understand):
$items = db_select('menu_items', 'mi')
->fields('mi')
->orderBy('position')
->execute()
->fetchAllAssoc('id', PDO::FETCH_ASSOC);
// Example of result. The ID as key is important for this to work.
$items = array(
3 => array('id' => 3, 'parent' => NULL, 'title' => 'Root', 'position' => 0),
4 => array('id' => 4, 'parent' => 3, 'title' => 'Sub', 'position' => 0),
5 => array('id' => 5, 'parent' => 4, 'title' => 'Sub sub', 'position' => 0),
6 => array('id' => 6, 'parent' => 4, 'title' => 'Sub sub', 'position' => 1),
);
// Create the nested structure. Note the & in front of $item.
foreach($items as &$item)
if($item['parent'])
$items[$item['parent']]['sub items'][$item['mid']] =& $item;
// Now remove the children from the root
foreach($items as $id => $item)
if($item['parent']) // This is a child
unset($items[$id])
At this point, all you need is a recursive function to display the menu:
function print_menu($items) {
echo '<ul>';
foreach($items as $item) {
echo '<li>';
echo '' . $item['title'] . '';
if(!empty($item['sub items']))
print_menu($item['sub items']);
echo '</li>';
}
echo '</ul>';
}
am trying to get all the categories on my wordpress as a heirarchial multidimensional array of objects of type category.
There are 2 functions in wordpress that kind of do what I want, but not exactly :
1) get_categories() - This one does return an array of category objects, which is great, but then the array is a flat one, where there is no differentiation between which category is a parent or which one is a child.
2) wp_list_categories() - which can return the categories with the herarchy intact, but problem is that the return value is html, and I need the categories to still be objects.
For example, if I had the following 3 trees as my parent-less categories :
/*
tree_stump
tree1
branch1
tree2
branch2
twig2
leaves2
flowers2
fruits2
*/
I am trying to get a return value which could be something like:
$cat_tree=array( 1=>$tree_stump,2=>array(1=>$tree1,2=>$branch1),3=>array(1=>$tree2,2=>array(1=>$branch2,2=>array($twig2,$leaves2,$flowers2,$fruits2 ) ) ) );
where each member of the multi dim array is still an object, and in all the child arrays, the first one is the parent of the rest of the members. I guess this would need some kinda recursive stuff - the stuff that gives me jitters !
Not sure if I made sense, please do let me know if I need to be more clear..
Thanks for reading.
Here's my version which assumes $categories is the output of get_categories() but it doesn't matter what order they come in.
$cats_tree = get_cat_tree(0,$categories);
function get_cat_tree($parent,$categories) {
$result = array();
foreach($categories as $category){
if ($parent == $category->category_parent) {
$category->children = get_cat_tree($category->cat_ID,$categories);
$result[] = $category;
}
}
return $result;
}
this is what i did:
First get an array using get_categories(). Then transform it into a new array.
I'm assuming that all children are below its parents in the original array.
$cats_tree = array();
foreach($categories as $category){
$parent = $category->parent;
if ( $parent && isset($cats_tree[$parent]) ){
$cats_tree[$parent]->children[$category->cat_ID] = $category;
}else{
$cats_tree[$category->cat_ID] = $category;
}
}
Hope this is still useful for someone
<?php
$args = array(
'taxonomy' => 'category',
'orderby' => 'name',
'order' => 'ASC',
'hierarchical' => true,
'hide_empty' => false,
);
$the_query = new WP_Term_Query($args);
$categories = $the_query->get_terms();
function get_cat_tree($parent,$categories) {
$result = array();
foreach($categories as $category){
if ($parent == $category->parent) {
$category->children = get_cat_tree($category->term_id,$categories);
$result[] = $category;
}
}
return $result;
}
$cats_tree = get_cat_tree(0,$categories);
get_categories() returns an array with lots of properties you can use to build the array. namely $category->category_parent and $category->parent