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

Immutability in PHP0:05

Hey there. Welcome back to a new lesson. It is time for us to talk a little bit about immutability. If you have been working with php for a while, you have probably heard that scalers are passed by value, whereas objects are passed by reference. Although in practice that's not quite what happens. Objects have reference-like behavior that's not relevant to the point we're gonna talk about, but you can just take it that when you pass a value

to the point we're gonna talk about, but you can just take it that when you pass a value to a function, what php does is essentially copy the value from the variable or the value that you're passing and give it to the new function. So you have two references in memory. They hold the same value, but they're distinct. When it comes to memory address space. With objects, things are a little bit different. If you have an object, if you instantiate an object,

With objects, things are a little bit different. If you have an object, if you instantiate an object, and you pass it to a function, you're essentially given a pointer to the same memory address, which means that that function can make changes to that object, and it is going to reflect on the state of the object on the caller. A good example are Eloquent models. If you have a model and it has some state,

A good example are Eloquent models. If you have a model and it has some state, it has some attributes like name matteas, and you've passed that over to a function, the function can modify that object. For example, you can change the name to Jaffy, and even if the function doesn't return that object back, the object is still going to be mutated. That's why with Eloquent, you can pass Eloquent models around.

That's why with Eloquent, you can pass Eloquent models around. You can make changes to it. You can call save afterwards and the state is going to be updated. Sometimes mutability is desired. Eloquent models are a great example. You want them to be mutable. You want them to be able to be passed around and to suffer those changes and have the state reflect that.

Invoice replication example1:46

You want them to be able to be passed around and to suffer those changes and have the state reflect that. In some cases, mutability is not so desired. So let's jump into some code and see an example of where mutability might not be ideal. This is a little function that our app offers. It essentially allows you to replicate and exist an Invoice and just change the dueDate. For example, I might build the same Client with the same amount 13 times a year.

For example, I might build the same client with the same amount 13 times a year. So every 28 days, instead of creating 13 invoices, I can create one invoice and then ask the application to replicate it 12 times with 28 days in between. So let's take a look at the code. Pretty simple. We have a controller. We take account, which is how many times we want to replicate this invoice and how many days we want between each invoice.

how many times we want to replicate this invoice and how many days we want between each invoice. The logic is also pretty simple. We're grabbing the invoice due date, the original due date, and then we're creating a collection with how many times we want to replicate this invoice. Within the slip, we calculate the new invoice due date. So this is going to be the original invoice due date. We're gonna add days, and the number of days we're gonna add is the number of days between

We're gonna add days, and the number of days we're gonna add is the number of days between and how far along we are on the iteration. So if we were to have, for example, count of two and 28 days in between, the first iteration would add 28 days. The second iteration would add 56 days. And then we call this method on the invoice called createCopy, where we just pass a new invoice due date. Pretty straightforward, I can understand it, no problems.

copy, where we just pass a new invoice due date. Pretty straightforward, I can understand it, no problems. Let's take a look at the task we have. We are creating an invoice pretty straightforward, and then we're making a call to the replicate endpoint, and we're saying we want to replicate this invoice twice. We want two copies and we want each copy to have 30 days in between them. And then we have some assertions. We want the first copy to have a due date 30 days

Debugging failing test3:48

And then we have some assertions. We want the first copy to have a due date 30 days after the original, and we want the second copy to have, it's today 60 days after the original. So 30 days in between each invoice. If we run this task, you're gonna notice it's failing. We were expecting July 8th, but we got August 7th. Why is that happening? Well, if we go back to the code, and here's what I'm gonna do.

Why is that happening? Well, if we go back to the code, and here's what I'm gonna do. I'm just gonna do a var_dump on this newInvoiceDate. Let me actually add it to the timeString. There we go. So you notice that the first one is correct, but the second one, hmm, it is incorrect. Why is that? Well, the reason for that is because this little guy right here is a Carbon instance and Carbon is mutable. So when we call addDays here,

and Carbon is mutable. So when we call addDays here, and we're first calling this with 30, and then we're calling this with 60, it is altering the original instance. This value is being mutated. The internal attributes of this instance is being mutated. What it means is for us to fix this while using this version of Carbon is to instead of calling addDays, daysBetween times count, we should just pass daysBetween

of Carbon is to instead of calling at days, days between times count, we should just pass days between because we are mutating that instance across each iteration. If we rerun this, it passes. So my question now is, is this code predictable? Well, if I were looking at it, I would know that carbon by default is mutable, so I would sort of make sense of it. But if someone knew someone who has never worked with PHP or has never worked with Laravel or just doesn't remember this, looks at this,

CarbonImmutable vs Carbon5:28

or has never worked with RFL or just doesn't remember this, looks at this, I don't know if they would find it. So predictable Carbon extends the DateTime interface that PHP offers, and there are two types of DateTime in PHP. There's a DateTime class and there is a DateTimeImmutable class. Same thing with Carbon. We have Carbon and we have CarbonImmutable.

Same thing with Carbon. We have Carbon and we have CarbonImmutable. So in this case, we could use the immutable version of Carbon to do this. Here's what we would do. Let's re-add our count here, and we could even refactor this and say daysToAdd is going to be the number of days between times the count that makes it even more readable. Let's rerun our tasks. It should fail. Now we could simply say toImmutable.

Let's rerun our tasks. It should fail. Now we could simply say twoImmutable. This is going to give us a CarbonImmutable instance. Let's say the following. Let's dump and dd this and let's run a test. As you can see, we have an instance of Illuminate\Support\Carbon, which just extends Carbon. If I look at it, you can see that it just extends the base Carbon instance, which is Carbon. Now, if I were to add toImmutable here.

extends the base Carbon instance, which is Carbon. Now, if I were to add to immutable here and rerun the test, you can see that we now get an instance of CarbonImmutable. And CarbonImmutable is a little bit different. Whenever you make a change, for example, addDays, instead of just changing the internal value of the current data on that Carbon instance, it gives you a new one. Essentially what it does is it calls copy and then calls the operation you want.

Essentially what it does is it calls copy and then calls the operation you want. So if we were to keep this mutable and we were to say that we want to copy the instance, and nowadays this will work because we're now getting a new Carbon instance and mutating the values in it, this one is not being mutated, but let's move on with our fix. So let's say that this should be immutable.

Eloquent immutable casting7:13

but let's move on with our fix. So let's say that this should be immutable and let's rerun our tests. It's passing. So now here's something cool we can do. We know we want this to always be immutable, right? So we can get rid of this. We can go to our Eloquent model, and within your cast method or cast property, if you're running an older version of Laravel, you can go to dueDate.

or cast property, if you're running an older version of Laravel, you can go to due date. That's the property we're casting. And instead of saying dateTime, we could say immutable, dateTime. And now we can notice we don't have immutable here, we're not doing any casting. If we rerun this, it is going to pass and it is going to pass because Eloquent is going to automatically convert

When to use immutability7:51

and it is going to pass because Eloquent is going to automatically convert that into an immutable timestamp, an immutable Carbon instance. Now, we used the example of a point in time being immutable, and I find that when working with timestamps, when you're working with points in time, I usually want them to be mutable. It's something that already happened or that is going to happen and that is fixed.

It's something that already happened or that is going to happen and that is fixed. If I want to create a new timestamp from that, I'm usually going to put it on another variable or I'm just gonna pass it as an argument to some method. With that said, there are situations where you want things to be mutable, like we discussed previously. Eloquent models are a good example of that. Or if you have, for example, a class encapsulates a collection of events.

Or if you have, for example, a class encapsulates a collection of events and you want to be able to add new events to it, you also want that to be mutable. With that said, I think it's always important to keep track to know in your head if the objects you're working with are immutable or mutable. If you have worked with other languages, JavaScript, for example, or TypeScript, you know that on JavaScript, you can even define whether a variable

for example, or TypeScript, you know that on JavaScript, you can even define whether a variable itself is going to be mutable. If it's a let, a const, if it's a constant, you cannot change its value, although in some cases, if you have an object assigned to the variable, you can still change its internal values. So there are some gotchas. Sometimes in PHP, you can have, for example, a class property to be read only.

Sometimes in php, you can have, for example, a class property to be read only. That means it cannot be changed. But if it's an object, sometimes, depending on the object, you might still be able to mutate its internals. In the next few lessons, we're gonna explore a little bit how do we create our own immutable objects and when should we leverage immutability or mutability. I hope you enjoyed this lesson and it's you guys on the next one.

I hope you enjoyed this lesson and it's you guys on the next one. Bye-bye.

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