Yii 2 nav widget visible vs accessible - yii

I have a yii\bootstrap\Nav, where I have several menu items. One of them is the logout. Consider these two examples.
$menuItems = [
[
'label' => 'Logout ('. Yii::$app->user->identity->username. ')',
'url' => ['/site/logout'],
'linkOptions' => ['data-method' => 'post'],
'visible' => !Yii::$app->user->isGuest,
],
]
vs
if (!Yii::$app->user->isGuest) {
$menuItems[] =
[
'label' => 'Logout ('. Yii::$app->user->identity->username. ')',
'url' => ['/site/logout'],
'linkOptions' => ['data-method' => 'post'],
];
}
My Application crashes with the error for the
Trying to get property of non-object
on the line with Yii::$app->user->identity->username.
I use the second solution which works fine, but can you explain why the code executes bypassing the 'visible' parameter for the first block.

In the second case you check for not a guest and this mean that
Yii::$app->user
is a correct objecy and then you can access to username
in first you use only the visible menuitem attribute this as described in doc mean
http://www.yiiframework.com/doc-2.0/yii-widgets-menu.html#$items-detail
Visible: boolean, optional, whether this menu item is visible.
Defaults to true.
this mean that this attribute manage the hide or show of the menu item. But in this case the code for user remain the same so based on fact that a guest don't crate a proper user object you have the rror for accessi a propert ofr a null object

In your first code block
Yii::$app->user->identity->username
change it to
(Yii::$app->user)?("Logout(".(Yii::$app->user->identity->username.")"):'Login'
NOTE: change url accordingly. visibility is not required to configure.
If there is no login user Yii::$app->user->identity->username statement cannot return username because there is no user identity exist (Yii::$app->user->identity is null)

Related

Yii how to highlight the current label

$this->breadcrumbs=array(
'Dailymarket Reports',
);
$this->menu=array(
array('label'=>'inbox', 'url'=>array('index')),
array('label'=>'sent', 'url'=>array('sent')),
);
Above I have two labels. I want inbox to be default active label if mouse moves out and selects sent as active it should be highlighted.
I think you are asking about how to show the menu item selected according to the current route if you are on Home page then the Home menu item should be selected.
Although you can see solutions like adding checks but if you provide full path to your link you won't have to add any further checks for activating the menu items, just make sure you are not passing activateItems=>false inside the CMenu widget in the layout file, otherwise it defaults to true and it decides whether to automatically activate items according to whether their route setting matches the currently requested route.
Go to your view where you are adding the menu specify a full path like /controller/action for your links for example if I have a menu for the site controller SiteController actions I will provide paths like below.
$this->menu=array(
array('label'=>'Home', 'url'=>array('/site/index')),
array('label'=>'Demo', 'url'=>array('/site/demo')),
);
and that's it you can make it work if you don't need to add separate checks for every menu item.
Just make sure you have a css class for li.active > a so that you can get the highlighted effect,
If you want to add active class for specified items:
$this->menu = array(
array(
'label' => 'inbox', 'url' => array('index'),
'active' => $this->action->id === 'index' ? true : null,
),
array(
'label' => 'sent', 'url' => array('sent'),
'active' => $this->action->id === 'sent' ? true : null,
),
);
You need to style items with active class yourself.
You may also omit specifying url for current item, so it will be displayed as text instead of link:
$this->menu = array(
array(
'label' => 'inbox',
'url' => $this->action->id === 'index' ? null : array('index'),
),
array(
'label' => 'sent',
'url' => $this->action->id === 'sent' ? null : array('sent'),
),
);

how to pass the id of the click CButtonColumn of CGridView going to the controller in Yii1.x?

I have a custom CButtonColumn within the CGridView.
one of the custom buttons is firing a CJuiDialog. now the problem is,
how to pass the id when clicked, so that the Controller will get the id, then I can do pass a model and renderPartial it inside the CJuiDialog?
here's what i have so far
'see' => array(
'label' => 'View',
'url' => 'Yii::app()->controller->createUrl("mycontrollerName/view,array("id" => "$data->id"))',
'click' => "function( e ){
e.preventDefault();
$( '#view-dialog' ).data('id',$(this).attr('id'))
.dialog( { title: 'View Details' } )
.dialog( 'open' ); }"
),
having given that code snippet.. in the controller action, I want to have the id ..is it $_GET['id'] ?, or $_POST['id'] ?..it doesn't matter for as long as I can get it so that I can use it to query in the model function
There are a few syntax errors in your code, but more importantly, you shouldn't wrap $data->id in any quotes.
'see' => array(
'label' => 'View',
'url' => 'Yii::app()->createUrl("mycontrollerName/view", array("id" => $data->id))',
'click' => "function( e ){
e.preventDefault();
$( '#view-dialog' ).data('id',$(this).attr('id'))
.dialog( { title: 'View Details' } )
.dialog( 'open' ); }",
),
So you are trying to pass the id value in the javascript code.
This is more of a jQuery issue rather than having much to do with Yii.
Run console.log($(this).attr('id')); you will probably see that you get an 'undefined' value. That is because the tag generated by Yii for the button does not contain an id parameter.
The easiest solution is to use jQuery to work with the url parameter.
e.g.
$( '#view-dialog' ).data('id',$(this).attr('href'))
if the entire URL is not needed, you could use a regex to parse only the numerical ID.
Alternatively you will have to pass the id in the buttons option parameter.
e.g.
'see' => array(
'label' => 'View',
'url' => 'Yii::app()->createUrl("mycontrollerName/view", array("id" => $data->id))',
'options' => array('id' => $data->id),
'click' => "function( e ){
...
However, please note that Yii will not render the value of $data->id in the 'option' parameter as this is not evaluated in CButtonColumn.
You will have to override Yii's CButtonColumn (see http://www.yiiframework.com/wiki/372/cbuttoncolumn-use-special-variable-data-for-the-id-in-the-options-of-a-button/)
Personally, if I were you, I'd implement some Javascript in some external code and have a regex to parse the id from the URL.

How to pass parameters to the rbac rule when using a permission name in yii\filter\AccesControl?

I want to limit all actions in a controller to the user who has a specific permission, in my case updatePost that is attributed to the author rĂ´le depending on an AuthorRule.
The controller aims at assigning translators to posts, not creating or updating the posts themselves.
The rule only verifies that the user is the creator of the post using a param whose name is author_id and value is the value of the author_id attribute of the post. Up to now, all this is common stuff.
I know I could check the
Yii::$app->user->can('updatePost', ['author_id' => <a value>])
function's result in each action. Nevertheless, I read in Yii's guide that the authorisation name (updatePost) could also be given in the behavior like this:
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'allow' => true,
'actions' => ['index','create', 'view', 'update'],
'roles' => ['updatePost'],
],
and that in this case the AuthorRule's execute method will be called.
My question is: " What is the exact syntax in this case to pass the author_id to the AuthorRule's execute function ?"
You can not pass parameters like that. I guess it's not prepared for parameters because usually you don't know the exact values during this step (these are usually passed as arguments to the action).
Move this check to the action or if you do know the value use matchCallback like:
'matchCallback' => function ($rule, $action) {
return Yii::$app->user->can('updatePost', ['author_id' => <a value>]);
}

Filter empty values in DetailView

Is there an easy way to force DetailView in Yii2 to ignore these fields in its attributes list, that for particular model are empty?
Or the only way is to define every attribute on attributes list with own function and filter empty fields inside it (sound like a little bit of madness)?
Edit: I thought, that this is pretty self-explanatory, but it turned out, it isn't. So, basically, I want to force DetailView to ignore (not render) rows for these elements of attributes list, that have empty (null, empty string) values in corresponding model and thus would result in rendering empty table cell:
You can define template parameter of DetailView widget as a callback function with following signature function ($attribute, $index, $widget) and this callback will be called for each attribute, so you can define desired rendering for your rows:
DetailView::widget([
'model' => $model,
'template' => function($attribute, $index, $widget){
//your code for rendering here. e.g.
if($attribute['value'])
{
return "<tr><th>{$attribute['label']}</th><td>{$attribute['value']}</td></tr>";
}
},
//other parameters
]);
Would something like this work better? It preserves some of the niceties like: updated_at:datetime, which with one of the solutions above will just show the underlying value, not a converted value.
<?= DetailView::widget([
'model' => $model,
'attributes' => [
'id',
[
'attribute' => 'my_attribute',
'visible' => !empty($model->my_attribute)
],
]
]);

Creating Prestashop back-office module with settings page

I'm creating a back-office module for Prestashop and have figured out everything except the best way to display the admin page. Currently I'm using the renderView() method to display the content of view.tpl.
I would like to display a table with values and an option to add a new row. Should I just create it in the view.tpl or is there a better way? I've seen the renderForm() method but haven't figured out how it works yet.
The biggest question I have is, how do I submit content back to my controller into a specific method?
ModuleAdminController is meant for managing some kind of records, which are ObjectModels. Defauly page for this controller is a list, then you can edit each record individually or view it's full data (view).
If you want to have a settings page, the best way is to create a getContent() function for your module. Besides that HelperOptions is better than HelperForm for this module configuration page because it automatically laods values. Define the form in this function and above it add one if (Tools::isSubmit('submit'.$this->name)) - Submit button name, then save your values into configuration table. Configuration::set(...).
Of course it is possible to create some sort of settings page in AdminController, but its not meant for that. If you really want to: got to HookCore.php and find exec method. Then add error_log($hook_name) and you will all hooks that are executed when you open/save/close a page/form. Maybe you'll find your hook this way. Bettter way would be to inspect the parent class AdminControllerCore or even ControllerCore. They often have specific function ready to be overriden, where you should save your stuff. They are already a part of execution process, but empty.
Edit: You should take a look at other AdminController classes, they are wuite simple; You only need to define some properties in order for it to work:
public function __construct()
{
// Define associated model
$this->table = 'eqa_category';
$this->className = 'EQACategory';
// Add some record actions
$this->addRowAction('edit');
$this->addRowAction('delete');
// define list columns
$this->fields_list = array(
'id_eqa_category' => array(
'title' => $this->l('ID'),
'align' => 'center',
),
'title' => array(
'title' => $this->l('Title'),
),
);
// Define fields for edit form
$this->fields_form = array(
'input' => array(
array(
'name' => 'title',
'type' => 'text',
'label' => $this->l('Title'),
'desc' => $this->l('Category title.'),
'required' => true,
'lang' => true
),
'submit' => array(
'title' => $this->l('Save'),
)
);
// Call parent constructor
parent::__construct();
}
Other people like to move list and form definitions to actual functions which render them:
public function renderForm()
{
$this->fields_form = array(...);
return parent::renderForm();
}
You don't actually need to do anything else, the controller matches fields to your models, loads them, saves them etc.
Again, the best way to learn about these controller is to look at other AdminControllers.