How to Use Laravel With Your Current Legacy Application

I have researched the topic a few times and never really found a good way to achieve having a Laravel application running alongside an existing legacy application.

The benefits for doing it this kind of side-loading way would be so that a section at a time can be ported across instead of actively maintaining two different projects and trying to keep the logic in sync with each other.

This guide assumes that there is no existing single entry point/front controller and no routing, e.g. that the url mysite.com/path/to/my/script/index.php is how the current site behaves.

composer global require laravel/installer

laravel new [directory name]

It might be easier to use a different directory from the project you want to use Laravel with and copy the files and directories over one at a time.

Create an .env file from the .env.example.

Create an app key:

php artisan key:generate

Add the database connections that you need, e.g.:

DB_CONNECTION=db
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my_main_app_db
DB_USERNAME=root
DB_PASSWORD=

SECONDARY_DB_CONNECTION=secondary_db
SECONDARY_DB_DRIVER=mysql
SECONDARY_DB_HOST=localhost
SECONDARY_DB_PORT=3306
SECONDARY_DB_DATABASE=my_secondary_app_db
SECONDARY_DB_USERNAME=root
SECONDARY_DB_PASSWORD=

If you only have one database then this will be even easier.

Then ensure that these are also in config/database.php:

'db' => [
    'driver' => 'mysql',
    'url' => env('DATABASE_URL'),
    'host' => env('DB_HOST', '127.0.0.1'),
    'port' => env('DB_PORT', '3306'),
    'database' => env('DB_DATABASE', 'my_main_app_db'),
    'username' => env('DB_USERNAME', ''),
    'password' => env('DB_PASSWORD', ''),
    'unix_socket' => env('DB_SOCKET', ''),
    'charset' => 'latin1',
    'collation' => 'latin1_swedish_ci',
    'prefix' => '',
    'prefix_indexes' => true,
    'strict' => true,
    'engine' => null,
    'options' => extension_loaded('pdo_mysql') ? array_filter([
        PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
    ]) : [],
],

'secondary_db' => [
    'driver' => 'mysql',
    'url' => env('SECONDARY_DATABASE_URL'),
    'host' => env('SECONDARY_DB_HOST', '127.0.0.1'),
    'port' => env('SECONDARY_DB_PORT', '3306'),
    'database' => env('SECONDARY_DB_DATABASE', 'my_secondary_app_db'),
    'username' => env('SECONDARY_DB_USERNAME', ''),
    'password' => env('SECONDARY_DB_PASSWORD', ''),
    'unix_socket' => env('DB_SOCKET', ''),
    'charset' => 'latin1',
    'collation' => 'latin1_swedish_ci',
    'prefix' => '',
    'prefix_indexes' => true,
    'strict' => true,
    'engine' => null,
    'options' => extension_loaded('pdo_mysql') ? array_filter([
        PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
    ]) : [],
],

and set the default database connection:

'default' => env('DB_CONNECTION', 'my_main_app_db'),

Be careful of the charset and collations above, if you don't use those then change them to what you currently use, e.g. utf8mb4 and utf8mb4_unicode_ci.

If composer and npm are already being used within the project, you will also need to consolidate composer.json and package.json in order to get the Laravel dependencies installed along with the currenty dependencies of the project.

Create or update .htaccess in your project vhost entry point:

Options +FollowSymLinks
RewriteEngine on

# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]

# Send Requests To Front Controller...
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ laravel.php [L]

Create laravel.php in the same directory as the .htaccess file above and insert the following:

<?php

require_once __DIR__ . '/../public/index.php';

Add the following to routes.web.php

/* Fallback route */
Route::any( "/{path?}", [RouteController::class, 'oldRoute'])->where('path', '.*');

Create a RouteController - php artisan make:controller RouteController

Open it app and add the oldRoute method:

/**
 * Include file at the given path
 *
 * @param  String $path
 * @return Response
 */
public function oldRoute($path)
{
    ob_start();

    if (file_exists(base_path('entrypoint-directory/' . $path))) {
        include base_path(sprintf('entrypoint-directory/%s', $path));
    } elseif (file_exists(base_path('public/' . $path))) {

        $publicPath = base_path(sprintf('public/%s', $path));

        header(sprintf('Content-Type: %s', mime_content_type($publicPath)));
        readfile($publicPath);
    } else {
        abort(404, 'Page not found');
    }

    return response(ob_get_clean());
}

Update Http/Middleware/Authenticate.php to point to the login url of the application, e.g.

// return route('login');
return 'login.php';

Make the custom auth middleware:

php artisan make:middleware CustomAuth

Set the contents of the handle method to:

$user = User::getLoggedInUser();

// Login the user manually using Laravel's Auth Facade
// don't use `Auth::login($user, true);` unless `remember_token` is added to the users table.
Auth::login($user);

// set the user for the current request
$request->setUserResolver(function() use ($user) {
    return $user;
});

return $next($request);

Open app/Http/Kernel.php and change the auth array entry so that it reads:

'auth' => \App\Http\Middleware\CustomAuth::class,
// 'auth' => \App\Http\Middleware\Authenticate::class,

Add a getLoggedInUser method to the User class:

public static function getLoggedInUser()
{

    if (!isset($_SESSION['user']['id'])) {
        return null;
    }

    // check if we want to impersonate someone
    if (isset($_SESSION['impersonate'])) {
        $user = self::find($_SESSION['impersonate']);
    } else {
        $user = self::find($_SESSION['user']['id']);
    }

    if (empty($user)) {
        self::logout();
        return null;
    }

    return $user;
}

Also, make sure the User class is using the correct database connection if you have more than one database. This needs to be correct for each of your models if  that is the case.

protected $connection = 'db';

That's it! Your legacy app should continue to operate as normal and now any new Laravel based routes should work alongside the existing application.