symfony und ember.js auf einer seite #codetalks14
DESCRIPTION
Internetnutzer sind es heutzutage gewohnt, dass Webseiten unmittelbar auf Interaktion reagieren und ohne häufiges Nachladen funktionieren. Techniken, die solche Webseiten ermöglichen, sollten durch die Zuhilfenahme zuverlässiger Frameworks umgesetzt werden. Serverseitig ist für viele Entwickler Symfony das Mittel der Wahl, im Browser kann es Ember.js sein, das vor allem im Bereich der Single-Page-Applikationen glänzt. Mit beiden zusammen kann also nichts mehr schief gehen, oder? Dieser Vortrag zeigt Symfony und Ember.js in Kombination und wie man umdenken muss, wenn man gewohnt ist, klassische Webseiten zu entwickeln. Speziell geht er auf die dabei auftretenden Herausforderungen im Templating, Routing und bei der Model-Synchronisierung ein.TRANSCRIPT
Symfony und Ember.js auf einer Seite
Paul SeiffertSensioLabs Deutschland GmbH
Klassische Webseiten
B R O W S E R
GET /
URL-Eingabe
HTMLWebseite
Link-Klick
Neue Webseite
…
GET /blog
HTML
S E R V E R
Klassische Webseiten
HTML
R O U T I N G
C O N T R O L L E R
S E C U R I T Y
T E M P L AT I N G
GET /
BusinessLogikS Y M F O N Y
Klassische Symfony-Seiten
Moderne Webseiten
Moderne Webseiten
View-Wechsel
B R O W S E R
GET /
URL-Eingabe
HTMLWebseite
Link-Klick
Neue Webseite
… S E R V E R
Moderne Webseiten
B R O W S E R
R O U T I N G
T E M P L AT I N G
C O N T R O L L E R
M O D E L
B R O W S E R
Moderne WebseitenR O U T I N G
T E M P L AT I N G
C O N T R O L L E R
M O D E L
GET /api/users
JSON
S E R V E R
PaulSeiffert
Symfony und Ember.js auf einer Seite
Paul SeiffertSensioLabs Deutschland GmbH
– FA B I E N P O T E N C I E R
“Symfony2 is an HTTP framework;it is a Request/Response framework.”
B R O W S E RHTTP + Socket
GET /api/…Lokal
VM
S Y M F O N Y A P P
B R O W S E R
GET /api/…Lokal
Server
S Y M F O N Y A P P
W E B S E R V E R
GET /
D AT E I S Y S T E M
R O U T I N G
Wohin möchte der Benutzer?
posts: pattern: / methods: GET defaults: { _controller: BlogBundle:Post:index }post: pattern: /blog/{slug} methods: GET defaults: { _controller: BlogBundle:Post:detail }
Symfony Routing
#http://example.com blog/first-article/
var Router = Ember.Router.extend({ location: 'history'}); Router.map(function() { this.resource('blog', { path: '/' }, function () { this.route('post', { path: '/:slug' }); }); this.route('about'); });
PushState FTW!!Ember.js Routing
server { listen *:80;
server_name blog;
location / { root /opt/blog/dist; try_files $uri /index.html; index index.html; } }
Konfiguration
/api
location / { root /opt/blog/dist; try_files $uri /index.html; index index.html; }
/
/api/posts ⇒
/app.php/posts
/app.php/api
location ~ ^/api { root /opt/blog/web; try_files $uri @rewriteapp; index app.php; }
location @rewriteapp { rewrite ^/api/(.*)$ /app.php/$1 last; }
location ~ ^/(app|app_dev)\.php(/|$) { root /opt/blog/api/web; fastcgi_split_path_info /(.+\.php)(/.*)$; include /etc/nginx/fastcgi_params; fastcgi_pass 127.0.0.1:9001; }
M O D E L
ember data
var Post = DS.Model.extend({ title: DS.attr(), body: DS.attr(), date: DS.attr(), slug: DS.attr(), comments: DS.hasMany('comment', { async: true }), teaser: function () { return this.get('body').substr(0, 100); }.property('body') });
m o d e l s / p o s t . j s
S T O R E
A D A P T E R
J S A P P L I K AT I O N
R E S T F U L A P I
store.find('post', { slug: ‘awesome-blog-article‘})
GET /post?slug=awesome-blog-article
{ "post": [ { "id": 1, "title": “Mein erster Blog Post", "body": “Das der Inhalt meines ersten Blog-Posts“, "date": "2014-10-04T14:23:10+0200", "slug": "first-post", "links": { "comments": "/app_dev.php/api/posts/first-post/comments" } } ]}
GET /post?slug=awesome-blog-article
/first-post
Router.map(function() { this.resource('blog', { path: '/' }, function () { this.route('post', { path: '/:slug' }); }); });
ro u t e r. j s
ro u t e r. j s
var BlogPostRoute = Ember.Route.extend({ model: function (params) { return this.store.find('post', { slug: params.slug }); }, serialize: function(model) { return { slug: model.get('slug') }; } });
ro u t e s / b l o g / p o s t . j s
Router.map(function() { this.resource('blog', { path: '/' }, function () { this.route('post', { path: '/:slug' }); }); });
DS.RESTAdapter.extend({ namespace: ‘app_dev.php/api’ });
a d a p t e r s / a p p l i c a t i o n . j s
this.store.find('post')GET /app_dev.php/api/posts
this.store.find(‘post’, { slug: ‘first-post’ })GET /app_dev.php/api/posts?slug=first-post
this.store.find(‘post’, 1)GET /app_dev.php/api/posts/1
R E S T F U L A P I
C O N T R O L L E R
R E P O S I T O R Y
D ATA B A S E
R E S T F U L A P I
jms/serializer
willdurand/negotiation
symfony/symfony
willdurand/hateoas
⇒ http://www.slideshare.net/seiffertp
composer require
ember data
T E M P L AT I N G
<div class="container"> {{render 'navigation'}} <div class="container-fluid" id="content"> {{outlet}} </div></div>
a p p l i c a t i o n . h b s
<div class="blog-post"> <h1>{{#link-to 'blog.post' slug}}{{title}}{{/link-to}}</h1> <div class="blog-post-content"> {{body}} </div> <div class="blog-post-comments"> {{#each comments}} {{partial 'blog/comment'}} {{/each}} </div></div>{{#link-to 'blog.index'}}Zurück zur Liste{{/link-to}}
b l o g / p o s t . h b s
var Post = DS.Model.extend({ title: DS.attr(), body: DS.attr(), date: DS.attr(), slug: DS.attr(), comments: DS.hasMany('comment', { async: true }), teaser: function () { return this.get('body').substr(0, 100); }.property('body') });
m o d e l s / p o s t . j s
<div class="blog-post"> <h1>{{#link-to 'blog.post' slug}}{{title}}{{/link-to}}</h1> <div class="blog-post-content"> {{body}} </div> <div class="blog-post-comments"> {{#each comments}} {{partial 'blog/comment'}} {{/each}} </div></div>{{#link-to 'blog.index'}}Zurück zur Liste{{/link-to}}
b l o g / p o s t . h b s
T O O L S
$ ember serve —-proxy=http://symfony-api:80 version: 0.0.43 Proxying to http://symfony-api:80 Livereload server on port 35729 Serving on http://0.0.0.0:4200
$ npm install -g ember-cli
B R O W S E RHTTP + Socket
N G I N X
S Y M F O N Y A P P
GET /app_dev.php/api/…
P H P - F P M
D e v e l o p m e n t
Lokal
VagrantVM
B R O W S E R
N G I N X
S Y M F O N Y A P P
GET /api/…
P H P - F P M
P ro d u c t i o n
GET /
https://github.com/ seiffert/ember-symfony-blog
D A N K E !
F R A G E N ?