Data-grid

Christopher Pitt
Arsenal Spotlight
Published in
7 min readAug 14, 2014

If there’s one thing I’ve built countless times, it’s a list view. You know the one I’m talking about. Rows of data, column headings you can click to change order, fields with which to filter.

The first few times it’s quite exciting, but after that it just becomes tedious. What makes it worse is that these kinds of lists/tables/grids often require both PHP and JavaScript to get right. Sure, you can reload the page on every interaction, but what if there are only 50 or 20 or 5 items and you want to filter, or change the order?

Today I’m going to tinker with the Data-grid component, from Cartalyst. A quick study of the documentation shows that it handles the PHP and the JavaScript side of things, and has neat integration with Laravel, so let’s dive in.

The code for this chapter can be found at https://github.com/formativ/tutorial-cartalyst-data-grid. It requires PHP 5.5 or greater.

If you spot any errors or typos, please comment on this post, or create issue at the repository.

Installing

Cartalyst packages are installed from a custom repository, so that’s got to be added to your composer.json file:

"repositories" : [
{
"type" : "composer",
"url" : "http://packages.cartalyst.com"
}
]

This is from composer.json

Follow this up by heading over to your terminal and running the following command:

❯ composer require "cartalyst/data-grid:2.0.*"

./composer.json has been updated

Loading composer repositories with package information
Updating dependencies (including require-dev)
- Installing symfony/http-foundation (dev-master d17938f)
Cloning d17938f07e168c4e5bb35c209d3c289ccff15d80

- Installing illuminate/support (dev-master 6fe2f4f)
Cloning 6fe2f4fd2250f9e5b490368734368a5189a17d74

- Installing cartalyst/data-grid (dev-master 5bb4a69)
Cloning 5bb4a6931ab2e45c4e22e0e155a973b52c475542

cartalyst/data-grid suggests installing dompdf/dompdf...

Writing lock file
Generating autoload files

This will add data-grid to your dependency list, and download it and all of its dependencies.

Getting Started

Let’s start things off simply. We’ll fetch weather data, from Yahoo! Weather RSS, and present it in a simple, static table.

The Data

Getting this data is fairly straight-forward. We do it by hitting an RSS feed, including the unit of measurement and WOEID:

$url = "http://weather.yahooapis.com/forecastrss?u=c&w=1591691";
$rss = file_get_contents($url);

This is from weather.php

This will contain a whole bunch of XML data, but the only things we’re interested in are these elements:

<yweather:forecast day="Mon" date="11 Aug 2014" ... />
<yweather:forecast day="Tue" date="12 Aug 2014" ... />
<yweather:forecast day="Wed" date="13 Aug 2014" ... />
<yweather:forecast day="Thu" date="14 Aug 2014" ... />
<yweather:forecast day="Fri" date="15 Aug 2014" ... />

So…what we need to do is load the RSS data up into a DOMDocument instance, and extract the forecast elements:

$document = new DOMDocument();
$document->loadXML($rss);

$namespace = "http://xml.weather.yahoo.com/ns/rss/1.0";

$elements = $document
->getElementsByTagNameNS($namespace, "forecast");

$data = [];

foreach ($elements as $element) {
$data[] = [
"date" => $element->getAttribute("date"),
"low" => $element->getAttribute("low"),
"high" => $element->getAttribute("high"),
"conditions" => $element->getAttribute("text")
];
}

This is from weather.php

The w=1591691 is the WOEID (Where On Earth ID) value for my home town. You can find yours by searching at http://woeid.rosselliot.co.nz. The u=c tells the feed that I want celsius units of measurement.

The Grid

Compared to getting and formatting the data, creating the grid is relatively straightforward:

require("vendor/autoload.php");

use Cartalyst\DataGrid\DataHandlers\CollectionHandler;
use Cartalyst\DataGrid\Environment;

$grid = new Environment(null, [
CollectionHandler::class => function ($data) {
return is_array($data);
}
]);

print $grid->make(
$data,
["date", "low", "high", "conditions"],
["sort" => "date", "direction" => "asc"]
);

This is from weather.php

The official documentation shows a slightly more complex example, which involves loading a bundled configuration file. I found that this wasn’t strictly needed. Instead I aliased the CollectionHandler class and provided it as the only handler (in that second parameter spot).

The grid is created in two steps. An environment is first created. This environment can be used to render multiple grids, while sharing the same HTTP request and handlers. Handlers are like formatters that are made to handle a specific kind of iterable thing. In this instance, we can use a handler which ships with Data-grid, to handle arrays.

These arrays are wrapped in an instance of Illuminate\Support\Collection, which is just a Laravel-based array container. You can learn more about it at http://jenssegers.be/blog/51/laravel-collections-are-awesome.

The first parameter, when creating a new grid environment, is the HTTP request object to use. This should be an instance of Symfony\Component\
HttpFoundation\Request
, but if you give null instead, a request will automatically be created.

The second step is to use the environment to create the grid response. I’ve provided simple parameters; data array, list of columns and a few additional settings (column to order and direction).

If you run this it will actually output JSON data, so technically speaking it’s not the grid itself, but rather a formatted representation of what the grid should look like

The Markup

Data-grid is structured so that you decide what markup to use. There is a JavaScript plugin to handle things like asynchronous data retrieval, ordering and filtering. To get this working, we need to include a few scripts and stylesheets:

<script src="//ajax.googleapis.com/ajax/libs/jquery/
1.9.1/jquery.min.js"></script>

<script src="/vendor/cartalyst/data-grid/public/
js/underscore.js"></script>

<script src="/vendor/cartalyst/data-grid/public/
js/data-grid.js"></script>

<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/
bootstrap/3.2.0/css/bootstrap.min.css" />

<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/
bootstrap/3.2.0/css/bootstrap-theme.min.css" />

This is from index.php

In this code we’re linking to scripts in the vendor folder. This isn’t a normal thing, and what you’ll want to do (in a production environment, at least) is host these files from a public folder. Then we should create the container element:

<table class="table results"
data-grid="main"
data-source="/weather.php">
<thead>
<tr>
<th>Date</th>
<th>Low</th>
<th>High</th>
<th>Conditions</th>
</tr>
</thead>
<tbody></tbody>
</table>

<script type="text/template"
data-grid="main"
data-template="results">
<% _.each(results, function(r) { %>
<tr>
<td><%= r.date %></td>
<td><%= r.low %></td>
<td><%= r.high %></td>
<td><%= r.conditions %></td>
</tr>
<% }); %>
</script>

<script type="text/template"
data-grid="main"
data-template="no_results">
<tr>
<td colspan="4">No results</td>
</tr>
</script>

This is from index.php

The HTML table is where the data will be rendered (when fetched from the JSON data we just created). The two templates are for when there is valid data and when there is no valid data. By now you should be seeing a pattern: the interface is completely up to us. There are no strict rules governing what markup/classes to use. Let’s finish the markup/templates, by adding markup/templates for filter text and pagination:

<ul class="filters" data-grid="main"></ul>

<script type="text/template"
data-grid="main"
data-template="filters">
<% _.each(filters, function(f) { %>
<li>
<% if (f.column === "all") { %>
<%= f.value %>
<% } else { %>
<%= f.value %> in <%= f.column %>
<% } %>
</li>
<% }); %>
</script>

<ul class="pagination" data-grid="main"></ul>

<script type="text/template"
data-grid="main"
data-template="pagination">
<% _.each(pagination, function(p) { %>
<li data-grid="main" data-page="<%= p.page %>">
<%= p.page_start %> - <%= p.page_limit %>
</li>
<% }); %>
</script>

This is from index.php

Lastly, we need to add a single line of JavaScript, to wire everything together:

<script>
$.datagrid("main", ".results", ".pagination", ".filters");
</script>

This is from index.php

To view this page, from a browser, start up a local PHP server:

❯ php -S 127.0.0.1:8000 -t .

PHP 5.5.15 Development Server started at Mon Aug...
Listening on http://127.0.0.1:8000
Document root is /.../tutorial-cartalyst-data-grid
Press Ctrl-C to quit.

Now, when you put that address in your browser, you should see a functional data-grid!

Filtering

We’ve got a simple table/grid, so now we can start adding filters and ordering. Let’s add a few buttons to change the order:

<div data-grid="main">
<button data-reset>
All
</button>
<button data-reset data-filter="conditions:Sunny">
Filter Sunny
</button>
<button data-reset data-filter="conditions:Showers">
Filter Showers
</button>
<button data-reset data-filter="low:15:20">
Low 15 - 20
</button>
<button data-reset data-filter="high:15:20">
High 15 - 20
</button>
</div>

This is from index.php

Here we’ve grouped all the filter buttons in the same div, so that they can share the data-grid attribute. We can have them anywhere on the page, but if they don’t share a data-grid attribute (from a parent element) then they need their own.

You’ll also notice each has its own data-reset attribute. This is s that they will each clear the current filters when clicked, and before applying their own filters.

Finally, the data-filter attribute is what does the actual filtering. There are two different kinds of filters displayed here. The first is a text filter (column:value) and the second is a range filter (column:lower:upper). Without the data-reset attributes, you can chain multiple filters. As you click them, you should see the .filters element update to show the currently applied filters.

Searching

Searching is quite similar to filtering, except that the input is less restrictive. You can, for instance, design a search form that will apply the applicable filters to grid data:

<form method="post" data-search data-grid="main">
<select name="column">
<option value="all">All</option>
<option value="date">Date</option>
<option value="low">Low</option>
<option value="high">High</option>
<option value="conditions">Conditions</option>
</select>
<input name="filter" type="text">
<button>Add Filter</button>
</form>

This is from index.php

The data-search attribute will turn this form into a grid filter form. When you click the button, anything typed into the input will be applied (in conjunction with the selected column) to the current filters. You can omit the select element, and the filter will be for all columns.

Repeated searches, on the same column, will clear previous searches. You can combine searches of multiple columns together.

Sorting

Sorting is just as easy as filtering! You can add buttons to each column header, or condense popular filters into a few buttons:

<a data-sort="date:asc" data-grid="main">
Soonest
</a>
<a data-sort="low:asc" data-grid="main">
Coldest
</a>
<a data-sort="high:desc" data-grid="main">
Hottest
</a>

This is from index.php

These links will alter the order of rows, without affecting the filters applied to the rows. There’s an interesting side-effect of these sort links; if the second click toggles the direction and the third disables the sorting. This is like column header links that have the same behaviour.

Official Documentation

You can find the official documentation at https://cartalyst.com/manual/data-grid and you can also find out more about Cartalyst at https://cartalyst.com.

Disclaimer

I am a technical writer for Cartalyst which means I get to use their libraries so that I can help others to learn how to use them. I also wrote the official documentation.

--

--