SlideShare a Scribd company logo
Datagrids with Symfony 2,
Backbone and Backgrid
Eugenio Pombi & Giorgio Cefaro
requirements - composer
http://getcomposer.org
Run this in your terminal to get the latest Composer version:
curl -sS https://getcomposer.org/installer | php
Or if you don't have curl:
php -r "eval('?>'.file_get_contents('https://getcomposer.org/installer'));"
requirements - symfony
http://symfony.com/download
Create a symfony 2.3.1 project in path/:
php composer.phar create-project symfony/framework-standard-edition path/ 2.3.1
requirements - dependencies
composer.json:
"require": {
[...]
"friendsofsymfony/rest-bundle": "0.12",
"jms/serializer-bundle": "dev-master",
"jms/di-extra-bundle": "dev-master",
"friendsofsymfony/jsrouting-bundle": "~1.1"
},
requirements - FOSRestBundle
https://github.com/FriendsOfSymfony/FOSRestBundle
app/config/config.yml:
fos_rest:
param_fetcher_listener: true
body_listener: true
format_listener: true
view:
view_response_listener: 'force'
requirements - javascript libs
Download the required libs:
http://backbonejs.org/
http://underscorejs.org/
http://jquery.com/
http://backgridjs.com/
http://twitter.github.io/bootstrap/
requirements - javascript libs
Place the libraries in src/Acme/MyBundle/Resources/public/js/ and include
them with Assetic:
base.html.yml:
{% block javascripts %}
{% javascripts
'bundles/mwtbrokertool/js/di-lite.js'
'bundles/mwtbrokertool/js/jquery.js'
'bundles/mwtbrokertool/js/underscore.js'
'bundles/mwtbrokertool/js/bootstrap.js'
'bundles/mwtbrokertool/js/backbone.js'
%}
<script src="{{ asset_url }}" type="text/javascript"></script>
{% endjavascripts %}
<script src="{{ asset('/js/fos_js_routes.js') }}"></script>
{% endblock %}
controllers - index
/**
* @ParamConverter("user", class="MyBundle:User", options={"id" = "userId"})
* @FosRestGet("/ticket.{_format}",
* name="mwt_brokertool_ticket",
* defaults={"_format": "json"},
* options={"expose"=true})
*/
public function indexAction(User $user)
{
$em = $this->getDoctrine()->getManager();
$repo = $em->getRepository('MyBundle:Ticket');
$tickets = $repo->findBySellerJoinAll($user);
return $tickets;
}
controllers - new
/**
* @ParamConverter("user", class="MyBundle:User", options={"id" = "userId"})
* @FosRestPost("/ticket.{_format}",
* name="My_bundle_ticket_new",
* defaults={"_format": "json"},
* options={"expose"=true}
* )
* @FosRestView
* @param User $user
*/
public function newAction(User $user)
{
[...]
}
controllers - new ticket
$ticket = new Ticket();
$form = $this->createForm(new TicketType(), $ticket);
$data = $this->getRequest()->request->all();
$children = $form->all();
$data = array_intersect_key($data, $children);
$form->submit($data);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($ticket);
$em->flush();
return View::create($ticket, 201);
}
return View::create($form, 400);
test index
public function testIndex()
{
$client = static::createClient();
$crawler = $client->request('GET','/'.$this->user1->getId().'/ticket');
$this->assertTrue($client->getResponse()->isSuccessful());
$json_response = json_decode($client->getResponse()->getContent(), true);
$this->assertTrue(is_array($json_response));
$this->assertTrue(isset($json_response[0]['event_id']));
$this->assertTrue(isset($json_response[1]['event_id']));
$this->assertTrue(isset($json_response[2]['event_id']));
}
test new ticket
$client = static::createClient();
$client->request(
'POST',
'/' . $this->user1->getId() . '/ticket',
array(),
array(),
array('CONTENT_TYPE' => 'application/json'),
'[aJsonString]'
);
$this->assertEquals(201, $client->getResponse()->getStatusCode());
json_response = json_decode($client->getResponse()->getContent(), true);
$this->assertTrue(is_array($json_response));
$ticket = $this->em->getRepository('ACMEMyBundle:Ticket')->findOneBy(array
(...);
$this->assertNotNull($ticket);
backgrid
backgrid
backgrid
backgridjs.com
The goal of Backgrid.js is to produce a set of
core Backbone UI elements that offer you all
the basic displaying, sorting and editing
functionalities you'd expect, and to create an
elegant API that makes extending Backgrid.js
with extra functionalities easy.
backgrid
Backgrid.js depends on 3 libraries to function:
● jquery >= 1.7.0
● underscore.js ~ 1.4.0
● backbone.js >= 0.9.10
backgrid
● Solid foundation. Based on Backbone.js.
● Semantic and easily stylable. Just style with plain CSS like
you would a normal HTML table.
● Low learning curve. Works with plain old Backbone models
and collections. Easy things are easy, hards things possible.
● Highly modular and customizable. Componenets are just
simple Backbone View classes, customization is easy if you
already know Backbone.
● Lightweight. Extra features are separated into extensions,
which keeps the bloat away.
di-lite.js
minimalistic dependency injection container
ctx.register("name", instance);
ctx.get("name");
My.Stuff = Backbone.Collection.extend({
dependencies: "name",
[...]
});
di-lite.js - example
var ctx = di.createContext();
var user = function () {
this.id = $("#grid").attr('data-user);
};
ctx.register("user", user);
var App.Collections.Articles = Backbone.Collection.extend({
dependencies: "user",
model: App.Models.Article,
url: function() {
return '/article?userId=' + this.user.id;
}
[...]
});
ctx.register("articles", App.Collections.Articles);
backbone model + collection
var Ticket = Backbone.Model.extend({});
var Tickets = Backbone.Collection.extend({
model: Territory,
url: Routing.generate('my_bundle_ticket', { userId: App.userId })
});
var tickets = new Tickets();
backbone associations
Associations allows Backbone applications to model 1:1 & 1:
N associations between application models and Collections.
https://github.com/dhruvaray/backbone-associations
var TicketGroup = Backbone.AssociatedModel.extend({
relations: [
{
type: Backbone.Many,
key: 'tickets',
relatedModel: 'Ticket'
}]
});
backgrid columns
var columns = [{
name: "event_name",
label: "Event",
cell: "string" ,
editable: false,
}, {
name: "event_datetime",
label: "Event Date",
cell: "datetime"
}];
backgrid initialize
var grid = new Backgrid.Grid({
columns: columns,
collection: tickets
});
$("#my-list").append(grid.render().$el);
// Fetch some tickets from the url
tickets.fetch({reset: true});
backgrid - computed fields
https://github.com/alexanderbeletsky/backbone-computedfields
var CartItem = Backbone.Model.extend({
initialize: function () {
this.computedFields = new Backbone.ComputedFields(this);
},
computed: {
grossPrice: {
depends: ['netPrice', 'vatRate'],
get: function (fields) {
return fields.netPrice * (1 + fields.vatRate / 100);
}
}
}
});
backgrid - computed fields
var columns = [{
name: "netPrice",
label: "Net Price",
cell: "number"
}, {
name: "vatRate",
label: "VAT Rate",
cell: "integer"
}, {
name: "grossPrice",
label: "Gross price",
cell: "number"
}];
backgrid - select editor
{
name: "country",
label: "Country",
cell: Backgrid.SelectCell.extend({
optionValues: ctx.get('countries').getAsOptions()
})
}
backgrid - select editor
App.Collections.Countries = Backbone.Collection.extend({
getAsOptions: function () {
var options = new Array();
this.models.forEach(function(item) {
options.push([item.get('name'), item.get('id')])
});
return options;
}
});
toggle cell - column definition
{
name: 'nonModelField',
label: 'Details',
editable: false,
cell: Backgrid.ToggleCell,
subtable: function(el, model) {
var subtable = new Backgrid.Grid({
columns: columns,
collection: model.get('tickets')
});
el.append(subtable.render().$el);
return subtable;
}
toggle cell - cell extension
Backgrid.ToggleCell = Backgrid.Cell.extend({
[...]
});
toggle cell - cell extension -
render
Backgrid.ToggleCell = Backgrid.Cell.extend({
[...]
render: function() {
this.$el.empty();
var new_el = $('<span class="toggle"></span>');
this.$el.append(new_el);
this.set_toggle().delegateEvents();
return this;
}
});
toggle cell - cell extension -
event
set_toggle: function() {
var self = this;
var td_el = this.$el;
td_el.find('.toggle').click( function() {
var details_row = td_el.closest('tr').next('.child-table');
if (details_row.length > 0) {
$(details_row).remove();
} else {
details_row = $('<tr class="child-table"><td colspan="100"></td></tr>');
$(this).closest('tr').after(details_row);
self.subtable = self.column.get('subtable')(details_row.find('td'), self.model);
}
});
return this;
}
retrieve data - model
App.Models.TicketGroup = Backbone.AssociatedModel.extend({
relations: [
{
type: Backbone.Many,
key: tickets,
relatedModel: 'App.Models.Ticket'
}
],
[...]
});
retrieve data - collection
App.Collections.TicketGroups = Backbone.Collection.extend({
model: App.Models.TicketGroup,
parse: function(tickets, options) {
[...]
return ticketGroups;
},
});
retrieve data - collection
var ticketGroups = [];
_.each(tickets, function (element, index, list) {
var foundElement = _.findWhere(
ticketGroups,
{event_id: element.event_id}
)
if (foundElement == null) {
ticketGroups.push({
"event_id": element.event_id,
"event_name": element.event_name,
"tickets": [element]
});
} else {
foundElement.tickets.push(element);
}
}, this);
testing!
describe("TicketGroups Collection", function () {
describe("parse", function () {
beforeEach(function () {
this.ticketGroupCollection = new App.Collections.TicketGroups();
});
it("parse should return a ticketGroup with nested tickets", function ()
{
var jsonWith3Records = [...];
var result = this.ticketGroupCollection.parse(jsonWith3Records, {});
result.should.have.length(2);
var firstResult = result[0];
firstResult.event_name.should.equal("Concerto Iron Maiden");
firstResult.tickets.should.have.length(2);
var secondResult = result[1];
secondResult.event_name.should.equal("Battle Hymns Tour");
secondResult.tickets.should.have.length(1);
//close brackets
thanks
@giorrrgio
giorgiocefaro.com
@euxpom
nerd2business.net

More Related Content

What's hot

Filling the flask
Filling the flaskFilling the flask
Filling the flask
Jason Myers
 
PHP5.5 is Here
PHP5.5 is HerePHP5.5 is Here
PHP5.5 is Here
julien pauli
 
A Little Backbone For Your App
A Little Backbone For Your AppA Little Backbone For Your App
A Little Backbone For Your App
Luca Mearelli
 
Introduction to Node.JS Express
Introduction to Node.JS ExpressIntroduction to Node.JS Express
Introduction to Node.JS Express
Eueung Mulyana
 
Inside Bokete: Web Application with Mojolicious and others
Inside Bokete:  Web Application with Mojolicious and othersInside Bokete:  Web Application with Mojolicious and others
Inside Bokete: Web Application with Mojolicious and others
Yusuke Wada
 
RESTful API 제대로 만들기
RESTful API 제대로 만들기RESTful API 제대로 만들기
RESTful API 제대로 만들기
Juwon Kim
 
Web Apps in Perl - HTTP 101
Web Apps in Perl - HTTP 101Web Apps in Perl - HTTP 101
Web Apps in Perl - HTTP 101
hendrikvb
 
Mojo as a_client
Mojo as a_clientMojo as a_client
Mojo as a_client
Marcus Ramberg
 
Composer
ComposerComposer
Composer
Tom Corrigan
 
Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5
Elena Kolevska
 
Webrtc mojo
Webrtc mojoWebrtc mojo
Webrtc mojo
bpmedley
 
Controlling The Cloud With Python
Controlling The Cloud With PythonControlling The Cloud With Python
Controlling The Cloud With PythonLuca Mearelli
 
Flask Basics
Flask BasicsFlask Basics
Flask Basics
Eueung Mulyana
 
Workshop 6: Designer tools
Workshop 6: Designer toolsWorkshop 6: Designer tools
Workshop 6: Designer tools
Visual Engineering
 
Mojolicious - A new hope
Mojolicious - A new hopeMojolicious - A new hope
Mojolicious - A new hope
Marcus Ramberg
 
To Batch Or Not To Batch
To Batch Or Not To BatchTo Batch Or Not To Batch
To Batch Or Not To Batch
Luca Mearelli
 
Python RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutionsPython RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutions
Solution4Future
 
Keeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkKeeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkJeremy Kendall
 
Flask patterns
Flask patternsFlask patterns
Flask patternsit-people
 

What's hot (20)

Filling the flask
Filling the flaskFilling the flask
Filling the flask
 
PHP5.5 is Here
PHP5.5 is HerePHP5.5 is Here
PHP5.5 is Here
 
A Little Backbone For Your App
A Little Backbone For Your AppA Little Backbone For Your App
A Little Backbone For Your App
 
Introduction to Node.JS Express
Introduction to Node.JS ExpressIntroduction to Node.JS Express
Introduction to Node.JS Express
 
Inside Bokete: Web Application with Mojolicious and others
Inside Bokete:  Web Application with Mojolicious and othersInside Bokete:  Web Application with Mojolicious and others
Inside Bokete: Web Application with Mojolicious and others
 
RESTful API 제대로 만들기
RESTful API 제대로 만들기RESTful API 제대로 만들기
RESTful API 제대로 만들기
 
Web Apps in Perl - HTTP 101
Web Apps in Perl - HTTP 101Web Apps in Perl - HTTP 101
Web Apps in Perl - HTTP 101
 
Mojo as a_client
Mojo as a_clientMojo as a_client
Mojo as a_client
 
RESTful web services
RESTful web servicesRESTful web services
RESTful web services
 
Composer
ComposerComposer
Composer
 
Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5
 
Webrtc mojo
Webrtc mojoWebrtc mojo
Webrtc mojo
 
Controlling The Cloud With Python
Controlling The Cloud With PythonControlling The Cloud With Python
Controlling The Cloud With Python
 
Flask Basics
Flask BasicsFlask Basics
Flask Basics
 
Workshop 6: Designer tools
Workshop 6: Designer toolsWorkshop 6: Designer tools
Workshop 6: Designer tools
 
Mojolicious - A new hope
Mojolicious - A new hopeMojolicious - A new hope
Mojolicious - A new hope
 
To Batch Or Not To Batch
To Batch Or Not To BatchTo Batch Or Not To Batch
To Batch Or Not To Batch
 
Python RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutionsPython RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutions
 
Keeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkKeeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro framework
 
Flask patterns
Flask patternsFlask patterns
Flask patterns
 

Similar to Datagrids with Symfony 2, Backbone and Backgrid

using Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API'susing Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API's
Antônio Roberto Silva
 
Code Splitting in Practice - Shanghai JS Meetup May 2016
Code Splitting in Practice - Shanghai JS Meetup May 2016Code Splitting in Practice - Shanghai JS Meetup May 2016
Code Splitting in Practice - Shanghai JS Meetup May 2016
Wiredcraft
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...Fabio Franzini
 
Rails Engine | Modular application
Rails Engine | Modular applicationRails Engine | Modular application
Rails Engine | Modular application
mirrec
 
Deep Learning for Computer Vision: Software Frameworks (UPC 2016)
Deep Learning for Computer Vision: Software Frameworks (UPC 2016)Deep Learning for Computer Vision: Software Frameworks (UPC 2016)
Deep Learning for Computer Vision: Software Frameworks (UPC 2016)
Universitat Politècnica de Catalunya
 
IOC + Javascript
IOC + JavascriptIOC + Javascript
IOC + Javascript
Brian Cavalier
 
Lambdas puzzler - Peter Lawrey
Lambdas puzzler - Peter LawreyLambdas puzzler - Peter Lawrey
Lambdas puzzler - Peter Lawrey
JAXLondon_Conference
 
Lecture: Webpack 4
Lecture: Webpack 4Lecture: Webpack 4
Lecture: Webpack 4
Sergei Iastrebov
 
Symfony2 revealed
Symfony2 revealedSymfony2 revealed
Symfony2 revealed
Fabien Potencier
 
Webpack: your final module bundler
Webpack: your final module bundlerWebpack: your final module bundler
Webpack: your final module bundler
Andrea Giannantonio
 
Designing REST API automation tests in Kotlin
Designing REST API automation tests in KotlinDesigning REST API automation tests in Kotlin
Designing REST API automation tests in Kotlin
Dmitriy Sobko
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing UpDavid Padbury
 
Symfony2 - from the trenches
Symfony2 - from the trenchesSymfony2 - from the trenches
Symfony2 - from the trenches
Lukas Smith
 
How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js frameworkBen Lin
 
Laurens Van Den Oever Xopus Presentation
Laurens Van Den Oever Xopus PresentationLaurens Van Den Oever Xopus Presentation
Laurens Van Den Oever Xopus PresentationAjax Experience 2009
 
RequireJS
RequireJSRequireJS
RequireJS
Tim Doherty
 
The Ring programming language version 1.8 book - Part 95 of 202
The Ring programming language version 1.8 book - Part 95 of 202The Ring programming language version 1.8 book - Part 95 of 202
The Ring programming language version 1.8 book - Part 95 of 202
Mahmoud Samir Fayed
 
Frontend JS workflow - Gulp 4 and the like
Frontend JS workflow - Gulp 4 and the likeFrontend JS workflow - Gulp 4 and the like
Frontend JS workflow - Gulp 4 and the like
Damien Seguin
 

Similar to Datagrids with Symfony 2, Backbone and Backgrid (20)

using Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API'susing Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API's
 
Code Splitting in Practice - Shanghai JS Meetup May 2016
Code Splitting in Practice - Shanghai JS Meetup May 2016Code Splitting in Practice - Shanghai JS Meetup May 2016
Code Splitting in Practice - Shanghai JS Meetup May 2016
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...
 
Rails Engine | Modular application
Rails Engine | Modular applicationRails Engine | Modular application
Rails Engine | Modular application
 
Deep Learning for Computer Vision: Software Frameworks (UPC 2016)
Deep Learning for Computer Vision: Software Frameworks (UPC 2016)Deep Learning for Computer Vision: Software Frameworks (UPC 2016)
Deep Learning for Computer Vision: Software Frameworks (UPC 2016)
 
IOC + Javascript
IOC + JavascriptIOC + Javascript
IOC + Javascript
 
Lambdas puzzler - Peter Lawrey
Lambdas puzzler - Peter LawreyLambdas puzzler - Peter Lawrey
Lambdas puzzler - Peter Lawrey
 
Lecture: Webpack 4
Lecture: Webpack 4Lecture: Webpack 4
Lecture: Webpack 4
 
backend
backendbackend
backend
 
backend
backendbackend
backend
 
Symfony2 revealed
Symfony2 revealedSymfony2 revealed
Symfony2 revealed
 
Webpack: your final module bundler
Webpack: your final module bundlerWebpack: your final module bundler
Webpack: your final module bundler
 
Designing REST API automation tests in Kotlin
Designing REST API automation tests in KotlinDesigning REST API automation tests in Kotlin
Designing REST API automation tests in Kotlin
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing Up
 
Symfony2 - from the trenches
Symfony2 - from the trenchesSymfony2 - from the trenches
Symfony2 - from the trenches
 
How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js framework
 
Laurens Van Den Oever Xopus Presentation
Laurens Van Den Oever Xopus PresentationLaurens Van Den Oever Xopus Presentation
Laurens Van Den Oever Xopus Presentation
 
RequireJS
RequireJSRequireJS
RequireJS
 
The Ring programming language version 1.8 book - Part 95 of 202
The Ring programming language version 1.8 book - Part 95 of 202The Ring programming language version 1.8 book - Part 95 of 202
The Ring programming language version 1.8 book - Part 95 of 202
 
Frontend JS workflow - Gulp 4 and the like
Frontend JS workflow - Gulp 4 and the likeFrontend JS workflow - Gulp 4 and the like
Frontend JS workflow - Gulp 4 and the like
 

More from Giorgio Cefaro

Alexa, AWS lambda & wikidata (ITA)
Alexa, AWS lambda & wikidata (ITA)Alexa, AWS lambda & wikidata (ITA)
Alexa, AWS lambda & wikidata (ITA)
Giorgio Cefaro
 
PHP object calisthenics
PHP object calisthenicsPHP object calisthenics
PHP object calisthenics
Giorgio Cefaro
 
Don't fear the rebase
Don't fear the rebaseDon't fear the rebase
Don't fear the rebase
Giorgio Cefaro
 
jsDay 2016 recap
jsDay 2016 recapjsDay 2016 recap
jsDay 2016 recap
Giorgio Cefaro
 
Import golang; struct microservice - Codemotion Rome 2015
Import golang; struct microservice - Codemotion Rome 2015Import golang; struct microservice - Codemotion Rome 2015
Import golang; struct microservice - Codemotion Rome 2015
Giorgio Cefaro
 
I came, I saw, I GO! - Golangit meetup @ Codemotion Rome 2014
I came, I saw, I GO! - Golangit meetup @ Codemotion Rome 2014I came, I saw, I GO! - Golangit meetup @ Codemotion Rome 2014
I came, I saw, I GO! - Golangit meetup @ Codemotion Rome 2014
Giorgio Cefaro
 
Nanos gigantium humeris insidentes (design patterns inside symfony 2)
Nanos gigantium humeris insidentes (design patterns inside symfony 2)Nanos gigantium humeris insidentes (design patterns inside symfony 2)
Nanos gigantium humeris insidentes (design patterns inside symfony 2)
Giorgio Cefaro
 
High Performance Web Apps con PHP e Symfony 2
High Performance Web Apps con PHP  e Symfony 2High Performance Web Apps con PHP  e Symfony 2
High Performance Web Apps con PHP e Symfony 2
Giorgio Cefaro
 
From LAMP to LNNP
From LAMP to LNNPFrom LAMP to LNNP
From LAMP to LNNP
Giorgio Cefaro
 
An introduction to Symfony 2 for symfony 1 developers
An introduction to Symfony 2 for symfony 1 developersAn introduction to Symfony 2 for symfony 1 developers
An introduction to Symfony 2 for symfony 1 developersGiorgio Cefaro
 
Netbeans e Xdebug per debugging e profiling di applicazioni PHP
Netbeans e Xdebug per debugging e profiling di applicazioni PHPNetbeans e Xdebug per debugging e profiling di applicazioni PHP
Netbeans e Xdebug per debugging e profiling di applicazioni PHPGiorgio Cefaro
 

More from Giorgio Cefaro (11)

Alexa, AWS lambda & wikidata (ITA)
Alexa, AWS lambda & wikidata (ITA)Alexa, AWS lambda & wikidata (ITA)
Alexa, AWS lambda & wikidata (ITA)
 
PHP object calisthenics
PHP object calisthenicsPHP object calisthenics
PHP object calisthenics
 
Don't fear the rebase
Don't fear the rebaseDon't fear the rebase
Don't fear the rebase
 
jsDay 2016 recap
jsDay 2016 recapjsDay 2016 recap
jsDay 2016 recap
 
Import golang; struct microservice - Codemotion Rome 2015
Import golang; struct microservice - Codemotion Rome 2015Import golang; struct microservice - Codemotion Rome 2015
Import golang; struct microservice - Codemotion Rome 2015
 
I came, I saw, I GO! - Golangit meetup @ Codemotion Rome 2014
I came, I saw, I GO! - Golangit meetup @ Codemotion Rome 2014I came, I saw, I GO! - Golangit meetup @ Codemotion Rome 2014
I came, I saw, I GO! - Golangit meetup @ Codemotion Rome 2014
 
Nanos gigantium humeris insidentes (design patterns inside symfony 2)
Nanos gigantium humeris insidentes (design patterns inside symfony 2)Nanos gigantium humeris insidentes (design patterns inside symfony 2)
Nanos gigantium humeris insidentes (design patterns inside symfony 2)
 
High Performance Web Apps con PHP e Symfony 2
High Performance Web Apps con PHP  e Symfony 2High Performance Web Apps con PHP  e Symfony 2
High Performance Web Apps con PHP e Symfony 2
 
From LAMP to LNNP
From LAMP to LNNPFrom LAMP to LNNP
From LAMP to LNNP
 
An introduction to Symfony 2 for symfony 1 developers
An introduction to Symfony 2 for symfony 1 developersAn introduction to Symfony 2 for symfony 1 developers
An introduction to Symfony 2 for symfony 1 developers
 
Netbeans e Xdebug per debugging e profiling di applicazioni PHP
Netbeans e Xdebug per debugging e profiling di applicazioni PHPNetbeans e Xdebug per debugging e profiling di applicazioni PHP
Netbeans e Xdebug per debugging e profiling di applicazioni PHP
 

Recently uploaded

Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1
DianaGray10
 
National Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practicesNational Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practices
Quotidiano Piemontese
 
20240609 QFM020 Irresponsible AI Reading List May 2024
20240609 QFM020 Irresponsible AI Reading List May 202420240609 QFM020 Irresponsible AI Reading List May 2024
20240609 QFM020 Irresponsible AI Reading List May 2024
Matthew Sinclair
 
Data structures and Algorithms in Python.pdf
Data structures and Algorithms in Python.pdfData structures and Algorithms in Python.pdf
Data structures and Algorithms in Python.pdf
TIPNGVN2
 
A tale of scale & speed: How the US Navy is enabling software delivery from l...
A tale of scale & speed: How the US Navy is enabling software delivery from l...A tale of scale & speed: How the US Navy is enabling software delivery from l...
A tale of scale & speed: How the US Navy is enabling software delivery from l...
sonjaschweigert1
 
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
Neo4j
 
Artificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopmentArtificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopment
Octavian Nadolu
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
Adtran
 
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdfUnlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Malak Abu Hammad
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
DanBrown980551
 
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
Neo4j
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
Safe Software
 
UiPath Test Automation using UiPath Test Suite series, part 5
UiPath Test Automation using UiPath Test Suite series, part 5UiPath Test Automation using UiPath Test Suite series, part 5
UiPath Test Automation using UiPath Test Suite series, part 5
DianaGray10
 
Enchancing adoption of Open Source Libraries. A case study on Albumentations.AI
Enchancing adoption of Open Source Libraries. A case study on Albumentations.AIEnchancing adoption of Open Source Libraries. A case study on Albumentations.AI
Enchancing adoption of Open Source Libraries. A case study on Albumentations.AI
Vladimir Iglovikov, Ph.D.
 
20240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 202420240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 2024
Matthew Sinclair
 
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex ProofszkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
Alex Pruden
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
Alan Dix
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
ControlCase
 
Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
Alpen-Adria-Universität
 
Microsoft - Power Platform_G.Aspiotis.pdf
Microsoft - Power Platform_G.Aspiotis.pdfMicrosoft - Power Platform_G.Aspiotis.pdf
Microsoft - Power Platform_G.Aspiotis.pdf
Uni Systems S.M.S.A.
 

Recently uploaded (20)

Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1
 
National Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practicesNational Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practices
 
20240609 QFM020 Irresponsible AI Reading List May 2024
20240609 QFM020 Irresponsible AI Reading List May 202420240609 QFM020 Irresponsible AI Reading List May 2024
20240609 QFM020 Irresponsible AI Reading List May 2024
 
Data structures and Algorithms in Python.pdf
Data structures and Algorithms in Python.pdfData structures and Algorithms in Python.pdf
Data structures and Algorithms in Python.pdf
 
A tale of scale & speed: How the US Navy is enabling software delivery from l...
A tale of scale & speed: How the US Navy is enabling software delivery from l...A tale of scale & speed: How the US Navy is enabling software delivery from l...
A tale of scale & speed: How the US Navy is enabling software delivery from l...
 
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
 
Artificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopmentArtificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopment
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
 
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdfUnlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
 
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
 
UiPath Test Automation using UiPath Test Suite series, part 5
UiPath Test Automation using UiPath Test Suite series, part 5UiPath Test Automation using UiPath Test Suite series, part 5
UiPath Test Automation using UiPath Test Suite series, part 5
 
Enchancing adoption of Open Source Libraries. A case study on Albumentations.AI
Enchancing adoption of Open Source Libraries. A case study on Albumentations.AIEnchancing adoption of Open Source Libraries. A case study on Albumentations.AI
Enchancing adoption of Open Source Libraries. A case study on Albumentations.AI
 
20240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 202420240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 2024
 
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex ProofszkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
 
Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
 
Microsoft - Power Platform_G.Aspiotis.pdf
Microsoft - Power Platform_G.Aspiotis.pdfMicrosoft - Power Platform_G.Aspiotis.pdf
Microsoft - Power Platform_G.Aspiotis.pdf
 

Datagrids with Symfony 2, Backbone and Backgrid

  • 1. Datagrids with Symfony 2, Backbone and Backgrid Eugenio Pombi & Giorgio Cefaro
  • 2. requirements - composer http://getcomposer.org Run this in your terminal to get the latest Composer version: curl -sS https://getcomposer.org/installer | php Or if you don't have curl: php -r "eval('?>'.file_get_contents('https://getcomposer.org/installer'));"
  • 3. requirements - symfony http://symfony.com/download Create a symfony 2.3.1 project in path/: php composer.phar create-project symfony/framework-standard-edition path/ 2.3.1
  • 4. requirements - dependencies composer.json: "require": { [...] "friendsofsymfony/rest-bundle": "0.12", "jms/serializer-bundle": "dev-master", "jms/di-extra-bundle": "dev-master", "friendsofsymfony/jsrouting-bundle": "~1.1" },
  • 6. requirements - javascript libs Download the required libs: http://backbonejs.org/ http://underscorejs.org/ http://jquery.com/ http://backgridjs.com/ http://twitter.github.io/bootstrap/
  • 7. requirements - javascript libs Place the libraries in src/Acme/MyBundle/Resources/public/js/ and include them with Assetic: base.html.yml: {% block javascripts %} {% javascripts 'bundles/mwtbrokertool/js/di-lite.js' 'bundles/mwtbrokertool/js/jquery.js' 'bundles/mwtbrokertool/js/underscore.js' 'bundles/mwtbrokertool/js/bootstrap.js' 'bundles/mwtbrokertool/js/backbone.js' %} <script src="{{ asset_url }}" type="text/javascript"></script> {% endjavascripts %} <script src="{{ asset('/js/fos_js_routes.js') }}"></script> {% endblock %}
  • 8. controllers - index /** * @ParamConverter("user", class="MyBundle:User", options={"id" = "userId"}) * @FosRestGet("/ticket.{_format}", * name="mwt_brokertool_ticket", * defaults={"_format": "json"}, * options={"expose"=true}) */ public function indexAction(User $user) { $em = $this->getDoctrine()->getManager(); $repo = $em->getRepository('MyBundle:Ticket'); $tickets = $repo->findBySellerJoinAll($user); return $tickets; }
  • 9. controllers - new /** * @ParamConverter("user", class="MyBundle:User", options={"id" = "userId"}) * @FosRestPost("/ticket.{_format}", * name="My_bundle_ticket_new", * defaults={"_format": "json"}, * options={"expose"=true} * ) * @FosRestView * @param User $user */ public function newAction(User $user) { [...] }
  • 10. controllers - new ticket $ticket = new Ticket(); $form = $this->createForm(new TicketType(), $ticket); $data = $this->getRequest()->request->all(); $children = $form->all(); $data = array_intersect_key($data, $children); $form->submit($data); if ($form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->persist($ticket); $em->flush(); return View::create($ticket, 201); } return View::create($form, 400);
  • 11. test index public function testIndex() { $client = static::createClient(); $crawler = $client->request('GET','/'.$this->user1->getId().'/ticket'); $this->assertTrue($client->getResponse()->isSuccessful()); $json_response = json_decode($client->getResponse()->getContent(), true); $this->assertTrue(is_array($json_response)); $this->assertTrue(isset($json_response[0]['event_id'])); $this->assertTrue(isset($json_response[1]['event_id'])); $this->assertTrue(isset($json_response[2]['event_id'])); }
  • 12. test new ticket $client = static::createClient(); $client->request( 'POST', '/' . $this->user1->getId() . '/ticket', array(), array(), array('CONTENT_TYPE' => 'application/json'), '[aJsonString]' ); $this->assertEquals(201, $client->getResponse()->getStatusCode()); json_response = json_decode($client->getResponse()->getContent(), true); $this->assertTrue(is_array($json_response)); $ticket = $this->em->getRepository('ACMEMyBundle:Ticket')->findOneBy(array (...); $this->assertNotNull($ticket);
  • 15. backgrid backgridjs.com The goal of Backgrid.js is to produce a set of core Backbone UI elements that offer you all the basic displaying, sorting and editing functionalities you'd expect, and to create an elegant API that makes extending Backgrid.js with extra functionalities easy.
  • 16. backgrid Backgrid.js depends on 3 libraries to function: ● jquery >= 1.7.0 ● underscore.js ~ 1.4.0 ● backbone.js >= 0.9.10
  • 17. backgrid ● Solid foundation. Based on Backbone.js. ● Semantic and easily stylable. Just style with plain CSS like you would a normal HTML table. ● Low learning curve. Works with plain old Backbone models and collections. Easy things are easy, hards things possible. ● Highly modular and customizable. Componenets are just simple Backbone View classes, customization is easy if you already know Backbone. ● Lightweight. Extra features are separated into extensions, which keeps the bloat away.
  • 18. di-lite.js minimalistic dependency injection container ctx.register("name", instance); ctx.get("name"); My.Stuff = Backbone.Collection.extend({ dependencies: "name", [...] });
  • 19. di-lite.js - example var ctx = di.createContext(); var user = function () { this.id = $("#grid").attr('data-user); }; ctx.register("user", user); var App.Collections.Articles = Backbone.Collection.extend({ dependencies: "user", model: App.Models.Article, url: function() { return '/article?userId=' + this.user.id; } [...] }); ctx.register("articles", App.Collections.Articles);
  • 20. backbone model + collection var Ticket = Backbone.Model.extend({}); var Tickets = Backbone.Collection.extend({ model: Territory, url: Routing.generate('my_bundle_ticket', { userId: App.userId }) }); var tickets = new Tickets();
  • 21. backbone associations Associations allows Backbone applications to model 1:1 & 1: N associations between application models and Collections. https://github.com/dhruvaray/backbone-associations var TicketGroup = Backbone.AssociatedModel.extend({ relations: [ { type: Backbone.Many, key: 'tickets', relatedModel: 'Ticket' }] });
  • 22. backgrid columns var columns = [{ name: "event_name", label: "Event", cell: "string" , editable: false, }, { name: "event_datetime", label: "Event Date", cell: "datetime" }];
  • 23. backgrid initialize var grid = new Backgrid.Grid({ columns: columns, collection: tickets }); $("#my-list").append(grid.render().$el); // Fetch some tickets from the url tickets.fetch({reset: true});
  • 24. backgrid - computed fields https://github.com/alexanderbeletsky/backbone-computedfields var CartItem = Backbone.Model.extend({ initialize: function () { this.computedFields = new Backbone.ComputedFields(this); }, computed: { grossPrice: { depends: ['netPrice', 'vatRate'], get: function (fields) { return fields.netPrice * (1 + fields.vatRate / 100); } } } });
  • 25. backgrid - computed fields var columns = [{ name: "netPrice", label: "Net Price", cell: "number" }, { name: "vatRate", label: "VAT Rate", cell: "integer" }, { name: "grossPrice", label: "Gross price", cell: "number" }];
  • 26. backgrid - select editor { name: "country", label: "Country", cell: Backgrid.SelectCell.extend({ optionValues: ctx.get('countries').getAsOptions() }) }
  • 27. backgrid - select editor App.Collections.Countries = Backbone.Collection.extend({ getAsOptions: function () { var options = new Array(); this.models.forEach(function(item) { options.push([item.get('name'), item.get('id')]) }); return options; } });
  • 28. toggle cell - column definition { name: 'nonModelField', label: 'Details', editable: false, cell: Backgrid.ToggleCell, subtable: function(el, model) { var subtable = new Backgrid.Grid({ columns: columns, collection: model.get('tickets') }); el.append(subtable.render().$el); return subtable; }
  • 29. toggle cell - cell extension Backgrid.ToggleCell = Backgrid.Cell.extend({ [...] });
  • 30. toggle cell - cell extension - render Backgrid.ToggleCell = Backgrid.Cell.extend({ [...] render: function() { this.$el.empty(); var new_el = $('<span class="toggle"></span>'); this.$el.append(new_el); this.set_toggle().delegateEvents(); return this; } });
  • 31. toggle cell - cell extension - event set_toggle: function() { var self = this; var td_el = this.$el; td_el.find('.toggle').click( function() { var details_row = td_el.closest('tr').next('.child-table'); if (details_row.length > 0) { $(details_row).remove(); } else { details_row = $('<tr class="child-table"><td colspan="100"></td></tr>'); $(this).closest('tr').after(details_row); self.subtable = self.column.get('subtable')(details_row.find('td'), self.model); } }); return this; }
  • 32. retrieve data - model App.Models.TicketGroup = Backbone.AssociatedModel.extend({ relations: [ { type: Backbone.Many, key: tickets, relatedModel: 'App.Models.Ticket' } ], [...] });
  • 33. retrieve data - collection App.Collections.TicketGroups = Backbone.Collection.extend({ model: App.Models.TicketGroup, parse: function(tickets, options) { [...] return ticketGroups; }, });
  • 34. retrieve data - collection var ticketGroups = []; _.each(tickets, function (element, index, list) { var foundElement = _.findWhere( ticketGroups, {event_id: element.event_id} ) if (foundElement == null) { ticketGroups.push({ "event_id": element.event_id, "event_name": element.event_name, "tickets": [element] }); } else { foundElement.tickets.push(element); } }, this);
  • 35. testing! describe("TicketGroups Collection", function () { describe("parse", function () { beforeEach(function () { this.ticketGroupCollection = new App.Collections.TicketGroups(); }); it("parse should return a ticketGroup with nested tickets", function () { var jsonWith3Records = [...]; var result = this.ticketGroupCollection.parse(jsonWith3Records, {}); result.should.have.length(2); var firstResult = result[0]; firstResult.event_name.should.equal("Concerto Iron Maiden"); firstResult.tickets.should.have.length(2); var secondResult = result[1]; secondResult.event_name.should.equal("Battle Hymns Tour"); secondResult.tickets.should.have.length(1); //close brackets