2013 - nate abele: http all the things: simplificando aplicaciones respetando las reglas de la web
DESCRIPTION
PHP Conference Argentina 2013TRANSCRIPT
HTTP ALL THE THINGSSimplifying Rich Applications byRespecting the Rules of the Web
Me
Nate Abele
Former lead developer, CakePHP
Founder & current lead developer, Lithium
Member, AngularUI
@nateabele
History
1998: SOAP & XML-RPC
SOAP<?xml version="1.0"?><soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> <soap:Header></soap:Header> <soap:Body> <m:SomeCommand xmlns:m="..."> <m:BodyOfMessage>...</m:BodyOfMessage> </m:SomeCommand> </soap:Body></soap:Envelope>
Or...
XML-RPC<?xml version="1.0"?>
<methodCall>
<methodName>examples.getStateName</methodName>
<params>
<param>
<value><i4>40</i4></value>
</param>
</params>
</methodCall>
:-(
POST *
Invocaction details in body
Format lock-in
No relationships
No discoverability
2000: REST
REST Constraints
Client-Server
Stateless
Cacheable
Uniform Interface
Opaque Layering
Code-on-Demand
The 4 Levels
RPC over HTTP (i.e. SOAP)
Resources
Verbs
Hypermedia Controls
Now: Hypermedia /HATEOAS
¿Hypermedia?Content negotiation
State traversal
Content negotiation
application/json ...?
Content negotiation
application/json
FALE
Content negotiation
Better:application/rss+xml
Content negotiation
application/vnd.twitter.stream+json
// I can decode this!
json_decode()
/**
* I can understand this
!
*/
class Tweet {
protected $username;
protected $text;
// ...
}
State traversal
Client: “What's next?”
We already do this!
Browsers<link rel="stylesheet" href="/css/app.css" />
<link rel="next" href="/next" />
<link
rel="alternate"
type="application/rss+xml"
href="/posts.rss"
/>
Users(with the help of browsers)
<a href="/next" />Next</a>
<form></form>
Atom?
<link
rel="foo"
type="text/foo"
href="http://foo"
/>
JSON![{ title: "Finish the demo", completed: false, $links: { self: { href: "http://my.app/tasks/1138" }, owner: { href: "http://my.app/users/nate" }, subtasks: { href: "http://my.app/tasks/1138/subtasks"} }}]
Why?
AngularJS
Valid alternatives
EmberJS
KnockoutJS
Backbone (kind of, not really)
AngularJS
Higher level of abstraction
Simple, well-designed architecture
Ridiculously simple unit testing
Event binding
Event bindingjQuery
<input type="text" id="name" /><h1>Hello!</h1>
<script type="text/javascript">
</script>
$(document).ready(function() { $('#name').keydown(function(e) { $('h1').html("Hello " + e.target.value + "!") }); });
Event bindingAngularJS
<input type="text" ng-model="name" />
<h1>Hello {{ name }}!</h1>
Hello !
IterationjQuery
<ul class="greetings"></ul>
<script type="text/javascript">
</script>
$(["Hello", "Hola", "Ciao"]).each(function(k, v) { $(".greeting").append("<li>" + v + " World</li>" });
IterationAngularJS
<ul> <li ng-repeat="greet in ['Hello', 'Hola', 'Ciao']"> {{ greet }} World </li></ul>
Hello World
Hola World
Ciao World
<ul> <li ng-repeat="greet in ['Hello', 'Hola', 'Ciao']"> {{ greet }} World </li></ul>
Hello World
Hola World
Ciao World
OrganizationAngularJS
Item: Qty: 0 Price: 0 Total: $0.00
Total: $0.00
OrganizationAngularJS
<div ng-controller="CheckoutController">
<div ng-repeat="item in items">
Item: <input type="text" ng-model="item.name">
Qty: <input type="number" ng-model="item.qty">
Price: <input type="number" ng-model="item.price">
Total: <div class="total">
{{ item.qty * item.price | currency: "$" }}
</div>
</div>
<hr>
<div>Total: {{ total() | currency: "$" }}</div>
</div>
OrganizationAngularJS
function CheckoutController($scope) {
$scope.items = $scope.items || [{ price: 0, qty: 0 }];
$scope.total = function() {
if ($scope.items[$scope.items.length - 1].qty) {
$scope.items.push({ price: 0, qty: 0 });
}
return $scope.items.map(function(item) {
return item.qty * item.price;
}).reduce(function(a, b) {
return a + b;
});
};
}
OrganizationjQuery
¿Ummm...?
OrganizationjQuery
*shrug*
TestabilityAngularJS
describe("Shopping cart", function() {
describe("Checkout widget", function() {
it("should create a default element", function() {
var scope = {},
controller = new CheckoutController(scope);
expect(scope.items.length).toBe(1);
expect(scope.items[0].qty).toBe(0);
expect(scope.items[0].price).toBe(0);
});
});
});
TestabilityAngularJS
describe("Shopping cart", function() {
describe("Checkout widget", function() {
// ...
it("should calculate order total", function() {
var scope = { items: [
{ price: 2, qty: 4 }, { price: 10, qty: 1 }
]};
var controller = new CheckoutController(scope);
expect(scope.total()).toBe(18);
});
});
});
¿jQuery...?
AngularJS + HTTPResources
$resource()
var Task = $resource("http://my.api/tasks/:id", {
id: "@id"
});
var newTask = new Task({
title: "New Task",
description: "..."
});
/* POST /tasks { "title": "New Task", ... } */
newPost.$save();
/* GET /tasks/5 */
var oneTask = Task.get({ id: 5 });
/* GET /tasks?completed=false&due=1381158984 */
var current = Task.query({
completed: false,
description: "..."
});
¡Muy mal!¡No me gusta!
¡Eso es shinaniganos!
Tight coupling
Client-side URL templates
Excessive parameters
No links!
Intent
/tasks?
completed=false
&due=1381158984
No meaning
/tasks/current
/tasks/:id
$resource("/posts/:id", {id:"@id"});
{
id: 5,
title: "Something New",
slug: "something-new"
}
...?
/posts/something-new
What to do?
uor/angular-model(on GitHub) *
* Current code is old and broken, new release coming tomorrow
modelProvider.model("Tasks", {
$instance: {
finish: function() {
this.completed = true;
return this.$save();
},
isCompleted: function() {
return !!this.completed;
}
}
});
Where's the URL?
3 options
Automatic: /tasks
<link
rel="resource"
name="Tasks"
href="<?=$this->url('Tasks'); ?>"
/>
GET /
Accept:application/vnd.resource-def+json
{
"Tasks": "http://my.app/tasks",
"Users": "http://my.app/users"
}
OPTIONS /tasks
Accept:application/vnd.resource-
schema+json
{
"title": "string",
"completed": "boolean",
...
}
More HTTP goodies...that you don't need to
reinvent
HTTP Range
GET /rickroll.mp4
Range: bytes=100-99999
HEAD /posts HTTP/1.1
...
HTTP 200 OK
Accept-Ranges: posts
GET /posts HTTP/1.1
Range: posts=1-20
HTTP AuthAlways use protection!
CachingImplement any invalidationstrategy you can imagine
PHP?
nateabele/li3_resources
(on GitHub)
Automatic CRUD manipulation with verbs
Parameter mapping
Links from model relationships
Schemas with OPTIONS
Proxies for mapping database values
Coming soon...Range pagination
Smart search with ?q=...
...Tests (runs away)
Thanks!