Refactoring to action pipelines in Laravel

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone maybe you can hear me hopefully you can hear me the stream has um the stream is behind so we'll just wait for that i suppose and then we will kick off i have done nothing in preparation for this i've got the day off work today and i thought i would spend some time doing this because i talked about it during the week on twitter the uh the whole um the whole actions thing so pavilis put out an article on laravel news the other week talking about refactoring controllers and some different approaches that you can take whether that's dispatching events or dispatching cute jobs or um you know any of that kind of stuff one of one of the things that he talked about there was using actions and that's something that i've used a little bit in the past it's a nice paradigm that allows you to kind of separate each of the individual pieces of your of your of your request into discrete testable classes um it's just one approach it's it's one that i found to be quite nice and in in my work i work for a fintech company and what we do is asset finance but we kind of do the in between between lenders and brokers and so there's some tooling and some um specifications around the transfer of financial application data between lenders and brokers and things like that and it's this massive document it's like a sixty thousand line json schema and we need to take that as a whole package and then we need to ingest the process and normalize the fields take all the data converter into our data structures and then ultimately once we have the the structures in place we need to then uh create eloquent models at the end of the day that's what we're doing we're creating a whole bunch of models but we need to create an application and we need to create a loan and we need to create um applicant records so people we need to set addresses and all these and we have to kind of link it all up and so we we have this thing where we've got a giant object and sometimes some of the linking data can be three four levels levels deep so you'd have like an applicant which belongs to a household which um which then has like some other thing and so three levels down they've got an id that we need to get we need to kind of massage that data around and so you can see how doing all this in a controller is going to be just unwieldy like it's not going to be possible to to manage that it's not going to be possible to to sort of really deal with that in a nice way um and so what we needed to do was to kind of separate that where we could ensure that all of the individual bits under the hood were working but and and then at the end of the day like our feature test all it needs to do is make assertions counts of records so um you know we know that we have a application and from this package of data that we get we i say package is just like they call it a package it's just a json payload so we know that we have one applicant which means that we need to have one person resource and then that person has two different types of income and then you know all of this kind of stuff building the financial model for that applicant for the application and everything links to an application so the end of it we have one application record we have one applicant or one person record we have two income records that link to both the application and the person and so a high level feature test literally eager loads all of those counts um and just makes assertions like given this um fixture data that has one applicant two incomes whatever that we've created the corresponding eloquent models on our side but obviously we need to make sure that the records that we're creating are correct you know we don't want to have records that are missing fields or they've got null set or that they have incorrect values or you know we've put surname in in last name and so where the actions come in real handy is that you can pass data into the action and then um you know test the action in isolation to make sure that you know that that piece of the puzzle works and then you know if you have a scenario where you've got a bug or something's not being mapped correctly in in that specific action we can go and write an action for that whole for that that single piece rather than having to test the whole outside functionality as well um so what i wanted to do and this is going to be a really contrived example i can't really take my my work stuff and and broadcast it but we're going to take a a really contrived kind of example which is we're going to create a user the the user is going to have an application created in have a have a model created in in the application we're going to dispatch an event to say um you know that they've signed up we're gonna maybe send something to mixpanel we're gonna send an email to our marketing team to say we've got a new user we're gonna send a welcome email to the user and and as i said it's contrived it's um it's the kind of thing that probably in practice you would realistically just all leave it in your controller or have some very small abstractions for that data where um you know what we're going to look at we're going to build that we're going to write tests for it and then we're going to refactor to using actions and then we're going to look if i have some time i mean i'm not really time time bound but people may not want to watch me program and fly under for too long is then take that that flat structure in a in a single controller action like a store method we're going to look at and then we're going to split that out into an action pipeline and then if if there's time we're going to look at another another thing that i've done which is which is taking a record and passing that between each of the actions so um you know we call that a traveler and um jessie's shut on the xengel blog um they're they're a laravel consultancy i talked about this where they have like this this object it's a package um that actually we can look at here i suppose um all right github.com zangle this one it's called angle pipeline and it's it's an abstraction that sits sort of on top of laravel's underlying pipeline functionality and it adds some nice things around it database transactions it gives you an interface for response and exception handling and if we have a look at this in uh i think it's in the tests no source force examples um you know you've got this pipeline where you're taking um your your traveler class you're setting some data on it you're running it through some pipes and then you can check you know did did this thing kind of work correctly um and and you know if not then you can handle the exception case as well and the travel is just a popo so just a plain old php object that allows you to set some arbitrary information on it so the abstract traveler is there to to kind of give you um some some baseline functionality so a status a message a string um setting the status you know all of this kind of stuff and determining whether that's passed or failed and then so from this abstraction you can then create your own specific functionality and that kind of gives you the best of both worlds um but we'll we'll see where we where we land on all of that we're not going to use this package um it's a it's certainly a nice package and and it does the job but i think this is going to be more around sort of looking at what what we're trying to achieve um before you ask i'm using nvim neovim version 0.7 it does act as a reasonably full of fully fledged ide and it does everything i need it to do so that's neither here nor there and the theme that i'm using is carbon it is one that i created created myself actually it's a port of a uh jetbrains team all right did i call it carbon.nvm or something like that i don't know i'm not even signed in uh all right i don't know where it is it's called carbon anyway if if you wanted to do that um how do i search repositories you can tell it don't use github carbon.vm there you go um it is a part of a part a port of the carbon theme for intellij um and i'm using jetbrains mono so let's get that out of the way at the start um cool let's get this underway so the first thing we're gonna want to do is kind of drive out the expectation here um so if we let's make a test um register user test probably is all we need um that's not a past test all right pest what do we need to do we need to take this and we need to say use can you pass an array here you can use multiple what's that um uh refresh where is it refreshes refresh database i don't know what this thing is called uh you can tell that i write php that i that i write laravel a lot um so uh refreshed whatever see it does work i think ah it's a trait this works though doesn't it i don't know undefined constant you reckon oh yeah it is class whatever anyway um we don't need any of this pest doesn't need any of this stuff you don't see my screen that's all right you um you wouldn't have seen you just i just showed you all of that stuff on github and you were just looking at my face which is amusing um great so here's the carbon theme here here's this pipeline thing the xengel pipeline um i'll quickly quickly go back through this so here's an example this is what we're going to try and achieve right where we're going to pass some data through um this example pipe there yeah everything i said before is the same it's just that now you've got some pictures um thanks uh gertian for pointing that out this would have been a terrible stream if you didn't actually see any of the video stuff so wonderful all right uh so what do you want to do it can register a new user right right so what happens we uh we this post this is always the thing that irritates me whatever just so we can get some completion um we're gonna post to [Music] users.store i suppose um with a name an email password uh secret one two three because there's some rules password confirmation secret one two three um and we're gonna want to see i suppose this created i don't know that's what we want that's what we need obviously that's not what i wanted at all i can't see my keyboard i have this uh this boom arm down here and uh it's covering my hand so all right we don't have a database which is probably a problem so uh what we're going to want to do is we're going to want to pop open our env and we're just going to do this like i said i did not prepare for this stream at all um and then we're going to touch database database database this this is um arrivals defaults you know what uh we want to do here we want to do this don't we i'm going to use sqlite memory so now our issue is going to be that there's no route users.store which is correct um hey we're going to learn about test driven development while we're here i suppose um all right let's leave that route post uh users users controller probably need to wrap that in uh some square brackets or some description and we're calling that um users dot store what's our next error probably that controller doesn't work make controller i'm going to use a lot of keyboard shortcuts um and things like that so yep i never remember are we doing singular or plural let's just use that and import that and then go there and i suppose we need to re-index and then we get what we need um all right so we've got our controller now and now we'll get the next error which is you know that this didn't do what we wanted to write we got a 500 because the store method doesn't exist of course public function store takes the request and right it's a bit um difficult doing this look we wanted to 201 201 we got a 200 so we're getting there um and what are we doing we're creating we're not we're not doing any validation here so we don't need to do that we just want to make the test pass so we're just going to do user create request only um we're going to need a hash to password aren't we and this is not going to hash the password automatically is it no that's fine uh visit name request input name email email hash make request input um i guess we can just return that i don't know so laravel will actually handle that return in such a way that because the model has just been created it will uh automatically return a 201 for us which is nice so we don't have to don't have to worry about it so if we run that now with that it can register a new user green green green green green green which is what we like to see um all right so what else are we gonna do when we when we register the new user do we do we need to create a profile for them i guess we need to create a new profile for them um so we could just it creates a profile for a new user right let's just grab all this because this is all the same i suppose um right so i'm gonna get a user equals user first where email uh expect user not to be null um oh you can do this it's fancy chaining i suppose let's do that let's make full use of pest um profile same thing not to be no all right it doesn't work because we need to import this class oh i pressed again the wrong button expecting now not to be oh we don't have a profile who saw that one coming so uh on our user model um what do you want to do i want to make a model make model is it dash a makes all the things the migration all right just yeah really um [Music] public function profile has one this has one uh profile plus i need to import that because we're in the same there's a factory does it though no that's just the fault default is not all right it's great create uh create profiles create profiles table on your nigel did you know that you can you can do this thing this this thing you can if you if you put your migration name inside quotes you can put spaces in there and laravel will figure out all the rest for you which is really nice create profiles table i don't care about any of this stuff i definitely don't care about a down method table foreign id for um user i don't know let's just put a twitter handle is that all we care about is that gonna be enough we just want a twitter handle um that'll be nullable so if we jump back to our register user test uh this will interestingly be null for some reason class user not found yeah okay um probably because we would like to import the user model different error what are you complaining about now table profiles already exists yeah we don't need to have two of those so this guy all right back to where we were starting from expect profile to not be null now that is because of course we've got the relationship but in our register user control no user controller uh we don't do anything here so rather than returning this we want a user to equal that i'm going to return the user here so that if we go to our test uh this first test should still pass right and it does this one still no good no bueno and that is because uh we just need to use a uh profile i'll probably do right we just want it we just want it to be there yeah um so as part of this process you know we've got the we create the user we create their profile uh we don't we don't do anything else with it but we want to make sure that we notify marketing i guess is that it logs creation of user in mixpanel now we're not actually going to do that because we don't care um like we're not we're not going to integrate with mixpanel in this scenario we're going to just fake it uh and we can do that using event fakes then fake and which event do we want to fake user was created um event search dispatched function user was created event use um [Music] oh yeah and what do we want to do in here my friends we want to ensure that event user no do we have to return you return event user is user email yeah so this syntax in case you're not familiar you can instead of passing um user was created and then the function uh this was added i think in like oh what am i doing um in like laravel 8 where you can where you can pass just the closure and larva will resolve the this bit automatically for you um typo unexpected unexpected what unexpected paren you probably don't need that there let's just run the test even though we know it's going to fail because that class doesn't exist the event well the user was created event was not dispatched was it it was not because we need to create it event make event user was created and now user was created dispatch there's um some stuff i've never really looked into it to be honest um where um lsp doesn't doesn't uh re-index when a file is created outside of of him um there's probably a better way of doing that tim mcdonald probably set me on the right path at some stage now this is still going to fail because because the property does not exist on the user on the event the user property doesn't exist so if we jump in here uh get rid of all of these yucky things that we don't care about these dog blocks um don't even need this because what we're going to do is we're going to say public user user that's a phpa thing where you can do constructor property promotion and it will take your constructor properties and it will turn them into instance properties so if we run that test again that works so that's easy but you can see um you can see here we're starting to kind of get out of hand in terms of like all this stuff that we're doing in here and we're kind of bundling it all up into into the controller and if we wanted to say now like um i suppose that's an event but you might want to you know welcome user how do we how do we notification no user modify welcome to actions so we do it you have to pass it something like this i don't know i don't know how laravel works it's magic i'll tell you that um make notification welcome to actions so go back to our test i suppose we're gonna do this properly um these all pass now right oh oh what happened 500 yeah probably because that you know doesn't exist you can't dispatch a class that doesn't exist can't instantiate a class that doesn't exist obviously you all knew that um it's there so it welcomes the user to our app function um all right let's just just let's just put it all in here um instead of a event fake now we want a notification take is this how this works mm-hmm notifications using the notifiable trait user notifier that's not what we want notification fake about this one here so it's sent to user okay i guess we need to fetch the user again do we do that anyway do uh yep so sent to user uh what was it welcome to actions class um i really need to figure out how to make that reindex i guess yeah male is the default how wonderful all right we don't really care about any of this stuff do we because we're not actually going to be sending an email anyway this will fail because we're not actually doing anything yet notification is not sent of course um yeah import that again look at that um yes i suppose this is this is where i was coming at right so we've got a whole bunch of actions as i said at the at the start of the stream it's it's a horribly uselessly contrived example but this is the kind of thing that we're trying to kind of isolate and test and sort of make sure that that we can break it up um and so what we'll probably start to do now is we'll start refactoring this to the actions and and start looking at what um you know what what that means for our application uh we're at about 30 minutes now so that's that's tracking okay i suppose so all of all of these tests when when we're finished will continue to pass we're not going to have to change any of the tests at all um because we're just going to sort of shift some stuff around um the refactor part of the red green refactor cycle so you know we've got all of this stuff out of the way now um so how do we want to do this we probably want we're going to do this in two steps the first is going to be um the first is going to be we're just going to leave the user here right and then um and then we'll look if we've got time at the at the traveler so the the pipeline stuff is fairly and look we've got three lines of code in here you probably wouldn't refactor this scenario um we're we're doing like heaps and heaps of stuff without so it makes more sense in that context but um well we've used facades so let's just keep using facades i guess make um pipeline uh the pipeline the pipeline not the routing pipeline and not the contract pipeline that's no good to you you want this one right and then we want to send what are we going to send we're going to send the user we're going to send the user through create user profile um create user profile then we're gonna add to mixpanel and then we're going to welcome user pass then return yeah so there's two ways of doing this you can do like then which takes a a callback of your of your traveler thingy what's it and then you can do like something else to it in here and then return it um if you don't as an outsider scenario need to do anything with it you can just return and what that is going to do is that will return this user which means we can just do this and then that first test will continue to pass now obviously this is going to be a horrible mess and it's not going to pass because um none of none of those those classes exist right scrolling i don't normally use flow term for my test output it just makes more sense in the constrained space of the stream but then of course you've gotta with giant text scroll up forever until we get to where we need to be to see what the actual error is but this is gonna tell us that uh yeah this create user profile class doesn't exist so um what we're gonna do we're going to we're gonna create a new class and we're going to put it in not our http controllers we're going to put in app actions create user profile and what we want is just an invocable right invoke uh this will take our user that we're passing and and a callable and so create user profile is just going to user profile uh actually we just need to create these classes for now to make that test pass again so create user profile um we have here add to mix panel add to mix panel now these are just like playing php objects they don't they're not fancy in any way shape or form um so yeah that's that just let me just type all this out so we can get that test past the the first test passing again and we'll go from there uh and what was it welcome welcome user cool my memory is not very good i can't remember between looking at the controller and typing in here welcome user right same thing uh public huh that's under that it's just like if we go back okay right right back here um i don't want the test i guess i want the controller don't i use a control because this is where so we're we're just doing this bit here and we're going to return here and all of this stuff like we're just going to pretend doesn't exist and then this pipeline is just going to return the user so this test should but does not oh i suppose we need to import all of those don't we import import import see how that beautifully that works when i create create the class inside of um vm it still doesn't pass well i hope you're not planning on learning anything from me receives a 201 hmm is this actually the user he's not a user interesting hmm very interesting we'll figure that out later i suppose it doesn't matter whatever fine turn user look see told you i know what i'm doing um so this test passes no problem this one's going to fail now because we're returning before any of that functionality happens right so we return here now um so we want to jump in here and we want to do this and then we want to return next user and so what this does is it's the same as in your middleware's handle methods where it will take the the request and you can kind of pass it through it is the exact same like this is the the class that that basically powers how middleware functions so um so we did that that one passes now um and then you know this one's gonna fail because we're not doing that thing so we're going to take this dispatch and we're going to go add to mixpanel and then oh that doesn't work because you have to import the class that's where we have tests because then it'll tell us what we've done wrong and then that one doesn't work because you know we're returning before we do this um so we can go here and we can just say do that and then let's remember to import the class and then do we run this test no yes maybe oh hello this one's failing interesting why are you yelling at me you welcome to actions aha because this is why this is why you need to return because if you don't return then it still doesn't work uh 500 user must be to type app actions user yeah i knew that because because because because this is what happens when you copy and paste um when you copy and paste it doesn't copy and paste the imports obviously so uh so go back to our controller and we can get rid of this duplication obviously and so we've not reduced any lines of code and as i said this is really a horribly bad example of what we're trying to achieve um but all of our tests continue to pass now so we're happy there um where this where this becomes useful though is when you start being able to test these individual classes when they're doing complicated logic as i said when when there's an error in some piece of functionality you know if the user profile was created with some missing field or if adding to mixpanel didn't send the plan across like you could create a test for that failing scenario and then make it make it pass um so that's where that kind of becomes really useful um we probably don't need this anymore seeing as that didn't work um let's test we'll continue to pass now anyway so but this this still requires us um to kind of couple our controller with couple i mean as i said in practice you would probably just leave this alone but um we're coupling the you know the model creation to to the request to the controller all that kind of stuff which like in this scenario probably doesn't matter too much but what we want to do see so we're sending the user through which is already the user model but if you if you were to pass the request through here that works well for this scenario but then when you get to this class you're not going to get the user you're going to you're going to still get the request like this will be you know request um now you might be thinking you might be thinking okay well instead of returning next user here you could um you know instead of next request you could create the user equals blah and then but the the issue with that which may not be an issue in practice is that you may want to change the order of these um and when you start saying that like this one is going to return a user but a different class is going to return something else and a different class is you know you then become kind of tied in the order of those operations now sometimes it's it's going to be significant anyway for us we have to create the application then we have to create the applicants then we have to create the addresses so that we can you know link them back so that there's an order to it but if your actions are kind of singularly focused and they don't have too much dependence on anything else other than it existing then then it probably doesn't matter too much but where it's also useful is that you don't have to keep re-querying the database you know if things get out of sync if you if you go from like request to user to to profile to to user like you've got to get the the user back and all of those so you have to keep hitting your database to get that and so that you know it gets the efficiency of the of the calls and and the requests a little bit out of whack um so this is where that the the notion of that traveler comes in where you've got um you know this abstract traveler that that this is the thing that that goes through each of your pipeline classes or your actions and then it it has some base level of functionality um and and you can add on whatever you want for specific scenarios so what we will do and the travel is important because that then allows you to discretely test your actions because you can you can instantiate a traveler and pass the traveler into an action class to test a single action class whereas this gets a bit funny because you've got to like new up the request and pass the request backwards and forwards and things like that so um let's uh let's just close that for now um let's just call traveler now there's some gotchas here that that you need to to be aware of um and i bumped on them so you don't have to really um so we're just going to say this is an abstract class traveler um the abstract class is going to be responsible for the invoke but we're going to do this um sorry we can have an abstract action we'll get there the traveler is just you know your thing um in this instance like there's not really anything that we're going to have to worry about it um you know we could call this a user traveler but because we're not you know we don't have all of this stuff in terms of exception and and messages and things like that we don't need to worry too much about it um but the the traveler kind of allows us to encapsulate something that needs to pass through so you know where i said that you would have the request that becomes a user that becomes the the profile that becomes the user again you could have here like um a public function set user which accepts the user and returns um the class again and we just say this user equals user return this um and then we just say all right protected user all right whatever now obviously this is coupled to our application in in reality you would have separate separate term i mean it may be that every traveler is always going to have a user attached to it or always going to have the ability to have a user attached to it so um now that we have that if we go back here what we can do is we can actually extract this because this now no longer has to be here we can just have a new traveler which takes the request i guess um public function construct uh all right protect it sure that's probably fine uh what is that it's illuminate http request i guess and we'll just say protected request request right don't need to do that do we because yeah let's get that back right so now create user and this is where we start to get a little bit more interesting i think um so we create a new action now create user function invoke uh now this is going to accept a traveler need to use a we need the hash um these instances of request will now be this request because they're uh oops not this um travel out let's say get request and make it a method call do you want to be a method call let's just let's just do that let's just say it's request and then we can put a function here that says public function request returns requests and returns this request not returns return now this is important because this is a protected property you probably i mean you could you could make this public it probably doesn't matter too much um you you could just like use the request helper um it's it's fine really um doesn't matter doesn't matter uh so now we're gonna we're gonna jump back to our test and we'll just go back to the first one this is this is going to fail right of course because we've like we're not creating the user anymore and we're like gracious um uh someone did this i just noticed is it tim or jess one of those wonderful australians made it so that the actual exception gets shoved here um can i instantiate an abstract class yeah of course you can't um let's just not make it abstract uh for the purposes of this right whatever doesn't matter uh right so now this create user doesn't exist because we haven't imported it so if we go back to our controller create user let's get rid of this and all of these things we're not using now i suppose so so now we've got like this traveler he's responsible for for actually going all the way through um so now each of our actions uh that one is okay uh we probably need to return next traveler right um and if we were to run that what have we done wrong here right so this is now failing because we're no longer passing the user well actually let's let's let's talk about this we could we could do that um could we my variable user we're just using test yeah okay um that um traveler requesting for yeah wait what was there did i leave it on the screen long enough you're all behind the stream anyway right okay so this is what we'll do you're in the traveler and we're going to say we've got set user but we also want to get user um which will maybe a returner user should probably update this because otherwise php will get upset if you ever called getuser before you had created the user um you say then return get user um let's probably say it's 200 or 21. all right it's 500 again i'm at all oh [Music] culture undefined method get user right because we're only passing the traveler in to create user um this is still user all the way through so this would actually already be the user right cooking with gas so this is this is passing but what's happening here is we're passing the user onto the next thing and as i said that's kind of coupling us to the specific um sequence for our action pipeline which we don't want to do um so what we're going to do is we're going to change this to traveler set user um and then we're just going to return the traveler now this will fail because this is now expecting a user and it's getting a traveler instance which is easily fixed by going in here right we're following along now you'll see that this is quite repetitive and we're going to address this shortly because i think we're doing okay for time um this is there's a little bit of magic that we can employ uh it's kind of like you know so we don't have to do um all of this it's not really magic um we'll get there stay with me i promise it'll make sense surely um so this becomes traveler this becomes traveler get user traveler um wow why don't i use the powers of my ide yep rename that to traveler bloom um it becomes that and then this and still return but now because we're passing the traveler all the way through we end up with this um if we go back to our we're just a user test i have demonstrated once again that i don't know how to program undefined property get user mm-hmm one of these i have made a mistake where i have not used the method call like here do that all of those tests pass so i call that a successful refactor so you can see here that we've got like we've passed we've passed the request into the traveler and then for each of these actions we've passed the traveler that we've you know we're sending the traveler through create user through create user profile through add to mixpanel through welcome user we'll return the traveler instance right traveler and then we call getuser on that which we defined on this traveler class down here so the next thing we'll look at let's worry about time we're just on an hour we'll do this quickly this and this will be the last thing um there's this repetition right uh you need to you know do something and then you need to call next and then you know every time you've got to do this and blah blah blah yeah add to mix panel same thing create user profile okay now this is the extent of our application you could leave it it would be fine it's not the end of the world um but we can abstract this in such a way that it kind of just tidies things up a little bit so what we're going to do here we'll just create a create a new class called action because why not we're going to make this one abstract we're going to say now there is a gotcha here laravel will look by default for a method called handle if we look in the pipeline dot php class method the default is method right now the problem with this is if the method exists laravel will call that method directly otherwise it will invoke it um if you want to do anything with your class constructor or whatever else then you'll need to to deal with that um actually in this scenario it's probably fine to be honest um the the reason i say we're not going to do this um is you used to have that duplication like you still need you you'll still receive um the traveler and the callable um and you will still need to return from there and that kind of defeats the purpose of what we're trying to achieve um because what we're gonna do is is to remove that duplication so i think the the laravel actions package laravel is actions.com or is it just one word they they do this kind of thing with i think they have like a run method or an execute method or something like that um does it show in here they have handle but they're they're not so this is and this is why i haven't used this package is this doesn't allow for for pipelines of passing from a to b to c through like with with that same scenario this allows you to reuse the action classes in different places you know you can use it as um as a controller as a job as an event listener as a command and that's really good for code reuse um and you could probably use a combination of that with with this functionality um so so using a different method we're gonna we're gonna use execute means that um because it's defined as an abstract anything that that extends from this action class has to use it and then we can implement the the invoke method right like so where we call this execute and then we return next um traveler okay so this this then means that in in these individual classes um we can get rid of this and so all of our methods just reduced to this um extends action um um this right um let's pass it let's just pass it to the execute method whatever let's let's be happy the the way that i had done this at work is that i had like a constructor so this does like a static make um like this kind of thing um and that that just makes this available as an instance property uh so you you'd still get this you would have like a protected traveler traveler um and then and then that allows uh this kind of thing where you've got um actually here's something cool public function underscore underscore call um name dot dot dot argument it's just name um and then you can do this this kind of trippy stuff where you go like um if method method exists this method is this object yeah method this um traveler name too many then you can do return this oi they right um this gets a bit funny i don't think um neo vim's lsp supports that correctly but what that's going to do is that will that'll automatically just do it i didn't complain about it at least it doesn't give me completion but it doesn't complain about um so that's gonna that's gonna be the same as doing this um you know six one half an hour's the other it's just a nice little hello buchan um it's just a nice little shortcut um and you know we're in laravel we'll like little shortcuts so execute uh do that we can we again we can just do this um this this this get rid of that right you see how you just tidy this up and uh and and remove that sort of duplication extends action uh we're not using this now um and it and it kind of just makes things just that little bit cleaner in terms of um except look if i knew how to um and then you can again this get rid of that right and it and it just keeps these these individual action classes really lightweight and really focused on what they're actually trying to achieve um yeah so this is going to complain because it doesn't think ah because we need to action right and so if we go back to our user test it doesn't work because uh you know as we've learned i don't know what i'm doing traveler must not be accessed before initialization who's accessing traveller before initialization it's a bit naughty um i think that's because of the the coal thing yeah because it does that um [Music] no problem what do you mean i thought that was a good hacks this does work i've made it work oh do you know why i'll tell you why this is why i have no constructor um i don't even need that now all right mate just private don't minutes arguments term self yes um new static right oh my word do i know how to program or what create user is not instantial who's trying to instantiate it stop it well maybe i should have stopped it an hour this was good i have actions he's not instantial oh actually this is not understandable no actions create user is not instantiable extends action maybe this has to be felt like whatever set user zero passed one required really rocky road got there in the end you all knew we could do it i believed in you hello ted nugent sorry i did not see you earlier um yeah so that's that and normally i have this in like a trait um you know just as a in my application i think laravel over ships well on as well uh and it's just like uh let's put it over here concerns uh make a bull i'll just throw it in here it just does that um yeah that goes there and then this is just use makeable what do you mean and that all sort of keeps going um so you know this is as i said it's a it's a very contrived example doing this thing the the way that we've done it um but i'm i'm kind of hoping that that at least sort of gets you in into the the mindset um of of what you can achieve here and and you can imagine that when you've got sort of like eight or ten or twenty or whatever different pieces of functionality like you can dispatch um methods or you know you can dispatch events or you can throw things on a queue or whatever it is that you feel like doing in that regard but i i tend to find events that are like they have side effects to be a little bit counterproductive in that they they like it's not clear in your application flow you know what's happening where you dispatch an event you've got to go and look at your event subscriber and then you've got to go and find like individual classes whereas this is very um clear and concise you know here that each of these things are happening and you know exactly what's happening in each of those things um except for this one like we're just dispatching an event here this this would probably make your actual request to to mixpanel in in practice um there's there's probably some work to be done in terms of being able to make these things curable you know um this this is certainly clear what is happening and what the steps are um and then as i said now that we've got this we can sort of like um make tests is it that's just um and then you can do like we don't really have anything useful to test honestly um create user profile test like you can do this and now you've got to test um you know it it's kind of tricky because we're trying to pass a request object into the traveler in this instance um but you you can like uh new have a traveler here um can you like create create there you go create from can i just pass it probably a bad example really um but you you can i don't know i just want to create a fake request object right is it possible how to how to make request objects is basically what i'm trying to get at here um new request can can i do it this way what is this acceptance parameters does it have a constructor capture instance method root construct it doesn't have its own it's a bit i guess the constructor is here then what the symphony one doesn't even have one how does one construct anyway what what i'm getting at this is going to go to too far down the the rabbit hole um let's just pretend you can do this right um bizarro michael dot com or something like that password of course i've stopped i'm obviously knowing what i'm doing that's right i was just fumbling around um the the idea being that you can do something like this and then you can uh create user make um traveler right because this will um hit our make method it'll uh and then you you can call uh what do you call it record execute right and then you can you know expect user uh first where uh email bizarro michael.com to exist not to be null run out of space there whatever so you know you can do these kinds of isolated assertions and and it comes down to like the isolated testing of of these individual actions um i i don't think this will work but i think that that kind of gives you that yeah because you can't you can't create a request that way um anyway so you know what what we're kind of getting to is this scenario where you know you can discreetly test the actions themselves to make sure that the actions work um another another nice place that i've seen this used just just to wrap this up is in the vapor cli is it vapor cli there's no closing square bracket in vapor cli if you have a look in here at the uh the not the build process but the build command this is a nice place to kind of source five and this this goes through all of the individual build process actions um and this does it much much simply it's just using a collect a collection it's iterating through each of them and then invoking you know running the invoke method on each of these these classes um this doesn't work in in in the scenario that we just went through because we're trying to pass the user through each of them so this is just saying like do this do this to this this is just a series of steps that have to happen one after the other um and and you can just pass you know the environment in this in this scenario um and each of these things are handled let's have a look at this method called with this class called harmonizing configuration files so you know this just does some stuff it spits some output to the screen and it iterates over your config files it replaces any environment variables um using this functionality here um but it does all of these kinds of things so it's a it's a nice pattern um for for certain scenarios where you know as as you saw with the the build process um there's you know like 20 different actions that need to take place so hope hope that is of use to someone there is i've just noticed on my my light is is causing a wave across my face which is interesting um so yeah i hope that kind of explains a little bit about actions um and it kind of helps you you know maybe see some situations in your own code where where you can make use of them um definitely check out this laravel actions package from uh loris loris lever i think his name is um it's it it seems like a very robust sort of solution especially in that scenario where it's oh adrian adrian you get me excited let's have a look because if we can actually make this this thing work make request make you reckon request make okay call static make request mic doesn't exist is on the request facade maybe requests hmm it's a shame no matter um yeah as i was saying i hope uh i hope that that you know is is of use to to people there are there are situations where it where it's really useful um like i said with with this um this build command it's it's quite nice in terms of um you know i've got a whole bunch of things that need to happen i don't want to have like one you know this command could have what's that 69 to 89 20 20 different protected methods you could like hide it away in traits um and and all that kind of stuff doing it this way kind of makes it much nicer much easier to see you know these are the steps that put everything together for me um and then i don't i don't actually know if yeah it doesn't um doesn't have any tests for for that for that process but um being able to test those things in isolation like i said at the start of the stream if you want to go back and and have a listen we kind of we get this this package that is you know potentially thousands of lines of json and being able to say okay i need to given given the package i need to make sure that i'm creating the applicant record correctly i don't i don't want to test the whole thing i just want to know that very specifically the applicant works so i can say like here is the package um create a new traveler with that package then take the the traveler pass it into the action that is responsible for creating applicants and then that then i can test that that functionality so i can say you know that the name first in the the incoming data translates the first underscore name in our database and things like that and make assertions against that and then as requirements change and we need to introduce new functionality you know when you create an applicant it also needs to then relate that applicant back to the application i can write that test discretely um and then test against just that action knowing that in the wider thing i can just do a count um so definitely definitely check that out if it's you know it's an approach that that is useful too um it's not an approach for everyone in every situation there are a a number of different approaches that you can take when you're refactoring your controllers you can use services you can use events you can use jobs and things like that i will put a link in in the youtube description that goes through that stuff that and pavilis wrote about on the laravel news website a few weeks ago but that's that's it from me um it was fun to to to spend an hour and a half going through with this stuff for you um didn't really have a plan or anything like that so um i hope you all have a good weekend um stay safe stay warm we are stay cool we are depending on on the season and uh until next time cheers you
Info
Channel: Michael Dyrynda
Views: 8,922
Rating: undefined out of 5
Keywords: laravel, php, actions, pipelines
Id: izFyFtpEGoY
Channel Id: undefined
Length: 85min 3sec (5103 seconds)
Published: Fri Jun 03 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.