Definitive Laravel 4 to Laravel 5 Migration Guide

Warning: Here Be Dragons

We like to stay on the cutting edge, so when Laravel 5 was released in February 2015, it wasn’t a question of whether or not we would migrate our 4.x applications across… it was just a matter of when.

After a few slightly painful (but successful) migrations, we decided to put together a comprehensive guide. Hopefully this will serve as a useful guide for other developers who want to bring their Laravel applications up to the cutting edge without pulling their hair out.

Disclaimer

This is a work in progress.

Use it in conjunction with your own common sense, and please report back if you have any suggestions or additions.

What’s changed?

Laravel 5 is a natural progression from 4.x. It takes the same design patterns and philosophies from earlier versions and leverages them to a larger extent to create a more opinionated and cohesive framework.

While there are a lot of similarities with older versions, there are some fundamental changes that make migration non-trivial. The most obvious non-trivial change is a new folder layout. Another important but less obvious difference is the shift to a architecture that more heavily relies on service providers. These changes, along with some subtle interface changes, mean that upgrading is not simply a matter of updating the vendor dependencies.

Migrating from 4.x to 5

The official recommendation is to not bother directly migrating 4.x applications to 5. Instead, it’s suggested that you create a brand new L5 application and then copy and paste things across.

For our projects, we ignore this advice and migrate our projects in-place: we update the dependencies, mould the old project into the L5 format and copy anything missing across.

There isn’t a clear answer as to which method is easier. Both are hard.

With that said, here are the steps we used to migrate our apps across.

You’ll need to clone an instance of Laravel 5 alongside your 4.2 app so you can pull across files. We’ll assume you do this in the parent folder of your project, so you should have something like this:

~/Sites/your-L4-project

~/Sites/laravel5

Assuming you’ve done that, let’s begin…

Step 1. Update Dependencies & Autoloading

The first step is to pull in the latest version of the Laravel framework using composer. Open up your composer.json file and bump the “laravel/framework” dependency as such:

    "require": {
        "laravel/framework": "5.0.*"
    },

This isn’t the only change to the composer.json manifest. You’ll also need to update the following:

    "autoload": {
        "classmap": [
            "database"
        ],
        "psr-4": {
            "App\\": "app/"
        }
    },
    "autoload-dev": {
        "classmap": [
            "tests/TestCase.php"
        ]
    },
    "scripts": {
        "post-install-cmd": [
            "php artisan clear-compiled",
            "php artisan optimize"
        ],
        "post-update-cmd": [
            "php artisan clear-compiled",
            "php artisan optimize"
        ],
        "post-create-project-cmd": [
            "php -r \"copy('.env.example', '.env');\"",
            "php artisan key:generate"
        ]
    }, /** etc...*/

You can also remove the "minimum-stability" clause.

An important difference between Laravel 4.x and Laravel 5 is that it’s now assumed that we will have a top-level namespace within which all our application code will reside. This is set to “App” by default. There are two implications that we need to be aware of here:

  • Our models and controllers no longer exist in the global namespace. So \User becomes \App\User and \UserController becomes App\Http\Controllers\UserController.
  • The PSR-4 declaration in our composer.json manifest links the top-level namespace for our application to the app/ folder. This means everything under app/ now exists within a namespace.

Note that Laravel sets our top-level namespace to “App” by default, but we can change this. We’ll do this during a later stage in the migration process.

Step 2. Update Bootstrap files

The way Laravel starts up an application has completely changed from version 4.2. As a result, you’ll need to make a number of changes:

  1. Copy public/index.php from the Laravel 5 repository, replacing the old 4.x version.
  2. Delete bootstrap/paths.php and bootstrap/start.php.
  3. Replace bootstrap/autoload.php and bootstrap/app.php with the versions from the L5 repository.
  4. Also copy the ./artisan executable from L5.

The required commands:

$ cp ../laravel5/public/index.php public/

$ rm bootstrap/paths.php

$ rm bootstrap/start.php

$ cp ../laravel5/bootstrap/autoload.php bootstrap/

$ cp ../laravel5/bootstrap/app.php bootstrap/

$ cp ../laravel5/artisan artisan

Step 3. Move config folder

Configuration in Laravel 5 has been completely overhauled. The first big change is that your config folder now lives at the top-level of your project, not app/config, so go ahead and move it:

$ mv app/config/ config/

The next big change is that environment-specific config files are no longer available. Instead we will use environment variables, which can be set at the OS-level, or using .env files.

This means you’ll need to remove any environment-specific config files (i.e.: subfolders under config/ such as “local”, “testing”, etc…).

Instead, any environment non-specific configuration will go in the config files, and environment-specific values are set using a .env file (or environment variables). Here’s an example of a .env file:

APP_ENV=local
APP_DEBUG=true
APP_KEY=SomeRandomString
DB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
CACHE_DRIVER=file
SESSION_DRIVER=file

As well as this, there have been some minor updates to the internal structure of some of the configuration files. Here’s a list of the notable changes:

  • app.php: a new ‘log’ key with default value ‘daily’ is included.
  • app.php: the service providers and class aliases (facades) have been completely overhauled (see section below for details).
  • app.php: default path for ‘manifest’ is updated to storage_path().’/meta’
  • auth.php: ‘reminder’ key in L4.x is now ‘password’ in L5.0
  • cache.php: pretty much completely different. I’d recommend copying and pasting it from L5.0 and then manually updating the config.
  • compile.php: includes new app service providers. Again, easiest option is to copy and paste it.
  • filesystems.php: a new config file for the Flysystem (file system abstraction layer) service. Copy this across from the L5.0 repo.
  • remote.phpdeleted. This is no longer included.
  • sessions.php: add the key ‘files’ with default value storage_path().’/framework/sessions’
  • view.php: another complete overhaul; just copy and paste it.
  • workbench.phpdeleted. This is no longer included.

Step 4. Service Providers and Facades

As mentioned above, the providers and alises keys within the app.php config file have been completely overhauled to support Laravel 5’s new architecture.

If you haven’t copied the new version of config/app.php from the Laravel 5 repository, you’ll need to add and delete a number of class aliases and service providers manually.

Removed Facades (AKA class aliases)

These are the facade (class alias) declarations that need to be removed from app.php:

  • ‘ClassLoader’ => ‘Illuminate\Support\ClassLoader’
  • ‘Controller’ => ‘Illuminate\Routing\Controller’
  • ‘Form’ => ‘Illuminate\Support\Facades\Form’ *
  • ‘HTML’ => ‘Illuminate\Support\Facades\HTML’ *
  • ‘Paginator’ => ‘Illuminate\Support\Facades\Paginator’
  • ‘Seeder’ => ‘Illuminate\Database\Seeder’
  • ‘SoftDeletingTrait’ => ‘Illuminate\Database\Eloquent\SoftDeletingTrait’
  • ‘SSH’ => ‘Illuminate\Support\Facades\SSH’
  • ‘Str’ => ‘Illuminate\Support\Str’

Added Facades (AKA class aliases)

Here are the new facade declarations introduced by Laravel 5. You’ll need to add these to app.php:

  • ‘Bus’ => ‘Illuminate\Support\Facades\Bus’
  • ‘Inspiring’ => ‘Illuminate\Foundation\Inspiring’
  • ‘Storage’ => ‘Illuminate\Support\Facades\Storage’

Removed Service Providers

A number of service providers have been removed. Delete these declarations from the app.php file:

  • Illuminate\Session\CommandsServiceProvider
  • Illuminate\Html\HtmlServiceProvider *
  • Illuminate\Log\LogServiceProvider
  • Illuminate\Database\MigrationServiceProvider
  • Illuminate\Remote\RemoteServiceProvider
  • Illuminate\Auth\Reminders\ReminderServiceProvider
  • Illuminate\Database\SeedServiceProvider
  • Illuminate\Workbench\WorkbenchServiceProvider

Added Service Providers

Finally, Laravel 5 introduces a number of new service providers. Make sure you add these to the app.php file:

  • Illuminate\Bus\BusServiceProvider
  • Illuminate\Foundation\Providers\FoundationServiceProvider
  • Illuminate\Pipeline\PipelineServiceProvider
  • Illuminate\Auth\Passwords\PasswordResetServiceProvider
  • App\Providers\AppServiceProvider **
  • App\Providers\BusServiceProvider **
  • App\Providers\ConfigServiceProvider **
  • App\Providers\EventServiceProvider **
  • App\Providers\RouteServiceProvider **

Notes

*: the HTML and Form facades (and supporting service provider) are no longer included in the default Laravel installation. If you want to continue using these, continue reading as we’ll discuss this at the end.

**: you’ll notice that a number of service providers are introduced under the top-level namespace of your application (which will be “App” by default). We’ll need to manually copy these classes across from the Laravel 5 repository, which we will do in Step 6.

Step 5. app Folder Layout

We’ve already moved config/ out of the app/ folder, but that’s not all that needs to get shuffled around. app/ will eventually become the root-level for our application-specific namespace (this may have been app/src/ for your L4.x project), but before this can happen, we need to move out everything else.

We can start with two easy changes. Move database and tests out:

$ mv app/database/ database/

$ mv app/tests/ tests/

The app/storage/ will also live at the top-level of the project, but there have been some internal changes to the folder structure. My recommendation here is to completely remove the app/storage folder from your L4.x project and simply copy the new storage/ folder from L5.0 across.

$ rm -rf app/storage/

$ cp -R ../laravel5/storage/ storage/

One more easy change: the app/start/ files are no longer used, so you can go ahead and delete the folder:

$ rm -rf app/start/

Finally, if you already have a top-level namespace for your app code, you should move the source files into the app/ folder. For example:

For our L4.2 projects, we have a top-level project-specific namespace, ProjectName. This is defined using a PSR-4 namespace declaration in composer.json:

"psr-4": {
    "ProjectName\\": "app/src/"
}

When migrating to L5, we move all the classes from app/src/ into app/ and update the autoloader declaration:

"psr-4": {
    "ProjectName\\": "app/"
}

Step 6. Add New Service Providers

You would have noticed in Step 4 above that we added a number of new service providers under the “App” namespace. Here they are again:

  • App\Providers\AppServiceProvider
  • App\Providers\BusServiceProvider
  • App\Providers\ConfigServiceProvider
  • App\Providers\EventServiceProvider
  • App\Providers\RouteServiceProvider

We need to create a folder under app/Providers and then copy these providers across from the Laravel 5 repository:

$ mkdir app/Providers

$ cp ../laravel5/app/Providers/* app/Providers/

Step 7. Create the resources folder

Laravel 5 has a new folder which will contain all the “front-end” parts of your Laravel codebase. This is the “resources” folder” – it contains assets (LESS / SASS files), translation files and templates (views).

Go ahead and perform the following steps to create it and then move your existing assets across:

$ mkdir resources

$ mv app/views/ resources/views

$ mv app/lang/ resources/lang

This is an optional step, but if you’re using a CSS processor for your project and want to leverage Laravel 5’s new asset pipeline, you should create the following folder and then add your LESS files to it:

$ mkdir -p resources/assets/less/

Step 8. Blade templating

Laravel 5.0 will now escape all output from the {{ }} and {{{ }}} directives. What this means is that any HTML that you output using these directives will be entity-escaped.

This is particularly impactful for your application if you’re currently using the Form and/or HTML facades.

At any position in your application where you are generating and outputting HTML using either of these directives, you will need to use a new directive: {!! !!}

An example. Before (Laravel 4.x):

{{ Form::open(['route' => 'users.store', 'method' => 'post']) }}

… and after (Laravel 5):

{!! Form::open(['route' => 'users.store', 'method' => 'post']) !!}

Step 9. Controller architecture

Routing and controllers have also been updated in Laravel 5. To start off with, we need to add the Http namespace under our top-level application namespace, and then add the Controllers folder to it:

$ mkdir app/Http

$ mkdir app/Http/Controllers

The next step is to add the Http “kernel” class, which you can copy from the Laravel 5 repository:

$ cp ../laravel5/app/Http/Kernel.php app/Http/

You may have noticed something interesting in Step 6 when we copied to service providers across. Laravel 5 introduces a RouteServiceProvider, which is where we define our application routes. A excerpt is copied below to illustrate the point:

In this excerpt you can see that our routes are defined in app/Http/routes.php, not app/routes.php as in Laravel 4.x. So make sure you move these across:

$ mv app/routes.php app/Http/

Next, we need to move all our controllers into the new namespace. Laravel 4.x applications include controllers under app/controllers by default, so if you haven’t changed this you can move them across quite simply:

$ mv app/controllers/* app/Http/Controllers/

$ rm -rf app/controllers

You’ll also need to update (or add) the namespace declarations at the top of each controller. If you didn’t have a namespace declaration before, be careful, as you’ll need to update all of your facade references to use the top-level namespace (Input::all() becomes \Input::all(), etc…).

Another important thing to note: in the RouteServiceProvider, we set a variable – $namespace. This becomes the default namespace for all controller references, so we no longer require the full namespace declaration when referring to controllers and/or actions.

As an example:

return Redirect::action('Acme\Controllers\[email protected]');

… becomes:

return Redirect::action('[email protected]');

Finally, all of your controllers should extend App\Http\Controllers\Controller, which you’ll also need to copy from the Laravel 5 repository:

$ cp ../laravel5/app/Http/Controllers/Controller.php app/Http/Controllers/

 Step 10. Commands

The next step is to migrate any commands across.

Even if you don’t have any custom commands for your project, you’ll need to pull the new “Kernel” file across for the CLI app entry point:

$ mkdir app/Console

$ cp  ../laravel5/app/Console/Kernel.php app/Console/

You’ll notice that console commands are now declared in this new Kernel file instead of the app/start/artisan.php file (which no longer exists). You’ll also notice that by default Laravel 5 defines an “Inspire” command. Go ahead and pull this across, otherwise you’ll get an error when trying to boot the app:

$ mkdir app/Console/Commands

$ cp ../laravel5/app/Console/Commands/Inspire.php app/Console/Commands/

Once you’ve done this, it’s time to migrate any custom commands you’ve defined for your application and remove the old folder:

$ mv app/commands/* app/Console/Commands/

$ rm -rf app/commands

Note that you’ll also need to update the namespace declarations for each, and then add them to the Console Kernel:

Step 11. Eloquent updates

The first thing you’ll need to do (if you haven’t already) is to move your models out of the top-level namespace and into the App namespace.

This means your models should go under app/, not app/models as in the default Laravel 4.x install. You can even go one step further (as I do in my projects) and add a separate sub-namespace for models.

For example: if you have a \User model in your 4.x project, for Laravel 5 this becomes App\User (or App\Models\User if you take the additional step of adding a Models namespace).

The next thing you need to do is to update your User model. UserInterface and RemindableInterface have been removed in L5, so you need to remove implements UserInterface, RemindableInterface from your User class declaration. Instead, use the following:

One more important change: the camel-case to snake-case mapping for eloquent relations has been dropped in Laravel 5. Illustrated below:

If you’re using relations with camel-case’ed names, you’ll need to either update the relation (i.e.: function associatedUsers() becomes function associated_users()), or update each individual reference to the relation (i.e.: ->associated_users becomes ->associatedUsers).

Step 12. Middleware

A new addition to Laravel 5 is the concept of “middleware.” Middleware replaces the Laravel 4.x concept of filters: logic that can be inserted between your routes and controllers.

The first thing you should do is copy across the default middleware from the Laravel 5 repository:

$ mkdir app/Http/Middleware

$ cp ../laravel5/app/Http/Middleware/* app/Http/Middleware/

The next step is to migrate any Laravel 4.x filters into middleware. The basic process:

  • Create a class in the Middleware folder that implements the Illuminate\Contracts\Routing\Middleware interface.
  • Add a function that includes the logic for the middleware: function handle($request, $next).
  • The function should either return a response (to intercept the request), or return $next($request) to pass the request on to the next middleware handler or the controller.
  • Add the middleware to the application via app/Http/Kernel.php. Global middleware should be appended to the protected $middleware class variable, route-specific middleware should be appended to protected $routeMiddleware.

An example is provided below:

Step 13. Exception Handlers

Laravel 5 includes a new namespace for exception handlers. By default, you get a single exception handler, which you should copy across from the new repository:

$ mkdir app/Exceptions

$ cp ../laravel5/app/Exceptions/Handler.php app/Exceptions

Step 14. Form and HTML helpers (optional)

If you currently use the Form and/or HTML facades, you now need to manually include these packages in your composer.json file:

    /* ...etc */
    "require": {
        "laravel/framework": "5.0.*",
        "illuminate/html": "5.0.*",
        /* ...etc */

… and then add the facades in app.php

    'aliases' => [
        'Form' => 'Illuminate\Html\FormFacade',
        'HTML' => 'Illuminate\Html\HtmlFacade',
        /* ...etc */

and finally, the service provider:

    'providers' => [
        'Illuminate\Html\HtmlServiceProvider',
        /* ...etc */

Step 15. App Namespace (optional)

You’ll notice at all points throughout the migration process we’ve been using the App\ top-level namespace. This will work, but it’s a much better practice to use our own, unique identifier.

The easiest way to do this is to complete a full-text search across your codebase for “App\” within your IDE and update all declarations to reference your top-level namespace.

There is also an in-built Laravel app:name command to change this…

$ laravel app:name <APP NAME HERE>

… but this approach has two issues. Firstly, you need to install the Laravel 5 packages before you have access to it. The second issue is that it can be buggy if you’re running the command on any thing other than a fresh Laravel 5 project. I’d recommend avoiding it and updating the namespaces manually.

Step 16. Finishing up

The final step is pull in the updated dependencies with Composer. Cross your fingers, pray to your favourite deity and run the following:

$ composer update

Miscellaneous points

With all that said and done, there are still some minor points we may want to address.

  • The .gitignore file has been updated to include some L5 specific cases (e.g.: .env files). You can copy this across.
  • Controllers are now automatically prefixed with the relevant namespace in route definitions. If you were already namespacing your controllers in your 4.x application, you should update your route definitions to not define the namespace option, otherwise Laravel will try to add the namespace twice when instantiating the controllers.
  • For pagination links: replace any calls to $paginator->links() with $paginator->render().
  • The SoftDeletingTrait has been renamed to SoftDeletes.
  • For testing: you’ll need to update your phpunit.xml file to point at the new location for tests. TestCase.php has also changed for Laravel 5, so you should copy it across.
  • The BaseController now has a validates() function. This may not be relevant to your situation, but in some of our projects we had defined our own validates() function. If you’ve done the same, you’ll need to change it to something else.

In closing

The 4.x to 5 migration process is long, but well worth it. Laravel 5 is a much more cohesive, understandable and powerful framework. Using Laravel 5 will enable you to create applications that are a joy to maintain, highly robust and long-lived.

Onwards and upwards.

 

Did we miss any steps? We’d love to hear feedback. Let us know in the comments below!

  • Anıl Ünal

    There is no database related changes, right?

    • http://www.coreymcmahon.com/ Corey McMahon

      There are some changes to Eloquent (soft deletes, implemented interfaces, relationship naming, etc…) but you don’t need to make any schema changes. Assuming you’re going from 4.2 -> 5. If you’re using <= 4.1 you should check out the remember_token changes: http://laravel.com/docs/5.0/upgrade#upgrade-4.1.26

  • Adrian P. Blunier

    Thanks a lot for this great article, was very useful for my migration process. Thanks to you it wasn’t a pain in the ass 🙂

    • http://www.coreymcmahon.com/ Corey McMahon

      Glad to hear it helped Adrian 🙂

  • http://tonyli.ca/ Tony

    You missed the lang file for validation has been updated with some new fields (boolean, filled, and timezone).

    • http://www.coreymcmahon.com/ Corey McMahon

      Good catch Tony. These can be copied from the Laravel GitHub repo, or from a fresh install of the latest version of Laravel if you have it locally.

  • Saleem Ahmed

    I am new to laravel. I am working on upgrading a project from version 4.2 to 5.1.

    I have done most of the work. But I am facing “FatalErrorException in Model.php line 906: Class ‘ProductImage’ not found”.

    All model classes reside in app/Models directory. Autoload classmap in composer.json is as under:

    “classmap”: [

    “app/Console/Commands”,

    “app/Http/Controllers”,

    “app/Models”,

    “database/migrations”,

    “database/seeds”,

    “tests/TestCase.php”

    ],

    Can anyone help me please!!!

  • http://jason.pureconcepts.net/ Jason McCreary

    Excellent article! I referenced it several times while creating Laravel Shift – an automated upgrade tool for Laravel projects. Future readers may find it an appealing option over performing this upgrade by hand. Would really value your feedback.

    • http://www.coreymcmahon.com/ Corey McMahon

      Cool! Laravel Shift looks great Jason!

  • ganesh

    I am getting below error.. how to fix???..thanks in advance

    PHP Fatal error: Uncaught ReflectionException: Class log does not exist in /home/vagrant/projects/mob-2/vendor/laravel/framework/src/Illuminate/Container/Container.php:741

    • http://www.coreymcmahon.com/ Corey McMahon

      This error message is actually deceptive and tripped me up many times during Laravel upgrades. This usually means there is a problem somewhere in your `config/` files. In my case it was because I was referencing `$app->environment()` which is no longer accessible from this context (use `env(‘APP_ENV’)` instead).