Well, I'm trying to get the description of a menu item and of a sub menu with the code attached below, but I'm not able to get it.
I'm trying to get the description of "About us" and the description of "Our board staff":
For the menu item (About us) is working good but for some reason the sub menu (Our board and staff) doesn't contain the information description and it just have ID, URL and Title, I already tried a var_dump() of the sub menu object (as you can see it in the code below) but it doesn't has it.
function get_menu_section_description($sectionUrl){
$menu = wp_get_menu_array("menu");
$desc = "";
foreach ($menu as $key => $item){
$arr = $item['url'];
// var_dump($item);
if ($sectionUrl == $arr[0]) {
$desc = $item['description'];
}
if(sizeof($item['children']) > 0){
foreach ($item['children'] as $key => $children){
// var_dump($children);
$arr2 = $children['url'];
if ($sectionUrl == $arr2) {
$desc = $children['description'];
}
}
} } return $desc; }
Anyone know why doesn't have the description item, how to activate it or a possible solution for that? Thanks in advance.
Since WordPress 3.0, you don't need a custom walker anymore!
There is the walker_nav_menu_start_el filter, see https://developer.wordpress.org/reference/hooks/walker_nav_menu_start_el/
Example
function add_menu_description($item_output, $item, $depth, $args) {
if (strlen($item->description) > 0 ) {
// append description after link
$item_output .= sprintf('<span class="description">%s</span>', esc_html($item->description));
// insert description as last item *in* link ($input_output ends with "</a>{$args->after}")
//$item_output = substr($item_output, 0, -strlen("</a>{$args->after}")) . sprintf('<span class="description">%s</span >', esc_html($item->description)) . "</a>{$args->after}";
}
return $item_output;
}
add_filter('walker_nav_menu_start_el', 'add_menu_description', 10, 4);
I found a solution few days a go, so may it help someone, the problem I had was was the function to call the menu wp_get_menu_array(), there I had to add the description in the sub menu, just that:
function wp_get_menu_array($current_menu) {
$array_menu = wp_get_nav_menu_items($current_menu);
$menu = array();
foreach ($array_menu as $m) {
if (empty($m->menu_item_parent)) {
$menu[$m->ID] = array();
$menu[$m->ID]['ID'] = $m->ID;
$menu[$m->ID]['title'] = $m->title;
$menu[$m->ID]['url'] = $m->url;
$menu[$m->ID]['classes'] = $m->classes;
$menu[$m->ID]['description'] = $m->description;
$menu[$m->ID]['children'] = array();
}
}
$submenu = array();
foreach ($array_menu as $m) {
if ($m->menu_item_parent) {
$submenu[$m->ID] = array();
$submenu[$m->ID]['ID'] = $m->ID;
$submenu[$m->ID]['title'] = $m->title;
$submenu[$m->ID]['url'] = $m->url;
$submenu[$m->ID]['description'] = $m->description; //Line added;
$menu[$m->menu_item_parent]['children'][$m->ID] = $submenu[$m->ID];
}
}
return $menu;
}
Related
Now I have a post_type created, called products
This shows me a table in the following order:
Title
Category
Date
Price
Mileage
As I can change the position as follows:
Title
Category
Price
Mileage
Date
???
I thank you help me, thanks
Add this to functions.php
add_filter('manage_productos_posts_columns', 'column_order_move_1');
function column_order_move_1($columns) {
$n_columns = array();
$move = 'Price'; // what to move
$before = 'Date'; // move before this
foreach($columns as $key => $value) {
if ($key==$before){
$n_columns[$move] = $move;
}
$n_columns[$key] = $value;
}
return $n_columns;
}
add_filter('manage_productos_posts_columns', 'column_order_move_2');
function column_order_move_2($columns) {
$n_columns = array();
$move = 'Mileage'; // what to move
$before = 'Date'; // move before this
foreach($columns as $key => $value) {
if ($key==$before){
$n_columns[$move] = $move;
}
$n_columns[$key] = $value;
}
return $n_columns;
}
It's not the best solution.
EDIT 3: Got it down to 300-500 ms by changing flatten method to only merge arrays if not empty.
EDIT 2: Got it down to 1.6 seconds by only calling array_replace for non empty array. Now all that is left to do is optimize the function sort_categories_and_sub_categories. That is NOW the bottleneck. If I remove that I am down to 300ms. Any ideas?
get_all_categories_and_sub_categories
foreach(array_keys($categories) as $id)
{
$subcategories = $this->get_all_categories_and_sub_categories($id, $depth + 1);
if (!empty($subcategories))
{
$categories = array_replace($categories, $subcategories);
}
}
EDIT
I improved performance by over 50% (6 seconds --> 2.5 seconds) by doing a cache in the get_all method. It reduces the amount of queries to 1 from 3000. I am still wondering why it is slow.
I have the following method for getting categories and nested sub categories. If a user has a couple hundred (or thousand) top level categories it does a bunch of queries for each category to find the children. In one case I have 3000 categories and it did 3000 queries. Is there a way to optimize this to do less queries? OR should I just check to see if they have a lot of categories NOT to try to show nested too.
function get_all_categories_and_sub_categories($parent_id = NULL, $depth = 0)
{
$categories = $this->get_all($parent_id);
if (!empty($categories))
{
foreach($categories as $id => $value)
{
$categories[$id]['depth'] = $depth;
}
foreach(array_keys($categories) as $id)
{
$categories = array_replace($categories, $this->get_all_categories_and_sub_categories($id, $depth + 1));
}
return $categories;
}
else
{
return $categories;
}
}
function get_all($parent_id = NULL, $limit=10000, $offset=0,$col='name',$order='asc')
{
static $cache = array();
if (!$cache)
{
$this->db->from('categories');
$this->db->where('deleted',0);
if (!$this->config->item('speed_up_search_queries'))
{
$this->db->order_by($col, $order);
}
$this->db->limit($limit);
$this->db->offset($offset);
foreach($this->db->get()->result_array() as $result)
{
$cache[$result['parent_id'] ? $result['parent_id'] : 0][] = array('name' => $result['name'], 'parent_id' => $result['parent_id'], 'id' => $result['id']);
}
}
$return = array();
$key = $parent_id == NULL ? 0 : $parent_id;
if (isset($cache[$key]))
{
foreach($cache[$key] as $row)
{
$return[$row['id']] = array('name' => $row['name'], 'parent_id' => $row['parent_id']);
}
return $return;
}
return $return;
}
function sort_categories_and_sub_categories($categories)
{
$objects = array();
// turn to array of objects to make sure our elements are passed by reference
foreach ($categories as $k => $v)
{
$node = new StdClass();
$node->id = $k;
$node->parent_id = $v['parent_id'];
$node->name = $v['name'];
$node->depth = $v['depth'];
$node->children = array();
$objects[$k] = $node;
}
// list dependencies parent -> children
foreach ($objects as $node)
{
$parent_id = $node->parent_id;
if ($parent_id !== null)
{
$objects[$parent_id]->children[] = $node;
}
}
// clean the object list to make kind of a tree (we keep only root elements)
$sorted = array_filter($objects, array('Category','_filter_to_root'));
// flatten recursively
$categories = self::_flatten($sorted);
$return = array();
foreach($categories as $category)
{
$return[$category->id] = array('depth' => $category->depth, 'name' => $category->name, 'parent_id' => $category->parent_id);
}
return $return;
}
static function _filter_to_root($node)
{
return $node->depth === 0;
}
static function _flatten($elements)
{
$result = array();
foreach ($elements as $element)
{
if (property_exists($element, 'children'))
{
$children = $element->children;
unset($element->children);
}
else
{
$children = null;
}
$result[] = $element;
if (isset($children))
{
$flatened = self::_flatten($children);
if (!empty($flatened))
{
$result = array_merge($result, $flatened);
}
}
}
return $result;
}
I'm trying to generate a JSON string that includes the id and url for all my categories, subcategories, and sub-subcategories. The code below only gets me as far as the top-level category, but I also need the second and third as well. My goal is to create a dynamically updating sitemap that decodes the string and makes an unordered list of every category level.
public function createCategoryTree() {
$_categories = Mage::getModel('catalog/category')->getCollection()->addAttributeToSelect('*')->addIsActiveFilter()->addLevelFilter(2)->addOrderField('position'); // load all categories
$arr = array();
if ($_categories){ // if there are categories
foreach ($_categories as $cat){
$cat_name = $cat->getName(); // name as key
if (strlen(stristr($cat_name,'default')) < 3) {
$cat_id = $cat->getId(); // id
$cat_url = $cat->getUrl(); // url
$arr[$cat_id] = array('title'=>$cat_name,'url'=>$cat_url);
}
}
}
print_r (json_encode($arr));
die;
}
I get something like this:
{
"637": {
"title": "bikes",
"url": "http:www.123.com/shop/bicycles "
}
}
But I want something like this:
{
"637": {
"title": "bikes",
"url": "http:www.123.com/shop/bicycles",
"children": {
"658": {
"title":"wheels",
"url":"http:www.123.com/shop/bicycles/wheels"
},
"489": {
"title": "pumps",
"url":"http:www.123.com/shop/bicycles/pumps"
}
}
}
Appreciate your time and help!
You can use some functionality provided by Mage_Catalog_Model_Resource_Category_Tree for this. See if this script works for you:
<?php
require_once('../app/Mage.php');
Mage::app();
function getCategoryTree($recursionLevel, $storeId = 1)
{
$parent = Mage::app()->getStore()->getRootCategoryId();
$tree = Mage::getResourceModel('catalog/category_tree');
/* #var $tree Mage_Catalog_Model_Resource_Category_Tree */
$nodes = $tree->loadNode($parent)
->loadChildren($recursionLevel)
->getChildren();
$tree->addCollectionData(null, false, $parent);
$categoryTreeData = array();
foreach ($nodes as $node) {
$categoryTreeData[$node->getData('entity_id')] = getNodeChildrenData($node);
}
return $categoryTreeData;
}
function getNodeChildrenData(Varien_Data_Tree_Node $node)
{
$data = array(
'title' => $node->getData('name'),
'url' => $node->getData('url_key'),
);
foreach ($node->getChildren() as $childNode) {
if (!array_key_exists('children', $data)) {
$data['children'] = array();
}
$data['children'][$childNode->getData('entity_id')] = getNodeChildrenData($childNode);
}
return $data;
}
print_r(json_encode(getCategoryTree(3)));
OK, I have a very specific question that I hope someone can shed some light on.
I have a page that lists authors outputting using the following code
<?php
$display_admins = false;
$order_by = 'post_count'; // 'nicename', 'email', 'url', 'registered', 'display_name', or 'post_count'
$role = ''; // 'subscriber', 'contributor', 'editor', 'author' - leave blank for 'all'
$hide_empty = true; // hides authors with zero posts
if(!empty($display_admins)) {
$blogusers = get_users('orderby='.$order_by.'&role='.$role);
} else {
$admins = get_users('role=administrator');
$exclude = array();
foreach($admins as $ad) {
$exclude[] = $ad->ID;
}
$exclude = implode(',', $exclude);
$blogusers = get_users('exclude='.$exclude.'&orderby='.$order_by.'&role='.$role.'&order='.'DESC');
}
$authors = array();
foreach ($blogusers as $bloguser) {
$user = get_userdata($bloguser->ID);
if(!empty($hide_empty)) {
$numposts = count_user_posts($user->ID);
if($numposts < 1) continue;
}
$authors[] = (array) $user;
}
echo '<ul class="contributors">';
foreach($authors as $author) {
$display_name = $author['data']->display_name;
$avatar = get_wp_user_avatar($author['ID'], 'medium');
$author_profile_url = get_author_posts_url($author['ID']);
$filter = get_userdata($author['ID'])->yim;
echo '<li><div class="home ', $filter,' "><div class="feature-image">', $avatar , '</div>
<div class="post-title"><h3>', $display_name, '</h3></div>
</div>
</li>';
}
echo '</ul>';
?>
(I got this from another support topic and tweaked it, although I can't remember where)
At the moment, the $filter variable is just a string I enter in the 'Yahoo IM' profile box (a dirty fix to test the filter). I'd like this to actually be a list of the categories (as slugs that I will output in to the class="" part of the loop) that the author has posted in.
I essentially want to be able to filter the authors by category that they have posted in, and the filter I'm using (Isotope) operates using the class, so outputting the categories in to the class of the markup is what I'm after.
Any suggestions gratefully received!
// Returns the posts made by the author
$authorPosts = get_posts("author={$author['ID']}");
$categoryList = array(); // reset previous values
foreach ($authorPosts as $post) {
$postCategories = get_the_category($post->ID);
// Add to the categories the author has posted in
foreach ($postCategories as $category)
$categoryList[] = $category->slug;
}
// Removes duplicate categories
$categoryList = array_unique($categoryList);
You can then use $filter = implode(' ', $categoryList); and add it to your html.
RE not storing the array from the other answer, you can just echo out the slugs there and then like this:
$authorPosts = get_posts("author={$author['ID']}");
foreach ($authorPosts as $post) {
$postCategories = get_the_category($post->ID);
// Add to the categories the author has posted in
foreach ($postCategories as $category)
echo($category->slug);
}
otherwise if you want to put your PHP at the top and echo out the slugs further down the page pop there where ever you want to echo them:
$i = 0;
foreach($categoryList as $category) {
echo($categoryList[$i]);
$i++;
}
I want to be able to call through the API to get an array of all the categories with the details like the URL key. That goal in the end will be an array like this
$massage_cats=array(
array("entity_id"=>78,
"name"=>"Massage Oils and Tools",
"url_key"=>"massage-oils-and-tools",
"url_path"=>"essential-accessories/massage-oils-and-tools.html"),
array("entity_id"=>79,
"name"=>"Massage Oils",
"url_key"=>"massage-oils",
"url_path"=>"essential-accessories/massage-oils-and-tools/massage-oils.html")
);
So I would want to call out something like
$massage_cats= array();
$allcats = Mage::getModel('catalog/cats?')->loadAll();
foreach($allcats $k=>$item){
array_push($massage_cats,$item->loadDetails());
}
I know that is totally made up and not real to the API but that is basically the goal. I do need the output as I showed it. Ideas on the code to achieve the need?
This will get your values. You can build your array however you like from here.
$categories = Mage::getModel('catalog/category')->getCollection()
->addAttributeToSelect('id')
->addAttributeToSelect('name')
->addAttributeToSelect('url_key')
->addAttributeToSelect('url')
->addAttributeToSelect('is_active');
foreach ($categories as $category)
{
if ($category->getIsActive()) { // Only pull Active categories
$entity_id = $category->getId();
$name = $category->getName();
$url_key = $category->getUrlKey();
$url_path = $category->getUrl();
}
}
EDIT
I adapted this from a post on MagentoCommerce.com. You can use this instead:
$category = Mage::getModel('catalog/category');
$tree = $category->getTreeModel();
$tree->load();
$ids = $tree->getCollection()->getAllIds();
if ($ids){
foreach ($ids as $id){
$cat = Mage::getModel('catalog/category');
$cat->load($id);
$entity_id = $cat->getId();
$name = $cat->getName();
$url_key = $cat->getUrlKey();
$url_path = $cat->getUrlPath();
}
}
HERE I WROTE FUNCTION UPTO THREE LEVELS RETURN IN ARRAY FORMAT
$array=hpCat(2,3); //categoryID,Sublevel upto three level
print_r($array);
<?php
function hpCat($id,$level=0){
if(!empty($id)){
$level=empty($level)?0:$level;
$category = Mage::getModel('catalog/category')->load($id);
$levelOneItems = $category->getChildrenCategories();
if (count($levelOneItems) > 0){
$array=hpCatDetails($category);
if($level>=1):
$i=0;
foreach($levelOneItems as $levelOneItem){
$array['sub'][$i]=hpCatDetails($levelOneItem);
$leveltwoItems=$levelOneItem->getChildrenCategories();
if (count($leveltwoItems) > 0){
if($level>=2):
$j=0;
foreach($leveltwoItems as $leveltwoItem){
$array['sub'][$i]['sub'][$j]=hpCatDetails($leveltwoItem);
$levelthreeItems=$leveltwoItem->getChildrenCategories();
if (count($levelthreeItems) > 0){
if($level>=3):
$k=0;
foreach($levelthreeItems as $levelthreeItem){
$array['sub'][$i]['sub'][$j]['sub'][$k]=hpCatDetails($levelthreeItem);
$k++;
}
endif;
}
$j++;
}
endif;
}
$i++;
}
endif;
}
return $array;
}
return array();
}
function hpCatDetails($cat){
return array('name'=>$cat->getName());
}
$array=hpCat(2,3);//categoryID,Sublevel upto three level
echo '<pre>';print_r($array);die();
?>
For the people looking for MySQL query to fetch all Magento categories.
SELECT
e.entity_id AS id,
e.parent_id,
e.path,
e.`level`,
IF (
at_name.value_id > 0,
at_name.
VALUE
,
at_name_default.
VALUE
) AS `name`
FROM
`catalog_category_entity` AS `e`
INNER JOIN `catalog_category_entity_varchar` AS `at_name_default` ON (
`at_name_default`.`entity_id` = `e`.`entity_id`
)
AND (
`at_name_default`.`attribute_id` = '41'
)
LEFT JOIN `catalog_category_entity_varchar` AS `at_name` ON (
`at_name`.`entity_id` = `e`.`entity_id`
)
AND (
`at_name`.`attribute_id` = '41'
)
A recursive function to make it :
private function __categoriesTree($id = 2) {
$category = Mage::getModel('catalog/category');
$_category = $category->load($id);
$details = new stdClass();
list($details->id, $details->name, $details->urlKey, $details->level, $details->children) = [
$_category->getId(),
$_category->getName(),
$_category->getUrlKey(),
$_category->getLevel(),
[]
];
foreach (array_filter(explode(',', $_category->getChildren())) as $childId) {
$details->children[] = $this->__categoriesTree($childId);
}
if (count($details->children) === 0) {
unset($details->children);
}
return $details;
}
And
$categoriesTree= $this->categoriesTree()
I prefer to use objects than arrays to model a node but you can easily replace.