Recently I went through the process of taking one of my Laravel projects and turning it into my first PHP package. I wanted to write this blog post to describe my discovery process. It's not intended to be a detailed explanation, but I will consider writing a tutorial in a future article.
First, I want to start off with the reason why I turned my project into a package. Not too long ago I spent a couple of days building a prototype for a flash card app that was going to help me learn another language, Spanish. I started by setting up a fresh Laravel project and installing the user authentication that Laravel provides. Then I built all of the features I thought I would need to get started and thought about how I wanted to deploy it to AWS. I already had an EC2 instance with my website and other projects in Docker containers, but I was hating having to register as a new user and sign into each one of my projects separately. That's when I started looking into packages.
After watching some YouTube videos and looking at the source code of a Laravel package I had installed on my own website, I was convinced that turning my project into a package was the right approach.
Before embarking on this journey, perhaps the biggest mystery for me was how I was going to develop a package before publishing it. I knew about Packagist, and I knew that if I put my project up publicly, I could install it with Composer, but I had no idea how I could make changes and test locally without having to publish those changes and go through the whole update process over and over again. Ultimately I arrived at the answer I was looking for which was contained within Composer itself.
Composer allows you to install a package from a directory on your local machine. This works via a symbolic link so you don't have to update or re-install the package with every change. When I learned this, I created a "packages" directory in my Laravel project to test it out along with 2 essential files in the package directory: a
composer.json and a Service Provider. After that I added some information to the
composer.json of my main project telling it to check the packages folder for this package instead of trying to download it off of the internet. After running
composer require I was able to see my package print some text to the browser.
Once I saw that it worked, I started copying over my routes, controllers, models, migrations, and resources and fleshed out the service provider so that it registered everything. I then added an install command to run the migrations and copy over the assets and the config file to the main project. In order for my code to work I had to switch everything to the package namespace as specified in the package's composer.json instead of the standard
App namespace like it is with every Laravel project. There was a lot of trial and error and troubleshooting, but eventually I got it working. I spent a lot of time referencing the source code from Canvas which is a Laravel package for publishing articles and blog posts.
After creating a public repo on Github, I published my project on Packagist. Then I set up a fresh Laravel project with user authentication and ran
composer install to test the installation of my package. It installed successfully, but not without a few critical errors that I needed to go back and fix. Eventually I deployed the entire project to one of EC2 instances and after fixing a final round of bugs, my flash card app was ready to be used.
Overall, I think developing projects as packages will really help in my development process. My pattern has been to build apps incrementally while using them internally. Issue Zero is one such app. I have been developing it for 10 months now, and I have seen over time how the core features have emerged. Now I can see how transitioning this project to a package can help address some of the roadblocks I have that are keeping me from launching it to the public.
Having gone through this entire learning process, I feel compelled to build all of my Laravel projects as a package by default. Maybe it won't work for all projects, but I can't imagine why it wouldn't work for most. When it is time to deploy a project to a production environment and start welcoming users, I can just set up another repo to provide a dedicated Laravel application shell instead of a shared one and I can keep the core functionality in one or more packages. If I need a management dashboard, I can add that in as another package. If I want to provide some premium features, I can create some private packages and add those in. I think then I have a lot more control over how I deploy my app and I will be a lot less hesitant to release some as open source.
When developing in packages, not only is it easy to have a single login for projects that share the same Laravel application, but all of that Laravel boilerplate code is reused. And when I want to start a new project, I can skip the whole Laravel setup process and avoid setting up a new server and database when I want it deployed for internal use. I can think of other benefits as well, but maybe I will save that for another blog post.
Here is the link to Github repo of the package I created if you are interested: https://github.com/aalcala07/cardflash. It's very simple and it's still a work in progress as I plan on adding more features.