We've just finished developing a basic Laravel 5 package. This time we'll talk about how to develop more complex packages, which include middlewares, configuration, database migrations, routes, controllers, and views. To start, implement boilerplate code of a Laravel package. Take a look at the first part of this tutorial if you need a refresher.

Now, testing something like this outside a Laravel project is not so convenient. Luckily, you can include a local composer package into a project. Let's create a new Laravel project (you can use an existing one). Then we'll reference a local package. Open the project's composer.json and add a new section.

"repositories": {
    "your-vendor/your-package": {
        "type": "path",
        "url": "relative/or/absolute/path/to/your/package",
        "options": {
            "symlink": true
        }
    }
}

Finally, run this in your terminal:

composer require your-vendor/your-package @dev

After that any changes you make to your package locally will be available in the Laravel project right away without the need to perform any additional actions. The only thing is, you'll have to re-run this command every time you add any new packages to your package's composer.json require section.

Middlewares

To add a middleware to your package, create a new folder called Middleware inside your src folder. Actually, this folder could be called differently, but I'll stick to the conventional name.

Create a new middleware file inside. I would just generate a blank middleware inside our Laravel project with artisan and copy the code over to our file. Don't forget to change the namespace to YourVendor\YourPackage\Middleware.

You can develop the middleware as usual. To use the middleware in your project register it as you would normally do inside the app/Http/Kernel.php file. For example, like this:

protected $middlewareGroups = [
    'web' => [
        ...
        \YourVendor\YourPackage\Middleware\YourMiddleware::class,
    ],
    ...
];

Database Migrations

To include database migrations into your project create a new folder database/migrations inside the src folder. Again, I'm just going with the familiar folder name and you are not limited here. Create the usual migration files inside the folder. Consider giving a prefix to your table names to avoid possible conflicts with the user's tables.

Now go to your package's service provider and add the boot method. Call $this->loadMigrationsFrom() and pass the path to the migrations directory to it. After this step your package's migrations will be run automatically when you execute php artisan migrate in your Laravel project.

/**
 * Perform post-registration booting of services.
 *
 * @return void
    */
public function boot()
{
    $this->loadMigrationsFrom(__DIR__ . '/database/migrations');
}

Models

Create any folder to store your models. I'd go with src/Model. Create regular Laravel model files that extend Illuminate\Database\Eloquent\Model inside the folder. The namespace would be in this case YourVendor\YourPackage\Model.

Configuration

Same as with everything you should create a new folder to store your configuration files, for example src/config. Create a new config file with a suitable name, for example your_package_name.php. A configuration file is a php file which simply returns an array with values.

Now open your service provider file. Add the following code to the register method to be able to get configuration values from inside the package.

if ($this->app->config->get('your_config_file') === null) {
    $this->app->config->set('your_config_file', require __DIR__ . '/config/your_config_file.php');
}

You probably want your users to be able to publish your config files so that they could change the settings inside. To do so insert the code below into the boot method. I talked about this method in the section about migrations.

$this->publishes([
    __DIR__ . '/config/your_config_file.php' => config_path('your_config_file.php'),
]);

Routes

Create a routes file anywhere, for example src/routes.php. Add the routes as you would normally do in a Laravel project. To tell Laravel to use these routes, add the following code to the register method of your service provider:

$this->loadRoutesFrom(__DIR__ . '/routes.php');

Alternative approach

Don't add the line above into your service provider. Instead, create a static method called routes inside one of your package's main classes. For example:

class YourMainClass
{
    public static function routes()
    {
        Route::get('/my-route', function () {
            return 'A route from the class!';
        });
    }
}

The routes won't be loaded automatically in this case. The users of your package will have to add this line to their routes file:

YourMainClass::routes();

This is less convenient but gives your users more control over the routes. Now they can add middlewares and/or prefixes to your routes by putting the line above inside a group, e.g.:

Route::middleware('auth')->prefix('admin')->group(function () {
    YourMainClass::routes();
});

Controllers

Create a controller file inside any folder, for example src/Controllers/ExampleController.php. This is going to be a simple class, we are not going to extend it from a Controller class like the usual Laravel controllers do. Then create a method inside, for example test and return a string from there.

<?php

namespace YourVendor\YourPackage\Controllers;

class ExampleController
{
    public function test()
    {
        return 'Hello from the controller';
    }
}

Now you can hook this method to a route inside your package. In order for Laravel to find your controller you have to specify the whole path to the class starting with a slash.

Route::get('/my-route', '\YourVendor\YourPackage\Controllers\ExampleController@test');

Views

Finally, create a folder to hold your package's views, for example src/views. Tell Laravel where your views are located by adding this line to the boot method of your service provider:

$this->loadViewsFrom(__DIR__ . '/views', 'yourpackage');

Now you can reference views from your package like this:

view('yourpackage::your_view');

Final Words

We've covered the main aspects of developing Laravel specific composer packages here. The process is easier than you might've thought before, it's just you need to know where to start from. Don't forget to give the proper namespaces to all of your classes depending on what aliases you specified in composer.json and what folders the files are located in, this is important. Check Laravel's documentation for additional reference and have fun developing!