September 1, 2016 · PHP Laravel Symfony Coding

Laravel vs. Symfony: Which PHP framework should you use?


(This comparison was finalized in September 2016 when Symfony 3.1 and Laravel 5.3 were the most recent versions.)

This post has been "brewing" in my head for a while. Instead of waiting longer, I thought I'd write it out and then update it with time.

As context, I worked on websites as a teenager at the end of the '90s, as a student even worked with C++ and later some C#, but then discontinued web development activity for about 6 years (but continued with IT admin stuff). I later started blogging (with WordPress), then decided to create small websites with flat PHP. One of the sites became larger and more complex, so I set out to learn how to organize the code a bit better. This was about 3 years ago, and I learned Laravel (4.2). 1 year ago, as a freelancer, I started developing with Symfony2 with a colleague. Now I've created small to medium sized websites - several on my own - using Laravel and Symfony. My web coding workload is split about 50-50 between Laravel 5 and Symfony 2 / 3.

Currently I host the local Laravel Meetup here, and I also enjoy going to the Symfony Meetups and occasionally to the PHP and other Developer events. So these thoughts are definitely biased (how can they not be), but I hope can still give some useful insights.

I want to go through some of parts of each framework and compare them and also point out things I like or dislike about them. You'll find a summary at the end including some recommendations for using the framework.

Speed of Development

It's exciting to see that Laravel is developing features really quickly and their ecosystem is thriving. Elixir, Spark, Echo, Scout are just some of the things that have popped up recently. Forge (Server Provisioning) and Envoyer (Deployment) are nice online services that I've come to use, not just for Laravel, but also for Symfony. And even Symfony has adopted Laravel's Homestead as a development Vagrant box. Development is so fast, I feel like they've taken over the motto that was used by Facebook for a few years "Move Fast and Break Things".

On the flip side, it's sometimes a bit more work to keep up. The upgrade from version 4.2 to 5.0 included: The recommended method of upgrading is to create a new Laravel 5.0 install and then to copy your 4.2 site's unique application files into the new application. This would include controllers, routes, Eloquent models, Artisan commands, assets, and other code specific files to your application. There were some big-ish changes, but fortunately, my app was not that large and I didn't do custom changes yet so it only took a few hours to upgrade, but I imagine other people might have more to do. There were also some changes in the directory structure. And here and there you might find some small features that are still undocumented.

Their 5.1 LTS (long-term support) Release shows that Laravel is committed to becoming more reliable for long-term projects. But this means you will also miss out on some (also larger) features. E.g. Laravel Echo or Scout only comes in version 5.3.

Symfony has been around the block a longer time, so their upgrades usually don't mean really big changes (I've heard the change from Symfony 1 to Symfony 2 was huge). They've laid out a Release Plan that can go indefinitely and it seems to me it's easier to keep up (e.g. the new major version is like the latest version of the previous major version - just without the deprecated features, e.g. 4.0 will be like 3.4 just without the deprecated stuff). But I've only worked with Symfony for a bit over a year, so this is very subjective.

ORM / Databases

Laravel's Eloquent was very cool to use in the beginning, but now I'm starting to like Symfony's Doctrine more and more. Yes, you can change the ORM and such, but these are the ones that come out-of-the-box and are most popular, so I will focus on these.

One of the things I've come to like a lot is that the Repository Pattern is more strongly built-in with Symfony. So instead of calling this with Eloquent

$activeUsersAfterDate = User::where('active', true)->where('blocked', false)->where('signup_date', '>', $date)->get();

I would use the EntityRepository in Symfony

$em = $this->getDoctrine()->getManager();
$activeUsersAfterDate = $em->getRepository('User')->getActiveUsersAfterDate($date);

and when updating that function call, I just need to update one function, instead of updating it everywhere I used it.

I also like the fact that entities in Symfony are simple PHP objects. So I can also call

$streetName = $company->getStreetName();
$address = $company->getAddress();

if I have the getter in the class. It's also less overhead compared to a Laravel Eloquent Model. Yes, it's a bit of extra work to generate the getter and setters, but that shouldn't be a problem with a modern IDE.

A class in Laravel might look like this

<?php
use namespace App\Models;

class Company {

}

And then I would get the data via

$streetName = $company->street_name; // "magic"
$address = $company->street_name . ' ' . $company->street_number ', ' . $company->city->name; // one option
$address = $company->getAddress(); // another option

which means I have to resort to underscores for (magic) class variables or I mix the the way I get data from the models.

And when using relationships, I might be using

<?php
use namespace App\Models;

class Company {

    public function users() {
        return $this->hasMany('App\User');
    }

}

and later I can get the users Collection via $company->users. The problem was in this case: I had a column called users as well (which was a text column with JSON string data). So when I used $company->users it was actually calling the column, not the relationship. It took me a while to realize that, because the errors were only shown during run-time (e.g. when I did a foreach on the column).

And by using magic, there's no real auto-completion directly available. You can use a generator / IDE-helper to do this work and generate annotations (and you need to update it every time you make a change) and it'a really good thing someone developed this (thanks, barryvdh!). But I prefer that the entity / class is the thing I define, and based on that the database structure is updated (this is done in Symfony). In Laravel, you would create your database migrations and then based on that, the IDE-helper would create annotations to do auto-completion. And - as I understand as of now - these class variables would all be public, whereas in Symfony you can set private / public / protected and use getters and setters to e.g. build in validation into the functions.

One small thing that also annoys me a bit, is that now I can't easily override a constructor that uses Eloquent as a base class. E.g. I can't just do

class Company extends Eloquent
{  
    public function __construct($name, array $attributes = [])
    {
        $this->name = $name;
        parent::__construct($attributes);
    }
}

because of Eloquent processes happening in the background.

Forms

Laravel dropped the forms package out of the core in Laravel 5, which is a shame, I had hoped - since forms are important in many apps - that this will be further developed. But now it is maintained by the community.

What I like about Symfony's forms is that you can create a PHP form class. So I can include some validation and options there as well and it's more encapsulated.

With Laravel's forms, I feel like, I need to remember more things and where to place them, e.g. the validation, the code generation, the updating to the models - it's all a bit more separate.

Though: I had sent this article for feedback to a fellow PHP developer, and she really likes the Form Request classes for validation in Laravel. So perhaps it's more a matter of taste.

Symfony has given the option to use Bootstrap form templates into their framework. Of course quite a risky choice and I saw this was also met with some criticism, but using Bootstrap helped me tremendously in getting speedier when starting to develop a new project. I don't need to worry about CSS layouting / styling early in the project for it to look decent.

Mails

The other way around (i.e. where I like Laravel's approach better) is when using email services. Laravel 5.3 introduced a new Mailable class which will allow you to setup the emails in a class. This encapsulates the stuff to be mailed. Together with the integrated connections to the different mail services in Laravel, this should make mail-sending a breeze! Really looking forward to using this.

Routing

I think this is strongly a matter of preference. Where Laravel has all the routes in a routing file (though not fully true anymore in 5.3), Symfony is more flexible and gives you several options. I use both with no problems, though I prefer Symfony's a bit, because the routes are closer to the actual code they execute.

Templating Engine

Laravel defaults to Blade while Symfony uses Twig. Twig is a bit more mature and is a bit more powerful, while blade depends a lot on PHP. It bothered me a bit in the beginning that I couldn't use PHP snippets in Symfony's Twig, but it had forced me to separate the views from the logic more, and I think that's a good thing.

(By the way: Here's an insightful write-up on how Twig became the templating engine for Symfony - it's 7 years old as of now - but still an interesting read!)

»Magic« in Laravel

For me, Laravel has too much »magic«. I've started using Facades, but similar to other »magic« behind Eloquent, I have trouble finding out, which function I can call and it's difficult using auto-completion. I feel like it's a bit of detective work to see what's behind it.

Similarly, I've had trouble customizing constructors / classes for Eloquent models. Or quickly figuring out the variables within an Eloquent model (without using an IDE-helper generator).

Or table names are plural names of the class, e.g. the Phone class will create the phones table. As a rule, I always overwrite it by using

protected $table = "phone";

now. And always in singular. Less headache. E.g., what happens with the class "Ox"? Will it create a table "oxs"? Or "oxes"? Or the correct plural "oxen"? What if someone uses e.g. class names in German (though I do all programming stuff in English)? E.g. the German word for boat is "Boot / Boote" (singular / plural). It might create the table "boots", which doesn't make sense. It might just be a small thing, but for me it's magic-by-default sprinkled on the wrong spot.

Built-in interfaces and modules for different service services

I think this is where Laravel shines and you can see what Laravel's purpose is. It's so easy changing e.g. mail providers. With a few small changes, you can use smtp or mailgun or mandrill. Or for queueing, it's easy moving from sync (i.e. not really queueing) to RabbitMQ or Redis. Same goes for the Caching system.

As you can see, Laravel can really give you an advantage in the beginning. And if you just need to test something and do prototypes, it's really fast. With Laravel Cashier for payments and Socialite for social logins, I think many of the routine tasks that need to be done are even more simplified.

In Symfony, you often need to add an external Bundle to have some of those features. Which would then solve that problem, but sometimes the speed of the development of some of those features don't keep up with the speed of Symfony and the framework. So if you want to upgrade to a new Symfony version and there's a really important Bundle you need as well, you might just need to wait until they're compatible (or fork / create your own). Using OAuth for social logins in Symfony was a headache for me - there were different documentations for different versions, written by different people. I finally got it to work, but then integrating it nicely with FOSRestBundle was another headache.

Laravel 5.3 - with Passport (OAuth 2 server for Laravel Apps), Mailable (classes for setting up mails) and Scout (driver-based full text search) - has now upped the game even more when it comes to features / services.

Documentation

I find both documentations really good. I like that Symfony has a Best Practices part which also gives you suggestions on how to do something more efficiently. Though Laravel has the excellent Laracasts if you want to dig deeper (with Knp University there's something similar for Symfony, but Laracasts is already much bigger by now). Actually, I would suggest any PHP developer to subscribe to Laracasts.

In the Symfony documentation it also gives you hints or nudges you to try this or that. Whereas with Laravel, I have the feeling, they give you the tools and you are more open to use as you want. Which is good, but might make you a feel a bit lost at times.

Where I use what. And some thoughts.

I think for simple web apps with some simple functionality, both Symfony 3.1 and Laravel 5.3 are really good. I'd suggest using the one you're more comfortable with.

For things where I need stability and where I know a whole team will work on it, I would use Symfony. The maintenance is a bit easier and more predictable and Symfony can be more strict (which is - I think - good when working in a team). For many important modules there are usually external bundles as well, but they might not keep up with the speed of Symfony development (e.g. FOSRestBundle and FOSUserBundle are things I use a lot).

For small to medium-sized apps where I might need social logins, integrated payments, queues, special caching (all that fancy stuff!) - an app that might not need to be updated a lot in the future (e.g. a simple one-time project that just needs a bit of maintenance work every now and then) - I might use Laravel. The development work is still a bit too unpredictable for my taste, but I will use Laravel when it seems a better fit. For long-term apps in Laravel, I would suggest having a very experienced developer to guide the development. Laravel's integration is more geared towards web sites / apps, but it's a bit more lenient on how you implement things, so it might also allow for more bad coding standards.

In Closing

I do like to use both frameworks, there are things that I like from each framework that I prefer over the other's framework, but possibly it's also a matter of taste.

A very positive thing is that with the thriving ecosystem for both frameworks, PHP developers are profiting from it.

From Laravel's side: Laracasts is an excellent online resource for any PHP developer. I use the Homestead Vagrant box for PHP development in general. For prototypes and first versions I like to use Forge for server provisioning and Envoyer for deployments. Laravel pushes me to try new things, e.g. since Forge already moved to PHP7 quite early, I was already using it just shortly after it was in stable. It also made me look for other things that I can test (caching, DBs, etc).

From Symfony's side: Other than the fact that Laravel uses some of Symfony's packages for the ground work, I really like using SensioLabsInsight for Project and Code Analysis and sometimes blackfire.io for (deeper) web performance analysis. Symfony encourages me to do things cleaner and when I started using SensioLabsInsight, it also encouraged me to try additional quality checking tools.

I wish it were possible to use parts of Symfony and Laravel more interchangeably, but for now I'm glad that these 2 frameworks continue to be developed and become better, giving PHP developers more modern tools and services at their disposal.


Elephants in Laravel and Symfony color, based on twemoji

Cover image based on twitter/twemoji (CC-BY 4.0 license)

  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket
Comments powered by Disqus