Package Development 101
The benefits of package development, including the ability to focus on core features, reusability of components, and the availability of documentation.
Transcript
00:00:00 I'm going to talk about package developments very quick about me because I have a lot to show you my name is Marcelo geodes from Germany I'm a managing partner and developer at a company called beyond coat and you can follow me on Twitter if you want to our tweet about level PHP related stuff all right so why should you care about package development so one reason would be or from Belgium and you like postcards but the bigger reasons I think would be like the main benefit in my opinion is you can concentrate on your
00:00:37 core features or your core domain so for example when you're working at a company that builds online shops and your boss comes in and wants you to build a CMS feature then adding this CMS feature would not be a part of your the core domain of your online shop business I mean it's not helping people directly to sell things online so by moving this into a separate package it becomes a lot easier to maintain it become because now the CMS feature is its own core domain and you can keep focusing on that piece
00:01:13 of code in its own little bubble and you don't have to care about what's going on and like the online shop around it and once you do this and build like multiple packages then you have the big benefit of being able to reuse these components and just pick the ones that you want to use in other projects so if at the same company your boss comes in like half a year later and wants to add another CMS feature to a different shop you can just go oh yeah I built this sometime and just pick that package the built and add
00:01:49 it to this other project of yours and even if you can't use it for 100% and you have to adapt some things it'll probably make your life a lot easier it will make faster that you develop these packages these features and the third benefit in my opinion is that you get documentation for free I mean as developers I don't we all love to write documentation if I'm not mistaken at least I'm not really loving it but once you approach it from documentation driven development kind of side and you see that you work on a
00:02:27 package and you only see and write the documentation as if the user doesn't know anything about what's going on behind the scenes it becomes a lot easier for your future self so if you go back to the package in a couple weeks or a month you just scan the documentation you know what's going on and also for your colleagues and if you share it on github then for basically the rest of the world so instead of just talking about how I approach package development I thought I'm going to be a bit risky and build a
00:03:06 package on stage so the package we're going to build is called parallel badges and has two main features so the first feature is of course we want to create badges so it's like a gamification feature where it can say I want to grant a one-year membership badge to a user and then I want to define a rule so that this badge should be associated with that user once he is a member for more than a year so the way we're going to do this is we use a packaged boilerplate because I only got 30 minutes so I'm not
00:03:41 going to build everything from scratch then we use TDD because that's the way to go right and then we implement the package and I'll go to publish it alright let's dive in so bigger there are a lot of resources on how you can get started with packages and have like these boiler plates one thing that I built is called Laurel package boilerplate it's at laravel packaged boilerplate calm and it allows you to choose between different like templates when you want to create your package one is a level preset it comes with things
00:04:22 like service provider it has and commented codes that shows you how to add configs publish acid stuff like that and you have a generic PHP package as well which is what I'm going to use during this talk and then once you are on this website you can define the vendor name the package name your name as well and this will then gets generated in the packaged outfit and one important thing is the license so whenever you publish a package and want to maintain it as or keep it open source please always go and
00:04:59 choose a license if you don't have any license at all it basically means you are not allowed to use this package so if you find a package or a piece of code somewhere without a license you are unable to use it basically and if you click on this question mark it goes to a website called choose licensed comm which helps you figure out which license you should use alright and then yeah if you fill this out you can download the package a zip file I already did that because I don't want you to watch me do a composer install
00:05:32 all the time so this is the the downloaded boilerplate and what we have here is basically the composer JSON file is it big enough for everyone to see yeah okay and so this composer JSON file is basically what tells you or what tells packages and composer that these bunch of files make up a package that you can install into your application so the important part is the name so this is what you will use when you do a composer require and then this package name and since we want to build a laravel related package I added two
00:06:16 requirements and dependencies to my package so one is aluminate support so we want to make use of things like a service provider and a facade and everything from version 5.8 and the other one is I'm going to use a package called Orchestra test bench and this one allows you to test your package as if it already lives inside a level application I'm going to show that in a minute then we have the Auto loading set up so everything within the source directory is in the beyond code Clarabelle badges namespace and everything in the tests
00:06:52 directory as a def Auto loading is in the beyond codes laravel badges tests namespace alright so let's get going the first feature that we wanted to build is we want to be able to create badges so since we want to create our tests first the tests would be it can create badges and the test would look like something like this so I would create a badge and it has a name so like one year membership and maybe a description whoop granted to users that are members for more than a year alright and the actual test would be
00:07:43 this we assured that the badge exists okay so of course this test is not going to pass because we don't have this badge class yet so let's build it so inside my source directory I'm going to create a models folder and we are going to create a batch class and this class is going to extend from the eloquent model and to make our lives a little bit easier I going to say that we don't have any guarded properties so that we when we create the badge in our tests we can just put in there whatever we want
00:08:24 okay so let's import this class and let's see what our test is going to say a lot of things are going wrong and that's the first thing is we have a call to a member function connection on null that's because our model is trying to save the data on a database connection but since our test is just a regular PHP unit test case it doesn't know that there is a framework behind that we have a database connection that needs to be set up and things like that so since we pulled in Orchestra testbench we can
00:08:57 just get rid of PHP unit test case and pull in the orchestra test bench case and now who run the tests again it still fails but the error is different so now we can see that the framework already booted up so Orchestra test bench basically gives you a blank laravel application that you can use to inject the package that you're building into it and the error we get is that the user Forge at local OHS cannot connect to the database and that's because the default laravel configuration just uses MySQL
00:09:31 with Forge as a username so I don't want people that I want to collaborate on this package to install MySQL just to run the tests so what we can do is we can go to our PHP unit XML file and tell PHP unit that it should set up an environment variables so when we run the tests go ahead and setup the DB connection environment variable to testing and this is making use of the in-memory sequel IDEs database that ships with our wall so if we run the tests again we still get an error but this time we are able to connect to the
00:10:11 database so our model is actually trying to save something but we don't have the table and that's because we don't have a migration yet so let's go and build the migration next the way I like to structure my packages is I have all my my models all my code inside the source directory and everything else like acids lives in directories that resemble the database folder structure so we have a database folder then a migrations folder and in here a PHP file crates badges table all right and this is going to
00:10:48 become our migration so these are not namespaced so we can just create it and this is going to extend from a migration let me import this and there we have an up method we use the schema great our badges table we have a blueprint for our table to import this because it's way too long like this and then we can define our table just as you would do that in your regular application so we have an ID we have a name and let's say we have a description and some time stamps and I'm actually going to create a second table because
00:11:40 we also want to associate the badges with some kind of models so I'm going to create a table that is called bad jables badger badger badger and this table is pretty simple we just have an unsigned integer for our badge ID and then we are using a morph relationship to associate the badge with whatever model we want to do okay so that's our migration but now our test still doesn't know that the migration exists so since migrations are just classes that perform an op method we can just call the op method in our
00:12:25 tests so I'm just going to do a setup we need this void called the parent setup method and in here we can just require this migration from here and then we can just new it up and perform the up method and with a bit of luck it's being so now our test knows about the migration and our tests can actually insert this badge model into the in-memory database that we have okay so this is not cool but once someone installs your package they don't know that you ship a migration with it so the way that our package can tell the
00:13:15 herbal application that you install it to that we're going to publish some files is by using a service provider so let's create a marital badges service provider class I'm just going to extend from the base service provider that ships with laravel and service providers basically have two methods once one is the register method we can add things to the ioc container we don't need that right now and the other one is the boot method which gets called once all service providers have registered whatever they
00:13:49 want to register so in our boot method what we're going to say is hey what you out laravel we're going to publish some files so we are going to publish our [Music] database create badges table file and when you publish it please move it to the database path inside the migrations folder and then we're giving it why am V H is current time stamp followed by create I just table dot PHP so the next time someone installs your package and runs PTR is invent or publish level is going to copy this migration file that
00:14:32 we have created into the database path with the current timestamp in the file name ok so let's take a look at the second feature the second feature we wanted to bet add was we want to associate a badge with some kind of model so our test would be it can associate badges that's a test and our badge is going to look something like this we want to create a badge and the way I'd like to develop my packages is I just make up API that I feel comfortable with and that I think is good to use so it would be something like bash provider
00:15:20 grant one-year membership to some kind of user class when a condition is true so when the user created a date is less than now - a year so when the user is older than a year we want to associate this badge to this user and I think the API looks kinda nice and that's the way I like to build these packages just make up the API that I would love to write and then we try to make it work so to actually test this we would need a user so we wouldn't need something like test user create I want to make use of levels built-in user user
00:16:09 table so we need a name you need an email I think we need a password and of course we need the created app date so we're saying creative add it's now - 12 month okay so we have this in place and the actual test would be something like we assert that the count of the user badges would be one so if we create a user that is over a year old we wanted to have one batch okay so a lot of things that aren't working here because we haven't built all of this yet so let's start with the test user first because that's
00:16:55 the easiest one so I'm just going to create in class inside my test here which is going to extend from the user class that chips with laravel so once again we don't have any guarded properties to make a life's a bit easier and I'm going to say the table is users because by default there will would guess the table name to be test underscore users and I don't want to use this okay so now this one's working so to have this working the badge Association I think it would be nice if our package would provide a trait that
00:17:31 other models can implement and use in their models so something X use has badges and let's build this what rights as badges as a trait okay go back and import this and all we need to do in this trade is we have a badges method we're just going to return the morph to many relationship to our badge class the name of it is a model that's what we specified in the migration and the table name was badger balls okay so if we go back to our test this should work we can retrieve the relations on the user we
00:18:22 have a user but we don't have the batch provider yet so let's build this as well so in here we're going to create a batch provider class doesn't extend anything and it holds like three basic properties so we grant the badge to a model when a condition is true and our API would look something like grant badge then we just set this up and always return this so that we can easily chain the methods to a model so once again and the last method was when the condition is true so this condition becomes condition and
00:19:14 there in here we're going to apply the badges somehow what's at that who likes private anyway okay so like this this would be like the easiest way how we can come up with the public API and if we go back to our tests let's skip this facade call in here or this static call and instead we just new up a batch provider instance and call the methods on it okay so that looks good let's see what a test says oh that's not so good no such table users so Orchestra test bench as I said it gives you a blank level application and the blank
00:20:01 level application comes with a user's migration as well but this one is not executed by default with test bench so what you can do in the setup method we can just tell a test bench to load the laravel migrations and we're good to go so now when we run our tests again it still fails but now it's a good failure because now it's nothing that's messed up in our tests but the actual test fails because we expect it to have a count of 1 but instead we don't have any badges associated to that user so let's
00:20:41 make this work as well real quick so to actually apply the badges we first need to pull the badge from the database so we have to say we get the badge where the name is equal to this badge and then the first or fail and then to actually apply it what I thought would be cool as we do an event listener every time the model gets saved so every time this model gets saved we jump into this callback function you have to use our badge in here and in here we are going to check if we call our condition with the model and this is going to
00:21:32 return true then we want to associate the badge to this specific model so then we're going to say model badges sync without deep dashing I'll explain that in a bit with the badge ID and that basically just says if the badge ID already exists we're going to just add it once and keep everything else in place so we we never end up having the same badge associated to a model twice now this is not like the perfect implementation it hooks on to like the eloquent saved the wind and it's just really good to show in this
00:22:18 talk but I wouldn't build it like this if it was a real application also this needs to bit of a be a bit more fail proof that this actually works okay but I think we have everything in place so if we run the tests again it's not green so we can associate the badge to the user and just to make sure let's say that the user was created 11 months ago and the test fails again which is good okay now coming back to our tests what I actually wanted to do was I wanted to build the facade and make use of it like
00:22:57 this so I wanted to call bash provider grant etc etc so let's go and create a facade as well so we could make use of a real-time facade here but for the sake of showing you how you can add these facades to your tasks we're going to build a real one so I'm going to put this as a facades class for cells directory and in here I have my batch provider class which is going to extend from the facade class that ships with laravel and then we have a gap facade accessor method and this one is telling laravel when someone is calling methods
00:23:39 on this facade class pull out this thing this object from the ioc container instead and call the methods on this object so in here we can just pull out the batch provider implementation class that we have and use this one instead okay since we haven't found that to the Container okay so let's see what our test says alright it fails because it says the batch Reiter class cannot be found so with orchids a test bench there's a cool way of telling it that we this this root class actually available so in here we can say get package
00:24:27 aliases and this is then going to return an array where say the batch provider alias is going to be pointing to our batch provider facade class like this okay and now it's all green again our test knows that we have this facade in place that's that it's in a root namespace that we can or without any namespace at all they can just use it like this and yeah that's all there is to basically facades and there's no real magic about it okay so the next step would be to actually go and publish our package I'm
00:25:15 not going to do that right now on stage because I think seeing me push code to get up is not the exciting thing but what I'm going to do is I'm going to push it up I created a repository it's called laravel badges I'm going to tweet the link to the source code in a minute so you can check it out but what you would need to do when you actually want to publish it you push the code to get up you tag a new release and then all you basically need to do is you copy the github URL go to packages and sign
00:25:47 in with your github username paste it in here and that's basically it and then your package is available on packages people can install it and start using it and yeah that's all I have we build a package from scratch if you want to know more about package development I created a video course by a PHP package development come so you might want to check that out and feel free to just talk to me around all the time here so just come and say hi thank you [Applause] you
Highlights
00:00-00:55 👤 Marcel Pociot introduces himself and his company, Beyond Code, and highlights the benefits of package development.
01:00-01:59 💡 The main benefit of package development is the ability to focus on core features and separate them from non-core functionalities.
02:00-02:36 ♻️ Reusability of components is another advantage of package development, allowing developers to easily integrate features into different projects.
02:37-03:47 📚 Documentation is automatically generated when approaching package development from a documentation-driven perspective, making it easier to understand and maintain code.
Key Insights
💡 Package development allows developers to focus on the core features of their application, separate from non-core functionalities. This improves maintainability and makes it easier to add and modify features.
♻️ By building reusable components through package development, developers can save time and effort by reusing code across multiple projects. This promotes code consistency and reduces duplication.
📚 Documentation is a crucial aspect of package development. By approaching development from a documentation-driven perspective, developers can ensure that their code is well-documented, making it easier for themselves, colleagues, and the wider developer community to understand and use the package.