تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Objects as Pseudo-Structs0:00

Hey there. Welcome back to New Lesson. On the last lesson, we spoke a little bit about immutability and we used DateTime objects as an example. On this lesson, we're gonna put that to the side a little bit and talk about using objects as pseudo structs in php, which is also going to tie in very nicely with immutability later on in the series. Just for context, what do I mean

with immutability later on in the series. Just for context, what do I mean by using objects as strict? Well, in php, as you know, we don't have strict types, but we do have associative arrays, and PHP ships with two types of arrays. We have pure arrays, which are collections of data, and we have associative arrays, which are key-value mappings. In other languages, you might have those as dictionaries.

which are key value mappings. In other languages, you might have those as dictionaries or maps, but they're essentially the same thing. And whenever we have structured data in php, we tend to rely on associative arrays. So if you have some information about an invoice like an ad, uh, a due date and an amount, if you don't have that in an Eloquent model or if you just have that as transient data, we typically put that within an associative array and they're great.

or if you just have that as transient data, we typically put that within an associative array and they're great. They're super versatile in a lot of situations. They're great. They're the right tool for the job. If you have some data you want to serialize into JSON, if you have some data you want to pass to the front end or to a mailable, they are great if you're just using them sparingly within a class. Also, perfect. If you want to represent something as a concept within your system,

Also, perfect. If you want to represent something as a concept within your system, associative arrays are likely not the best choice. For one, you cannot enforce any structure. You would never know if an array has or doesn't have a key. Secondly, you cannot properly enforce types. You don't have IDE auto completion. And to me, one of the most important things, arrays do not have names. Classes do classes

arrays do not have names. Classes do classes represent something concrete within your system. You can search for usages. When it comes to classes, you cannot do that with arrays. You can type properties and you can enforce those types at runtime with classes. You cannot do that with arrays. You can enforce structure, you have ID, auto completion, et cetera, et cetera, et cetera.

You can enforce structure, you have ID, auto completion, et cetera, et cetera, et cetera. In those cases, I think objects serve that purpose better. And this lesson, let's jump into some code real quick. We're gonna see an example of what it would look like with an associative array and how we can turn that into an object, and then we're gonna explore a few extra things on the following lessons. So let's jump into some code.

Reviewing Interest Simulation Code2:37

things on the following lessons. So let's jump into some code. Okay, here we have some code, and what this code does is it creates some simulations, some scenarios of what interest look like if applied against an Invoice. Let's say that you have an Invoice of a thousand dollars and it's due, and you wanna see how much interest would occur at different scenarios in 30 or 45 or 60 days.

how much interest would occur at different scenarios in 30 or 45 or 60 days. This is what this code does. Pretty specific, pretty niche, but this is just an example. We're just validating the request and then we're grabbing those simulations. You can see it's scenarioA, and then we have a monthlyInterestRate and then a referenceDate. And within that mapping, we are calculating

and then a reference date. And within that mapping, we are calculating how many days it has been due for, and then we're calculating the interest that is going to be applied against that invoice. If we look at this function, pretty simple, we just calculate the daily interest rate, we see the total interest as a percentage, and then we multiply the total by that interest percentage. Afterwards. We just returned this array.

and then we multiply the total by that interest percentage. Afterwards. We just returned this array. So we have some structured data here. We know what it is supposed to look like. Then we go through each one of those simulations and we shoot a notification. And after that, we just return some js. Let's take a look at what the task looks like. Okay, we're just creating an Invoice. Then we are making a POST request.

Okay, we're just creating an invoice. Then we are making a POST request to the interest simulations route, and we are passing some scenarios. So on the first one we want to simulate and a monthly interest rate of 2% and a reference date. That's the same as the invoice's due date. That is, this is not going to apply any interest. On the second scenario, we are also passing a monthly interest of 2%,

On the second scenario, we are also passing a monthly interest of 2%, but now we are adding 15 days to the reference date. That is 15 days after the invoices due date. After that, we're just asserting that the notification was sent. And here on this block is where we assert that we got the correct data for the first invoice, we do expect a 2% interest rate, a monthly interest rate, but we expect a zero total interest.

we do expect a 2% interest rate, a monthly interest rate, but we expect a zero total interest and zero elapsed days as there was not enough time for any interest to be applied against that invoice. For the second invoice, we also expect a 2% monthly rate, but, and in this case we are using 30 days as a month for this application. We also expect a 1% total interest percentage. That is how much interest was applied against that invoice. We expect 15 days as the amount of time we had

That is how much interest was applied against that invoice. We expect 15 days as the amount of time we had between the invoices due date and the simulations target date, and we expect $10 a total interest. That is because the invoice was worth a thousand dollars. Pretty simple stuff. If we run this, it'll pass. Great. However, let's look at the scope. So we're not doing a lot with this simulation. One thing that we're doing though is we're passing it

Array Data Pitfalls5:27

So we're not doing a lot with this simulation. One thing that we're doing though is we're passing it to this InvoiceInterestSimulationIssuedNotification. And if we look at it, we have two things being injected on the construct. We have the invoice, which is a rich object, it's a model, and we have this data and we have no idea what data is from this context. If we were looking just at this file, we don't know what data is.

If we were looking just at this file, we don't know what data is. We would have to go and see where this is being called from and then see what is being passed. Now this is a very simple example. In real life, sometimes you have to go back to three, even four files to figure out what is being passed. Sometimes what's being passed comes from a request, so you're never really sure exactly what you're getting. If you've played a little bit with php stand,

so you're never really sure exactly what you're getting. If you've played a little bit with php stand, you probably know that you can do some typing, you can use some php send types to sort of tell php stand in your id, what is present in NRA. But for now, we're gonna keep things simple. We have this data array, we don't really know what that is. And here we are referring to some keys on that array. So does monthlyInterestRate exist on that array? Well, I hope so. I'm not sure, right?

So does monthly interest rate exist on that array? Well, I hope so. I'm not sure, right? Anything could be passed here. An empty array could be passed and this code would be executed. So we're gonna change that. Instead of having that as an array, we are going to create an object with the exact same properties as the keys on this array. Some of you might call this a DTO, a data transfer object. I'm just gonna call it an object. I'm gonna give it a name.

Creating a DTO Object6:55

Some of you might call this a DTO, a data transfer object. I'm just gonna call it an object. I'm gonna give it a name. We're gonna call this an invoiceInterestSimulation. So let's add a new file here. We're gonna call it invoiceInterestSimulation.php. I'm gonna put it in an app for now. This is a small application, so let's not worry about directories right now. And I'm going to use the magic of editing so you don't have to watch me type a bunch of properties.

And I'm going to use the magic of editing so you don't have to watch me type a bunch of properties. And there we go. Here we have an object that represents an InvoiceInterestSimulation. We have the exact same keys as properties within this class, and we are able to type things. So we know that invoiceToDate is going to be an immutable date. referenceDate is same thing. ELAP states an integer. That's great. You might be looking at this

Reference date is same thing. ELAP states and integer. That's great. You might be looking at this and thinking, well, should we really use floats for monetary values? And the answer is no, but we're gonna cover that later on the series for now, we're gonna go with floats. So now let's go back to our controller, and I'm going to replace this with an object. I'm gonna say we're gonna return a new

and I'm going to replace this with an object. I'm gonna say we're gonna return a new Invoice, interest simulation. And again, I'm gonna use the magic of editing so you don't have to see me type all of this. There we go. And you can see that we have some minor differences here. We are being explicit in casting this referenceDate, which is a string at this point into Carbon immutable instance.

which is a string at this point into Carbon immutable instance. And we are also casting the difference in days. This guy right here, which Carbon returns as a float into an integer. Now, if we were to try and run the test right now, this would fail. Let's run it. Okay, here's why. If we go into our invoice interest simulation issue, we have to update this into an object.

If we go into our invoice interest simulation issue, we have to update this into an object. So we're gonna say that this is an invoice interest simulation, and we're gonna call this interestSimulation. Let me break this into two lines. There we go. And we're just gonna refactor this. So now, instead of calling this as an array, we're gonna say interestSimulation, monthlyInterestRate and interestSimulationReferenceId. And we have ID auto completion, which is

and interest simulation reference aid. And we have ID auto completion, which is fantastic, total interest. So this is great. We have ID auto completion. We know that this property is access. They must access php is gonna make sure they exist, otherwise the code is going to crash. And we know what their types are. So let's try this again. Okay, we're past the notification. The notification is being sent, that's great,

Fixing JSON Serialization9:15

Okay, we're past the notification. The notification is being sent, that's great, but we're getting an error. Undefined reiki monthly interestRate. Let's look at what we have here. Let's dd() what we got back? Hmm. Okay, that's interesting. So you can see that we are getting the properties as camelCase, but we're expecting snake_case. Why is that? That's because we are returning an object, and then we are serializing that into js.

Why is that? That's because we are returning an object, and then we are serializing that into js. And php by default is going to grab all of the public properties in an object and convert that into the JSON response, into the JSON object, if you will. So what we have to do is we need to add a serialization strategy to this class. And this is going to be some boilerplate, but this is the type of good boiler plate.

And this is going to be some boilerplate, but this is the type of good boiler plate. I'm gonna show you why. So I'm gonna create a twoArray method, which is going to return an array, and I'm going to return an array with the same shape as what we had in the beginning of this lesson. Again, editing magic. There we go. We have an array. And right here we're gonna say we want to map through each one of the simulations. Thank you. And we want to cast it into an array.

through each one of the simulations. Thank you. And we want to cast it into an array. Let's rerun our tests. And now we're getting something that makes sense, right? So let's remove that dd and rerun our task. And it's back to green. So you wanna look at this and think, isn't this useless? Isn't this just boilerplate? Sometimes it is. If you have something very simple, if the only reason you have that object is to pass it

If you have something very simple, if the only reason you have that object is to pass it to the front end or to return it as json, maybe this is useless. But this also represents a boundary within your system. So your json representation or your array representation of this object is no longer coupled to the object itself. What does it mean? That means that if I went to change what monthlyInterestRate is called within the class,

What does it mean? That means that if I went to change what monthlyInterestRate is called within the class, and we're gonna say this is called MIR, and we rerun our tasks, it still passes. If we didn't have this function rate here, that meant that every change that we did to any public properties could be breaking changes. Not only can we change names, we can change the types. Maybe we want to store things differently. Maybe we want to store, for example, totalInterest.

Maybe we want to store things differently. Maybe we want to store, for example, total interest as a richer object instead of a float. But it would still want to represent that as a float on its array representation. So essentially what we have here in let Do That change is a boundary. Our array representation is no longer coupled to what the object internals lookalike. And this is super, super important if you're passing those

Recap and Benefits11:47

what the object internals lookalike. And this is super, super important if you're passing those objects throughout your system. This is a very simplified example. In real life, you could expect those objects to be passed around multiple times. And if you have good boundaries, it makes refactoring so, so, so much easier. So let's do a recap. We started with an array,

So let's do a recap. We started with an array, something we cannot enforce types on, something we cannot properly enforce the structure on. It doesn't have auto completion, and we cannot use, for example, static analysis or your IDE to find out where it is being used. Now, we are still doing all of the calculation within the controller, which means that this feature is limited to this controller.

of the calculation within the controller, which means that this feature is limited to this controller. This is the only place where you can calculate those interest simulations, which isn't ideal. But we are now returning this richer object. Again, we only return this back to the front end and to this notification we send. So even though it's not being widely used within your system, we do have one consumer, which is our invoice interest simulation.

system, we do have one consumer, which is our invoice interest simulation. And now that we have this as an object, this guy right here, this is a consumer, it knows. It doesn't have to check whether that object has a property, whether that object is valid, whether the types it knows, it knows that it is a valid object, 'cause speech instantiated it, and he knows exactly what types are available on it. That makes your experience much more fluent.

and he knows exactly what types are available on it. That makes your experience much more fluent. It makes your code much more predictable, and I think it makes your project much more defensive. There's less uncertainty, if you will. Let's take a step back. Let's close all of this and let's just go through our code again. We're looping through the simulations, we're doing some calculations,

We're looping through the simulations, we're doing some calculations, and then we are returning this object invoiceInterestSimulation, which previously was just an array that doesn't need much, but now it is an active actor within our domain. It is something that has a name. And I find that is very, very important because now it is within our photo structure, it is within our directories.

because now it is within our photo structure, it is within our directories. And someone can look into this and see, okay, that's something our system supports. That's something we can do. We have invoice interest simulations. They can go here and they can, for example, I'm using PHPStorm. They can see where it is being used because again, and I wanna reinforce this, we turned it from an array,

They can see where it is being used because again, and I wanna reinforce this, we turned it from an array, which is something transient. It's something we create on the spot into an object, which is something that lives within your system. It's something organic, it's something that has a name, it's something that can be typed, it's something that can be used in static analysis. It's something that we can find usages for. So I can go here

It's something that we can find usages for. So I can go here and I can see exactly where it is being used. And we're only using it in one place. So we're only instantiating into one place. But if we scroll down a little bit, let me close this and let me close this. You can also see where it is being used. So if I were to join this project now and I were to stumble upon the file,

So if I were to join this project now and I were to stumble upon the file, I could simply see its usages and I would see where it is being instantiated and also where it is being used. And if I click here, we go to the notification, something you absolutely cannot do with an array. Okay, I think that is a lot for a single lesson. We're gonna stop here. On the following lessons, we're gonna explore a few extra things.

We're gonna stop here. On the following lessons, we're gonna explore a few extra things. First, how can we make that object a little bit richer? We're just instantiating something that we calculate ourselves. It can only be calculated on that controller. How do we give more autonomy to that object? How do we make sure that we can calculate interest simulations at any place on the application? We're gonna explore that a little bit.

interest simulations at any place on the application? We're gonna explore that a little bit. How can we make that object a little bit more reliable? What is the bare minimum in needs when it comes to information for it to work properly? And also, where does immutability play a part here? So I hope you enjoyed this lesson. I hope you're enjoying the course, and I'll see you on the next one. Bye-bye.

and I'll see you on the next one. Bye-bye.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟