Joel: Welcome to No Compromises. A peek into the mind of two old web devs who have seen some things. This is Joel.
Aaron: And this is Aaron.
You know, I really hate magic strings. You know what I mean when I say magic strings or magic numbers?
Joel: Yeah, abracadabra.
Aaron: No. But strings are numbers in our application that mean something. So, for example, ID 1 is a user, maybe negative 99 means something else
which is a horrible thing to do.
Joel: Right.
Aaron: Or, you have a status, a connection status, and it's the word good. Or problem, or something like that.
Joel: It's something you're matching against in logic where it has a special meaning to it.
Aaron: Yeah. They're called magic because they are strings, but they have a meaning so, that's the magic to it.
So, in PHP, for a long time, we didn't really have anything to do about that. I was really jealous of
other languages when they started talking about things like enums, and we didn't have that. So, what we did a lot of times is use constants.
Joel: Sure.
Aaron: You could define a constant in your runtime, but then I extended that. And I learned it from someone, too, I'm sure.
But interfaces can have constants defined on them. So you could use an interface in PHP to define the properties...
I'm sorry, to define the methods that a class needs to have, but you can also add constants onto it. So, that's kind of what I started wit
Is like, well, I'll have four session keys, for example. A session key contract, and it has all a bunch of public const.
And it's all because that's the only way it's used and that's how it's used. And that was really nice. Until we got what we were
actually supposed to have, which was enums in PHP.
Joel: Ah, yes.
Aaron: So, from a very high level, it just takes it one step further. It's a data structure that says, "I am a set of values.
I'm only the set of values, and they mean something." And so, in PHP, there's two different types. There's regular enums and there's string-backed enums.
And based off that is how you set up the thing. So, I'm going to make some assumptions that our listeners kind of know what an enum is,
or can Google, and stuff like that. So, I don't want to get too much into that.
Joel: Or read the manual. There's a nice section on enums.
Aaron: Right. My question comes more so down into a specific part of enums I wanted to talk with you about, Joel.
Because I've been using them more and more in my project, and I didn't have this problem with the interfaces, because they didn't have as much flexibility.
Joel: Right.
Aaron: But with enums, you can now add other stuff onto them besides just the values. You can add on methods. So, my question or my basis here is,
how do you decide what methods to put on an enum? What methods even are valid or good on an enum?
And how do you make sure you're not putting too much logic inside of a class that is really just meant to be like values?
Joel: Yeah, I've struggled with this as well. And, can I throw a concrete example out, because it might make it easier to talk through?
Aaron: Yeah.
Joel: Like, the one I think we probably reach for the most is like, let's say it's a string-backed enum. And there's these strings, but you want to say...
Like, maybe they're going to present them in a drop-down list, and so the backed enum value isn't necessarily what you want to show to the user.
So, we might have a method that's called Labels or something, where it converts the enum to a human-readable version.
Is that sort of what you're thinking about, or even broader than those sorts of things?
Aaron: Yeah. I mean, that's a good example, that's a very simple one. So, it might be that we have a public method called Label,
and inside of there it then looks at the current instance of itself. Because remember, I didn't say the public static because of the public.
So, it looks at the current instance of itself, maybe runs a match and returns a string that you know says, "This is the textual label."
Joel: Right.
Aaron: Or you could argue that since they're just string back enums, you could have the textual label as the value.
Joel: You could.
Aaron: So, the key could be whatever you want to use in your code and the value could be that. But I think where the label comes in is
like when maybe the label is, I don't know, much larger than you want to store in your database.
Or, you have a historical set of data that has keys and values. That's the one I run to a lot of times.
Joel: Or you want to translate it. I mean, there's other reasons, too, yeah.
Aaron: Translation too, yeah. Whereas, it makes sense, but the client just wants to see it differently all the time.
Aaron: Yeah, that's one example. And then maybe, where it gets even... Well, let me just pause on that first because that one...
You mentioned, you could just put it in a database, there's other solutions. I've seen another solution, I've never tried this.
But where somebody uses attributes on the enum cases for things like label. You could create like a label attribute,
and then you can use reflection to get that stuff out of it. You're still putting logic in there, right?
It's just maybe a different syntax or way of expressing it. But then there's things-
Aaron: Yeah, that seems like wrapping of something we're just talking about. Like, I don't want to write this method so I'll just put attributes,
and there's a method in the background somewhere. Because that doesn't just magically happen. So, I'm not talking about bringing in packages,
even for our enums. What you're describing is a package, and so we can get to that. But that's not...
Joel: Yeah. But the other one that's maybe even fuzzier is like when there's logic. Like, I can't remember the specific example,
but I remember going through a code review with you where I had an enum that had to like... we had to convert between two types of enums.
And what I had done worked and it was functional. But you're like, "Why is that method on this enum? I don't like it."
Like, we had to talk through it, and there was sort of the same thing. Is like, does this logic belong in the source enum, the target enum?
Is it in like a different class altogether? And so, sometimes it's convenient to put them by the enum, but it doesn't always feel the best architecturally.
Aaron: Well, I think that there's probably... Again, with all these things, there's never necessarily 100% rule.
But there is this idea of, what is the logical stuff that the enum has to do? So, for example, I might have a public static method on enum, saying Blog.
I can't think of-
Joel: Post.
Aaron: Or, Post, right? And it gives me like a type, like an enum, looking at that from the content. And so, as long as it doesn't have to do a
query or reach in a database, if it's just something structural, where it's like I can pass them some data, and that data relatively tells me
that the enum that would represent that data. That's some logic I'll put on that class, that enum class.
But if it's doing queries and trying to convert stuff back and forth, I don't think like an enum should understand that it can do Eloquent queries, for example.
Joel: I would agree with that. Yeah, to me, that would be crossing a line as well. Yeah, so I'm on board with that.
Aaron: So, the question kind of is like, what are all these useful things? I'll give one other quick example, because I think this is actually really useful.
I've seen packages for this, but you don't need a package for this. It's kind of what you said with the enums for a drop-down.
Sometimes you have an enum where all the cases need to be in a drop-down, and then they have to have their labels and stuff.
So I've seen also, you know, static methods on enum saying like for select, or something like that. Where it will go and take that and determine...
Like, if you have a label method, it'll use that for the label. Otherwise, it'll use the value, and then but the key is that the ID.
There's all kinds of different things you can do. So that's the other thing, where I've seen where it sort of just takes the stuff, knowing
kind of how you would use it all the time, and sort of wraps it. So it's not returning HTML, it's not returning anything like that,
it's just returning a different data structure that happens to work for selects.
Joel: Okay. Yeah, I haven't dabbled with that, but I could see how that could be useful.
Especially if you're doing it a bunch of times and don't want to repeat these methods all over the place.
Aaron: Oh, you have dabbled with it, you just don't remember. Like, I'll give you an example. We use MaryUI in some of our projects.
Joel: Oh, yeah.
Aaron: So, some of our enums there were using X choices offline now, or whatever. And so, we want to get those values out of those enums.
And so we made a method that was like for MaryUI choices, or something like that. But only because it was also very specific.
Like, they wanted specific keys. Not a key value, it was an array with two keys and stuff like that. So, those are known data structures.
Again, maybe it's data structure, it's not necessarily the content afterwards. Oh, that's a good idea. Since enums technically are a data structure,
they should be able to work with and create and manage other data structures, but not necessarily manage or create or work with other data.
Joel: Okay. Yeah, I think especially once it crosses the line into dealing with other types of data, especially if it's like an Eloquent model,
or something like that. That's where it feels more like you're kind of bending this, you're expanding the scope of what an enum should do.
But, yeah, if it's just purely data manipulation. And I think content is part of that, if you're just mapping,
like from a backing value to a user-readable value. Or, more complicated, like into a different data structure, but it's the same basic data.
I have no problem with that, we have done it, so of course it's okay. Because we wouldn't do something if it was wrong.
Aaron: I think the whole, you know, whether it uses Eloquent or whatever, is also like a judgment call, too. Because I can see two different ways.
Like, we don't make that a hard, fast rule, but I don't want it to query Eloquent. But for an incoming parameter value, should it be able to understand a model?
I don't know, maybe. Because maybe it's like give me this enum from these two models or something. And those are all hydrated and they have their properties,
and then based off that it's... I'm just thinking theoretically here, right.
But I don't know if it's so hard of a seam, saying like, "Well, you can never use Eloquent." You just can't do Eloquent queries.
Joel: For sure, but yeah. And then I would be like I wonder if a cast is the solution instead for some of those translation things.
Like, you could still... that logic is on the model and it returns an enum and then the cast contains the logic to get from the model to the enum.
But, yeah, some of those are judgment calls based on the project and the complexity of the logic.
Aaron: So, I think the whole wrapping around point here is that you can put methods on your enums but don't use it as just
another helper class or like another God class or service. Yeah, they should be specifically targeted to the data structures and retrieving,
and showing the data in those data structures.
I don't know if I'm weird, but I have a personal boundary or a personal bubble that I don't want people inside of.
Joel: Okay.
I live a more of a suburban life, so I don't have to go on to the subway, not bumping into people all the time, they're not just touching me.
Joel: So, a physical bubble, it's not emotional bubble?
Aaron: Yeah, physical bubble Like, stay out of my personal space, bro.
Joel: Got it.
Aaron: And so I guess not everyone has this.
Joel: Unfortunately.
Aaron: Because recently, I was somewhere in a meeting, and someone was like sitting a couple of chairs away from me,
and then got up and sat right next to me. And was like touching my arm, even during the meeting. Not like touching my arm-
Joel: Foul. Technical foul.
Like, "Oh, interesting conversation." It's like we were both staring forward, interested in the meeting concept, but there was like a chair between us,
and then they just got up and sat right next to me. No mention of why, no mention of why you're so close and touching me.
My assumption is that the chair was like wonky or something, but swap the chair then. Don't just come sit next to me and touch me.
Joel: I'm with you 100%, man. Like, that would just.. Can I ask how you responded? Did you like just grin and bear it, or did you move over?
Aaron: Well, there wasn't anywhere else to move over. I was right already on like the aisle seat, so I guess I got to move into the aisle.
I just kind of stared forward and just, you know, wondered about this person next to me. I initially went angry, right?
Because I'm a fighter and I go angry. But then I was like, "That's weird." Like, "I don't think this person's a psychopath."
So I was more just interested in that concept then. For the rest of the meeting, I was like. Because it was the entire time, still touching.
I was like, "What is it? What possibly is happening?" So, I'd like move over just a tiny, tiny bit, and like no touching,
and so then I straighten my back, touching me again. Like, what? I guess you could say, like, "Well, you found a way not to be touching,
so you should have moved over." But no, I was there first, Joel.
Joel: Yeah.
Aaron: You know, we can just solve everything by saying, yeah. Except all edits and changes from Claude or Copilot, but there might be a better way to do this, too.
Joel: If you'd like someone on your team that actually thinks through the decisions, while still using the efficient tools that AI gives us,
head over to nocompromises.io and see how we can work with your team.