From Idea to Laravel Package

Recently I started extracting common functionality I have requirements from between projects. To do that I wanted to create a package.

First thing I realized over time is not to try to build package in advance just assuming that is something I will be using often. So until you really use it in at least two separate projects do not create package for it.

When you see yourself copying same folders from one to another project that is a good time for it.

Also the good thing about creating package is that it forces you to separate your code - to use good code practices.

The point is not to immediately create package. This is how I do that.

Note: before starting with new package, check if something similar already exists. Maybe it is easier to use existing package or modify it.

Step 1: Separate Folder

Put all the code in the same folder. I'll take for example Charts package. In Laravel app I would create folder app/Chart. Move all my classes and adjust namespaces (App\Chart) and see if that works. At that point I'm still not trying completely to extract things to package.

Let's say this is first project this code is used on. On the next project, I would copy content of the folder and make classes work with new project. This is a moment when you can detect all the parts of the package that should be configurable and which parts are tightly coupled with previous app.

Now it is possible that you have "framework's" pieces of code with your package, for example routes. If that is the case, it is time to go to next step - which is optional.

Step 2: Service Provider

To connect package routes and other assets with host framework, create a service provider, for example MyPackageServiceProvider. There you can register routes, config files, which assets will be published (views, styles or scripts) or register console commands.

This is example of service provider I'm using when going through this process:

If you do not need any of these integrations with framework, for example, you have just plain php classes, you don't need this. If you have at least some framework related elements, I suggest using service provider.

Step 3: composer.json

Now it is time to completely remove package from the project. Create a folder for it and move everything inside. In previous example that would mean from app/Chart folder to for example ~/Projects/MyPackage. And again you must adjust namespaces of the classes. Now you can organize code in some typical way, for example: src, tests folder, but it is not mandatory.

To make it a real package you have to define composer.json file. To do that, run composer init. Command will guide you through creating composer file.

Laravel Package Auto-Discovery

If you are making package for Laravel new feature in version 5.5 is Package Auto-Discovery. You can something like this to inform Laravel to automatically include your service provider with Laravel.

"extra": {
        "laravel": {
            "providers": [
                "Package\\Chart\\ServiceProvider"
            ],
            "aliases": {
                "Chart": "Package\\Chart\\Facade"
            }
        }
    }

Loading your code

You must define your composer autoload strategy for mapping namespaces to your folder structure:

"autoload": {
    "psr-4": {
        "Package\\Chart\\": "src/"
    }
},

Step 4: Using newly created package from local folder

When package is in separate folder, it is still "fresh" code and not something that is ready to be published, you will want to use it in another project. If it fits there well, then it is good for publishing.

Host app that will use the package needs to be configured to pull package from local folder. Change host's composer.json in following manner:

"repositories": [
    {
        "type": "path",
        "url": "~/Projects/MyPackage"
    }
]

Folder ~/Projects/MyPackage will have previously generated composer.json which will define name of the package. That way you can composer require package that is located in that folder.

Because composer will yell at you for not using package witch comply with minimum stability in your package:

Could not find package dam1r89/passwordless-auth at any version for your minimum-stability (stable). Check the package spelling or your minimum-stability

... you have one good and one bad option to resolve this (in my opinion).

  1. In composer.json of the package add "version": "1.0.0". This is not perfect because you have to remember to remove it before publishing package.

  2. Aliases. Create alias like in composer.json

"require": {
    "vendor/package": "dev-master as 1.0.0"
}

And then run composer update vendor/package which will install (update) only your package.

This is great way of integrating package but still being able to quickly make changes which will be automatically reflected on main app.

Step 5: Publish on gihub

When working with many people on a project you want to allow them to use the package, but the package may not yet be ready for publishing. You can push code to github/bitbucket and pull it as a dependency by defining your package repo url in composer.json like this:

"repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/username/package-name"
        }
    ]

Step 6: Publish on Packagist

Packagist is able to read git tags and provide same versions to end users. So when you think you have something that is fine for first version you can tag it with git:

git tag -a 1.0.0
git push --tags

And push it on your branch.

When everything works and package is integrated into the project, it is probably time to publish it on packagist.

Also make sure that you have a good tests and documentation in readme file. If possible screenshot is nice too.

TODO: Check how to put those fancy badges.

Open Packagist and submit link to your github repo. It is good to setup a webhook from github which would automatically update your package any time you push new version.

And that is it. Your package is available through composer require <your_package_name>

Author

I plan to write more articles about common laravel components. If you are interested let’s stay in touch.
comments powered by Disqus