How to create a Laravel 5 package in 10 easy steps

So you want to create your own package? Surprisingly, this topic has little information online and not properly described in the official docs. Let’s fill in the gaps and create a new Laravel 5 package from scratch.

To avoid a general Hello world example, let’s take a more practical idea: say… a package which shows current time in various timezones. Still very simple, but somewhat useful. Ok, here we go.


1. Fresh Laravel 5.1 installation

At the time of writing, the newest Laravel version is 5.1, so let’s download latest build to start fresh – whichever installation way you prefer, I will use this one:


2. Package folder and name

Let’s create a folder /packages in our root, and we will put our package there.
Now, every package has to have a name, which actually consists of two parts: [VENDOR or CREATOR] / [PACKAGE NAME]. For example, Laravel itself has a full name of laravel/laravel (same vendor), and one of the most popular packages Jeffrey Way’s Generators has a name of way/generators. So logic is simple: package has a creator name and its own name.

So let’s call our package laraveldaily/timezones – then we need to create a folder inside our /packages:

1019_laraveldaily_package_01

Then inside of it we need to create another folder called /src:

1019_laraveldaily_package_02

Ok, folder structure ready, now let’s give our package an “official description” with Composer:


3. Composer.json file for the package

Every package needs to have composer.json file, which contains not only potential dependancies, but also information about the package itself. So we go to our timezones folder and run this command:

You will get a guide of questions to fill-in your future composer.json file. If you answer then one by one (don’t worry if you make any mistake or skip something, you can add it later), you will end up with something like this generated:

That’s it – in our case, no external requirements, nothing fancy. But that’s enough.


4. Loading package via main composer.json and PSR-4

Now, let’s make our package “visible” to main Laravel structure, and assign alias to it, we do that by adding this line to main composer.json section called “psr-4”:

And then we run this command from main folder:

Have you tried our tool to generate Laravel adminpanel without a line of code?
Go to QuickAdminPanel.com

5. Creating a Service Provider

This might sound a little complicated at first, if you want to dig deeper into what a Service Provider actually is, here’s official documentation (not easily readable for less experienced developers, imho) or another blog post about it.

Essentially, Service Provider is a Class which would contain main information about package – what Controllers does it use, what Routes file or Views to load etc. You can look at it as a set of rules for the package.

So, how do we create it? There’s an Artisan command to create a service provider:

It will generate a file called TimezonesServiceProvider.php in folder app/Providers – then we should move that file to our folder /packages/laraveldaily/timezones/src. After that don’t forget to change the namespace of the Provider class – it should be the same as we specified in main composer.json file – in our case, Laraveldaily\Timezones:

1019_laraveldaily_package_03

As you can see, we have a generated class with two methods: boot() and register(). A little later we will fill them in with actions, but now – let’s add our new Service Provider to the array of Service Providers in file config/app.php:

Cool, we’re done for now with Service Provider, let’s move on to Controllers.


6. Create a Controller

Our example package will have only one Controller, let’s call it TimezonesController – and we create it in the same src folder of our package.

Controller will have only one method index($timezone) which will just show time in specified timezone – we use Carbon library for that (you don’t need to install it, it’s already within Laravel).

So, here’s our packages\laraveldaily\timezones\src\TimezonesController.php:

Again, don’t forget correct namespace and adding use App\Http\Controllers\Controller on top.


7. Create our Routes.php file

You are probably familiar with usual app/Http/routes.php file, so we have to create our own similar file in our package folder. Again, it will be a simple one-liner:

packages\laraveldaily\timezones\src\routes.php:

Now, how does Laravel know about this routes.php file and our Controller? This is where our Service Provider comes in: we add these lines to its method register():


8. That’s it – load URL in the browser!

1019_laraveldaily_package_04

(before any questions – yes, I’m writing this tutorial at night, and yes, I’m using Windows/XAMPP as local server)

Now, here I immediately think of two potential problems:

  • What if user specifies a timezone with slash symbol (/) – it would count as URL
  • Maybe we should have a default timezone, if none specified?

For that, let’s make an assumption that user would add timezones with dash – instead of slash / and we str_replace it. Also, we make that parameter optional and default to whatever timezone is in our config/app.php file.

packages\laraveldaily\timezones\routes.php – added question mark:

packages\laraveldaily\timezones\TimezonesController.php:

Now, we can load URLs like /timezones or /timezones/Europe-London.

Ok, so we have the basics of our package, and it works! Yay! But there are some additional things you might want to add.


9. What about the Views?

Of course, you wouldn’t just echo into the browser like that in real world – there should be at least some basic HTML template, which should be stored in a View file. Let’s do exactly that – we need to create a folder /src/views and then put a View file in it. Let’s copy a default Laravel 5 welcome.blade.php with adding just one variable $current_time.
src/views/time.blade.php:

Now, let’s return to our Service Provider and this time we will use boot() method by adding a command, where to load our views from. The second parameter is our Namespace which we will use in the next step.

Next thing – we need to change our Controller to load this view with a parameter. Now, notice that we are loading the view with the specific namespace of our package that we just specified in Service Provider.

And, here we go – reload the browser:

1019_laraveldaily_package_05

So, this is how we use and load the views in the package.


10. Publishing the Views

And the last step – what if we want user of our package to customize that view himself? That’s a pretty common scenario – we provide a basic template, but then user wants it to look as his app, not ours.

But they wouldn’t go to our package folder and edit views directly – that would ruin all future updates process. That means we should copy our views into Laravel folder resources/views. To do that, we add this line to Service Provider’s boot() method:

As you can see, we are copying not to views, but deeper – to stay with our structure.

And then, to perform actual copying, user should publish our views, with Artisan command:

This is the result – we now have views in both our package folder and Laravel folder:

1019_laraveldaily_package_06


Bonus: publishing your package to Packagist

The last last thing is if you want to show your package to the world, so that other people would add it to their composer.json file. I won’t provide detailed instructions on this, cause it kinda goes outside Laravel topic, but here are basic steps:

  1. You form your package as a repository and upload it to GitHub;
  2. You register on Packagist.org;
  3. You make sure that your composer.json contains all necessary information;
  4. You submit a link to your GitHub repository to Packagist – and then – BOOM! – magic happens!
  5. For all detailed information about composer.json fields, package versioning and other topics – see Packagist documentation.

So, now we’re (finally?) finished with the tutorial. Here is the basic way to create a package. Of course, you can grow it much bigger with Models, Assets and your own sub-folder structure, but I will leave it for you to play with.

And if you create your own package with the help of this tutorial, don’t forget to give the link in the comments – tell the world about your achievement!

Want to generate Laravel adminpanel online?
You don't need any packages to do that!

75 thoughts on “How to create a Laravel 5 package in 10 easy steps

  1. Awesome tutorial. I am learning laravel and I haven’t read the documentation about package creation yet. But now I understood why we need to specify service provider and vendor:publish command. Keep it up!!!

  2. Great job, nice tutorial to quickly start a package.
    Could you make an other one with a bit more complexity? More Controllers, Models, Migrations and Services. This would be very great 😉

    1. Hi Sushilkumar, might be various reasons.
      1. Check config/app.php – have you really added provider there?
      2. Maybe composer dump-autoload would help, but not sure
      3. Maybe you misspelled something or have put different namespace somewhere?

      1. Hello Povilas,

        The error was occurred because in the 4th step, you have given wrong path for package as “Laraveldaily\\Timezones\\”: “packages/laraveldaily/timezones/src”.

        When I removed ‘src’ from the path, then the url is working perfectly. It will show error as there is no file in the src folder.

        Also there is misunderstanding of path in 6th step :

        1) Our example package will have only one Controller, let’s call it TimezonesController – and we create it in the same src folder of our package.

        2) So, here’s our packages\laraveldaily\timezones\TimezonesController.php

        Please correct any one statement so that either the controller should reside in timezones folder or src folder.

        Thanks for the great tutorial.

          1. I think there’s still something to adjust in the article in order to use it as step by step tutorial 🙂

            instead of:
            “then we should move that file to our folder /packages/laraveldaily/timezones”

            it should say:
            “then we should move that file to our folder /packages/laraveldaily/timezones/src”

            … this is actually what you is also shown on your screenshot. Otherwise the ServiceProvider does not get loaded.

        1. Hello Sushilkumar

          I think 4th step was correct. When I remove the src from psr-4, it shows the error as Class Not Found. But When I added src in psr-4, it works properly.

          Thanks

          1. For other readers, problem can be also that we add step 4 to package’s composer.json instead to root’s composer.json. I’ve had trouble with this right now just because I missed that.

  3. On creating routes.php file (Point 7) above you wrote the route file is placed on packages\laraveldaily\timezones\routes.php but I got errors from it, then I move the routes file to packages\laraveldaily\timezones\src\routes.php and everything works fine. Anyway I love your tutorial, as always. Thanks!

  4. Great Article! One question, which I think was answered by one of the screenshots, Step 3 – composer init should be done inside the src folder? And for noobs (such as myself), there’s no issues having a composer.json file in a project where a composer.json file exists at a higher level?

  5. Great topic! Thanks.

    It would be good to get an example how to create a package that will be not a single module, that will be used to build something in your app. Like “laravelcollective/html” or “laravel/socialite”.

    You’ve got a little mistake in text “A little later we wil fill them in with actions” )

  6. Hello Povilas,

    Great tutorial. I follow the steps and it works good.
    But one problem.

    After publishing the view I have the new time.blade.php in the correct folder (resources/views/laravelDalily/timezones).
    But when I edit this template. I don’t see the changes in the browser.
    It still uses the template in ‘packages/laraveldaily/timezones/src/views’…

    What did it wrong?

    1. Hi Surtep.
      Good question, you went a little further than the article.
      Probably you should change this line
      return view(‘timezones::time’, compact(‘current_time’));
      to
      return view(‘time’, compact(‘current_time’));

      1. Thank you for your fast answer.

        return view(‘time’, compact(‘current_time’));
        gives a file not found… But with the complete path
        return view(‘laraveldaily.timezones.time’, compact(‘current_time’));

        It is OK.
        Thank you again…

      2. I made a test to check if the view was published.
        If the views exist in ‘resource/views/laraveldaily/timezones’ then use this view. if not use the view in the package.
        Now my boot function in the Service Provider is
        public function boot()
        {
        \File::exists(base_path(‘resources/views/laraveldaily/timezones/time.blade.php’)) ? $this->loadViewsFrom(base_path(‘resources/views/laraveldaily/timezones’), ‘timezones’)
        : $this->loadViewsFrom(__DIR__.’/views’, ‘timezones’);

        $this->publishes([
        __DIR__.’/views’ => base_path(‘resources/views/laraveldaily/timezones’),
        ]);
        }

        No changes in the controller.

        Is this approach correct or should I place the test in the controller?

        1. The Laravel function loadViewsFrom checks if exists folder basePath().’/resources/views/vendor/’.$namespace

          So, the TimezonesServiceProvider boot() should be this:

          public function boot()
          {
          $this->loadViewsFrom(__DIR__.’/views’, ‘laraveldaily\timezones’);

          $this->publishes([__DIR__.’/views’ => base_path(‘resources/views/vendor/laraveldaily/timezones’),]);
          }

          And the last line of TimezonesController should be this:

          return view(‘laraveldaily\timezones::page’, compact(‘current_time’));

  7. Thanks a lot for this tutorial. A very easy step to understand how a package was developed.
    I’m waiting for the part 2.

  8. Hi. I trying to implement these package from tutorial. I come up to 7 step.
    Then, when I run something like http://homework.app/timezones/UTC I always receive “NotFoundHttpException in RouteCollection.php line 161”.

    When I run these “php artisan route:list” I receive
    | GET|HEAD | timezones/{timezone} | | bkowalczyk\timezones\TimezonesController@index |
    I mean – my route could be registered.

    Could you help me how to cross these problem?

    1. Hi, good question. Maybe you missed something in Service Provider? Or maybe something’s wrong with lowercase-uppercase letters?
      Or maybe Laravel changed the way of registering routes in latest version, which Laravel version are you using? My tutorial is for 5.1 version.

      1. Thank you. Sorry for spam. It was my mistake. Probably I didn’t have these error. As you can assume – I use a Homestead vagrant. I added to .homestead/Homestead.yaml new map (to homework.app with new path) BUT it’s still provide to my very first project. In short – I didn’t add a new “address” to homestead vagrant. Now I’m trying to find how to add new solution\map to my homestead.

        Could you tell me how to storage array(which can be changed) inside package? I should add whole new database (only for package??) or just use a session?

  9. I followed the tutorial, but i got this error : Uncaught exception ‘InvalidArgumentException’ with message ‘A non-empty PSR-4 prefix must end with a namespace separator.’
    Any suggestions?

    1. Probably because you forgot to add 2 backslashes at the end of namespace in main composer.json -> “Laraveldaily\\Timezones\\”

  10. Great tutorial bro ! 🙂 Have never published a package before, but I am thinking about doing it soon and this has helped me to setup a package wireframe in a local L5.2 project. Following through was easy along with Laravels documentation. Combined your tutorial along with Laravel’s Service Container documentation in order to make a Facade inside of the package and use it inside the project without a problem! Thanks

  11. If our custom packages need other packages as dependacies then how to load it from that /packages folder?

    or maybe we need to add manually our dependecies by running composer required xx/zzz ?

    thanks

  12. Hi Povilas Korop,

    Really Nice tutorial you have done in laravel. This is really very helpful to create custom packages as per requirement and re-usable modules.

    Query :
    1. Why we need to create separate package folder for this. why we not add in Vendor Folder ?

    Suggestion :
    (if you think is right not on forcefully)
    After we run the 10 suggestion, please also add one line to render view in controller to.
    Is easy to follow the steps for starters.
    #changes
    return view(‘laraveldaily.timezones.time’, compact(‘current_time’));

    Once again is really is helpful to start one bigthing.. Great 🙂

  13. Very Informative. Best among other Laravel package creation tutorials online.
    I was wondering why you added this line in register method:

    $this->app->make(‘Laraveldaily\Timezones\TimezonesController’);

    I thinkits really unnecessary and could cause some unforeseen performance hit.

    All you need to do is specfiy the namespace in your route like you did here:
    Route::get(‘timezones/{timezone?}’, ‘laraveldaily\timezones\TimezonesController@index’);

  14. hi
    Your tutorial is very helpful, but this is package with routes , views and controller.
    instead of making a view and controller i want to know how develop a package like an extension of laravel like laravel collective class HTML and Form. This will help me to turn YouTube video api php package in a laravel package

  15. Hi

    A great extension to this tutorial would be how to maintain the package from within two or more projects that have pulled it in via a composer dependency.

    I know that there are some options with composer’s prefer-source option that I have yet to fully investigate…

  16. Hi, nice tutorial, helped me a lot. I want to give a contribution:
    In your Controller, instead of using:
    “use App\Http\Controllers\Controller;”
    you can do like this:
    “use Illuminate\Routing\Controller as Controller;”

    So you dont need to depend of the namespace in every project;

    Hope this helps, sorry for my english(im from Brazil).

  17. Thanks for this good and clean Tutorial, helped me a lot to understand the process of creating a package.

    Keep up the good work, Greets

  18. Thanks. Really worthful information.
    But how can we use model files. Where to create , ? how to call..?
    Instead of creating controller file under src can we have mvc patteren like Model, View, Controller

  19. I have followed @Janis upgrades and later extended the published view with

    @if(Auth::check())
    ID: {{Auth::id()}}.
    @else
    not logged in
    @endif

    But the view the check doesn’t work. I always have the not logged in.
    How to make the Auth::check() work?
    I another tab of my browser I am logged in – all is OK on this side

    1. All laravel routes registered in web.php or added from files loaded from there have middleware WEB set by default. Routes loaded from packaged dont have that so you have to add such group first in your routes.php file and then place all your routes inside it. Auth will start working then.

  20. What about the case when the pacakage needs other dependencies? I added this dependencies in the composer.json file of the package, but how can I make that this dependencies automatically install when run composer install in the root of the Laravel project?

  21. HI,Thank you for your helpful tutorial,
    I some packages you can call some methods in laravel blades or controllers like {{jDate::forge(‘timestamp’)->ago()}}

    how can i call my packages abilities in my blades or controllers? have i to do something spacial or only after creation of package , my package controller method are available in all the project ?

  22. HI,Thank you for your helpful tutorial,
    In some packages you can call some methods in laravel blades or controllers like {{jDate::forge(‘timestamp’)->ago()}}

    how can i call my packages method without running the route in my blades or controllers? have i to do something spacial or only after creation of package , my package controller methods are available in all the project ?

  23. i am new lerner of laravel but 1 confusion can i use model,reposotery in this ,,,,,,,,

    on top comment one user says that his controler is created on path(under src/Http/controlerfile) but we have not http folder in src folder,,,,,,,,,,,why,

    and in src folder -am i using sub laravel prject? sir pls tel me

  24. Great write up!
    -Could give a few details regarding how to extend the functionality of a validator? So for example writing a package allowing to add new validation rules to laravel?

  25. really good tutorial but i have a couple of thoughts ,

    1- for laravel 5.3 we could replace include __DIR__.’/routes.php’; with $this->loadRoutesFrom(__DIR__.’/routes.php’);

    2- in laravel documentation they mention that :-

    https://laravel.com/docs/5.3/providers

    You should never attempt to register any event listeners, routes, or any other piece of functionality within the register method. Otherwise, you may accidentally use a service that is provided by a service provider which has not loaded yet

    so include __DIR__.’/routes.php’; should be moved to boot method

    3- in publishing views for resources/views/laraveldaily/timezones should be resources/views/vendor/laraveldaily/timezones as the implementation of ( loadViewsFrom() method searches first in /views/vendor/$path) ( it’s for laravel 5.3 don’t know about earlier versions )

    4- in the register method , i don’t know what’s the use of this line , as i removed it and it worked fine

    $this->app->make(‘Laraveldaily\Timezones\TimezonesController’);

    1. I wanted to write the same but i see you already did it. This comment should be distinguished from others or set in the beginning of the comments section

  26. I am doing this step by step when I want see my route say this
    NotFoundHttpException in RouteCollection.php line 161:

    1. Hi Mahdi,

      I get the same error. Previous packages succeeded but when I’m creating a new one I get the same error.

      Did you fixed this?

      Thnx

    2. Hi, I had the same issue and fixed it. My service provider was declared “deferred”, so it was not booting, and the routes were not loaded. My solution:

      protected $defer = false; // Or simply remove this line as it is the default value

      php artisan clear-compiled

  27. Hi and thanks for sharing!

    I managed to get my first package into the wild 🙂

    This is a package meant to make it easy to integrate Coconut video encoding service with Laravel.

    https://github.com/codeurco/laravel-coconutpalm

    It still needs a lot of work, but thanks to your article I could fastly get my feet wet on package development with Laravel!

    Cheers

  28. Shame that author didnt add any info about loading migrations -> creating database tables for package. It is as easy as the rest of actions. Also you can fill those newly created tables with your predefined values by Seeding. But personally i didnt try that.

  29. Two more things to add about views
    1. From Laravel 5.3 published views are not loaded from /resources/views/companyname/packagename but from /resources/views/vendor/viewsnamespace.

    so instead of
    __DIR__.’/views’ => base_path(‘resources/views/laraveldaily/timezones’),

    it should be
    __DIR__.’/views’ => base_path(‘resources/views/vendor/timezones’),

    2. When you slicing your blade and try to load external layout remember to add namespace in @extends methods. So if you want to load layout.blade.php in your time.blade.php you have to use @extends(‘viewsnamespace::path.to.blade’);

    Otherwise layout.blade.php will be searched in main resources/views

    layout.blade.php

    Laravel Timezones

    html, body {
    height: 100%;
    }

    body {
    margin: 0;
    padding: 0;
    width: 100%;
    display: table;
    font-weight: 100;
    font-family: ‘Lato’;
    }

    .container {
    text-align: center;
    display: table-cell;
    vertical-align: middle;
    }

    .content {
    text-align: center;
    display: inline-block;
    }

    .title {
    font-size: 96px;
    }

    @yield(‘content’);

    time.blade.php

    @exteds(‘timezones::layout’);

    @section(‘content’)

    {{ $current_time }}

    @endsection

  30. Just had an idea for a package and great to see that there was a tutorial from you after your amazing unconference talks last week.

    This tutorial helped a ton! Thanks!

    Frank

  31. Hello! Thanks for this tutorial!
    I just have a question: how do I add js and css to the views that I’m creating? Or how can I make them also available for the user?

Leave a Reply

Your email address will not be published. Required fields are marked *