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.
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'm using Doctrine2 and CodeIgniter2, and am new to both, as well as to OOP/MVC, so please use simple explanations :)
For testing purposes, I have a Controller a Model and a View. I want to display data from a table that contains user information. First name, last name, ID number, and so forth.
My controller makes a call to the model- which retrieves data from the doctrine entity, and then the controller passes that data to the view.
(controller)
class Test_v_to_m extends CI_Controller {
public function index() {
$this->load->model('testing/test_v_to_m_model');
$data = $this->test_v_to_m_model->display_user_info();
$this->load->view('testing/test_v_to_m_view', $data );
}
}
(model)
class Test_v_to_m_model extends CI_Model{
public function display_user_name() {
$query = $this->doctrine->em->createQuery("select u from ORM\Dynasties2\Users u");
return $query->getResult();
(view)
//print_r($data);
First question is: How do I pass the object or array along to the view in a useful way? This works if I'm just dealing with a single variable:
(controller)
$user = $this->doctrine->em->find('Entities\User', $user_id);
$data['firstname'] = $user->getFirstName();
$this->load->view('testing/test_v_to_c_view_2',$data);
(view)
echo $firstname;
But I don't know how to do something similar when its an array, or a multidimensional array.
The second question is whether or not to let the view do any real work (php logic, loops, foreach, etc) or to do all of that in the controller and have the view only do formatting and display.
Yes, You can just pass multi-dimensional array to the view and then access it as required.
e.g.
$template_date['result_arr'] = array(
array('firstname' => 'abc', 'lastname' => 'xyz')
, array('firstname' => 'abc', 'lastname' => 'xyz')
);
in your view file -
foreach($result_arr as $key => $row) {
echo $row['firstname'].' <br />';
}
Re your 2nd question - As per my understanding - it's fine to use some foreach, for loops in the view but it's best if business logic is kept to controllers and models. Hope it makes sense to you.
As for your first question, I don't know the answer off the top of my head (sorry!). I would imagine, however, that an array can be passed as part of the data (as a single item), but you would need to iterate though it in the view (see below). Just a guess, however...
As for your second question, the principle of MVC is to have only display logic in the view - so all of the "real work" should be done in the controller.
Now, if you want to have a loop to display data in a table, that's "real work" being done in the view, but since it's part of formatting and display that would be acceptable.
Regarding your first question, it's actually quite simple:
$data = array(
'firstname' => 'string',
'array' => array(1, 2, 3),
'multidimensional_array' => array('ocean' => 'deep')
);
In the view, you can access these as:
$firstname;
$array;
$multidimensional_array;
They're just exported to the view, so you can treat each key in the $data array as a variable, and the values in the $data array as the variables' values.
Regarding the second question, it is generally best if you have the view only do formatting and display. In some cases, it might be useful to use ifs or loops, for example, if you want to display different messages based on a certain variable, or if you want to fill a table with a bunch of rows. However, I strongly recommend that you keep out as much logic as possible. The view is meant to receive all the data it needs and display it in a way that suits it.
There are plenty of reasons for this, namely maintainability (if your logic changes, you don't need to update the view), reusability (if you make views as general as possible, you can reuse them very easily) and even the ability to create new views or to replace that view with a different one, without worrying about the logic.
I hope this helps. :)
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'),
Is there a Zend Helper to generate a Html Table using and array as input ?
partialLoop() is probably best if you need a lightweight, easily customizable table generator. If you want something a little more to take all but the business logic of report generation in Zend, take a look at zfdatagrid.
Most of all I use partialLoop() to generate tables. But sometimes, for simple data that don't require formatting, I use my simple view helper: https://gist.github.com/812481 .
Usage:
<?php echo $this->table()->setRows($rows); ?>
or...
<?php echo $this->table(null, $rows); ?>
The $rows can be associative array or any object that has toArray method (Zend_Db_Table_Rowset, Doctrine_Collection etc.). Following is more complicated example, with headers, caption, additional column:
echo $this->table()
->setCaption('List of something')
->setAttributes(array('class' => 'mytable', 'id' => 'currenciesList'))
->setColumns(array(
'currency' => 'Currency',
'rate' => 'Rate',
'edit_options' => '' // Custom column
))
// content for custom column.
->setCellContent(
'Delete', 'edit_options'
)
->setFooter('Something to write in footer...')
->setEmptyRowContent('Nothing found')
->setRows($rows);
But this approach is not as convenient as partialLoop, cause it takes input data and display it as is - it doesn't allow you to format values using Zend_Date, Zend_Currency or do custom cell formatting.
There is no native table zend view helper. However, you could use partialLoop view helper to ease generation of tables.
You can also use a PEAR's HTML_Table package. You can throw an array to the table class and it is gonna populate the table for you. It has some nice features like colouring odd and even lines, set parameters per row, per columns and per table body.
Find more info at http://pear.php.net/package/HTML_Table/docs/latest/HTML_Table/HTML_Table.html
How would I go about creating a real world form creation class that I can use to display a new form with fields of different types, as how many fields I want, I can use drop downs and I can do all of this by using OOP?
To be honest I wouldn't roll my own, considering there are a few mature form packages out there for PHP.
I use PEAR's HTML_QuickForm package (http://pear.php.net/manual/en/package.html.html-quickform.php) for PHP4 sites.
For PHP5, I'd have a look into Zend_Form (http://framework.zend.com/manual/en/zend.form.html).
For my quickform code, I use a helper class that lets me define forms using a config array. For example:
echo QuickFormHelper::renderFromConfig(array(
'name' => 'area_edit',
'elements' => array(
'area_id' => array('type' => 'hidden'),
'active' => array('type' => 'toggle'),
'site_name' => array('type' => 'text'),
'base_url' => array('type' => 'text'),
'email' => array('type' => 'text'),
'email_admin' => array('type' => 'text'),
'email_financial' => array('type' => 'text'),
'cron_enabled' => array('type' => 'toggle'),
'address' => array('type' => 'address'),
),
'groups' => array(
'Basic Details' => array('site_name', 'base_url'),
'Address Details' => array('address'),
'Misc Details' => array(), // SM: Display the rest with this heading.
),
'defaults' => $site,
'callback_on_success' => array(
'object' => $module,
'function' => 'saveSite',
),
));
Note that the above element types 'address' and 'toggle' are in fact multiple form fields (basically, meta-types). This is what I love about this helper class - I can define a standard group of fields with their rules (such as address, credit_card, etc) and they can be used on lots of pages in a consistent fashion.
You definitely can. Consider a Form class which stores information about the form itself: the method, action, enctype attributes. Also throw in stuff like an optional heading and/or description text at the top. Of course you will also need an array of input elements. These could probably be put into their own class (though subclassing them for InputText, InputCheckbox, InputRadio maybe be a bit over the top). Here's a vague skeleton design:
class Form {
var $attributes, // array, with keys ['method' => 'post', 'action' => 'mypage.php'...]
$heading,
$description,
$inputs // array of FormInput elements
;
function render() {
$output = "<form " . /* insert attributes here */ ">"
. "<h1>" . $this->heading . "</h1>"
. "<p>" . $this->description . "</p>"
;
// wrap your inputs in whatever output style you prefer:
// ordered list, table, etc.
foreach ($this->inputs as $input) {
$output .= $input->render();
}
$output .= "</form>";
return $output;
}
}
The FormInput class would just need to store the basics, such as type, name, value, label. If you wanted to get tricky then you could apply validation rules which would then be converted to Javascript when rendering.
I will go against other advice here and suggest that you build your own library to generate forms. If you fail, you will still learn a lot in the process.
The design process is most important here. You start from the top and ask yourself what goes on a form. At an abstract level, a form is full of elements. Some are visible, some are not, some can be entered by the user but others cannot, some elements can trigger other elements... and the list goes on...
Eventually you end up with elements that are "decorative" (Text, Headings, Separators, Fieldsets, Links, Images), elements that are interactive (Inputs, Dropdowns, Checkboxes, Radio buttons, Submit Buttons) and finally elements that are neither decorative nor interactive (Hidden Inputs, Anchors and elements that act as containers to group other elements.)
Once you have the different categories organised you start looking into features that all elements have and you can put that into the base element class. Then you go up the chain making your classes doing more and more, inheriting from other simpler element classes. In my library, the base element class is called form_element and each form_element has a unique name that no other element within the same form can have. A form_element also has a set of attributes. It has a function that all elements have called render(). In the base class render() does nothing (so a base element is always invisible) but in derived classes it starts producing HTML. By the way, I never make any of my classes create HTML. Instead I have a static class called html which writes HTML for all the classes that needs its services.
Very early in the chain of form elements, you should have one, a container that groups others. It should have an add() function and its render() function should consist of calling the render() function of all its sub-elements. the form class will be derived from this container class.
Spend plenty of time on the design. Pay attention to compatibility with the rest of your library.
If you want the data from the form to come from a database and be saved to one, you will need to add this functionality and have a form element class linked to a table and column. Here too, I have a separate DB class that can retrieve/save the data. I have a query class that creates queries. Form elements should have nothing to do with creating HTML, creating queries or accessing a database. My static class DB and my query class take care of the dirty work. The form class should only be involved with form stuff. The form class collects into an array all the tables and columns for the fields that need to be saved and pas it to the query class which creates the query which is then passed to the DB class which executes it.
Once you are properly setup, what appears to be horrendously complicated suddenly becomes very easy with properly designed classes.
Because you have a class that can write HTML, your form class needs to just html::init() and follow it with render() and the entire HTML code for the form is available within the html buffer. html::output() flushes everything out.
Validation is also handled externally with a static validation class. Form elements that can be validated hold validation instructions within an array in a format that can be passed directly to the validation class. Each element that needs to be validated is bound to an error element which displays the error if the element does not validate or remains invisible if all goes well.
This is to show you that when you design a form environment (or anything else) you really need to consider absolutely everything before you get started. The work that you put into it may not immediately translate into code that can power your application but it will sure make you a much better developer, thus making your future projects much easier to handle.
The form class creates a form, the html class creates the HTML, the query class makes queries and the DB class handles the database. If your classes start doing work that should be done by separate classes, you have a design problem.
Here is a code sample to show how my form library works:
$fm = new form('myform');
$fm->binding(FORM_DATABASE);
$fm->state(FORM_RETRIEVE);
$fm->set_recno(1);
$fm->add(new form_heading("My form"));
$fm->add($el=new form_input("name",40));
$el->bind_data('mytable','mycolumn');
$el->set_attribute('size', 25);
$el->set_default('Name');
$fm->add($el=new form_submit("submit_btn","Submit"));
if($fm->manage())
{
redirect or do something else here. The interaction with the form is done. The initial state for the form was FORM_RETRIEVE. If it had been FORM_NEW it would have displayed default values instead of the retrieved record and saved the form as a new record in the table.
}
Note that the manage() function of the form takes care of absolutely everything, retrieving data from the database, rendering the form into the view, validating data and saving it back to the database.
One of the advantages of creating forms programmatically (as above) is the option to write your own form-based code generator to create the code to make your forms.
I hope this can help you or someone else.
Just for reference, Object Oriented Forms by Khurram Khan is an excellent OO forms implementation for PHP.
Here is a sample of what the code looks like:
$form = new Form("Register", "form.php");
$personal = new Block("Personal Information");
$name = new Text("name", "Your name");
$name->setDescription("this is my description");
$name->addValidator(new MaxLengthValidator("The name you have entered is too long", 30));
...
Another more popular implementation is PHPlib. However, I find this to be a bit clunky; it seems like it's just some standard functional programming wrapped in a class.
Another option would be writing an abstraction for the built in DOM library. This will allow you to manually create any kind of form and form element using OO notation, with the added benefit that you will be returned an OO DOM instance that can be used elsewhere in your program.
You definitely should use OO PHP to do forms, and all the rest of your HTML output. I could not find any PHP library (many of the links in these answers are dead) to do what I wanted, so I wrote PHPFUI. It is not a generic HMTL output library, but outputs pages for the Foundation CSS Framework. You could easily use the same technique to output a more vanilla page, or Bootstrap or what ever. I did not want to write a generic HTML OO PHP library, as I wanted something lean and mean for performance reasons. Also I don't like to over engineer stuff, so it is hard coded to Foundation. But the same principles would apply to any PHP library that would want to output clean HTML with no validation issues, which you often find in hand written HTML.