Podcast Introduction & Over-Engineering Humor
00:00:06
Speaker
All right, welcome back to Over Engineered, the podcast where we ask the very important question, what's the absolute best way to do things that don't particularly matter? I am here today with Caleb Porzio. Hey, Caleb. Chris, boss man Chris, great to be here. Thanks for having me. Thanks for coming. I often start the episode saying I'm here with my friend so and so. I feel like we're more acquaintances, but I have to thank you for us.
Friendship Dynamics: Friends or Acquaintances?
00:00:36
Speaker
Why would you say that? Chris, we thought it'd ping pong together for like an hour. That's true. And didn't keep score. Okay, I am here with my friend, Caleb Porzio. There you go. Who I think, I don't know which of you did it, but I think I'm gonna give you credit for giving me the name Boss Man Chris, which- Oh, is that right?
00:00:59
Speaker
Now I get boss man dad from my kids, which I kind of love. Yeah. That's amazing. It's pretty fun. Chris, I'm sorry to prevent you from continuing on, but I feel I'm really hung up on this acquaintance designation. I'm not going to get past this right away. I think our bar for acquaintance and friend is different.
00:01:27
Speaker
Okay. You know what I'm saying? Like we've hung out in person. That's true. We've hung out on, like we pair programmed like multiple times. You know, that's true. Okay. That's true. That's a fair point. Like telegram conversations.
00:01:42
Speaker
We've, Chris, come on. All right, you're making me feel bad here. I didn't want to extend, here's the thing. I feel like I knew you more through No Plans to Merge for a long time.
Parasocial Relationships in Podcasting
00:01:56
Speaker
And there's that whole weird parasocial thing that happens with podcasts. And so I think I'm always,
00:02:04
Speaker
I'm always very hesitant with like online stuff to like presume that like, Hey, we've been like talking on Twitter for like a while. That doesn't really mean anything. Yeah. And you were in the position of like, because you were the person saying whether or not we were friends or not. If you didn't want to presume and be like, Oh, this is my friend Caleb. And then I'm like, dude, we're not even like that close.
00:02:27
Speaker
You were just erring on the side of acquaintance, which I totally agree with. And the parasocial thing, you're right. However, most of the time, if you just listen to a podcast of mine, you would have that. But on our podcast, you are a character. So I have that with you too. That's true. So it's like I have just as much of a parasocial relationship
00:02:49
Speaker
as you do with me. So I think that turns it into a social relationship. All right. I think that's technically how that works. I think you're right. I think we pinned it down and we overthought it and we figured it out. Right. I mean, that's the whole premise. That's the podcast. So, okay. I'm going to, I'm going to do the pitch of what, uh, what I'm
Laravel Event Sourcing & Livewire v3 Challenges
00:03:08
Speaker
thinking about here. Right. Great. So
00:03:13
Speaker
Daniel and I are working on verbs. This is our unified theory of event sourcing for Laravel. You just launched LiveWire v3. I had a couple of interesting conversations on top of that around
00:03:30
Speaker
some features that are being worked on in on like Laravel core or like ideas that people are are wanting to PR that all like fall into this same category of lifecycle hooks or just hooks in general. Yeah. And it's it's a thing that I just I constantly bump into
00:03:50
Speaker
this question of like, how do you make, how do you make package, you know, this is mostly talking about like a package code, right? Not application code. Framework code. How do you make code extensible in really flexible ways, especially around,
00:04:13
Speaker
around things that sort of happen in a specific kind of important sequence, right? And like we see this in the framework around like hooking into different, like how different traits hook into tests, right? Refresh database, like that sort of thing. Sure, okay.
00:04:34
Speaker
I think of that as like an opportunity to maybe have like a formal hook system.
Implementing Hooks for Flexibility
00:04:40
Speaker
I know that Livewire has a huge like life cycle hook system and, and verbs is going to as well. And so I just like, it feels to me like I have this thing like tickling my brain. That's like, there should just be this concept of a hook that just works across the whole ecosystem.
00:04:58
Speaker
Yeah, I mean, this is interesting. I mean, I feel like I'm the guy because I've converted like the entire backend and frontend to a hook system. So pretty much every feature I write, I start with hooks, both frontend and backend. Everything, core and plugin and is all hook related. And I've written like a handful of different implementations and even just today.
00:05:25
Speaker
I did like a hook related addition to make the hooks even more flexible. Um, so I think I'm your guy, but also there regarding like Laravel lacking these hooks, I found a need for times where you want to like, if you want to hook in, okay, the main hook in Laravel that we use is a service providers, uh, register and boot method. Right. Sure. Absolutely.
00:05:52
Speaker
And I have found that it's actually like, it's useful to hook into those points from other areas in an application and it's difficult.
Extending Laravel's Service Providers with Hooks
00:06:02
Speaker
So in Liveware, like if I want to scope a feature to a specific folder or file, but I need it to do something inside this at that point in time in the service provider boot to like register a blade directive or something. Yeah. It's kind of difficult. So, and there's no like great affordance in Laravel to do it. So what I ended up doing in the core,
00:06:22
Speaker
is I take the instance of the service provider, and I'm pretty sure this is what I do. I forget exactly. This might not be 100% accurate. I think I abandoned this, but I took the actual instance of the service provider because there's certain methods on provider that don't exist globally. I'm trying to think of which one it is, but there's a few that's like, if you want to add like a view namespace or something,
00:06:48
Speaker
things like that, like only exist on this arrow inside of a service provider. So I've like taken the service provider object and like passed it around in different places so that I still have the ability to call a method on that provider. Yeah, for sure. In my hooks in, in Live Warrior, I have a static method called provide that gives me a hook into the provider lifecycle.
00:07:12
Speaker
So from one of my like feature hooks, like if I'm developing a new live or feature, like whatever. And, uh, I don't know. I can't even think of one right now. If I'm developing a new feature that involves like a blade directive or something, I do that all in my provide method because it's really useful. So whatever that's a little bit. That's pretty cool. Yeah. It's like the, it's, it's almost like the inverse. It's like a hook that then can reach back out into other places.
00:07:38
Speaker
Yeah, it gets called from the service provider, those provide methods, and I think they get past the instance of the service provider so that I can easily call those public or protected methods that normally you can't call from other places, so whatever. That's just a little trick I do. Yeah, that's cool.
Event Listeners vs. Hooks in Coding
00:07:55
Speaker
Yeah, I mean, I think it also...
00:07:59
Speaker
I think probably a reason why a lot of people don't bump into this is really the place where hooks feel the most important and powerful and also like the hardest to pin down is when you have a library, whether it's Laravel or something like Livewire, and then you have another library that works with it.
00:08:22
Speaker
And so like Laravel, I mean, one of my favorite things about Laravel is every time you need to do something, you go source diving and usually there's some empty function just waiting for you to put your code into and it just gets called for you, you know? There's always a hack path, like. Yeah.
00:08:40
Speaker
I've never, there's very few times where I've been totally stumped because even if you get stumped with like, there's not an empty method or something, most things are registered in the container and you can, you can like wrap them in your own extended class that is gets injected in the container and basically do whatever you want.
Complexities in Modular Package for Laravel
00:09:01
Speaker
Um, yeah, there's like very, very few times that I've been really stumped. So yeah, kudos to Laravel for being so hackable.
00:09:09
Speaker
And those are like application level hooks. I would call them right there. They're like things that are meant to be called from application code, right? True. Like a con base controller that it has like a method. Yeah. And, and the thing that I bump into is, you know, say I have a package that I want to.
00:09:29
Speaker
i wanna register a little like extra something somewhere yeah i can't like i can't i don't wanna ask the end user to be like well instead of extending service provider send my service provider so that i can like.
00:09:43
Speaker
also hook into those places. If you want to add some method onto the base controller class from a package, you're kind of screwed. If you want somebody in their project to be able to use your thing in their controller right from their controller, there's no good story for that. Right, yeah. And a great example is another package that I maintain is Modular, which I know Daniel has talked about a bunch with you.
00:10:10
Speaker
We basically have to extend every single make command in the framework just to add an additional argument to them. We're barely doing anything more than that. We're just adding another argument and then potentially changing where the file gets written to.
00:10:32
Speaker
But we, you know, it ends up being this sort of awful hacky thing that I have to maintain. And every time like a new change happens to those make commands, because I'm kind of like messing with things that aren't really part of the public API.
Composable Hooks in Laravel for Layered Functionality
00:10:46
Speaker
Yeah. Yeah. Right. I mean, I know you've dealt with that. Yeah. Yeah.
00:10:51
Speaker
It's like, it's moments like that where I feel like if there was a more sort of formal system of like, here are all the places where you might want to hook in and they need to be composable. They need to be like, I can stack one.
00:11:09
Speaker
implementation from this package and another package can push itself into it and then my application can push its own logic from there. You're not replacing the other side effects of that hook, you're just jumping in the pile of people hooking in.
00:11:27
Speaker
Is that how- I don't know your listener, like the level of the listener listening to this. Do they know what we mean by hooks and stuff? Like, do we need to intro this concept of what a hook is and anything like that? I think it's okay. I mean, I would say.
00:11:42
Speaker
I would be amazed other than my daughter, my, my 11 year old does listen to this, which is so funny to me. Um, does the listener know her name? Is this private information? Uh, okay. I don't think so. You assigned a fake name yet so that we can say hello. I should, I should think I should. Sure. Um,
00:12:05
Speaker
I don't, I actually don't think she listens anymore, but she did listen the first couple of days, which really made me correct me up. But, uh, other than that, I feel like the listener to this, this podcast is a very highly technical layer of L PHP developer. I would imagine because we're talking about pretty, pretty wild, wild things. Um, but yeah, I mean, what, what would be your definition of hook? Cause I think it's a useful question anyway.
00:12:31
Speaker
Yeah, I purposely said hook. Like you did earlier, you dropped lifecycle because lifecycle hook. Well, one, it's like one of the worst names. It's just it screams boring. You don't want to know about me. Right. You know, it's kind of like service provider. React docs. Yeah, right. It's at the bottom of the docs. And I unfortunately use it high up in the docs in Liveware.
00:12:54
Speaker
and Alpine. But I really hate the word. And it's like the way the thing we're talking about is more than like, like a lifecycle hook to me is like a view component that's like mount and destroy
Hooks in JS & PHP: Importance of Mutability
00:13:07
Speaker
or something, where like a hook can be anything anywhere, you know, right, right. Yeah, so I guess my definition of a hook is basically how I use them, which is from some place in your code anywhere.
00:13:20
Speaker
You trigger a hook, you just, I call this little trigger method and I pass a name to it and then I pass any parameters that anybody would want. And it's just, it's sole purpose is just for other people or other places in the code to hook into that without me having to call those things directly. And then, so to hook into it, I have a little on method. So let's say that the trigger is called, I'm trying to think of something not live wire specific. You can use live wire, it's fine.
00:13:47
Speaker
Yeah. Okay. So like a really common one for me on the front end is the request of a component when a component makes a request to the server. So I just have like in the code base, when I actually do the Ajax request, I'd call like trigger and then I pass the string request and then I pass in the payload I'm sending to the server. Um, and then anywhere else in the code base, I can just use on and then pass in request and then a callback that receives those parameters. And I can have.
00:14:18
Speaker
100 on request hooks. So to me, a hook is that. It's just a little trigger with a name and some parameters, and then that's the trigger, and then the hook part is the listener that I've used the word both hook and on for that method. But yeah, you hook into that place, you get the parameters, and you do whatever you want.
00:14:38
Speaker
And that's its most like simple form, which it can expand beyond like with affordances. I've been thinking about like, because there's the difference or the distinction between a hook and an event listener is like a little bit fuzzy. And it's almost like a hook is a specialized event dispatching system where the
00:15:10
Speaker
the intention, this is a question that I've been kind of like pondering is, is this just a thing where you just use the event dispatcher but there's a different intention? Or is there something fundamentally different? Because the main differences I see it is like a hook implies that like either what you're passing to the hook might be manipulated
00:15:34
Speaker
or the results of those calls might be used by the code that's calling the hook, right? Yeah, I think that is the distinction. When I think of an event, its purest form is in the browser, like a click event or something, and there is some ability of mutation with a browser click event.
00:15:54
Speaker
But in general, I think a browser event is meant the person dispatching the event is just like, Hey, anybody else is listening to this, do whatever side effects you want. And that's that I'm doing my own thing, where a hook to me is like, I'm doing a thing. And I'm giving you the opportunity to change stuff right here. And then the code that follows that trigger,
00:16:16
Speaker
might be impacted by what you put inside that hook. I might be passing mutable data like an object by reference that you might be mutating even though I depend on one of those pieces of data right after the hook.
00:16:29
Speaker
Right, right. Or maybe what you return in the callback is then used. Yeah, it might be like a middleware type thing, like, yeah, changing that. Yeah, there's a lot of interesting, very specific things because I went down that road of like returning things and using like middleware type hooks and such. And I've pretty much backed out of it in favor of just mutating references, you know, because it's interesting. Yeah. Okay. Why, why did you make that like,
00:16:59
Speaker
Well, A, there's just a lot of times where you don't want to mutate anything, but you still have to like return the thing. You know, like if you have that call, so let's take for example, just to have like a specific example, let's use this request thing as an example. You trigger request, right?
00:17:16
Speaker
And then all these listeners that have on request, except in the payload, that's going to go to the server, right? As a parameter, you can make it so that you have to return a payload object from that. Sure. Little hook. And then every, every, each one has to return it.
00:17:34
Speaker
The problem with that is it's A, there's a lot of times you don't need to mutate payloads, you just have to return it. But you can even get around that. I had a system at one point where if you return something, you meant to override it. If you return nothing, then I leave it alone. But then you get into like, it's just a little bit vague for the user. And then also, it's inflexible.
00:17:57
Speaker
because you might want to add something else to be mutated. And now what are you returning to things? Return a tuple or an object of things. It just gets restricting where if you just pass in payload, if payload is an object in JavaScript or PHP,
00:18:14
Speaker
it is by reference and you can just mutate it and then reset. Yeah. Yeah. I mean, that's true.
JavaScript Hooks: Callback Returns & Parameters
00:18:20
Speaker
And I mean, even if you have a scaler, like the consuming code, as long as you're planning for it, the consuming code can accept it by reference and manipulate that too. That does get really gnarly if you start passing scalers and trying to take them in by reference and such. My solution to this is pretty much like,
00:18:39
Speaker
I switched in JavaScript most of my hooks. I stopped passing individual parameters instead an object of parameters. So that's the whole things by reference and then covered with scalars. And with JavaScript you can easily de-structure those object frames. It even looks nicer. And the user has, they don't have to worry about parameter order.
00:18:58
Speaker
they can just de-structure the value they want, the parameter they want. And they don't have to worry. And you don't have to worry about like, if you had to have to add new stuff to that object, it's much better for sure. Yeah. So a lot of the PHP code base is still that parameter order thing where it then it restricts you. Now it's like, it's a breaking change if you need to like add one before something and whatever. And in JavaScript, it's mostly the object syntax, which was actually a breaking change between V2 and V3 live lawyer JavaScript.
00:19:25
Speaker
But it's, I did it the last minute and it's so much better. Yeah. Yeah. Yeah. So much better. Yep. Yeah. I, I get, I think that I have, I've been tempted to do kind of what you were talking about before where it's like, okay, well, if you return something and it's of the type that like we're expecting, then use it. Otherwise just continue to use the original thing and I can see how that would get brittle.
00:19:51
Speaker
Yeah. It's like, it's a little tricky. Yeah. One thing I do. So this is the, another reason why that's not great. Um, cause if in your hook you were, you're returning a value, I like to instead most of my hooks, you can return a callback from, and it's like a followup. So in the PHP side and in the JavaScript side of live wire, when I trigger a hook, I'm often I'll say like, let.
00:20:20
Speaker
finish or follow up equal trigger. And that variable, so I trigger the hook, okay? This is where the trigger side. I assign the result of trigger to a callback to this finish or whatever variable. And so in the request scenario,
00:20:37
Speaker
With a request, you often need to hook into the response. So if I'm just using hooks, one-off events, then it's like I trigger request, then you can do something, and then I trigger response. But often you need the request payload and the response. It's like you need both, and then you have all this cobbly code to share scope between them. It's so much nicer if, now I'm talking the consuming side, if you just say on request,
00:21:02
Speaker
You accept in the payload and then you can return a callback that is the response. And now you have the scope of the request and you can optionally follow up. That return callback is like a follow up.
00:21:17
Speaker
That's like a common pattern in JavaScript. Do you do that in the PHP side as well? Yeah, I do it in the... I use it heavily in the PHP side and yeah, pretty much like that got lodged in my brain from use effect. Like that to me was like React use effect was like, okay, this is now a pattern and I can use that pattern without feeling like I'm just introducing some random API. It's like, no, there's like a precedence for this. Right.
00:21:43
Speaker
But I will add one more thing that I like in the scenario of the request, you are hooking into the response, but often it's like helpful. What if the response fails, you know, it's like, Oh, now is the response follow-up callback. Now, what are you dealing with? Like maybe you give status and, you know, and then you deal with. So what I've opted for instead is so when you're consuming requests, like on request, okay, you get past the payload.
Advanced Hook Techniques & Response Handling
00:22:14
Speaker
And the first scenario I was describing, you were returning a callback to deal with the response, right? Well, now instead when you're consuming it, you say on request and you can get the payload, but you can also get a few other object parameters and they are respond and succeed and fail. And I'm not like sold on the names or anything, but now you can follow up on different things.
00:22:37
Speaker
you're just like getting callbacks that you can then use. Yeah. So I found that is actually, that's the ultimate in flexibility is like pass mutable object parameters into the hook and also pass in their follow-ups as parameters. And then you have to do a little bit of glue work, you know, on the trigger end to deal with like when you call respond and when you call, you know,
00:23:02
Speaker
You have to kind of aggregate a bunch of individual trick or hook callbacks. But but anyway, this is what I've sort of found as the most flexible and powerful way to deal with hooks. So what do you do you think like what's the what's the advantage there? I can't I can't see it immediately. What's the advantage there of using that pattern versus just having additional hooks for those other moments in the request lifecycle?
00:23:30
Speaker
Right. So here's, let me give you a live wire example where I, I started down just the return callback follow-up path and then I had, I got kind of bit by it. Okay. So when a live wire, let me just get this right. It's like when you're, maybe it was render. There's some callback where it's like, I want to hook in, or maybe it's
00:23:52
Speaker
Maybe it's Mount. It's like a components mounted. And then I want the return callback to be like the HTML. I don't know. I needed some three chain, not just a trigger and followup. I needed three things. I needed like the trigger is like Mount. The followup is like the HTML that's returned or something. And then the follow followup is like the final HTML or the.
00:24:18
Speaker
whatever, maybe the second one is the, the blade view that's going to be rendered. And then the third one is the HTML that gets rendered. It was like, I needed a three chain. If I didn't use that three chain and I just had individual methods like Mount and you know, HTML or whatever, you end up with all these conditionals. Like if the thing, like if you don't do that three chain, if you just have like, listen for Mount, then listen for render that you have all these conditionals, like,
00:24:45
Speaker
I only want to respond to the render if something happened in Mount. I have to set like an outside variable and there might be scope that Mount passed that I have to set by reference outside of it so that I can use it in the listener. And there's all sorts of things that it gets like totally. Oh, and also like, if it's a, if it's an event sent from a component, you might be hooking into it for like that component. But then the render hook.
00:25:14
Speaker
It's going to trigger for every component and you have to like scope that reaction just to that specific component. Um, yeah, I don't know. It just gets really gnarly. So I've again, found that like a solution to that was return a follow-up callback and then enable you to return infinite nested follow-up callbacks to hook in all these stages. And it's like, this is stupid. Just, just pass a parameter that somebody can call and pass in their follow-up callback. It's named. Um,
00:25:44
Speaker
and they can do anything they want, so.
00:25:47
Speaker
If anybody's still following that, those are kind of the hurdles I've found with all the stages of the life cycle hook stuff. And where I've kind of come to is like, this works for me.
Unified Hook Management Across Systems
00:25:57
Speaker
I like that as a pattern. I mean, I can imagine maybe having a more formal concept of hook context or something like that where you could generalize that.
00:26:14
Speaker
I do have like component context that because they're yeah I have you're right that especially in PHP where you can't just create like a quick you know object literal you know like you kind of can but yeah in JavaScript you can just make an object literal now it's mutable you pass any params you want yeah PHP it doesn't feel like that you could use like standard class
00:26:41
Speaker
but then, or like JSON encode, decode to an object to get the mutability. But then, yeah, just it doesn't, in PHP you end up making a class and whatever. So yeah, maybe there's a context class that's like built into the system that's meant for this kind of thing that has all the juice on it.
00:26:58
Speaker
Yeah, or like a sequence, like a hook sequence where it's like you pass in a bunch of hooks and they only fire if they like, like they only fire if they fire in sequence or something like that. But in the end, I mean, honestly, just passing a couple of callbacks that you can use feels a lot better, I think.
00:27:19
Speaker
It's so nice. Yeah, that part kind of feels like, again, I'm not using that heavily in the PHP side. I am in the JavaScript side because it's so much more flexible and simple. In PHP, I would have to build it out as a const. You know what? I could just do it in PHP. There'd be no problem. Right. There's no difference. There's no difference. I can see that there's probably some
00:27:44
Speaker
There's more complexity hiding in like how do you orchestrate calling all those callbacks in like the right sequence in the right at the right times, I would imagine. Yeah. Well, the thing is, is that you control it. You can you know, the person triggering the hook.
00:28:02
Speaker
you control when those callbacks are run. Because it has to be that way. It has to be bespoke. And that's the beauty of it. Coming up with clean APIs for it that aren't the most gnarly implementations ever is not the simplest thing. You need some helpers.
Managing Hook Priority: Before & After Methods
00:28:19
Speaker
Because if you think about it, already you have a... We talked about a hook is not just like
00:28:25
Speaker
A trigger happens and then only one hook happens. It's like everybody who hooked in has a callback that needs to get run and passed these things in an order. These follow ups have to be aggregated as well. They have to be collected. Do you deal with any like priority handling in any of that? That's another problem I fortunately
00:28:45
Speaker
there's inherent priority in when on is called the first person to call on on that hook is going to get run first. So because a lot of this stuff is core, I can just change the order that the listeners are registered in, you know, like a feature needs to take priority over another feature. I can put that higher up literally in like the file that includes that feature. Yeah. But there are times where it's like, Oh, I am in some random file and I need to hook into
00:29:14
Speaker
the response before morphdom does anything like I need to, you take that HTML before morphdom happens. So I just want to be able to, there's, you could go so deep with like priority and such.
00:29:28
Speaker
but I think the simplest thing that I've used is before and after. So it's like, I have methods, there's on that just as a listener, then there's before, which is just on with top priority. And then there's after, which is bottom priority. And if that's not enough for you,
00:29:45
Speaker
You're really in the weeds. And you're playing with fire. Sure. Because you're creating order dependencies anyway. Yeah, for sure. So before and after have worked for me, I've just bumped to the top, bumped to the bottom. Yeah, I mean, that feels like enough for almost all cases. Yep.
00:30:06
Speaker
So I can imagine a world where there is basically a generic thing called a hook, right? Yeah. And you can fire a hook.
00:30:19
Speaker
I would argue that like you should be able to chain on as many, for lack of better term right now, like lifecycle moments for that hook, right? So I'm imagining a world where I do like hook colon colon, make a request, right? And then I can do like, you know, arrow,
00:30:47
Speaker
incoming and I see that, or, you know, arrow lifecycle, I don't know, and give it like incoming arrow lifecycle outgoing or whatever. Right. So these are like the two moments of the request hook. Um, and then I would call like fire and pass in the arguments. Gotcha. Right. And all of my like consuming anywhere that I had registered,
00:31:16
Speaker
to interact with that hook, I would get basically all of the arguments that had been passed into Fire as well as a callback for every lifecycle moment that I had registered on the hook when I called it, right? To just sort of generalize that concept so you're not just passing random functions in and then having to keep track of them. And then the hook system, when I get, I would just like,
00:31:44
Speaker
get back the result of that call as like a, you know, a hook object that then I can, I can then just call like hook call incoming and pass more arguments into that, right? And then hook call outgoing and pass arguments into that.
00:32:03
Speaker
and just have the system handle with all the orchestration, you could have a little bit of an API around before and after, and maybe some sort of thing where if you need to, you can manually push your callback into the collection in the exact place that you care about.
00:32:26
Speaker
Yeah. Um, yeah, I think that's, yeah, that's good. Um, right. I, the only part of, so that's good. And that would solve the problems. The only part of that, that's like fuzzy on an API level. Okay. Two, like random hangups with it. One, it's very PHP, which is, you know, fine for PHP, but like hook, colon, colon, make I'm already like, Oh, like, can't this just be a function called hook? And it can be sure.
00:32:53
Speaker
Um, so I can solve that by just making it a function done that internally calls hook, make fine. Sure. If you say hook on colon, make you specify a name and now you're specifying life cycle chains like arrow incoming, you know, and then a callback that, or, oh,
Laravel Hook System Vision: Lifecycle Moments
00:33:08
Speaker
right. Okay. So I guess this is the part where it's like, those are names bespoke hooky names, incoming, outgoing, they're whatever the user wants to call them.
00:33:19
Speaker
Yep. Whatever you need. How do we take those names and use them throughout? You know, so is it a magic function when you're saying hook, on call and make request, you're saying arrow incoming. Is that a magic function that
00:33:34
Speaker
Well, I would probably do like arrow lifecycle or arrow break point or like that, and you just give it another name. And that would just turn into a...
00:33:50
Speaker
closure that's passed through the hook. And then on the object that you'd get back, so that's on the consuming side, your code that's hooking into things would get a function with that name for each break point that you add. Then on the end, I call that hook and I get back an object that now has
00:34:14
Speaker
you know, magic methods on it, essentially, or, or you could just like call, have a method called call break point or whatever, or call lifecycle that you passed in that name again. And now that would orchestrate calling all of the callbacks for each hook that had been registered.
00:34:32
Speaker
Yep, yep, yep, yep, yep. Yeah, and the rest is just how you wanna do the API. Like I could see, so let's say you just call those magic methods on hookmake, or maybe they're not, maybe they're breakpoint or whatever, and then you, or hook. I kinda like breakpoint. Yeah, breakpoint's not bad. So maybe it's breakpoint and then you pass in a name, and then now you have that hook object and you call, like hook arrow call breakpoint or something, you pass, or maybe it's called break, and then you pass in,
00:35:00
Speaker
the name of the breakpoint and then any parameters you want to pass to it. And now on the consuming end, you say hook on, call on or whatever, pass in the name and then a callback that receives the initial hook parameters. But maybe there's also a like a context object, like a hook object on the consuming end that you would say arrow. And then maybe it's a magic method called incoming or maybe it's just breakpoint and then it's like a breakpoint listener, you know.
00:35:26
Speaker
Yeah, or it could just be that it spreads closures into the arguments. So like, I just get each breakpoint in the order that they're defined. I guess the only random problem with that for me is it feels like you're defining a magic string at the trigger end and then on the breakpoint end. So even just for something as simple as because the primary drawback of using hooks and triggers
00:35:54
Speaker
is that you can't follow the code anymore because it goes into a bus. So the way to follow code for me often is global find in my project and then that string, that hook string. So it's like to use a breakpoint as a magic string at the trigger level, but then a magic method at the listener level, kind of like neuters the find replace story. How about this?
00:36:18
Speaker
you just pass in a break points object as the last argument to the listener. And that object has some methods on it that you can call if you prefer that syntax, but it's also invocable. So you could just do break points, open parenths, and then just give it the name, and then pass a closure as the second argument.
00:36:44
Speaker
and you get basically the same API as what you're describing, just with one object. Just pass a breakpoint closure that you can just call. I mean, it can just straight up, we could just offer it as an invocable
00:36:59
Speaker
thing that break point always accepts the string of the break point and then a callback to receive the parameters of the break point. Yeah. And like in a lot of cases, if they're simple, you can just do it in arrow functions. So you don't even have to worry about scope. You just have access to the scope inside of your main hook call. Yeah, right. Yup. Yeah. That's the other drawback of PHP is all the uses, but, um, but even that, yeah, whatever. It's just part of the part of the thing. And yeah, you can use short closures. So.
00:37:27
Speaker
Yeah, I mean, I like that a lot. I think the break kind of hardening that break point concept is useful. Yeah, that honestly, if I had started with that, like I my journey through hooks and triggers and everything is codified in the code base at every point, you know, unfortunately.
00:37:47
Speaker
And it would be great if I could start fresh and just have like, like, I mean, why we're here, a codified, um, like cannon hooks, cannon. Yeah. These are the words. This is the API. You can see it and use it in JavaScript. You can see it and use it in PHP. It's simple. Like it could be a GitHub gist to save you the hour. It'll take you to build it, you know? Right. Yeah. Yeah. I mean,
00:38:14
Speaker
I feel like you can handle priority pretty easily there because the breakpoint object could handle its own priority and the initial hook call could handle its own priority.
00:38:31
Speaker
You know, I'm kind of thinking probably you just use an integer and you just like, say before is starting at zero and after is starting at like 10,000 and everything's registered at like 5,000 or whatever. And then like, you can push things into other spots if you need to with like a special method. True. And like that can just exist on both the break points and the initial hook call. Yep.
00:38:56
Speaker
Yep. Okay. Yeah. I mean, the only other piece that I can think of is like, sometimes you need to like halt, right? Like, yeah, there's like your stop propagation.
00:39:12
Speaker
Yeah. It's basically stop propagation, right? I've done that. Like in my hooks, I'll pass a callback called stop propagation that does
Stop Propagation Pattern in Hook Systems
00:39:20
Speaker
that. I'm trying to think of the point where I've needed that, but I think it's, it's, um, I think even error handling in live wire, like validation errors and stuff, like there's times I need to hook into exceptions that are thrown in live wire. So I actually have like a try catch and that catch fires a trigger.
00:39:38
Speaker
And when you're listening for that, there's times where you need to bail out. So it's only one or two plays in the code base, but I have used the stop propagation pattern to basically allow a hook to prevent going on. So yeah, build it. You're right. Building it into the system. So we got like.
00:39:56
Speaker
aggregate hooks, break points and aggregate break point calls, bespoke break points, and then ordering numerically and then a stop propagation mechanism to prevent everything else from reacting. Problem solved. Bam. We did it. I mean, honestly, those are all of the points, good point on the stop propagation because I have needed that and I totally forgot about that.
00:40:19
Speaker
But those are, that's it, that's a robust hook system that any more robustness you wouldn't even need, I can't even think of something you would need. But I would be really like, you know, I don't know, I think this is a solved problem, I think we just solved it, like we cracked it. Okay, so here's the test, right? Because I have a few places in the framework that I would love to see this happen in Laravel, right? So I have noticed
00:40:49
Speaker
somebody maybe isn't thinking about the problem in this way, but someone is aware and thinking about it a little bit in like APIs that have been added because like,
00:41:02
Speaker
I have, you know, there, there are a bunch of places in Laravel and I'm blanking out on the, on one. So maybe you'll have one where there's like a static, like create X using, or like do something using, and you just can pass it a callback. And if you don't do it, there's like a default implementation that Laravel calls, but you can just like pass different different classes, like a callback and they just use them if they're there. That's like a pattern throughout the framework.
00:41:32
Speaker
And I feel like all the old ones accepted a single closure and replaced a single property on the class itself. And all the ones that have been created anytime recently
00:41:50
Speaker
basically push that closure into an array and then loop through the array. I mean, to the degree that it makes sense, there are some places where you wouldn't want to have it be. And that's actually an interesting question. I got a Laravel example of this for you. Okay. If you want to hook into Laravel's pagination,
00:42:12
Speaker
You say like whatever pagination and then colon colon resolve and now like you're the person resolving the paginator instance and you can you can basically use it however you like. Right. Instead of this is how Livewire overrides the paginator to do its own pagination. There's there's there's those there's those anytime in Laravel I think what you're talking about is anytime you see
00:42:42
Speaker
resolve or resolve using which has been, there's been more and more resolve and resolve using type things in Laravel.
00:42:51
Speaker
And I think, is that what we're talking about? Well, that's one of them. I mean, the one that I can think of is like the queue has a create payloads using static method that basically you can register a callback and it's going to cycle through all those callbacks and essentially like let multiple callbacks kind of push additional stuff onto the queue payload.
00:43:15
Speaker
Right. And that's useful, you know, the, the like bug snag, uh, yeah, just put some other stuff on there or like, um, and so that's an example where that is a hook. Honestly, hooking into handle would be great. And maybe there is a way in Laravel, but just like, or even like hooking into database queries to like log time and stuff, any sort of like logging, like time performance and database stuff and exception handling, like basically anything
00:43:45
Speaker
when you install like the bug snack package, they probably want all this stuff. So you don't have to like, you know, add something to the, your own handler in your file or, or override the handler. Yeah. And there's a way, I mean, there's a way to do it, right? In almost all these cases, to some degree, some of them are not as flexible as I would like, especially older APIs, like don't account for potentially wanting to have like multiple hooks registered.
00:44:11
Speaker
But in most cases there is a way to do timing or logging or whatever some other way, but it would be really cool if there was just sort of like a unified way to approach all this stuff, you know? So here's the, so this is great because what we've, we've already, we've started with the premise that hooks are useful and good for extending and building like modular systems.
00:44:40
Speaker
And now we're identifying where Laravel falls short with that and how nice it would be if Laravel used what we're describing, this sort of universal hook thing. So a Bugsnack package could easily hook into something logging, right? I'm assuming you have some kind of WordPress background.
00:44:56
Speaker
Yeah, WordPress definitely has a billion concepts of hooks. Yes. So like where I remember when I was digging into WordPress development, like building plugins and stuff, it's like you can do anything you want to do because you can hook into anything. Yeah. And that experience sucked.
00:45:14
Speaker
And I have such a bad taste in my mouth for that. I bet like if there's a sufficiently talented WordPress developer on here, they're like, Oh my gosh, it's actually amazing. Cause whatever. But I just remember that system feeling so.
00:45:27
Speaker
And using it doesn't feel ergonomic. It's overwhelming. And it's indirect, where when you're interacting with your Laravel application, it's well-documented. There's distinct APIs that feel ergonomic and you can source-dive where it's setting the thing and calling the thing.
00:45:48
Speaker
Maybe that's what it is, is like, I've eaten the cost of lack of source dive ability in my project. Yeah, for sure. But in Laravel, if I lost that ability, oh, that would suck because it's not my project. Yeah. I mean, okay. I have, I'm going to push back a little bit. I have two thoughts there. Okay. My first, my first thing is I do think that
00:46:12
Speaker
WordPress hooks could be a nightmare to deal with. I also think that WordPress's success is almost entirely because of the extensibility that comes through the hook system. The reason that so many packages can do the things that they can do is because
00:46:32
Speaker
Almost every function in like WordPress core has multiple hook calls in it to like hook into the beginning and the middle and the end and like after the end, you know, like there's a million hooks. I got a lot of like knowledge and hook inspiration just from new WordPress, like their new hook system or whatever. It's kind of been the same thing for awhile, but, um, but yeah, like they, everything we're talking about WordPress uses fundamentally.
00:46:58
Speaker
I think you're right though that that shouldn't be the like, the thing that makes it bad in WordPress is that is the primary interface for everything, right? Whereas I think that hooks lend themselves to specific moments in time and shouldn't be, it shouldn't be that every single feature that you're building
00:47:26
Speaker
requires like, you know, you're basically
Comparing WordPress & Laravel Hook Systems
00:47:29
Speaker
talking about like a fully like a vented system, not like, but not like event source system, like just a system that is fully like listening for events and firing events. Right. Which is so hard to reason about in general. Yep.
00:47:49
Speaker
And that, yeah, I don't think that anybody should have to, that is not the solution to like, or that's not the new paradigm for Laravel by any means. That's not good. Yeah, it's so tricky because I'm the offender in this case where I've totally wholeheartedly gone in on, you know what though? Actually, this is good. Here's a counterpoint.
00:48:14
Speaker
Um, let's say Laravel goes to this universal hook system, the way WordPress has. I, again, I told you my like just gut reaction is like, Oh no, you like following the premise, everything's good. And then I'm like, Oh wait, we did this in WordPress. It didn't feel good. And let's say that that exists in Laravel. I mean, the problem is that when you introduce hooks like this, like here's the problem in WordPress, you install a plugin and yeah, it's crazy powerful and can freaking overhaul everything, but it adds all sorts of,
00:48:43
Speaker
Uh, like maybe there's slowness, maybe there's weird side effects to other parts of the system that you don't, you wouldn't expect it to. So you're like, Hey, like, why is this thing so slow? Oh, because this plugin is hooking into that process and it's slowing the page render or slowing, saving a new post or, or parsing, uh, the tags, whatever those like short tags or something. So that feels horrible as a user.
00:49:12
Speaker
as, you know, it just feels horrible. And similarly with Laravel, if let's say all its exception handling, it might even be this way, but let's just say it's all like what we've described with our hooks. If you install Bugsnag and everything works magically, but then there's some slowness or something you can't pin down. You're not like going into vendor Bugsnag Bugsnag find all that a certain, you don't even know what string to look for.
00:49:37
Speaker
just doesn't feel great. Where in the the alternate universe, the one we have right now, which isn't hooky, it's like, Oh, I know where to look, I'm going to look in my handler because I have control over my handler. And I let bugs nag into my handler. I gave them that.
00:49:52
Speaker
And I know I did that and it's in their docs because I had to add it to my app. So it's friction. This same argument could be made about auto registering service providers that before you had to, you had to hand write in the service provider and the facades you wanted included in your app from a package. And now their package.json can just auto register their own service providers, which is awesome. Especially for stuff like Liveware where I want it, I want zero config. It's like, now you actually don't know where to look.
00:50:21
Speaker
for stuff and you don't have control over it, that's that push and pull that trade off of this, that indirectness that it introduces. And it really, not only does it introduce that indirection of like,
00:50:33
Speaker
just following code, it also removes control from the user, from their application. You're like taking things away from them, which is good for their onboarding and experience, but bad for their discovery and all that, you know? Yeah, I mean, I think that my main feeling there is like, I'm not proposing that like, I don't think that the framework should add
00:51:00
Speaker
basically any new hooks.
Proposing Unified Laravel Hook System
00:51:03
Speaker
The only thing that I could imagine is introducing a new hook system and in Laravel 12, just unifying all the different ways that the framework allows for this
00:51:18
Speaker
reaching into different components and adding callbacks and just make it all work the same way. And like that would even make things more discoverable because potentially you could just have a like PHP artisan hooks list method now that just are a command that just lists all the registered hooks, right? It'll give you all the locations and all the files that are calling it, right? Like you would basically be able to go to like more discoverable, not less.
00:51:48
Speaker
and you're not making anything, because right now I can go Q colon colon create payloads using and give it a callback.
00:52:01
Speaker
And that will hook into the queue system and like, and the difference is I happen to know that, but like the way that, uh, using like refreshes database on my test, like the mechanism that makes refreshes database call the right things at the right time is totally different.
00:52:26
Speaker
from this mechanism. So the only way to find this stuff is to go in the docs for all the different points that there's these extension points where if there was a reference of global hooks, like you could just read through the list of 50 or 15 and see them all and know what's available to you.
00:52:44
Speaker
Yeah, I agree in the areas that it's already kind of doing its own hooking in different points You could unify it and then it would be like a single it would be more discoverable In a sense. Yeah, and then like verbs could just use the layer of L hook system live wire for could just use the layer of L hook system like and it wouldn't be a
00:53:07
Speaker
a new concept for people to learn when they come to your package or like a slightly different API for the same problem. It just be like, oh, it's on library. Yeah, this is just another hook. This is a Livewire hook. They'd just be namespaced like everything else. It'd be Livewire colon colon mounted or whatever. You know what I mean? And like, if you wanted to add an internal function that
00:53:31
Speaker
abstracts away some of the you know yes so i think i think you're right chris and i think cuz what what you're saying is like. They're already doing hooks it's just not universal and so there really isn't anything to be lost by choosing a more universal approach so i agree.
00:53:50
Speaker
So the thing I'm about to say, it's not saying but, because you're right. Yes, full stop. This but is just another layer on the hooks versus non hooks trade offs. Like first, it's just like indirection in general. That's just a problem that just exists a trade off. And then, so this, I guess I'm fleshing out the second con that I'm trying to describe that is user control.
00:54:17
Speaker
It's like a zillion hooks gives, gives a user or an extender complete control. Um, but yeah, it's actually, there's, there's the extender and the consumer, you know, there's the library maintainer like you and me, and we have complete control, but we are potentially robbing the.
00:54:36
Speaker
the owner from some of that control in this sense. Like let's say, let's just stick with the exception handler scenario, even though I don't think it's the right one. If, if instead of having your own app handler, it just fires some handler thing that all these packages can hook into and do stuff with the handler, for example, detect if it's a certain status code and return a payload of a certain shape.
00:54:59
Speaker
or return us a different screen or something when it's a specific error status code or something. So that's good in all the ways we described, but in the control way, it's the same thing as using private or yeah, private classes, you know, or what are they? Final classes, you know, right? Which everybody hates. So I'm giving a pro to final classes, even though I've never used one, but
00:55:26
Speaker
When I, so for example, I was writing a feature, um, yesterday that is a, it might not be in live wire or not, but it hooks into the HTML that gets returned from the server and mutates it. Right. Sure. So the work I was doing in there though.
00:55:45
Speaker
If I wasn't careful, I'm very aware that like, oh, this thing is going to get called every single time any Liveware component returns HTML. So I want to be careful to not penalize all requests. So I add a little bit of juice to conditionally process what I need to process, only when I need to process it so that I'm not introducing any extra overhead. But hooks make it so that you don't have to be good like that.
00:56:14
Speaker
where when you're not using hooks, you are inherently good like that. Cause you just have normal imperative code. We're like, if this thing, then do this thing. Where with hooks, it's like tempting. And I find this actually, this is really relevant because almost anytime I'm using a hook, I have it in my head that this hook.
00:56:31
Speaker
If I add a hook, even if it's just got a single conditional to not do itself, it's still an extra callback in the stack with a conditional. If that conditional depends on querying something in the DOM, it's like that adds slowness. And then if inside that hook, I do something that I'm very acutely aware of when it runs and to be careful to minimize that impact. And I think that requires a lot of developer knowledge about the whole system. And so it's almost like, I don't trust you guys to use my hooks.
00:57:01
Speaker
So I'm not going to document them. There's a handful that I don't document because they're not public. I'm using them, but don't use them. I'm not advertising it because you're probably going to abuse them and you're going to hurt the whole application. So that's along those lines of the owner control. The owner of the app is like, if there's no hooks, you have full control. People extending it have a harder time. WordPress is so successful because everybody gets to extend it and you have all sorts of powerful plugins.
00:57:30
Speaker
But it also is at the cost of the integrity of the system. So for Taylor to maintain an experience where the community, where you're in the ecosystem and you're installing packages into your app and whatever, it increases the likelihood that everything is going to be stable and sound and harmonious when there's not extension points flying all over the place, you know, or when they are. So I think it's just a it's just a trade off. I'm not saying it's a blocker.
00:57:59
Speaker
But it is a trade off. I mean, and and I think that that's like that's like part of the reason why Laravel is good and not bad is that like the right trade offs
Laravel's Aesthetic Code Design
00:58:12
Speaker
have been made. Right. Yeah. And I mean, that's like that's the thing that Taylor has done so well is just like.
00:58:19
Speaker
be willing to bring things in when there's really gonna be value, even if you could really shoot yourself in the foot with it, right? And not be willing to bring things in that have undeniable value, but are just not worth it, right? And I feel like this is the same thing. There are undeniably,
00:58:43
Speaker
valuable places where having these types of extension points just makes sense. And it's up to the Laravel maintainers to kind of
00:59:00
Speaker
I mean, they're already making these decisions, right? They already decided that like, you know, I just saw another one and like, well, yeah, create payload using on the queue. That's what I have. Like they already decided that that's worth it to be able to hook into that moment for various reasons, right? Yeah, for sure. And I think they're right. Like I think it's a hundred percent good thing to be able to hook into. And so,
00:59:28
Speaker
Yeah, like all things like you can abuse. Yeah, you can abuse any tool. Right. Yeah. I think strings in our hooks. I'm just thinking about what you were saying about Taylor and taste and knowing the right trade offs when like let's say that you make a giant PR that introduces these hooks and let's say that Taylor is not necessarily intimidated by like breaking stuff or whatever. I think
00:59:53
Speaker
his taste. And similarly, my taste, although clearly I've already accepted that trade off, is strings are gross and not aesthetic. And he cares so much about how that code looks and the prettiness of it, where like Q colon colon, something using and then passing a callback looks like it fits right in. It's beautiful.
01:00:16
Speaker
passing in a bespoke string feels gross. You know, like anytime there's a bespoke string, it is now not aesthetic. Um, I see what you're saying. It would just under the hood, you would have the bespoke even so, but yes, you're right. Because there's, there's bespoke strings with the container, you know, there are strings all over the framework, but you just don't have to think about it.
01:00:38
Speaker
Yeah, you don't, you're not typing in a string. Yeah. I mean, like when you call the request helper, all that's doing is app string request, right? Laid.compile or everything. All that stuff. Yeah. So I don't think, but I do think, yeah, I, I think that that is.
01:00:58
Speaker
As I get older, I find myself less and less willing to put up with just like magic strings, you know? Like I can accept that they are useful in different places, but like if I can avoid having it be a string, I do, you know? Yep, sorry, I got any yums. Yep, and yeah. Constance.
01:01:26
Speaker
Yep. Been really loving enums lately. Nice.
01:01:30
Speaker
Like as a, I feel like I have, I had mostly been thinking of enums as like kind of a list of strings or integers. Right. But I've been, I've been doing a few things where like the enums aren't actually even backed by anything. Right. It's like a more pure enumeration. And it's, it's cool to just be like, there's just one, this is just a thing. It's his own space. It's not strings. It's not, it's not anything. It's it's in directions or whatever.
01:01:58
Speaker
It is nice. Like I was pleasantly surprised at PHP making enums, not just like backed by default and being like their own powerful entity that, you know, if you can add methods to something, I'm already just like, Oh, sick. All right. I'm it. You know, it's like, Oh, I can hack this and do crazy cool stuff. And this makes me more able to make cool APIs. Um, yeah. Okay. The last, the last thing that like was kind of on my.
01:02:26
Speaker
on my mind that we didn't talk about is, in verbs, we, and I know if you did go, we can stop, I'll throw this out and we can decide if we wanna talk about it, but I know we've been going for a little while. In verbs, we do a lot of stuff with PHP attributes, right? You can annotate a function and that,
01:02:54
Speaker
that basically queues that function up to be registered as a life cycle hook, right? Which I think is how LiveWire 3 works in a lot of places, right? Yeah, not necessarily the life cycle part, but you know, yeah, yes, it is totally.
01:03:13
Speaker
Um, and I'm wondering if that, like, is that, is that its own thing or is that part of the hook system? Like is, is discovery, does discovery through, through PHP attributes, does that solve some of the problems that we talked about or does that just add another layer of complication?
PHP Attributes in Hooks for Discoverability
01:03:42
Speaker
I think it does, like the Liveware case is an example of it, I think solving some of the problem. Yeah, I was gonna bring this up before where like, this is kind of what we were talking about, like you want Laravel to have a hook system. You're not saying you want Laravel's API to expose magic strings to a consumer, you know, keep the helper function, keep the nice API and then internally use a hook, right?
01:04:07
Speaker
Yeah, exactly. In Livewire, I started out really hook strong. I was using it all over the place. I expected people to extend Livewire using those hooks all over the place, but I ended up cannibalizing hooks in that there's actually very few places. It is available for me if I do need to actually truly hook in with a magic string with that on thing I described.
01:04:34
Speaker
But instead I turned hooks into like a class, like, so there's component hooks. And, but let's forget about that for a second. Let's just pick on attributes. So a PHP attribute used in live wire that you would decorate a property or a method with. If you source dive it, that attribute has lifecycle methods that it can use. It has all the methods that you would normally use inside your.
01:04:59
Speaker
inside your component right inside the attributes. So there's mount, there's hydrate, dehydrate, boot, render, destroy, all of those things are just methods in the attributes. So it's very source divable. You're not exposed to hooks at all. Everything under the hood is hooks. All those PHP attributes go through a hook system for every, all of that. But from a consumer's perspective,
01:05:23
Speaker
if you are in user land and you use one of these attributes, you can command click to view the definition and you'll see just PHP methods called mount and you know what those do and there's code inside of them.
01:05:35
Speaker
And it's like very discoverable. There's no place where it's like on magic string mount and then gets past a component instance and all this stuff. It's sure. Yeah. So I think it's, um, it's gift wrapped and it enables some of that, uh, discovery makes it feel better. So yeah, PHP attributes in this scenario, the way I'm describing them and the way I think you're describing them help that, that user experience feel more, um, ergonomic and discoverable. Yeah.
01:06:04
Speaker
Yeah, I mean, the way we do it in verbs is a little different, but I feel like it's, I'm trying to think. I mean, basically, so in verbs, there are like five lifecycle moments. You've got authorize, validate. Those happen before events are fired. You can authorize whether an event can be fired. You can validate that the event state is valid. Then you have apply.
01:06:30
Speaker
Yep. Uh, I, it doesn't really matter. But apply and I have the docs here. Daniel just sent it to me actually yesterday. So I'm, I'm seeing validate and apply. Um, and I see the state ID attribute or whatever, but yeah. Yeah. So, um,
01:06:47
Speaker
There's a convention right now, right? Where on an event, if you're, if you write a method that starts with the word apply or starts with the word validate or whatever, uh, that is automatically registered as a hook on that moment in the life cycle. Right. But like.
01:07:04
Speaker
if you want to name your function something else, like you should be able to annotate it with just like an apply attribute. Yeah, that's nicer than a magic prefix for sure. Yeah. And in that case, the attribute itself like is named for the hook that you're hooking into. Yeah. So I would imagine like in that case,
01:07:24
Speaker
like there would still be like an attribute for each hook that you're like hooking into and that attribute would then just like have a reference to the like string name for the hook. So it would still be like, I don't think passing around like, you know, verbs colon colon apply into some generic discovery attribute would be good.
01:07:50
Speaker
Yeah, I definitely think like having bespoke attributes for each of these hooks makes a lot of sense. Yeah, I don't know Verbs enough or event sourcing enough or anything to actually speak to To have any credibility with my taste when it comes to that so it could be good or bad But I'll say in like in live wire instead of a mount method if I had a randomly named method and then a mount attribute above it I think
01:08:19
Speaker
I think the ergonomics wouldn't line up there for me. I think it would feel, I don't know. I don't like the way it feels where if I'm looking at the verbs docs and I just see a plain standalone method called validate, that feels right to me. Single word, clean, predictable, where if I saw a validate
01:08:41
Speaker
You know, yeah, I don't know. It's tough to know if I saw a validate or apply or something attribute above a randomly named method. Then we're back to, I feel like when, when a non Java user encounters like.net and.
01:08:55
Speaker
And, and it's like, what are, what are these attributes running and how did, you know, then I'm back to like, I don't know about that, but, um, but I actually can't say because I don't know the distinction here is that like, you may have multiple, right? So you, if you have one validate method, then that's fine. But if you want to validate a couple different things, um, you need different methods.
01:09:18
Speaker
And, and in this case, like, you know, I'm, I'm like, one of the examples that I've been working on in verbs is just like trying to rebuild monopoly. Uh, and this is what JD's been tweeting about. No, he's working on his own game, but he was saying you were showing. Oh, maybe. Yeah. Yeah. That probably is. Um,
01:09:41
Speaker
And when you fire an event, oftentimes you need to validate the game and validate the player that's doing the thing. I need to make sure that the player is in a state that I would expect to do that thing. And I also need to validate that the game is in a state that allows the player to do that thing.
01:10:07
Speaker
And so it makes sense to have multiple hooks into the validate moment in verbs. And for me, like from a taste perspective, I'm always going to name those functions, validate something, right? And so going with the naming convention is not a problem.
01:10:27
Speaker
But I think that having the attributes as a, an option feels kind of nice as like a, yeah. If I was a verbs user and I wanted multiple methods.
01:10:40
Speaker
I could see myself being on board with the validate attribute as a non verbs user just because it's not out and I haven't worked with an app with it. I would prefer to just see a validate method that calls two sub methods that do their own thing. You know, personally, it's so simple. People would see it to be so discoverable. It would just be like, I know exactly what's happening here. Yeah. You know,
01:11:06
Speaker
personally. I hear what you're saying. Sweet. I feel like we solved it. I think we did. I think we defined it. We defined the problems, solved the problems, and we defined the solution. And the great thing is I got all of the
01:11:25
Speaker
pay off a feeling like I solved it and now I can just move on and not actually like actually this. Yeah, it's like the best. Yep. If I were to be in the middle of the live lawyer rewrite, I'm almost positive I would go and implement what we've described.
01:11:42
Speaker
But unfortunately, I'm probably going to do nothing about it. Yeah. I mean, unfortunately for, unfortunately for Taylor, I probably will PR at some point now that we talked about this. I, uh, I can't tell, I don't know what your, what I feel like you've got a bunch of, uh, layer of LPRs not recently, but back in the day. Yeah.
01:12:05
Speaker
Um, what's your, did you ever like look at your like ratio? Cause I feel like my PR ratio is, especially in the beginning, heavily skewed towards not merged. Yeah. I mean, I definitely have a podcast called no plans to merge, but my PR merge ratio
01:12:26
Speaker
I think started out kind of low, but it's almost, I could be wrong here, but I think it's pretty fricking high because I would just never PR anything. It's like you watch Suits.
01:12:41
Speaker
You know, I don't know. Come on. You're the first person in the world that doesn't watch suits. But the main one of the main characters, Harvey Specter, he's he never goes to trial. You know, like he he doesn't have any like losses on his record because he always settles before trial. Right. So it's kind of like I'm settling before trial by I'm only going to PR the thing if there's a tweet that's really popular and people are amped about it. And if I just have like almost 100 percent gut that tailors into it, he's expressed he's into it.
01:13:11
Speaker
then I'll PR and when I PR I'd make sure the footprint's really small and I'm like, you know what I'm saying? So that's where I am now. But I definitely feel like.
01:13:21
Speaker
Every once in a while I like will swing for something just to see what happens. It is good to swing and you should swing, but, uh, but yeah, it definitely tanks your ratio. It does. It does. Yeah. Take your ratio and PRing this. He's not going to merge it straight up. Yeah. It feels like a really hard one. He wouldn't merge it. You would have to, he would have to be in love with the idea in some way or have enough community pressure.
01:13:49
Speaker
I have a hard time seeing him merge in it, dude. Unfortunately. Here's my pet peeve. This is my pet peeve.
01:13:57
Speaker
I keep on bringing up the test case refresh database thing. I don't know why it bugs me so much, but it has always bugged me that in the Laravel test case setup, it maintains a bespoke list of traits that it's looking for and initializes those traits.
01:14:22
Speaker
that it's not like some generalized system that just like, like everywhere else, like on your model, if you have a trait, right? You can call boot and you can all call initialize. Like there's, there's multiple moments that your trait can hook into the model life cycle. Right. Right. Right. And like, that's a, that's a thing that exists in a lot of the framework, but because of like a few weird issues, uh, mostly having to do with like, uh, PHP unit doesn't call annotated
01:14:51
Speaker
like set up functions in a predictable order. Uh, Laravel needs to like maintain this list. And I've like, I've taken, I've probably taken like four stabs at trying to like trying to break into this, trying to fix that because it just like annoys me. And this is, this is just another one of them. That's what, that's really all I care about is like,
01:15:14
Speaker
getting those to be a generalized system. Yeah, that's funny. I haven't dealt with that because I haven't tried a trait, but if I did and was blocked, I would feel the same way. Yeah, wow. Well, you can get it someday, Chris. Chris, this was great. This was a great discussion. I'm coming away with this with more clarity on hooks. This is great.
01:15:41
Speaker
Hooks are good. They could be bad. They're mostly good. I'm gonna say. Good and bad. Sweet. All right, well, thanks for hanging out. Dude, thanks for having me, Chris.