When I started at the day job about 6.5 years ago, I inherited a jumble of custom PHP/MySql web applications. One of my first goals, after migrating a Drupal 6 site to Drupal 7 (ugh!), was migrating the custom PHP applications to a coherent and supported framework. I evaluated a few smaller frameworks (Slim, F3), but landed on Laravel. So I migrated the largest custom app, known as the Metrics Database, to Laravel 4 (and eventually to Laravel 5). In the process I created a “meta-app” known as the Portal that housed the Metrics Database, provided some administrative features and development commonalities, and was supposed to eventually house all of the custom applications. As it played out, I only got a chance to build a few new “apps” in the Portal, and never got a chance to migrate existing legacy apps. But the Portal is still running on Laravel 5, and as other organizational priorities arose and key people left, my maintenance of Portal diminished. In retrospect, it was so rock solid it kind of just ran without needing much intervention.
Anyway, Laravel itself keeps getting updated, but the version of Laravel that Portal and Metrics are running on is falling out of date. One of the things I don’t like about Laravel is it’s need to constantly update. In the enterprise world, the release cycle seems too short and too fast (anybody remember Laravel Bundles?). (Other things I don’t like about Laravel: facades! Blade over Twig … why?! Unsupported packages! I had to rewrite a modules package for Laravel.) So, since we are moving all of our content sites to Drupal 8, which is built on Symfony, we decided to migrate the Portal to Symfony rather than upgrade the Laravel version.
Why move to Symfony? There is still a lot of custom code and legacy cruft in Metrics and Portal, and really, the Laravel portion is fairly small so we don’t need a batteries included solution. Several of the major Laravel packages we use (Administrator and Verify are the main ones) aren’t supported anymore anyway, so we have to re-implement those either way. We like the Symfony release cycle and ecosystem a little better. And like I said, we have experience with Symfony from using Drupal 8.
So with all that out of the way, I started the migration process a couple sprints back and like most things, it’s held to the 80/20 rule. The initial work went very fast. I migrated the Staff Mobile site from Laravel to Symfony in about a day. Another day and I had the main Portal app migrated, minus a couple of administrative features.
The easy steps include migrating Blade to Twig. You could probably automate this as it’s nearly a one-to-one translation. Blade uses @foreach(iterable as item) while Twig uses {{ for item in iterable }}. There were a few tricky parts here, like recreating a custom Blade directive as a Twig macro.
Things like caching are very similar. In Laravel, it’s something like
$cache->put($name, $data, $time)
while in Symfony it’s $cache->set($name, $data, $time)
Eloquent, the Laravel ORM, is also pretty similar to the Doctrine QueryBuilder, so that merely entailed switching things like this:
$current_table->fields = \DB::connection('metrics')
->table($table_desc)
->where('display_name', '!=', '')
->where('display', '1')
->whereNotIn('field_name', $this->ignore_fields)
->get();
to something like this:
$current_table->fields = $conn->createQueryBuilder()
->select('*')
->from($table_desc)
->where('display_name != ""')
->andWhere('display = 1')
->andWhere('field_name NOT IN ("' . join('","', $this->ignore_fields) . '")')
->execute()
->fetchAll(FetchMode::STANDARD_OBJECT);
Routing in Laravel was done in a routes.php file, while in Symfony I’m using annotations, so that took some manual massaging to move all the routes. But I’m finding I like having the routes right next to the methods using annotations.
In other cases, some classes have migrated nearly wholesale from Laravel to Symfony services. An API client wrapper and a Kitchen Sink helper class I wrote migrated with only a namespace change. Autowiring in Symfony is pretty nifty, although the configuration overhead for things like the Security system is a bit ridiculous in my view.
The Metrics Database also has a REST API, and migrating that was very straight forward, although the default output of Laravel JSON Responses differs slightly from Symfony. For instance, empty responses are arrays in Laravel, while they are objects in Symfony.
So far, the most difficult part has been migrating features like roles and permissions from a Laravel package to a solution using the built-in Symfony auth system. I recently implemented SAML in the Portal, and now I’m having to migrate that to Symfony. It took me a week to wrap my head around the Symfony security system to get SAML authentication working.
Next most difficult has been getting the tests working. I use codeception for acceptance tests, and there is a lot of custom, kind of Laravel-specific code involved (like mocking Laravel sessions). I’ve had a difficult time getting all the tests to run to successfully.
Also, I’m dropping a few features. For instance, the current app allows users to save files to their Google Drive for editing, but only a handful (8) of people have used that feature in the past year. It’s not worth keeping.
I still have quite a bit on the todo list (database sessions, SendGrid API, logging among other things), but all-in-all, I don’t think it’s been a bad migration considering I haven’t been able to dedicate all of my time to it. I briefly wavered and investigated just upgrading Laravel, but like I said, I’d have to re-implement most of the difficult stuff anyway, so I feel comfortable with the Symfony direction.
If anyone out there is in a similar boat, or thinking about going this direction and has questions, please let me know. Would be happy to answer questions.
So if I had my eye on an app and wanted to port it over to Symfony, are you in the market to do that kind of thing for someone? Let me know and lets open a dialog.