I'm new to the Yii framework and I'm trying to access a property of the of a widget let's say CMenu where I want to change the values of some public properties like activeCssClass, firstItemCssClass, lastItemCssClass, htmlOptions etc. so how do you change the property of a widget in Yii.
Details:
I'm using Yii version 1.1.12 (Aug 19, 2012) and I'm trying to generate a multilevel menu but I need to change the values of some public class parameters and I don't know how?
Well, normally you apply needed values when you call the widget. You set them at the appropriate array inside widget call after widget class name.
$this->widget('zii.widgets.CMenu',
array(
'items' => $items,
'id' => 'main_menu',
'htmlOptions' => array('class' => 'nav'),
'activeCssClass' => 'active',
'firstItemCssClass' => 'first_item'
)
);
BUT! If you want to apply the values after you created some widget, but have not rendered it yet (really rare case) you can do this thing:
$widget = $this->beginWidget('application.components.MyOwnWidget');
$widget->public_property = 'aaa';
$widget->renderSomething();
$this->endWidget();
Adding on the previous answer, in case you missed it, don't forget to check the short and simple official documentation on this.
Related
I have one view in Drupal 7, which displays user information like (Name, address, Status, etc..). I have one column in this (Table)view as "Published event". Basically events are created by users do I want to make this column sortable. I have attached image for more reference.
I tried with applying relationship but no success.
table settings
my handler code is like below :
$handler->display->display_options['sorts']['event_count_published'] ['id'] = 'event_count_published';
$handler->display->display_options['sorts']['event_count_published'] ['table'] = 'search_api_index_user_search_index';
$handler->display->display_options['sorts']['event_count_published'] ['field'] = 'event_count_published';
$handler->display->display_options['sorts']['event_count_published'] ['order'] = 'DESC';
'mail' => array(
'sortable' => 1,
'default_sort_order' => 'asc',
'align' => '',
'separator' => '',
'empty_column' => 0,
),
'event_count_published' => array(
'align' => '',
'separator' => '',
'empty_column' => 0,
'sortable' => 1,
),
above code is in "tcd_reporting.views_default.inc" file, if I put 'sortable => 1', it still does not provide sorting
field is created by below code:
$properties['event_count_published'] = array(
'label' => t('Published Events'),
'description' => t('Number of published events authored by user.'),
'type' => 'integer',
'getter callback' => 'tcd_event_content_type_count_published_get',
'computed' => TRUE,
'entity views field' => TRUE,
);
[Introduction] Which function is responsible for 'click sort' in views?
Click sort -this checkbox from your second screen- in views table settings is function which is enabled only for fields which have properly defined handlers. As you may know each field in views have few handlers (for displaying, filtering, sorting). And for click sort to be possible on specified column its field handler must have two functions defined: click_sortable and click_sort. First one just need to return true, while second need to properly implements sorting on view. For example see handler: [views_module_path]/handlers/views_handler_field.inc.
Your case:
It seems that your column "Published event" have defined handler which does not have click_sortable and click_sort functions (or click_sortable simply returns false).
Possible fix:
Find place where you defined your view source (it depends on how you informed views about it, if I understand its something like "User info" - maybe in hook_entity_info function or hook_views_data function), check what handler is assigned to your "Published event" field and change it.
It's hard to tell where you need to look as it depends on your implementation.
I suggest you to try create hook_views_data_alter function and dpm() it for start. Later you can alter it like that:
mymodule_views_data_alter(&$data) {
$data['some_view_info']['published_event']['field']['handler'] = 'views_handler_field_numeric';
}
Edit 1
First could you tell where this code is? Is it inside handler class, or maybe some views hook? Views gives you a lot of flexibility but this make them hard to understand, and I'm not sure what exactly you achieve and how.
Assuming your field works properly you can try to simply enable click sort.
Example: I created hook_views_data_alter function to see content of views data
function mymodule_views_data_alter(&$data) {
dpm($data,'d');
}
You might need to clear cache to see dpm of *_alter hooks.
Inside dpm'ed array I found "users" for generic example, and its field name looks like this:
I suggest you to try alter your field with click_sortable = TRUE and see what happens. If this wont help please provide more information about your field, how you created it, how it looks in hook_views_data_alter and which handlers it has defined.
Edit 2
Ok, so you have your views exported to code into views_default file. But this only allows you to export view you created from database to code, so it is basically a reflection of what you done in views web editor (eg. page yourwebsite.com/admin/structure/views/view/your_view_name/edit). What you need to do is to change behavior of one of your fields so it became sortable (add click_sortable and click_sort functions in handler class) or change handler of this field to one with sorting option (change field handler to other one like views_handler_field_numeric). If you don't have experience in creating handlers and this is one of generic handlers i suggest you to go back to my Edit 1, examine your dpm, and try to alter $data array to find solution.
Edit 3
Little explanation to prevent confusion. When creating new view you select collection on which this particular view base on (simpliest example - it may be MySQL table, and view will use SQL queries to retrieve data from it). By digging down we have:
Collection - eg. User which is database table user, it is what you select as source when creating new view.
Field - eg. mail which is database column mail, this fields you add to your view.
Field handler - eg. views_handler_field_numeric, this is class name of handler to use by specified field
Now, if you don't created your own handler then your field "Published event" have one of generic views handler. You shouldn't ever change code of contributed modules - especially so widely used as views handlers. That's why my suggestion to add functions click_sortable and click_sort is incorrect. Instead you should change handler responsible for field "Published event".
Best way is to define proper handler in place where you define your field "Published event". If it's somehow impossible the only way I can think of is hook_views_data_alter see docs for more info and examples. I suppose you should try to redefine handler of your field to generic numeric handler views_handler_field_numeric as it should have full sorting functionallity, or try to add click_sortable property to field array as you can see in first image of my post, but I can't provide you fully tested example.
I am changing the data provided by my model in the afterFind() method, so the id is clickable text, like this:
$this->id = CHtml::link($this->id, array('/admin/auditTrail/view', 'id' => $this->id));
However, this changes every single occurrence of id value - it is in lists, in detail page, even in breadcrumbs, which is unwilling of course. How can I decide which format I will use in different views? For example, in breadcrumbs and in view.php I just want the raw value, but in the list (admin.php) I would like to use the html link, like this:
'columns'=>array(
array(
'name' => 'id',
'type' => 'html',
),
On a separate note - is this a good approach in terms of MVC, I mean changing display in a model? Should not be model only used for a database manipulation stuff?
No, this is not a good approach. There are a number of alternative approaches that would be better, for example:
Let the view do the conversion
Don't do anything in the model. When the view wants to display clickable anchors instead of bare ids, it should generate the URLs itself. This is somewhat quick and dirty (it puts the logic of URL generation in the view, which is not ideal) but it's easy and it works well if you only need to do it in one or two places.
Expose the URL as a separate property
Place a calculated read-only property in your model:
public function getAuditTrailUrl()
{
return Yii::app()->createUrl('/admin/auditTrail/view',
array('id' => $this->id));
}
You can then use the auditTrailUrl property in any view. The nice thing about this approach is that the URL generation is opaque to the views and therefore easily modifiable.
You can use this syntax to easily render links from these URLs in your CDataGrid:
'columns' => array(
array(
'class' => 'CLinkColumn',
'urlExpression' => '$data->auditTrailUrl',
)
),
In the above definition $data refers to each model in the grid, as per the documentation.
I am writing an app that allows users to modify and change some of their site settings. I am just constructing a form generator that will send various options to variuous plugins to generate the code what I am wondering is whether I should be using objects for this rather than multidimensional arrays? If so how would I change my code?
So right now I have made this- its very long and going to get longer so I have only pasted part of it for the sake of brevity:-
$scopeSettings = array(
'site_background' => array(
'subpanels' => array(
'colour' => array(
'plugins' => array(
'colourchooser' => array(
'tip' => "The background colour appears underneath the 'Background Image' (if set)-hover over the '?' around the colour chooser for extra tips on how to use it",
'element' => 'body',
'gradientenabled' => 'true',
'opts' => array (
'closed' => 'true',
'advanced' => array(
'tip' => "You can paste in your own generated gradient codes in here",
'checkbox' => true
)//end advanced
)//end Opts
)//end colour chooser
)//end plugins
),//end colour sub panel
'pattern' => array(
'plugins' => array(
'patternselector' => array(
'tip' => "Use the pattern selector to apply effects like moire or scan lines to your background image",
'element' => 'patimg'
)//end patternselector
)//end plugins
),//end pattern sub panel
)//end subpanels
)//end site background
);//end scope settings
What would be best practice with this sort of thing?
Maybe this is a stupidity, but you can use "YAML" or "JSON" as configuration format for an application no?
As for example Symfony or other framework.
My advise: try YAML or XML or JSON to get a more readable config file, then parse it back to an array in your own code.
I would store the settings in <insert your markup language of choice> (XML, JSON, YAML, etc.).
You can then cache these in a $_SESSION variable, and populate it when you bootstrap if they don't already exist:
session_start();
if (!isset($_SESSION['settings'])) {
// Assuming you choose JSON...
$settings = json_decode(file_get_contents('settings.json'), TRUE);
$_SESSION['settings'] = $settings; // array
$_SESSION['settings'] = (object)$settings; // object
}
Whether or not you use an array or object then becomes just a matter of what access syntax you prefer:
$_SESSION['settings']['site_background']['subpanels']['colour']...
// vs.
$_SESSION['settings']->site_background->subpanels->colour...
I would say that some like Array Oriented Programmation and that, with PHP 5.4 they can express themselves in a great way.
However, more people are used to OOP -so do I- and It can be a more readable way to code your solution.
I think in this route I would go with a structured object oriented route. You can have a parent object, that holds all of the children objects (setting groups) and you can even retrieve on setting group as an object of its own. Each object will have its own definitions and properties which can be documented in the code that provides useful information within an IDE (if you use docblocks).
I would use objects. Assuming you are making a form, you should have several classes:
Form - to hold the form, will have a property $input_list (or $node_list to hold all of the inputs
Input - to describe a single input item, it should have properties like label, type, tip etc.
Fieldset - to describe a fieldset, to hold additional items inside. Like the Form class, it would have an $input_list to hold all of the inputs inside of it.
Those classes can stand alone, and can be extended to have customized common input types (for instance)
class Checkbox extends Input {
public $type = 'checkbox'
....
}
As other's people here also my opinion is to use YAML or JSON - using this it could be done in very simple way.
Just for instance an example of JSON format of Your data structure:
var settings = {
'site_background' : {
'subpanels' : {
'colour' : {
'plugins' : {
'colourchooser' : {
'tip' : "The background colour appears underneath the 'Background Image' (if set)-hover over the '?' around the colour chooser for extra tips on how to use it",
'element' : 'body',
'gradientenabled' : 'true',
'opts' : {
'closed' : 'true',
'advanced' : {
'tip' : "You can paste in your own generated gradient codes in here",
'checkbox' : true
}//end advanced
}//end Opts
}//end colour chooser
}//end plugins
},//end colour sub panel
'pattern' : {
'plugins' : {
'patternselector' : {
'tip' : "Use the pattern selector to apply effects like moire or scan lines to your background image",
'element' : 'patimg'
}//end patternselector
}//end plugins
}//end pattern sub panel
}//end subpanels
}//end site background
};//end scope
You can use PHP functions like json_encode and json_decode for JSON <-> PHP data transformation. Using curly braces means that the elements are objects while when replaced by [ and ] you got arrays...
But also a PHP OOP approach could be used succesfully especially when using extendability. You could have one main class Settings having some default properties and e.g. magic __get and __set functions and then You can implement many subsettings subclasses extending from this main Settings class.
I would like to change the the labels of pages in Yii.
I used Zii.widegt.CListView to show the list of items. The default structure of yii pagination is [previous] 1 2 4 5 6 7 [next] required structure is < 1....10 11 12 13 14 ....40 >.
I read "How can I customize the labels for the pager in Yii?" which is helpful, but how can I show the firstPageLabel as page number 1 instead of << and lastPageLabel as 40 instead of >>.
If you can't find a way to pass in the total item count (i.e. 40) to the lastPageLabel override, you will need to override the CLinkPager class to have this work automatically. The $lastPageLabel is static in the current implementation and does not provide access to variables like "itemCount". You can see the code:
$buttons[]=$this->createPageButton($this->lastPageLabel,$pageCount-1,self::CSS_LAST_PAGE,$currentPage>=$pageCount-1,false);
It just echos $this->lastPageLabel, which is static text.
If you make a new pager (called, say, MyLinkPager), use it like so:
$this->widget('zii.widgets.CListView', array(
'dataProvider' => $categoryProjects,
'itemView' => '_itemDetailsView',
'ajaxUpdate' => false,
'pager' => array(
'class' => 'MyLinkPager', // here is your pager
'firstPageLabel' => '<<',
'prevPageLabel' => '<',
'nextPageLabel' => '>',
'lastPageLabel' => '>>',
),
));
You will have to create your own class that derives from CLinkPager. Ultimately, what you want to achieve is to change the line that thaddeusmt mentions, inside CLinkPager::createPageButtons:
$buttons[]=$this->createPageButton($this->lastPageLabel /* the rest doesn't matter */);
to do the equivalent of
$buttons[]=$this->createPageButton($pageCount /* the rest doesn't matter */);
Now obviously the direct way of doing this is by overriding createPageButtons, but that's not a trivial method and if you do override it completely, you risk your pager becoming "out of sync" with code on later versions of Yii. So let's look for alternatives.
Alternatives
(you might want to skip this part if you 're only interested in the solution)
One alternative would be to override the method, have it call the standard implementation and then simply change what you need to change:
protected function createPageButtons() {
$buttons = parent::createPageButtons(); // Yii's implementation
array_pop($buttons); // remove last item, which is the link for the last page
$buttons[]=$this->createPageButton($this->getPageCount() /* the rest unchanged */);
return $buttons;
}
That's better, but it still involves copy/pasting code so your implementation needs to keep that part in sync with future Yii releases. Can we do better than that? It turns out that yes. Here's the method CLinkPager::run:
public function run()
{
$this->registerClientScript();
$buttons=$this->createPageButtons();
if(empty($buttons))
return;
echo $this->header;
echo CHtml::tag('ul',$this->htmlOptions,implode("\n",$buttons));
echo $this->footer;
}
As you see, CLinkPager doesn't really do a lot other than call createPageButtons. So you could override run and dynamically set the value of $this->lastPageLabel before letting Yii's code run, like this:
public function run()
{
$this->lastPageLabel = $this->getPageCount();
parent::run();
}
Well, this is nice. We managed to achieve the goal by overriding just one method and writing two lines of code. As an added bonus, there's nothing in our code that needs to be kept in sync with Yii if the implementation of CLinkPager changes in the future.
On the other hand, all of these solutions introduce an impurity that could be problematic: when someone writes a view that uses our custom pager class, they might not know that we are actually overriding the value of lastPageLabel! Imagine the "why is it not outputting the label I 'm telling it to?" confusion.
A really nice solution
Fortunately, you can have your pie and eat it too by overriding CLinkPager::init like this:
public function init()
{
// "Hijack" the default values for properties that the user did not set.
// This allows the user to still override this if they want to.
if($this->nextPageLabel===null)
$this->nextPageLabel='<';
if($this->prevPageLabel===null)
$this->prevPageLabel='>';
if($this->firstPageLabel===null)
$this->firstPageLabel='1';
if($this->lastPageLabel===null)
$this->lastPageLabel=$this->getPageCount();
// and let Yii do the rest like it always does
parent::init();
}
You can then configure your view to use this pager, and everything will work just fine without any further ado:
'pager' => array('class' => 'CustomLinkPager'),
How can I implement AJAX calls to my custom module's one of the field. My requirement is when we change some dropdown, it should check in the database and update the result in another field in same screen.
For normal modules, i See the .tpl files where I can insert script code and call. But for custom modules I didnt see the .tpl files except the cache directory (which are generating runtime).
Is there any method to implement.
I am using Sugar Professional.
Here are the steps you need to follow
Create a .php file which will read the values with query string and process the result as echo/print.
Go to editviewdefs.php in your custom module directory (eg. /custom/modules/...)
Find your field name and call a javascript function
array (
'name' => 'days_required_c',
'label' => 'LBL_DAYS_REQUIRED',
'displayParams' =>
array (
'field' =>
array (
'onChange' => 'setRenewalDate();',
),
),
),
Here RenewalDate is your javascript function name.
Create the script function which will call AJAX functionality like below.
var connectionObject =
YAHOO.util.Connect.asyncRequest ("GET", "getnextnum.php", callback);
YAHOO.util.Event.onContentReady("EditView", function () {
ldelim
}
initEditView(document.forms.EditView);
//alert("Loaded");
//document.getElementById("keyid_c").value = initCall();
{}
);