tag

Monday, 23 March 2020

3 years ago

7 minutes read

  • Laravel
  • Github
  • Package
  • Open Source
  • Testing

1 on 1 with Laravel package development

This article was written for those who want to start developing packages for the Laravel community, but haven't quite figured out how to start, what to do or how to do it. Here, I will explain how I developed one of my packages, and try to show you some tips and tricks I learned along the way that have helped me develop and maintain the code.

Tiago Sousa

Tiago Sousa

Software Engineer

This article was written for those who want to start developing packages for the Laravel community, but haven't quite figured out how to start, what to do or how to do it. Here, I will explain how I developed one of my packages, and try to show you some tips and tricks I learned along the way that have helped me develop and maintain the code.

First of all, if you want to develop a package, that's probably because you are facing the same problem that you faced a few months ago or because you want to extract some code to use across multiple applications, at least for me that was the motivation that came across and the reason that I started writing packages. That's a great initiative, in my personal opinion, and has a lot of pros. The first package that I wrote came out when I was working on an internal project for the company that I work for and the problem was quite straightforward … I was always reproducing the same steps.

  • Create the Migration
  • Create the Factory
  • Create the Model
  • Create the Seeder
  • Create the Controller
  • Create the API Resource and the Collection
  • Create the Request Validator
  • Create the Policy

So I decided to automate the process and invest some time coding a speed up development package to improve all the artisan commands and boilerplate generated by them. And yes, that's how Laravel Resources came out.

1. Introduction to the Inceptionlink

The first thing that I did, before writing Laravel Resources, was taking a step back and of course, read carefully the composer and laravel documentation (once again πŸ˜‚) regarding package development and configuration. After that, I started to think, "well…. that's a lot of configuration and boilerplate to start developing my first package! This should be automated as well". It might sound ridiculous, but after this thought, I googled "laravel packager" and, guess what … the first result was a Laravel Package from Jeroen to develop Laravel packages, crazy inception here.

gif

So I started by reading the documentation, as I always do before installing any package, and quickly came across the feeling "Dude, that's insane …". So I decided to give it a try and avoid "reinventing the wheel".

2. First things first, initial configurationlink

After reading all the documentation that I needed to start coding my package I just used the magic artisan command available from Jeoren package.

php artisan packager:new tiagomichaelsousa LaravelResources
copy

This command will create, in the root directory of your project, a package folder with all the boilerplate that you need to start coding your package. After this step, I normally update all the documentation that I already can, like license, composer.json file, code of conduct, how to contribute, etc. Don't forget, you are publishing a package for the open source community, so documentation is a must!

In order to use a package you must pull it from composer so, from my shell I just typed:

composer require tiagomichaelsousa/laravelresources --dev
copy

And boom, the magic happened πŸ§™πŸ»β€β™‚οΈ You can already use the package. But, how?! Well, when you use the packager command to create a new package it will update your application composer.json file and add the following lines out of the box.

These lines will allow you to add a package locally and you can interact directly with it like if it was published in packagist. What about if my package is already published in packagist and I want to add new features to it? I will cover that too, keep reading πŸ˜‰

3. Build something amazinglink

After the initial configuration, I was finally ready to write some php code πŸŽ‰. So I started to generate the artisan command that would be responsible for the creation of all the resources for a model.

Inside your package folder, you will find a ServiceProvider File. If you don't know what is a service provider it's literally what it sounds! It provides a service to your app, allowing you to trigger some functionality from a component. If you are not comfortable with Service Providers I would recommend taking a look into Service Providers at Laracasts.

At the bottom of your service provider file, you will find a bootForConsole() method where you can add the path to your commands to make them available to the php artisan shell command.

I also like to add the path to the commands at the top of the service provider to immediately see the commands that the package will provide.

private $commands = [
'tiagomichaelsousa\LaravelResources\Commands\ResourceCommand',
];
copy

And in the bootForConsole() method, I register the commands.

$this->commands($this->commands);
copy

4. Package functionalitylink

The artisan should receive the model name as a parameter and should be responsible for checking if the model already exists in the project. If the model wasn't created yet it should ask if you want to create the migration, factory and the seeder by just saying yes or no. If you just press enter it will assume the answer as a yes and will create the resource.

After that, I decided to create a Class to start writing the code for the Controller. The implementation went really well and quickly I already had the package generating the controller with all the stubs that I needed so I started to write the code for the FormRequestValidator. As soon as I started to write the FormRequestValidator code I understood that the coded started to get the same structure. It should have a constructor to receive the model name that you had written in the shell, it should have a stub, it should have a replacement function for the stub, it should check if the file already exists and so on.

So once again I took a step back and decided to extract all these functions, that will be the same across all the generated resources, to an interface that I called Generator. Now every resource that was going to be created should implement all the functions present in the contract. Basic Object Oriented Programming πŸ˜‰

After some time I had pretty much every features that I wanted for the package done and was really satisfied with the result! πŸ™Œ

5. Testing? Hell yeees!link

At this time, the package was ready to be published to the entire community and I was so hyped that I almost forgot that I didn't even think about testing. Since it's an open source project it's really important to have tests for the core of your package! Imagine that a PR is submitted to add some feature to the core of the package and you just accepted because it was a really cool feature and the code looks pretty good. So far so good, but the developer that implemented the feature made a typo in the artisan command to generate the resources and you didn't see it… Well, the package just died there because you didn't even have a test to ensure the IO for your package. So yes, don't write any code without tests.

gif

The packager command already boosts you with testing by adding out of the box the phpunit/phpunit and orchestra/testbench to your composer dependencies, so use it! Writing tests is not rocket science and nowadays it became so simple! The first thing you should do is create a TestCase class where you set the default behavior for every test. It should have a setup and a teardown function. The setup function will run before every test and the teardown function will run after each test.

After some time I already had all the test suites that I think I should have, but how could I be sure that I have good coverage for the entire package? So I used the phpunit command to execute my tests with the coverage flag.

./vendor/bin/phpunit --coverage-html report
copy

This command will generate all the coverage report in HTML files, inside the report folder, for your test suite and you should add it to your .gitignore file since it's not relevant for the features of the package.

And guess what …. so many tests that were missing πŸ˜ƒ

NOTE:

if you don't know all the flags that a command has just use the --help flag!

To generate the test coverage you will need debug. You can just install it with pecl.

pecl install xdebug
copy

6. Make it livelink

After a couple of days, I was finally ready to make the package available for the entire laravel community but, how does this works? Let me check the packager documentation and …. guess what?

php artisan packager:publish tigomichaelsousa LaravelResources https://github.com/tiagomichaelsousa/LaravelResources
copy

Boom, everything published in a clean repo on GitHub πŸ˜‰

But just the package in GitHub will not allow other developers to use it, you must publish it in packagist. To do so, you just need to login with your GitHub account in packagist and submit a form with the link of your GitHub repository.

7. Add new features to the package once submittedlink

I submitted the package to packagist and decided to send it to LaravelNews in order for everyone to see and hopefully give me some feedback. After a couple of days, I wanted to make some changes to the core of the package but I wanted to have the same environment that I had when I was coding the package in the first hand, but suddenly a wild problem appeared.

The package was already published to packagist and when I was trying to install it locally I was pulling the last version in packagist instead of the local copy that I had in my machine. It took me a while until I finally understood and solve this problem! The solution was pretty simple in fact, I just needed to add dev- before the branch in the composer.json file for your default laravel application.

"tiagomichaelsousa/laravelresources": "dev-master"
copy

Or you can require a default branch when you are running composer require:

composer require tiagomichaelsousa/laravelresources "dev-master"
copy

After that, if you run composer install you will see in your shell a message saying the symlink between the package and the composer dependency was created based on your package path.

8. Final noteslink

I used and I definitely recommend TravisCI integration for maintaining your packages. It's definitely a must for these kinds of projects because when someone makes a PR it will automatically trigger the test suite and ensure that everything is fine with your code. Besides that, it will give you feedback directly in the PR if the tests are passing or failing!

Regarding the open source and the community itself, I would like to just say some words. When someone opens an issue in your repo, always try to see this as something that you can improve, not only in the code but in yourself. What I mean is, try to always see the critic as a constructive one instead of destructive. That's how communities are built.

I hope you enjoyed reading this article and most importantly, I hope it will help you someday with your personal projects or even at work πŸš€

If you found this article interesting, feel free to share it with your colleagues or friends, because you know … Sharing is caring!

Also, if you enjoy working at a large scale in projects with global impact and if you enjoy a challenge, please reach out to us at xgeeks! We're always looking for talented people to join our team πŸ™Œ

Useful links and tipslink

Coday: Codacy is a static code analysis tool with metrics like Coverage, Duplication and Complexity. Also, it will generate cool badges to use in your readme file

Tiago Sousa

Written by Tiago Sousa

Hey there, my name is Tiago Sousa and I'm a Fullstack Engineer currently working at xgeeks. I'm a technology enthusiast and I try to explore new things to keep myself always updated. My motto is definitely "Sharing is caring" and that's exactly why you are currently reading this!

Interested in collaborating with me?

Let's Talk
footer

All the rights reserved Β© Tiago Sousa 2022

memoji12

Designed with love by Mauricio Oliveira