In the PHP community we have been professionalizing at an amazing rate, quickly bigger and bigger web-applications are written in PHP to a point where it is almost unheard of to write a huge project without using a Framework and ORM of some sort.
The same is starting to happen in the JS space, having a data-model in the front-end is becoming more and more common. This talk will focus on how Symfony2 (/ Doctrine) can be used as a powerful back-end store for your JavaScript front-end framework models. And if you already have a Symfony2 application how to transition gradually into using more JavaScript in the front-end.
3. WHEN TO USE MODELS IN
THE FRONT-END?
AVARAGE GOVERNMENT COMPUTER BROWSER:
4. WHEN TO USE MODELS IN
THE FRONT-END?
When developing for a modern browser (>
IE8, FF 3.5)
When building a single page application
(SPA) for desktop
When building a single page application for
mobile devices
5. WHEN TO USE MODELS IN
THE FRONT-END?
HYBRID SITUATION
Endusers
(website front-end)
Administrators
(website back-end)
HTML / CSS - Twig Symfony2 Entities Fancy Javascript / HTML5
6. WHEN TO USE MODELS IN
THE FRONT-END?
FROM PROGRESSIVE ENHANCEMENT TO BUILDING
ACTUAL APPLICATIONS IN JAVASCRIPT
SunSpider 2001 until Q1-2009 Google Trends 2007 until 2014
7. SO WHY USE MODELS IN THE
FRONT-END?
MODERN WEBDEVELOPMENT PARADIGM
90's early 2000's Today
8. SO WHY USE MODELS IN THE
FRONT-END?
Code structure
Maintainability
Working in a team of front-end developers
Clear seperation between front and back-end (developers)
The front-end and back-end share a common data model
Stateful
Ability to synchronise state from and to the back-end
Less data usage. (Important for mobile devices)
Less server resources required
Ability to keep data client-side (HTML5 data storage for example)
Bonus: Free API!
RESTFul API supporting both XML and JSON
Import / Export entities to XML and JSON
Easy to migrate to newer versions of the data model
16. SYMFONY2 ENTITY
SERIALIZATION
THERE IS A BUNDLE FOR THAT!
Exposing your routes to JavaScript
FOSJsRoutingBundle
Make your entities automatically serialize
JMSSerializerBundle
Create a RESTFul API, the lazy .. ahum .. easy way
FOSRestBundle
17. EXPOSING YOUR ROUTES TO
JAVASCRIPT
/APP/CONFIG/ROUTING.YML
#IfyoucouldgoaheadandaddthecovertotheTPSreportsthatwouldbeterrific
add_cover_to_tps_report:
pattern:/tps_report/add_cover/{reportId}
defaults:{_controller:OfficeSpaceTpsBundle:Reports:addCover}
#Theimportantpart
options:
expose:true
18. FOSRoutingBundle provides Routing.generate();
Generate a JSON object based on exposed routes
EXPOSING YOUR ROUTES TO
JAVASCRIPT
INCLUDING FOSJSROUTINGBUNDLE DEPENDENCIES IN
<HEADER />
<script
type="text/javascript"
src="{{asset('bundles/fosjsrouting/js/router.js')}}"
>
<script
type="text/javascript"
src="{{path('fos_js_routing_js',{"callback":"fos.Router.setData"})}}"
>
* Ignore the weird indenting
19. EXPOSING YOUR ROUTES TO
JAVASCRIPT
USAGE
varroute=Routing.generate(
'add_cover_to_tps_report',
{reportId:143}
);
alert(route);
20. Adding annotations to your existing entities
http://jmsyst.com/libs/serializer/master/reference/annotations
MAKE YOUR ENTITIES
SERIALIZABLE
JMSSERIALIZERBUNDLE ANNOTATIONS
useRednoseTodoBundleModelTaskasBaseTask;
//AnnotationclassprovidedbytheJMSSerializerBundle
useJMSSerializerAnnotationasSerializer;
classTaskextendsBaseTask{
/**
*...
*...
*@SerializerType("boolean")
*@SerializerGroups({"details","file"})
*/
protected$ready=false;
}
23. MAKE YOUR ENTITIES
SERIALIZABLE
DESERIALIZE AND PERSIST
useJMSSerializerDeserializationContext;
classProjectController{
functionupdateProjectActions(){
$em=$this->getDoctrine()->getManager();
$serializer=$this->get('jms_serializer');
$context=newDeserializationContext();
$context->setGroups(array('details'));
$project=$serializer->deserialize(
$this->getRequest()->getContent(), //TheJSONsendbyJavascript.
'RednoseTodoBundleEntityProject',//Baseentitynamespace.
'json',$context //Format=JSON,Context=details
);
$em->persist($project);
$em->flush();
}
}
24. MAKE YOUR ENTITIES
SERIALIZABLE
GOOD TO KNOW: EVENTLISTENERS
useJMSSerializerEventDispatcherEventSubscriberInterface;
useJMSSerializerEventDispatcherObjectEvent;
classEntityListenerimplementsEventSubscriberInterface
{
protected$user;
publicfunction__construct(ContainerInterface$container)
{
$this->user=$container->get('security.context')->getToken()->getUser();
}
staticpublicfunctiongetSubscribedEvents()
{
returnarray(
array(
'event'=>'serializer.post_deserialize',
'class'=>'RednoseTodoBundleEntityTask','method'=>'onPostDeserialize'
),
);
}
publicfunctiononPreSerialize(PreSerializeEvent$event)
{
$task=$event->getobject();
$task->setOwner($this->user);//Javascriptisnotawareofthesessionuser
}
}
25. Replacing the default ObjectConstructor
MAKE YOUR ENTITIES
SERIALIZABLE
GOOD TO KNOW: DOCTRINE OBJECTCONSTRUCTOR
If entities are not created using Doctrine they will not be referenced to the
existing entity in the database and therefore when persisted new entities will be
created.
But . . . . JMSSerializerBundle provides us a special ObjectConstructor to solve
this issue.
<containerxmlns="...">
<services>
<service
id="jms_serializer.object_constructor"
alias="jms_serializer.doctrine_object_constructor"public="false"
/>
</services>
</container>
26. The Symfony2 way: Convention over configuration is ,
there should at least be 19 ways to do the same thing.
Routing.yml
ProjectController.php
RESTFUL API THE EASY WAY
BASIC ROUTING ANNOTATIONS
stupid
todo_app:
resource:"@RednoseTodoBundle/Controller/"
type: annotation
prefix: /
useSensioBundleFrameworkExtraBundleConfigurationRoute;
/**
*@Route("/project/{projectId}",requirements={"id"="d+"},defaults={"id"=1})
*/
publicfunctiongetProjectAction($projectId)
{
returnnewResponse("Thesearenottheprojectsyou'relookingfor");
}
27. Configuration equals extendability:
RestBundle provides us with CRUD annotations
RESTFUL API THE EASY WAY
CRUD ROUTING ANNOTATIONS
convenient
useFOSRestBundleControllerAnnotationsGet;
useFOSRestBundleControllerAnnotationsPut;
/**
*Getallproject
*
*@Get("/projects",name="todo_app_projects_read",options={"expose"=true})
*
*@returnJsonResponse
*/
publicfunctionreadProjectsActions(){/*...*/}
/**
*Updateaproject
*
*@Put("/project",name="todo_app_project_update",options={"expose"=true})
*
*@returnResponse
*/
publicfunctionupdateProjectActions(){/*...*/}
Using @GET and @POST routes in your docblocks will create a self-documenting API !