Chapter 8: The Flexigrid
The Flexigrid framework was the original framework incorporated into the admin interface. Curry supports it out of the box. However, the ModelView framework is now the preferred framework. In this chapter we shall learn to work with the Flexigrid. Let us study its pros and cons and then look at the Silva framework. Silva "inspired" the ModelView framework.
Let's create a module to manage (i.e. Add, Edit and Delete) quotes with the Flexigrid. We will provide the facility to create quotes from the backend just for demo purposes. This has nothing to do with the specifications of the demo project.
Create a new Php file named QuotesFlex.php in the cms/include/Project/Backend
path. Here is the skeleton code:
<?php
class Project_Backend_QuotesFlex extends Curry_Backend
{
public static function getGroup()
{
return 'Demo Flexigrid';
}
public static function getName()
{
return 'Quotes';
}
public function showMain()
{
}
}
The code does not create the grid as yet. However, if you refresh the admin you should see a new group icon created in the Module panel. That is created by the getGroup
method. When Curry_Admin
builds the admin interface it will query for all backend modules and group them before creating the Module panel. If you are interested in knowing how this is done see the Curry_Admin::show
method.
The getName
method is used to alias the module name when displayed in the module panel. By default, the name of the module is used.
You know why we have defined the showMain
. It's because the Cities module subclasses an abstract class Curry_Backend
and showMain
is declared to be an abstract method. It's simple OOPS concepts. Don't believe me? then comment the showMain
method and try rendering the module.
Below is the code to create the grid.
protected function getQuotesGrid()
{
$grid = new Curry_Flexigrid_Propel('Quote',
url('', $_GET)->add(array('json' => 1)));
$editUrl = url('', array('module', 'view' => 'Quote'));
$grid->addAddButton($editUrl);
$grid->addEditButton($editUrl);
$grid->addDeleteButton();
return $grid;
}
We then "insert" the grid into the "main view".
public function showMain()
{
$grid = $this->getQuotesGrid();
if (isset($_GET['json'])) {
Curry_Application::returnJson($grid->getJSON());
}
$this->addMainContent($grid->getHtml());
}
We create the grid object by instantiating Curry_Flexigrid_Propel
. This class is a subclass of Curry_Flexigrid
. It is Curry_Flexigrid
which is concerned with constructing the grid. The subclass is concerned with fetching and manipulating data in the grid. The advantage of separating the logic in this manner allows us to interface the Flexigrid with another ORM, for example Doctrine or Eloquent. The first argument to the Curry_Flexigrid_Propel
constructor is the model class name.
The second argument is a "callback" URL. Notice the json=1
query parameter used in the URL. When the JavaScript for the Flexigrid is inserted in the HTML, the script triggers (i.e. callback) an Ajax request to the specified URL to query the database and fetch data to show in the grid. We determine the trigger by checking for the json=1
parameter in the showMain
method. If this query parameter is found in the URL, we return the data in JSON format which is then rendered in the grid. Where is the querying done? To answer this question see the getJSON
method.
The other arguments to the Curry_Flexigrid_Propel
constructor are optional.
At this point, the Flexigrid should be showing in the view. If there is any data in the quote table, it should be listed in the grid. Even though we created the Add and Edit buttons, nothing "magical" happens when you click them. An error message about a missing view is shown though. The Delete function works. It's always the dangerous commands that work.
Before we deal with the Add/Edit functions, let's try to understand how the Flexigrid gets rendered. When a module's view is rendered we inject the Flexigrid's HTML and JavaScript into the HTML document. The Curry_Flexigrid::getHtml()
helper returns the proper HTML and JavaScript. We then inject it into the HTML document with this code $this->addMainContent($grid->getHtml())
. The figure below shows the Flexigrid's rendered HTML and JavaScript.
Once the document is ready or loaded, the JavaScript of the grid will make an Ajax POST request to the callback URL. Our module must be able to identify this request. One way to identify this callback request is by using some identifier in the URL. We use json=1
. You can use another parameter if you wish but json=1
makes more sense. When this callback occurs, our module must return data in JSON format to the grid. This data will then be rendered in the grid. The Curry_Flexigrid_Propel
class has a getJSON
helper that will fetch data from the database and prepare it in the proper format to return to the grid.
Now, we shall deal with the Add/Edit functionality. When we created the Add and Edit buttons we specified a URL. This kind of a button is known as a Link button. There are different types of buttons defined for the Flexigrid. You can see the code in Curry_Flexigrid
if you are interested. When a Link button is clicked, the Flexigrid makes a GET request to its URL. In the URL, we specified a view=Quote
query parameter. That parameter informs Curry to render or "execute" a view named showQuote
in the specified module
. So let's define a view named showQuote()
.
public function showQuote()
{
$quote = null;
// Edit function
if (isset($_GET['quote_id'])) {
$quote = QuoteQuery::create()->findPk($_GET['quote_id']);
}
// Add function
if (!$quote) {
$quote = new Quote();
}
$form = $this->getQuoteForm($quote);
if (isPost() && $form->isValid($_POST)) {
$this->saveQuote($quote, $form->getValues());
$this->returnPartial(''); // close the dialog-box
}
$this->returnPartial($form); // show form in the dialog-box
}
In the showQuote
view, we handle both the Add and Edit functions (aka. actions). Both are similar except that the Edit action has the primary key of the model instance to edit.
Here is the code to construct the form:
protected function getQuoteForm(Quote $quote)
{
$mf = new Curry_Form_ModelForm('Quote', array(
// Unlike ModelView, we need to pass the PrimaryKey of the model-instance
// in the action for "Edit" to work.
'action' => (string) url('', array('module', 'view', 'quote_id' => $quote->getPrimaryKey())),
'withRelations' => array('RecyclingType', 'ClientCity'),
'columnElements' => array(
'created_at' => false,
'updated_at' => false,
),
));
$mf->addElement('submit', 'save', array('label' => 'Save'));
$mf->fillForm($quote);
return $mf;
}
In the ModelView framework we didn't have to specify the form's action parameter. However, in the Flexigrid framework this is mandatory. At the bare minimum, we need to specify the module, view and the primary key of the model instance to edit.
The code to save the model instance is as follows:
protected function saveQuote(Quote $quote, Curry_Form_ModelForm $mf)
{
$mf->fillModel($quote);
$quote->save();
}
In the above example I have used Curry_Form_ModelForm
. In some scenarios where a model instance is not directly concerned you could also use Curry_Form
. Basically, to create one view in the Flexigrid we can divide the code into three portions:
- Render the grid
- Handle Add/Edit
- Construct the form and save data
I have tried to keep the code as simple and concise as possible, putting together just the bare minimum. Even then we have to write a lot of code to generate one simple view. If you had multiple views, then each view would have the three portions described above. Most of this code is repetitive and it would become very difficult to maintain such a module. It was necessary to find a solution to this problem. The solution I came up with was Silva. We shall discuss Silva briefly in the next chapter.