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).
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.
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 Github
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>