Courses

How to Create Laravel Package: Step-by-Step Example

Our First Controller and Route

Let's finally test if our package does something (anything, really), by creating a Controller.

Let's create this file manually, cause it's not worth using make:controller - you would spend more time changing the namespaces and removing the default BaseController functionality if you don't need it.

So, we create a separate src/Http/Controllers subfolder inside our package, and create this:

packages/laraveldaily/laravel-permission-editor/src/Http/Controllers/RoleController.php:

namespace Laraveldaily\LaravelPermissionEditor\Http\Controllers;
 
use Illuminate\Routing\Controller;
 
class RoleController extends Controller
{
 
public function index()
{
return 'It works!';
}
 
}

Important note: we extend from the Illuminate\Routing\Controller from Laravel core, but the default Laravel project extends their Controllers from the BaseController in the app/Http/Controllers folder. In some cases, some packages may need the same functionality as that Controller's traits, but in our case, we don't need it, so the core Laravel Routing Controller is fine.

Now, how do we create a Route for what Controller? Simply by creating a familiar routes/web.php file, just inside the package structure.

packages/laraveldaily/laravel-permission-editor/routes/web.php:

use Laraveldaily\LaravelPermissionEditor\Http\Controllers\RoleController;
use Illuminate\Support\Facades\Route;
 
Route::resource('roles', RoleController::class);

Notice: we're creating the routes outside of the /src folder of the package. You can think of the /src folder of the package as the /app folder in a typical Laravel application. So, there will/may be src/Models, src/Http, and other subfolders, but things like /resources or /routes go outside of the /src.

As you can see, we're loading the Controller, providing its full namespace, the one that we registered a bit earlier in the composer.json file.

Now, we need to register the routes. And here's the first time we actually make use of the Service Provider.

packages/laraveldaily/laravel-permission-editor/src/PermissionEditorServiceProvider.php:

use Illuminate\Support\Facades\Route;
 
class PermissionEditorServiceProvider extends ServiceProvider
{
public function boot()
{
Route::prefix('permission-editor')
->as('permission-editor.')
->group(function () {
$this->loadRoutesFrom(__DIR__ . '/../routes/web.php');
});
}
}

So the routes are loaded with the $this->loadRoutesFrom() method, just providing the routes file.

In addition, I wrap those routes in the group immediately, to avoid conflicts with other routes of the application or other packages, so I assign the specific URL prefix and route name prefix to all the routes.

You can actually do that inside of the routes/web.php, too, but I prefer it in the Service Provider, as it's more like a global configuration setting than the Route behavior.

Now, we need to launch composer update to update all the functionality of the package in the main application. In case of success, you should see a line something like this:

  • Upgrading laraveldaily/laravel-permission-editor (dev-main cac9b96 => dev-main a066e56): Source already present

And now, we can launch the route /permission-editor/roles in our browser.

Laravel package - it works

avatar

Hi, I do exactly as a tutorial, but I got an error.

Target class [Laraveldaily\LaravelPermissionEditor\Http\Controllers\RoleController] does not exist.

avatar

Solved. It is composer thing, now its work

avatar

Hello. Could you explaing how did you solve the composer thing?

avatar

I'm getting the same error - how did you resolve this?

avatar

Maybe try composer dump-autoload?

avatar
You can use Markdown
avatar

since the service provider in the same directory as routes folder, why $this->loadRoutesFrom(__DIR__ . '/../routes/web.php'); instead of $this->loadRoutesFrom(__DIR__ . '/routes/web.php'); ?

avatar

Good point, probably your suggestion would work better.

avatar
You can use Markdown
avatar

Thank you for this course! It's unclear to me where exactly we would write unit tests for our package, and where would we trigger those tests from.

Would you mind clarifying that? Thank you!!

avatar

Sorry I don't have a QUICK answer for you and I don't have specific tutorial for this, please google "create laravel package unit tests", there are quite a lot written about it elsewhere online.

avatar
You can use Markdown
avatar

how are the routes from the package loaded inside the main routing of Laravel? Is there a merging happening somewhere under the hood?

avatar

In the PermissionEditorServiceProvider we are registering routes using Route:: class. This adds the routes to the combined list (merging them)

avatar
You can use Markdown
avatar
You can use Markdown