Let's say, that we have:
$pages = array(
array(
'controller' => 'controller1',
'label' => 'Label1',
),
array (
'controller' => 'controller2',
'label' => 'Label2'
),
);
$container = new Zend_Navigation($pages);
When user clicks Label1, controller1/index action is rendered and Label1 becomes active state - everything is ok.
On this page I have many links, such as: controller1/action1, controller1/action2, etc
When one of these links is clicked, Label1 looses active state.
I understand, that I can add all sub-pages into Zend_Navigation, but there are plenty of these pages and I never need it anywhere for navigation, so, I'd prefer to have something like:
public function init()
{
$this->view->navigation()-> ... get item by label ... -> setActive();
}
inside controller1. Is it possible?
Your init method is very close!
$page = $this->view->navigation()->findOneByLabel('Your Label'); /* #var $page Zend_Navigation_Page */
if ( $page ) {
$page->setActive();
}
I think this is exactly what he is looking for:
Simply paste this into your menu.phtml or any .phtml where you print your menu:
// apply active state to all actions of controller
$controller = Zend_Controller_Front::getInstance()->getRequest()->getControllerName();
$page = $this->navigation()->findOneByController($controller); /* #var $page Zend_Navigation_Page */
if ( $page ) {
$page->setActive();
}
echo $this->navigation()->menu();
Of course you need to init first a navigation structure with Zend_Navigation_Page_Mvc pages.
Somehow this does not work for me with url pages...
Related
My aim is to have product links like:
domain.com/test-product
domain.com/second-test-product
instead of:
domain.com/products/product/id/5
domain.com/products/product/id/123
Info about each product is get in ProductsController in productAction().
It works fine:
ProductsController:
public function productAction() {
$products = new Application_Model_DbTable_Products();
$nicelink = $this->_getParam('nicelink', 0);
$this->view->product = $products->fetchRow($products->select()->where('product_nicelink = ?', $nicelink));
// nicelink is always unique
}
The link to this method looks like:
for ($i=0; $i < count($this->products); $i++) {
echo 'LINK';
}
Info about each product is displayed in product.phtml view:
<?php echo $this->escape($this->product->product_name); ?>
And my Bootstrap.php file:
<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap {
protected function _initRoutes() {
$router = Zend_Controller_Front::getInstance()->getRouter();
include APPLICATION_PATH . "/configs/routes.php";
//$frontController = Zend_Controller_Front::getInstance();
$route = new Zend_Controller_Router_Route_Regex(
'(.+)',
array(
'controller' => 'products',
'action' => 'product'
),
array(
1 => 'nicelink',
),
'%s.html'
);
$router->addRoute('profileArchive', $route);
}
}
However, this solution has 1 major disadvantage: all other links such as
domain.com/contact
domain.com/about-us
are not working (probably due to the Bootstrap file).
How to fix the problem in order to make another links work and maintain current product links?
The issue is that you are matching all routes to product action in products controller. Probably you want to keep using the default routes in zend 1, which match "contact" and "about-us" to the corresponding actions in your default controller.
If you check "Zend_Controller_Router_Rewrite" function "addDefaultRoutes()" and "route()", you can see that the default routes will be checked after any custom ones.
The simplest solution, looking on what you are asking would be to match any routes that end with "-product":
$route = new Zend_Controller_Router_Route_Regex(
'(.+)\-product',
array(
'controller' => 'products',
'action' => 'product'
),
array(
1 => 'nicelink',
),
'%s.html'
);
In this case nicelink should take values "test" and "second-test" in your corresponding examples.
I have a table on my database from where I get some slug texts:
art_and_culture
business_and_financial
auto_and_moto
and display on my website through
<?php echo lang($slug_from_database); ?>
I use this method as LanguageSwticher:
multi-language-support-in-codeigniter
So in My_Controller.php I have in construct:
$this->categories_list = $this->categories_model->entries();
$list_categories['categories'] = $this->categories_list;
$this->data['sidebar_categories'] = $this->load->view('blocks/sidebar_categories', $list_categories, TRUE);
and all the categories are available on all pages from my website. The problem is when I send to the view the categories, the language is not set from the LanguageLoader.php controller witch is initialised in hooks.php (see the link example). If no language no text on echo $slug_from_database. How do you suggest to do?
I found one solution:
Created:
public function LanguageLoader() {
$site_lang = $this->session->userdata('site_lang');
if ($site_lang) {
$this->lang->load('message',$this->session->userdata('site_lang'));
} else {
$this->lang->load('message', $this->config->item('language'));
}
}
in MY_Controller.php
and call it in the constructor: $this->LanguageLoader();
and delete
$hook['post_controller_constructor'] = array(
'class' => 'LanguageLoader',
'function' => 'initialize',
'filename' => 'LanguageLoader.php',
'filepath' => 'hooks'
);
from hooks.php so now I load the language in controller`s construct not with hook.
I'm using CakePHP to create a frontend UI for PowerDNS, using a MySQL backend. On the front page of the app I want to have a handful of widgets ('Quickly add a record', 'Quickly add a domain' etc.). One of the widgets I want is a paginated list of existing domains.
The index function in DomainsController.php looks like this:
public $paginate = array(
'fields' => array('id', 'name'),
'limit' => 25,
'order' => array( 'name' => 'asc' ),
'conditions' => array( "NOT" => array( "name LIKE" => "%.arpa" ) )
);
public function index() {
$domains = $this->paginate();
if ( $this->request->is('requested')) {
return $domains;
} else {
$this->set('domains', $domains);
}
}
I've created an element that looks like this:
<?php $domains = $this->requestAction('Domains/index'); ?>
<ol>
<?php foreach( $domains as $domain) :?>
<li>echo $domains['domain']['name']</li>
<?php endforeach; ?>
</ol>
<?php echo $paginator->numbers(); ?>
When I visit the front page, I get an 'Undefinied variable: paginator' error. I've tried using $this->Paginator->numbers() instead but that just gives me 'Undefined property: View::$Paginator'. Adding the 'Paginator' helper to PagesController.php doesn't help either - $this->Paginator becomes available but I get 'Undefined index: pageCount'.
Is it possible to do this kind of pagination from an element on home.ctp or am I going to have to do some custom JavaScript stuff?
EDIT
Now I'm getting somewhere: I changed my DomainsController index function to this:
public function index() {
$domains = $this->paginate();
$paginator = $this->params;
if ( $this->request->is('requested')) {
return compact( 'domains', 'paginator' );
} else {
$this->set('domains', $domains);
}
}
And added the following to the domainList.ctp element:
<?php
$result = $this->requestAction('Domains/index');
$domains = $result['domains'];
$this->Paginator->request = $result['paginator'];
?>
<ol>
<?php foreach( $domains as $domain) :?>
<li>echo $domains['domain']['name']</li>
<?php endforeach; ?>
</ol>
<?php echo $paginator->numbers(); ?>
$this->Paginator is now working properly and I can access all of its methods and properties and so on as normal. My problem now is that if I click on, say, '2', the browser navigates to /pages/home/page:2 but the domain list still shows page 1. Just need to figure out how to pass 'page:2' to the element. And AJAX-ify the whole thing so that I don't need to refresh the whole page.
Firstly, don't do this:
$domains = $this->requestAction('Domains/index');
It's expensive and not good practice and I'm not sure why you need to be doing it from your example.
Secondly, call your paginate like this:
$domains = $this->Paginate('Domain');
OK, I solved this problem, although my solution probably isn't very elegant.
DomainsController.php has a listDomains() function that looks like this:
public function listDomains() {
$domains = $this->paginate();
$paginator = $this->params;
if ( $this->request->is('ajax') ) {
$this->set( 'domains', $domains );
}
if ( $this->request->is('requested')) {
return array( 'domains' => $domains, 'paginator' => $paginator, 'paging' => $this->params['paging'] );
} else {
$this->set( 'domains', $domains );
}
}
home.ctp references an element called domainList.ctp. domainList.ctp, below, in turn uses requestAction() - I know, I know - to call the domainList() function above. Bequest the request is requested, an array containing the values of $domains and $paginator is sent back to the element.
domainList.ctp contains this code:
<?php
$result = $this->requestAction('Domains/listDomains', array('updateId' => 'domainList') );
$domains = $result['domains'];
$paginator = $result['paginator'];
$this->Paginator->request = $paginator;
$this->Paginator->options(array(
'update' => '#domainList',
'evalScripts' => true,
'url' => array('controller' => 'Domains', 'action' => 'listDomains', 'updateId' => 'domainList' ),
));
?>
Essentially what I'm doing here is manually re-populating $this->Paginator->request with the params that were originally sent to the DomainController's domainList() function. This lets me access the various paginator functions, like numbers(), prev() and next(), properly. It's a bit messy but guess what? It gets a little messier.
When you click on the links created by those paginator functions, the 'if ( $this->request->is('ajax') )' segment is executed and the div object on the page is updated with the contents of View/Domains/domainList.ctp instead of View/Elements/domainList.ctp. The contents of View/Domains/domainList.ctp is more or less the same as the corresponding element and the two have to be kep more or less syncronised. The difference is that we don't need to manually populate $this->Paginator:
<?php
$this->Paginator->options(array(
'update' => '#domainList',
'evalScripts' => true,
'url' => array('controller' => 'Domains', 'action' => 'listDomains', 'updateId' => 'domainList' ),
));
?>
Like I said, it's messy and inelegant but it worked for me. I'd be happy to know if anyone has a less kludgy way to do this.
I used the following code to display the menu items.
Here by default the 'Home' link should be activated. So that i used the code
active' => $this->id =='default' ? true : false
$this->widget('zii.widgets.CMenu',array(
'linkLabelWrapper' => 'span',
'items'=>array(
array('label'=>'Home', 'url'=>array('post/index'),'active'=>$this->id=='default'?true:false),
array('label'=>'About', 'url'=>array('site/page', 'view'=>'about'),'active'=>$this->id=='about'?true:false),
array('label'=>'Test', 'url'=>array('site/page', 'view'=>'test')),
array('label'=>'Contact', 'url'=>array('site/contact')),
array('label'=>'Login', 'url'=>array('site/login'), 'visible'=>Yii::app()->user->isGuest),
array('label'=>'Logout ('.Yii::app()->user->name.')', 'url'=>array('site/logout'), 'visible'=>!Yii::app()->user->isGuest)
),
));
I referred the url http://www.yiiframework.com/doc/api/1.1/CMenu#activateItems-detail
But i dont know how to use these parameters. Please help
Not the Yii way, but the (simpler) jQuery way:
// find menu-item associated with this page and make current:
$('a').each(function(index, value) {
if ($(this).prop("href") === window.location.href) {
$(this).addClass("current-page");
}
});
The code you give will be typically found inside a view. The documentation for view states that
Inside the view script, we can access the controller instance using
$this. We can thus pull in any property of the controller by
evaluating $this->propertyName in the view.
So, we have established that in the current context, $this->id refers to the CController::id property. This id will typically be the prefix of your controller's class name. For example, inside MyController you will have $this->id == "my".
With all this in mind, you can now tell that active will be true if and only if the current view is rendered from inside a controller named DefaultController. However, from the url property of that menu item we see that the associated controller for that action (assuming default routing) is PostController. So the $this->id == "default" idea is faulty.
If you want to make the "Home" item active whenever any action in your PostController is running, you should change this to $this->id == "post". Normally it should not be necessary to do this, because the activateItems property (which defaults to true) used with default routing will take into account both the controller id and the current action to determine which menu item to make active.
if you have some advanced link structure, put method below in Controller
/**
* Checks if the current route matches with given routes
* #param array $routes
* #return bool
*/
public function isActive($routes = array())
{
$routeCurrent = '';
if ($this->module !== null) {
$routeCurrent .= sprintf('%s/', $this->module->id);
}
$routeCurrent .= sprintf('%s/%s', $this->id, $this->action->id);
foreach ($routes as $route) {
$pattern = sprintf('~%s~', preg_quote($route));
if (preg_match($pattern, $routeCurrent)) {
return true;
}
}
return false;
}
//usage
'items'=>array(
array('label'=>'Some Label', 'url'=>array('some/route'),'active'=>$this->isActive(array(
'some/route',
'another/route',
)),
),
I used this way:
array('label'=>'Contact', 'url'=>array('site/contact'), 'active'=>strpos(Yii::app()->request->requestUri, Yii::app()->createUrl('site/contact'))===0)
The menu will work as we use default pages as site/contact, site/login
But many times it will not work for the module url.
suppose i have module user and i had login action in login controller, so i can do below thing form menu as how menu item gets active defined below.
array('label'=>Yii::t('frontend','Login'), 'url'=>array('/user/login/login'), 'visible'=>Yii::app()->user->isGuest), // Working, Login tag activated on login page
array('label'=>Yii::t('frontend','Login'), 'url'=>array('/user/login'), 'visible'=>Yii::app()->user->isGuest), // Not working, Login tag not activated on login page
So, we can have problem to beautify url..
For yiistarterkit just add this code in the _base.php file:
function isActive($routes = array())
{
if (Yii::$app->requestedRoute == "" && count($routes) == 0){
return true;
}
$routeCurrent = Yii::$app->requestedRoute;
foreach ($routes as $route) {
$pattern = sprintf('~%s~', preg_quote($route));
if (preg_match($pattern, $routeCurrent)) {
return true;
}
}
return false;
}
And then use:
echo Nav::widget([
'options' => ['class' => 'navbar-nav navbar-left'],
'items' => [
['label' => \Yii::t('frontend', 'Home'), 'url' => ['/'], 'active'=>isActive()],
['label' => \Yii::t('frontend', 'Services'), 'url' => ['/services'], 'active'=>isActive(['services'])],
]
]);
Take as a base the file yiisoft/yii2-boostrap/nav.php function renderItem():
...
if ($this->activateItems && $active) {
Html::addCssClass($options, 'active');
}
return Html::tag('li', Html::a($label, $url, $linkOptions) . $items, $options);
And modify $option on $linkOption for a link.
Html::addCssClass($linkOptions, 'active');
As result active class will be added to a.class, instead li.class
this is a handler for building menu
new MenuItem('Owner', lang('Owner'), assemble_url('Owner'), get_image_url('navigation/Company.gif')),
new MenuItem('Client', lang('Client'), assemble_url('Client'), get_image_url('navigation/people.gif')),
One system module class in which i've mapped the route
$router->map('Owner', 'Owner','null', array('controller' => 'companies', 'action' => 'index_owner'));
$router->map('Client', 'Client','null', array('controller' => 'companies', 'action' => 'index_client'));
which calls the controller class in which methods are defined with hte name index_client,index_owner...both method has same code.
function index_client(){
if(Company::canAdd($this->logged_user)) {
$this->wireframe->addPageAction(lang('New Company'), assemble_url('people_companies_add_client'));
} // if
if($this->request->isApiCall()) {
$this->serveData(Companies::findByIds($this->logged_user->visibleCompanyIds()), 'companies');
} else {
$page = (integer) $this->request->get('page');
if($page < 1) {
$page = 1;
} // if
list($companies, $pagination) = Companies::paginateActive($this->logged_user, $page, 30);
$this->smarty->assign(array(
'companies' => $companies,
'pagination' => $pagination,
));
} // if
} // index */
Which inturn calls smarty template named index_owner,index_client.
I want that only one template should be called that is "index" because same things are being displayed only one flag in template is checked "is_owner" and according to that display of company is done..please tell me how flow goes like handlers,controller,module,view ??????
You must assign the METHOD magic constant to smarty resource.
After you do this, customize Smarty::fetch method to catch and renderize if this attribute is seted.
If you has using url rewrite and method name is into url. You can get this through the Smarty.
This feature is native in zend framework mvc implementation. Check this.