If you have errors like “No media linked to the object” (Field error) and “The file could not be found.” (form error) in your SonataAdmin form or Symfony2 form when you are trying to add File constraints validation to SonataMedia, you are probably in the right place!
What I wanted
I needed to have a job application form containing 2 file fields (a CV and a motivation letter) on a Symfony2 website.
These 2 files should be PDF or DOC lesser than 4MB.
All job applications should be listed on the backoffice and files downloadable.
I already use the great Sonata bundles suite (Admin + Media + Formatter) and wanted to manage them with it.
First try with errors
So I first write my JobApply entity properties like these:
With this form:
But it can’t works like that, errors “No media linked to the object” and “The file could not be found.” appears because I tried to add File validation to a Sonata media entity!
Solution with Sonata InlineConstraint
After navigating throught Sonata CoreBundle/AdminBundle/MediaBundle documentation and the few stackoverflow/blog ressources availables I came to this solution: Using Sonata InlineConstraint
1. Create a service which will be called to validate your entity:
2. Add the InlineConstraint class constraint to your bundle’s validation configuration file:
You can see that I call the validate function on the service created previously.
3. Add the entity media properties like this:
4. Build your validate function:
You can use $errorElement->addConstraint(new NotBlank()) syntax instead of $errorElement->assertNotBlank() for all constraints.
Validator component will be used in all cases.
You can see that my validation service class is my form type class.
It’s not a best practise but like that, I have my validate function where my form is build..
I used Jasny Bootstrap to skin file input. It was not simple and maybe the subject of a next post.
YourPhotoTravel is a social network about travel experiences, we launched it first 2 years ago.
We worked hard with Jerôme and Yoann to release this new version. Members can now create and share their Etins more simply than ever! (An Etin is the name we choosed to speak about a trip, trek, cruise,.. on the service.)
The service is only in French but English is coming soon..
This second version rocks for several reasons:
A new design 100% responsive, you can use it on tablets and phones (All Etin editor features included).
An easier navigation with less and clearer pages.
New features like the ability to build a real book based on your Etin, visualize it with an online flipbook and order it (only France delivery currently)!
Use of all last frameworks version (Symfony2, AngularJS,..) which improves performances and functionnalities.
The new Etin editor, an SPA using AngularJS:
I’m gonna talk more about the new Etin editor I coded.
The last one used backboneJS, the widest used frontend framework at this time.
And Flash was still alive and even needed for some fonctionnalities like image resizing on client side, photo editing, and upload progress.
Now, 2 years and a half later, web development changed a lot.
Browsers are almost all evergreen, more and more html5 apis are usable (geoloc, history, canvas, drag&drop,..) and AngularJS has emerged and became the new standart frontend framework.
Firstly, I coded all the Rest api with FosRestBundle (the whole service uses Symfony2) and a simple html page to test it (AngularJS is wonderfull to quickly code a basic interface). Do it first is a best practise or simply logic.
Bower is a good friend to manage your frontend libraries and dependencies, like Composer for PHP.
For the style, SASS is needed, impossible to work without nowadays. Compass was not essential. Note this lib sass-css-importer to import CSS files inside SCSS (don’t know why not a default feature, LESS does it..).
Grunt is a tasks runner with many many plugins for everythings. I build 3 tasks (ypt-dev / ypt-watch / ypt-prod) to suit my needs. JS files are ngAnnotate, concat and uglify, my templates are added to $templateCache with ngtemplates and CSS are minified.
Use AngularJS 1.3 with latest features like $compileProvider.debugInfoEnabled(false), one time binding, as inside ngRepeat,..
Images resized on client side, sended with upload progress, and added to the interface.
Image edition with canvas and KineticJs. You can crop it and apply filters.
Native HTML5 drag and drop at several places.
Mobiles/tablets version (small resolution) with CSS media queries and special features (avoid native DnD).
Nested routing with ui-router using clean HTML5 urls (no more # inside).
Add your places on the map with autocomplete or by dragging markers. Edit your path or import your tracking files (KML and GPX support).
Working with 100% REST api but use the classic Symfony2 authentification system (no token), you can’t access the webapp if you are not logged (of course all rest are secured too).
Translations: I choosed to keep the Symfony2 yml file (because my app is inside a dedicated bundle) and use grunt tasks to convert it to json with grunt-convert and then to js with grunt-json.
Finally, angular-translate brings it gracefully in the application.
All libraries used:
Here are all frontend vendors used (bower list --offline).
The goal wasn’t to use as much as possible but it’s a fact that with angular ecosystem, more and more libs are well-tested and works really well together.
angular-ui-router: A must-have to build nested routes in your app.
lodash: Similar to underscore, it avoids a lot of loops.
animate.css: A small CSS3 animation lib who works nice with angularjs.
blueimp-load-image: Resize JPG images on client side.
angular-google-maps: Good but use carefully, I used polylines with it and performances sucked.. Finally, I get the map instance and add it “manually”.
kineticjs: An old but heavy library to work with canvas, documentation not really good and up to date.. I don’t think I’m gonna use it for my next project.
angular-bootstrap: Usefull to work with bootstrap-sass-official, $modal service wich return a promise is really cool.
restangular: A must-have for your api calls.
ng-file-upload: Sadly, restangular doesn’t handle progress event on upload request because it uses $http, issue here. This lib handles it.