So before I start my talk, let me tell you a secret Freak is not going to like to hear it, but package development is not some black magic in a small town in Belgium, so you can all you can all do it and I’ll show you how So we’re a quick intro about me.
My name is Massimo geode, I’m a managing partner and developer at beyond coach, which is my company, and earlier this year, together with freek and all the spicy team, we worked on Flair and ignition feel free to follow me on Twitter. If you want all right, so why should you care about package development in the first place? I think there are three major benefits. There are a couple more, but these are the three I want to focus on.
So the first thing is by stripping out parts of your application into packages. It allows you to come to concentrate on your core domain and on your core features. So let’s say that you work at a company that develops web shops and your boss comes in and wants you to create a CMS for one of your shops. Now the core domain and the core feature of what your application is doing is selling things online and making it easier for your customers to sell and make money online, and the CMS is not necessarily part of that core domain.
So if you would add this to the monolithic application, you would suddenly make the CMS a part of your core domain, or at least it will live in the same thing. So I think that if you strip that out to a separate package, you get a lot of benefits, because suddenly the CMS is its own core domain and it becomes a lot easier to maintain. It becomes a lot easier to add features to it, because it’s just a small thing: it’s just the CMS and nothing else, and once you start doing it, it becomes a lot you to reuse these things.
So let’s say a colleague of yours comes in to your office like six months later and says: hey, I need to add a CMS to another project, and then you start copy pasting things around from your old project. At least I did some time and when you do just use packages, you can just say: okay use this package, and even if you cannot apply 100 % off the package and reuse all of it, I’m pretty sure that you will still save some time by reusing Existing things – and even if you do this, you can then extend this package to make it work with other pieces of your software.
And the third benefit is that you can get documentation for free. Well, it’s not really for free, but because the scope of a package should be very small, it becomes a lot more enjoyable to document it. I guess we all don’t really like writing documentation as developers, but at least I feel when the scope is small enough. It’s at least easy to do it and more enjoyable than if I would have to document the whole shop with how the CMS is somehow attached to it.
So the package that I want to build with you today is called arival badges. It has two main features. The first one is, I want to create a badge, so I want to add some kind of gamification to my applications. So a badge would be a one-year membership and the second feature is, I want to define rules based on which the badge gets applied. So, for example, the one of your membership badge gets applied when a user in my system has been created less than a year ago.
The way we’re going to do this is I’m going to use a package boilerplate because they simply don’t have the time to start with an empty directory and start from scratch. We’re using TDD because that’s how we do it right: yeah, okay and then we will implement the actual logic of the package and then I’ll show you how you can publish the package if you want to open-source it. So let’s start to build the actual boilerplate codes.
I created a web. I called laravel package boilerplate comm, I’m also going to tweet that out later, and the website basically allows you to choose what kind of package you want to generate and what kind of boilerplate you want to generate. So you can choose between a level specific package which comes with a service provider and some predefined config files included or a generic PHP package, which is what I am going to do right now.
Then you can fill in the details, like your vendor name, the package name, information about the author and, last but not least, the license, which is really important. Whenever you want to publish something on the open, please always ensure that you have at least one license. If you don’t know which license to pick, you can click on this question mark which will lead you to choose a license com and there you can see which license allows you what to do what the user needs to do when they download your software and what They can do with it if you find software online without a license at all, it basically means you are not allowed to use it.
So please always choose license. If you want to and then next you can download the project as a zip file. I already did that because I thought it would be boring. Reading me do a composer install over the Wi-Fi here. So this is basically what it looks like. We have an empty class. We have a test that returns true and we have so a bunch of files that ensure that once we run this on some CI systems, it will make our lives easier.
So the important file is the composer JSON. Is this big enough for you in the back? I can see anything, but I guess silence to see yes, okay, cool, so the composer JSON file is basically what tells composer and packages. If you publish the package that all the files in here are a package that you can install. So we have the name of the package, then we have some meta information like description, keywords, information about the author, the license stuff like that, and then we come to the required section so similar to the require section in your laravel application.
This is the list of dependencies. My specific pack chassé, so in here, I’m depending on PHP, is some point, one which is a bit outdated and I’m depending on illuminate support in version six, which basically allows us to make use of laravel features like the service provider. In version 6, so everyone can install this in level projects from version 6 and up then we have the DEF section and those are the dependencies that we use while developing this package.
So we use PHP unit to run our tests and I’m also using Orchestra test bench, which is a very cool package that basically allows us to test our package as if it would live in a level package. I’ll show you later, then: we have the auto load section, which basically just says that everything in the Bianco Daryl badges namespace lives in the source directory and during development. Everything in the Beyond code level, badges tests, namespace lives in my tests directory and that’s pretty much all that we need for now.
So let’s start implementing our features. So the first feature was that I want to be able to create badges. So let’s create a test. It can create badges like this, so the tests would look like. I want to create a badge, and the badge has a name, something like when your membership and, let’s give it a description to grant it to users, are members for more than yeah cool. I just like alright and then the actual test would be that we assert that the badge exists so pretty simple test, but this would test the functionality that we have a model.
We have a database, we can insert the badge in the database and after inserting it, it exists. Please note that this test is not extending the regular PHP unit test case, but instead I’m extending from the orchestra test bench test case which, in the back when running the test, is going to boot up laravel. So, let’s see what happens when I run this test. Spoiler, it fails okay, so class badge not found.
Okay, that makes sense. So, let’s create the batch model now so create a new class badge, it’s going to extend from Alcuin model and I’m going to say that we don’t have any guarded properties just to make my life a bit easier during the test. So I don’t have to fight the math assignments. Then, let’s import the class in here and rerun the tests. It still fails – and this time we get the error that the access dinner is the night for forge at localhost, which is kind of strange, because we haven’t defined that we want to use my sequel or Forge or anything.
But this comes from Orchestra test bench. So the default level application under the hood has forged as the default username and it uses my sequel now when, like a colleague of mine or when I published this on open source, I don’t want people to have my sequel installed and the specific database just to Run my tests, so I want to use an in-memory sequel, Lite database and luckily enough level, ships with in-memory database.
So we can just specify that we want to use one in our PHP unit X in our file by adding a new section called PHP. And then we just define an environment variable, so we say the DB connection will have the value of testing and it’s still going to fail. But now we were able to connect to the database because it’s now using the in-memory sequel, Lite database and we don’t have a badges table which makes sense because we only have the model but no migration, so let’s create the migration.
Next I like to structure my packages so that they resemble the default laravel structure, so the source directory is basically my app directory and everything else lives outside of it like assets or even by babies. Migrations, that’s great database directory and there we have a migration stork dream, and in here we have a new file, all great badges, tabletop PHP. So let’s write our migration. Migrations are not namespace, so we don’t need to do that in here.
Create badges table. It’s going to extend from a migration, let’s import this, and then we have an up method where we create the table. So we have email, creates badges and we get the blueprint of our table. I’r going to import this, so there becomes readable, alright and our badges table will increment an ID. It will have a string for our name. It is going to have text for the description and some timestamps alright.
So this should be enough and then we need a second table that will hold the connection between the badge and whatever model we associate with it. So, let’s create that I gave it the fancy name, vegetables of the table, and now this is just an unsigned. Integer of the badge ID and we’re going to use a polymorphic relation that holds the model that we associate because we don’t know upfront which models we want to associate later on with our batch.
Okay cool. So I think that’s our migration, but our test still doesn’t know about it and the test will still fail. So how can we let our test know about it? Since migrations are just basic PHP classes, we can just create them and run the app method on it. So I’m just going to override the setup method and do this beautiful void, call the parent setup and in here we can just require our migration. Here there we go and then we can new it up and that one and run up on it and now, every time a test runs, we will load the class and perform the app method on it with a bit of luck: yeah, okay, cool.
So our first feature is basically done. Our package can create badges, but there’s a problem with that, because, even though we have the migration in our package, if someone would install the package right now, they wouldn’t know that a migration exists. So the way we do this with laravel is by using a service provider. So let’s create a service right in here. Let’s say: badges service provider going to extend from the service provider that comes with laravel and in here we have a register method where we can add things to the Container.
We don’t need this right now, but what we can also do in a service provider. It’s we can say: hey this package is going to publish some files, so we are going to publish the migration in this location and when it gets published, please copy it over to my database path migrations. Then we use the current time stamp, followed by create badges table dot PHP. So now, when someone installs your package and runs PHP, artisan, Bend or publish and chooses your service provider, laravel is automatically going to copy our database migration that we have in here to the user’s database, migrations, folder and giving it the current timestamp as the full name.
As the file name – okay, cool – so that’s pretty much. The first feature, the second one. That was that I want to be able to associate a batch to a model. So, let’s write the test, wait, it can associate badges, it’s going to be a test and the way this test would work is first of all, we need a batch that we can associate and then, in my packages I like to just come up with an API That, I think, is good to work with and then later on.
I try to make it work so for this I think something like this would be good. We have a batch provider where I want to grant the one year membership badge to a test user class when a specific condition returns true, so when the user created add, is less than now a year. So when it’s older than a year, I want to grant this batch to this user model, so we would have set up this and now to actually test this.
We still need to create a user, so we’re going to say test user create. I want to reuse the existing user table that ships with laravel – and I don’t have to create a migration in here just to test this feature and a default user of a level migration where the user has a name and needs an email, and we need a Password and we need to provide the Creator that date, because that’s how we based the page for later on, so then we say now: 12 month, ok and then the actual test would be that we assert that the count of the user badges would be 1 okay.
So this is how such a test could look like. We create a badge. We define the rule that it gets to find when the user was created less than a year ago we created the user, and then we have the batch. Ok, let’s see how we can make this work, let’s start with a test user. So I’m just going to create a class in my test in here. So here we just do the test user, which is going to extend from the default user that comes with laravel.
Since I want to reuse that table once again, we done we say we don’t have any guarded properties and I’m going to overwrite the table as well and set it to users, because by default, Errol would now guess the table name based on the class, and it Would come up with tests under score users, and I don’t want that so like this. So that’s basically to make this part work, but we don’t have the badges.
The way I imagined this to work is that every model that wants to create the relation between badges can use a trade. So in here we would say something like use: has badges and then this trade would give the relationship that we want in here. So, let’s create that trait, let’s put in traits territory as badges will be a trait okay, let’s import it so that phpstorm can do its magic. Okay and basically, all that this trade does is.
It provides one function called badges where we are going to return a morph to many relationship to our batch model. We called it model in the migration and the database table and the table itself is called vegetables all right. So now this should work. We should be able to access this, we can create our user, but this batch provider still does not exist. So let’s do that too. So we have a batch provider class, it’s not going to extend anything, and this class basically holds three properties.
We grant a badge to a model when a condition is true, and then our public API looks something like grants badge. Then we return this so that we easily chain our methods to a model. All right. You turned this again and then we have the wind method. With the condition and in here we would do our apply by just logic, okay, so this would be basically all we need to apply the badge. As Janek told you yesterday, we are going to use an eloquent event, so this is definitely not the most performant way to do this, but it works.
So what I want to do is I want to listen for an event, namely the eloquence saved event. So, every time this model gets saved, we’re going to call a closure, and in this closure we are going to check if the condition returns. True we’re going to associate the badge with this model. So, since we only have the name of the badge, we first need to pull it out of the database, so this would be fetch where name, that’s badge first or fail in reality.
You would have to do some more exception handling in here and throw a best custom exception, but I’ll leave it at is, and then we pass this badge to the closure and then perform the condition check. So this, then, basically, if a call to this condition with a model that just got saved returns true, we want to associate the badge with the model, so we’re going to say model badges, think without detaching.
So this is going to attach the badge without removing the existing badges and without adding it multiple times. So here we have the badge ID okay. So let’s go back to our test here. We I wanted to actually make this work like if shot now. I could use a real-time façades, I’m not a big fan of those so to see if it actually works. Let’s just create a new instance of our batch providers, so in here we have a new batch provider.
We grant the one-year membership to the test user when the condition returns. True, okay, cool, let’s see what it test says, all right: it fails no such table users, which is strange because it comes with laravel right, but Orchestra test bench actually does not migrate. These sort of default migrations because you might want to override it in your own tests, but luckily we can just tell test bench that it should do this by saying this loads clarify migrations, and then we have our users table.
So if we run this again cool all tests are green and to prove that this really work. If I do this with 11-month, it should fail, and it does so now the actual size is zero. While we assert that we have one perfect, okay cool now, I still have time to create the facade. So, as I said, we could use a real-time facade, but let’s take a look at how we can do a regular facade. So, let’s create a new directory facades, and in here we are going to create our batch provider, facade class, which is going to extend from the facade that comes with the luminate support, and all we need to define here is the ghat facade, accessor method.
So this method should return what laravel should pull out of the container when this facade gets accessed. In our case, we don’t have any interface or contract that we need to pull out, so we can just return the concrete implementation which, in our case, is the batch provider class itself like this okay cool now back to our test. So we should now be able to replay if this, by doing something like this and use the facade instead, so let’s run the tests and well it fails, because the class batch provider was not found so in laravel, these facades work by they registered themselves.
Basically, and so that you can access them as in the root namespace. So when we want to test this, we basically just have to tell Orchestra test bench that we want to do the same thing so in here we can just define a new method, get’ package aliases. We can return an array of aliases that we want to be accessible in this test. So in our case it’s the bash provider and it points to the batch provider, facade, class and look it’s.
There works, ok, cool. So now we can access and test that our batch provider can be used using the facade that we registered. It can be used using the regular implementation. Ok and, as I said, this is not the most performant way. I wouldn’t really use this in production, but it can show you what you can do now to actually publish this thing. It’s pretty easy: if you want to publish it on github, you only need to do is create a repository and gets up, which I did I’m not going to push it now, because that’s kind of boring to see.
I guess once you do this, you can then go to packages which is the composer package registry. There can create an account and login with github. That’s the easiest way to do it go to submit and then in here you just paste in the github URL press check and then it will check if an existing package exists with the given name or if the vendor name is already taken. If it does not, you can just add it to the registry tag a version and then you can use it, but even if you don’t want to submit it, you can still use private packages and points to the github URL in your composer JSON file.
Okay – that’s all I have so. I hope that it was able to show you that can create a package. It’s not like magic, it’s really more about knowing how you can test the package and what features or Crysta testbench gives you there’s a lot more. If you want to know more about package development, I created the article course that you can access at this bitly URL, which has, I think, $ 10 coupon for you and in addition, if you want to try out FLIR, this is a coupon code that gives you One month of flare for free, if you want to do that so yeah.
Thank you very much.