SlideShare une entreprise Scribd logo
1  sur  36
Télécharger pour lire hors ligne
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

Contenu connexe

Tendances

Controlling The Cloud With Python
Controlling The Cloud With PythonControlling The Cloud With Python
Controlling The Cloud With Python
Luca Mearelli
 
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
Jeremy Kendall
 
Flask patterns
Flask patternsFlask patterns
Flask patterns
it-people
 

Tendances (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
 

Similaire à Datagrids with Symfony 2, Backbone and Backgrid

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
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing Up
David Padbury
 
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
Ben Lin
 
Laurens Van Den Oever Xopus Presentation
Laurens Van Den Oever Xopus PresentationLaurens Van Den Oever Xopus Presentation
Laurens Van Den Oever Xopus Presentation
Ajax Experience 2009
 

Similaire à 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
 

Plus de 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 developers
Giorgio 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 PHP
Giorgio Cefaro
 

Plus de 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
 

Dernier

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

Dernier (20)

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 

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