A while back I was working on an application where the back end is a full stack Symfony application. The front end had some rich functionality built with React. I was working on decoupling the front end completely by having the Symfony app serve only as an API backend.
I eventually abandoned this approach because it added complexity and yielded few benefits in that case. I still wanted to keep working with these two applications fluent, so I decided to share the state of the object via a dedicated object.
For that case it worked out fine, so I thought I would share the simple concept via a prototype implementation. The prototype is built with Symfony, but the simple concept can be used anywhere in PHP and why not other languages as well.
Prototype application description
The prototype application itself is deliberately limited in scope. It is a simple application that displays apartment listings from a relational database.
You can choose a few options and on the front page the app uses an API to add load data asynchronously.
- Using the React view library
- Using the Vue.js view library
The implementations are very bare bones and are not in themselves very useful and definitely need a supporting client side state management library like MobX or Redux, but they can provide insight into how the three approaches work.
Sharing state between Twig and client side apps with an object
This just contains properties like the apartments, sorting, etc. and in implements the JsonSerializable interface so we can serialize it later. The AppState objects are created in controller actions:
The apartments are populated from a Doctrine ORM repository and other properties should be set on whatever you desire at the state when the page is loaded. In the end we'll pass the Twig templating engine the object as well as the serialized state.
The Twig template itself loops through apartments and prints out the views with standard syntax, just next to what happens to be the root of our Vue.js root node (we will focus on Vue.js for brevity):
Once this is complete then upon pageload the server renders the Twig view, but the front end application shares the same data and with Vue templating even the syntax looks very similar:
The simple JSON API is powered by the same exact state object, so I can already know what my endpoints will return:
And that's pretty much all there is to the prototype, really.
For example if I were to add a new field to my Doctrine entity to an app that partly works via Twig, partly via Vue.js and partly via API, I would only need to take these steps:
- Add field to entity
- Add output where applicable in Twig
- Add output where applicable in Vue.js templates
If I didn't share the same object I would need to make sure the properties are passed forward here and there, invent some new variable names and so forth. I feel approach makes things more predictable, at least when working on a project of limited scope and complexity.
For larger projects you could expand the approach by having more State objects for each bundles, for example. This will allow developers to have insight into what should (no mechanism enforces using this) be available in a front end initial state object, simply by peeking in the bundle's State directory.
As a bonus when working with TypeScript you could create a Symfony command to serialize your dynamic models to TypeScript Type Definition and dump them to state.d.ts files to have insight into the data structures at front end development time:
I would be very interested in hearing feedback on it as well as any weaknesses or limitations you might see with this approach.
- A Symfony hybrid app sharing state object with Twig, React and Vue
- Universal Rendering in PHP/Twig could be done with the Angular 2 Template Compiler
- Testing React.js isomorphic rendering with php-v8js and the Symfony Microkernel
- What is TypeScript and why should I care?