Rendering Active Branch of Zend Navigation Without Top Level - php

I am rendering the top-level elements of a Zend Navigation object in one place like this:
echo $this->navigation()->menu()->setMaxDepth(0);
How do I render the navigation tree from the second level on down for the active branch? I've tried creating a partial that loops the $this->container object, but I don't know how to determine if my current item is the active branch. Once I've determined that it's the active branch how do I render the menu? Am I doing this the hard way and missing something obvious?
Thanks!
UPDATE:
I accepted a solution because that's what I used, but I also would like to provide the answer to my actual question, for reference sake. ($this is the view object)
// Find the active branch, at a depth of one
$branch = $this->navigation()->findActive($this->nav, 1, 1);
if (0 == count($branch)) {
// no active branch, find the default branch
$pages = $this->nav->findById('default-branch')->getPages();
} else {
$pages = $branch['page']->getPages();
}
$this->subNav = new Zend_Navigation($pages);
$this->subNav can then be used to render the sub-menu.

If I got your question right, this is how I do it:
print $this->navigation()->menu()->renderMenu(null, array(
'minDepth' => 1,
'maxDepth' => 1,
'onlyActiveBranch' => true,
'renderParents' => false));
Renders only the submenu of currently active menu.

I do something similar. My main navigation is handled with something like this...
$this->navigation()->menu()->setPartial('tabs.phtml');
echo $this->navigation()->menu()->render();
Then in my tabs.phtml I iterate over the container like so...
if (count($this->container)) {
foreach($this->container as $page) {
if ($page->isVisible()) {
if ($page->isActive(true)) {
$subcontainer = $page->getPages();
foreach($subcontainer as $subpage) {
// echo my link
}
}
}
}
}
I hope that helps a bit.

I do it this way:
<?php
// Render top-level elements
echo $this->navigation()->menu()->setMaxDepth(0);
// Render 2nd level elements for active element
echo $this->navigation()->menu()
->setOnlyActiveBranch(true)
->setRenderParents(false)
->setMinDepth(1);
?>
but this is not a good solution. Better one for each level as a separate menu:
<!-- level 1 -->
<?php echo $this->navigation()->menu()->setMaxDepth(0); ?>
<!-- level 2 -->
<?php echo $this->navigation()->menu()->setOnlyActiveBranch(true)->setRenderParents(true)->setMinDepth(1)->setMaxDepth(1); ?>
<!-- level 3 -->
<?php echo $this->navigation()->menu()->setOnlyActiveBranch(true)->setRenderParents(false)->setMinDepth(2)->setMaxDepth(2); ?>

Related

CakePHP 3 create dynamic Menu

I am trying to create a short submenu for CakePHP with a Database Table.
First I tried this solution how to create dynamic navigation menu cakephp which seemed outdated (because its two years old) and since I don´d need to add menus over the add function, its to big anyways.
So can I solve this with just the table in my DB, the model in Cake and an element?
Thats what I got so far:
src/template/element/main.ctp
<ul class="header main-menu">
foreach($something as $something ) :
?>
<li>
somethine
</li>
<? endforeach; ?>
</ul>
my view
<?= $this->element('main'); ?>
My Model
class Menu extends Entity
{
var $name = 'Menu';
}
Is this the way to go?
your question is a little generic, but here's something you want to consider:Html helper class
and in particular Html helper lists. That way you can do in your main.ctp:
$list = [];
foreach ($menu as $menuItem) {
$list[] = $menuItem;
}
echo $this->Html->nestedList($list);

Trying to highlight current menu item in Drupal 8 using hook_preprocess_menu

I have the below function to create active trail functionality. So if I were to have /blog as a "parent" and a post of /blog/mypost, when on mypost the blog link would show as highlighted. I don't want to have to make menu items for all the blog posts. The problem is when caching is turned on (not using settings.local.php and debug turned off) the getRequestUri isn't changing on some pages. It seems to be cached depending on the page. It works fine with page caching turned off but I'd like to get this working with caching. Is there a better way to check for the current path and apply the active class?
function mytheme_preprocess_menu(&$variables, $hook) {
if($variables['theme_hook_original'] == 'menu__main'){
$node = \Drupal::routeMatch()->getParameter('node');
if($node){
$current_path = \Drupal::request()->getRequestUri();
$items = $variables['items'];
foreach ($items as $key => $item) {
// If current path starts with a part of another path i.e. a parent, set active to li.
if (0 === strpos($current_path, $item['url']->toString())) {
// Add active link.
$variables['items'][$key]['attributes']['class'] .= ' menu-item--active-trail';
}
}
}
}
}
I've also tried putting this into a module to try and see if I can get the current path to then do the twig logic in the menu--main.twig.html template but I have the same problem.
function highlight_menu_sections_template_preprocess_default_variables_alter(&$variables) {
$variables['current_path'] = $_SERVER['REQUEST_URI'];
}
After a very long time trying all sorts of things, I found an excellent module which addresses exactly this problem. Install and go, not configuration, it just works:
https://www.drupal.org/project/menu_trail_by_path
Stable versions for D7 and D8.
I tried declaring an active path as part of a custom menu block, and even then my declared trail gets cached. Assuming it's related to the "There is no way to set the active link - override the service if you need more control." statement in this changelog, though why MenuTreeParameters->setActiveTrail() exists is anybody's guess.
For the curious (and for me when I search for this later!), here's my block's build() function:
public function build() {
$menu_tree = \Drupal::menuTree();
$parameters = new MenuTreeParameters();
$parameters->setRoot('menu_link_content:700c69e6-785b-4db7-be49-73188b47b5a3')->setMinDepth(1)->setMaxDepth(1)->onlyEnabledLinks();
// An array of routes and menu_link_content ids to set as active
$define_active_mlid = array(
'view.press_releases.page_1' => 385
);
$route_name = \Drupal::request()->get(RouteObjectInterface::ROUTE_NAME);
if (array_key_exists($route_name, $define_active_mlid)) {
$menu_link = \Drupal::entityTypeManager()->getStorage('menu_link_content')->loadByProperties(array('id' => $define_active_mlid[$route_name]));
$link = array_shift($menu_link);
$parameters->setActiveTrail(array('menu_link_content:' . $link->uuid()));
}
$footer_tree = $menu_tree->load('footer', $parameters);
$manipulators = array(
array('callable' => 'menu.default_tree_manipulators:checkAccess'),
array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
);
$tree = $menu_tree->transform($footer_tree, $manipulators);
$menu = $menu_tree->build($tree);
return array(
'menu' => $menu,
);
}
[adding a new answer since this is a completely different approach than my earlier one]
If a CSS-based solution is acceptable, this seems to work okay:
.page-node-type-press-release {
a[data-drupal-link-system-path="press-room/press-releases"] {
// active CSS styles here
}
}

Concrete 5 Pagination (Page List Blogs)

Is it possible to have some sort of pagination within the URL in Concrete 5. I see many CMS's such as Wordpress and Drupal etc that have such a feature.
At the moment my blogs are the following:
/blog?ccm_paging_p_b348=2
and the way I want it to be is:
/blog/page/1 ... /blog/page/2 etc (or something similar)
Any tips or advice would be appreciated
Your only choice is to create a custom page type for your blog page and then a custom controller for that which handles the pagination.
Please see this page:
http://www.concrete5.org/documentation/developers/pages/mvc-approach
And particularly the "Page Types" section under "Controllers". It explains how to create your page type controllers. For them, you can create similar functions that you would for normal single pages, so you can paginate the results there according to the parameters you get from the URL.
This example is for 5.6 and earlier:
<?php
class BlogPageTypeController extends Controller {
public function view($page=1) {
$pageIndex = intval($page)-1;
if ($pageIndex < 0) {
$pageIndex = 0;
}
$pageList = new PageList();
$pageList->setItemsPerPage(25);
$this->set('pages', $pageList->getPage($pageIndex));
}
}
And then you would use the $pages variable in your view to go through the pages:
<?php foreach($pages as $page) : ?>
<h2><?php echo $page->getCollectionName()</h2>
<?php endforeach; ?>

CakePHP 2.2.1 what is DRY method to highlight current menu item?

I'm trying to implement highlighting of current menu item on my website.
For now I only found a View::set method $this->set('activeMenuButton', 'posts'); in current view.
But I have 9 menu items in my website layout. For example:
<li><?php echo $this->Html->link('Main page', array('controller' => 'pages','action' => 'index'), array('class' => 'button'));?></li> etc.
How I can automatize checking of is current active page == $this->set('activeMenuButton', 'posts')? Or I'm supposed to add check to every < li > in layout?
Like a
if $activeMenuButton == posts { echo = 'class="activebutton"' } else { echo class="button" }?
It looks terrible to me but I can't think of no alternative.
You could use Jquery. Pass the $activeMenuButton with json_encode() to a javascript function, then check all the buttons.

What is the best way to handle recursion in smarty?

I found a couple of ways to handle recursion in Smarty, mostly based on including templates into themselves, which seems like ridiculous waste of resources. I found one solution, by Messju over at Smarty that seemed to be just right - but it is not supported and fails in the latest version of smarty :(
For people asking: What I want smarty to print out is a discussion thread that is defined by an array of entries. If an entry has one or more answers, those are listed as children to said entry in an array, and so on.
array(
array(
'id'=>0,
'headline'=>"My parent headline",
'body' =>"My parent body",
'children'=>array(
array(
'id'=>1,
'headline'=>"My firstChild headline",
'body' =>"My firstChild body",
'children'=>array()
),
array(
'id'=>2,
'headline'=>"My secondChild headline",
'body' =>"My secondChild body",
'children'=>array()
)
)
),
);
The nested array has an arbitrary depth, and each entry will have an arbitrary number of children. To me this is something I want to do with within the scope of the template, as I consider it pure display logic. I do not want to have to handle HTML or some type of HTML placeholders outside of the template.
I want smarty to print this as nested lists:
<ul>
<li>
<h1>My parent headline</h1>
<p>My parent body</p>
<ul>
<li>
<h1>My firstChild headline</h1>
<p>My firstChild body</p>
</li>
<li>
<h1>My secondChild headline</h1>
<p>My secondChild body</p>
</li>
</ul>
</li>
</ul>
I'm starting to realize this might be a very case-by-case problem, so I figure I'll just write a smarty plugin to handle this specifically, although I'd rather have an all-around solution.
Is there a way?
With Smarty 3, this can be done using {function}. The following code will produce the required ouput.
{function name=printList}
<ul>
{foreach $items as $item}
<li>
<h1>{$item['headline']}</h1>
<p>{$item['body']}</p>
{if $item['children']}
{call name=printList items=$item['children']}
{/if}
</li>
{/foreach}
</ul>
{/function}
{call name=printList items=$comments}
More information can be found at the docs.
Side note: Just because something is complex or recursive it doesn't mean that it can't be inside a template. For God's sake the HTML ul-li structure is naturally recursive and by hiding it away or moving it somewhere else (just because it is too complex for a template) you are introducing an extra complexity into the application.
"In order to understand recursion, you must first understand recursion..."
Just kidding. This should do what you want:
<?php
/*
* Smarty plugin
* ————————————————————-
* File: function.recurse_array.php
* Type: function
* Name: recurse_array
* Purpose: prints out elements of an array recursively
* ————————————————————-
*/
function smarty_function_recurse_array($params, &$smarty)
{
if (is_array($params['array']) && count($params['array']) > 0) {
$markup = '';
$markup .= '<ul>';
foreach ($params['array'] as $element) {
$markup .= '<li>';
$markup .= '<h1>' . $element['headline'] . '</h1>';
$markup .= '<p>' . $element['body'] . '</p>';
if (isset($element['children'])) {
$markup .= smarty_function_recurse_array(array('array' => $element['children']), $smarty);
}
$markup .= '</li>';
}
$markup.= '</ul>';
return $markup;
} else {
return 'not array';
}
}
Place the file into your smarty/plugins folder. Assign your array to Smarty then call it in your template like so:
{recurse_array array=$data}
Here's nice tutorial for making custom Smarty functions:
Creating Custom Smarty Functions
Be aware of the dependency that this example has on your underlying data structure. Also, keep in mind that an unusually long or deeply nested set of data could be really slow. Manage your complexity, keep things well documented, and you should be fine. Good luck!
You might want to consider creating custom function/modifier/plugin for smarty.
Pass the array to the custom function along with defining what is the template the function should use. If it is that simple, only to insert a text to certain place, load the template within function and in PHP work with the template using regexes/str_replace/...
Or do it directly in PHP without using smarty templates, because all you need is h1, ul, li and p tags and to change the layout use CSS.
Or if your concern is the overhead with opening and closing files in Smarty, estimate what is the amount of levels in 90% of cases and create template which will cover those 90%. For the rest use recursion by including the template itself...
The best way is not to do it.
Smarty is supposed to be simple. This deesn't sound it.

Categories