Skip to main content

Of course, users need to know how much they would pay for parking. It should happen in three phases:

  • Before parking - when getting the list of zones (done)
  • During parking - when getting the parking by ID (not done yet)
  • After parking - as a result of the stopping function (not done yet)

As I mentioned in the very beginning, we won't cover the payments themselves in this tutorial, we only take care of the calculations.

So, we need to create some function to calculate the current price by zone and duration, and then save that price in the parkings.total_price when the parking is stopped.

For that, let's create a separate Service class with a method to calculate the price. In Laravel, there's no Artisan command make:service, so we just create this file manually in the IDE...

The Full Lesson is Only for Premium Members

Want to access all of our courses? (30 h 09 min)

You also get:

55 courses
Premium tutorials
Access to repositories
Private Discord
Get Premium for $129/year or $29/month

Already a member? Login here

Comments & Discussion

A
andywong31 ✓ Link copied!

Povilas i notice the comment editor here is not very intuitive and its hard to paste/write a block of code. is there any way you can fix this?

PK
Povilas Korop ✓ Link copied!

Well, I use laravel-comments.com from Spatie, so not much I personally can fix. I do see you guys are pasting code with Markdown and it's shown quite well to me, or am I missing something?

GK
Gavin Kimpson ✓ Link copied!

I just use the three backticks method and that seems to work ok for me

GK
Gavin Kimpson ✓ Link copied!

Noticed an error with this if there isn't a vehicle already set on the $this->vehicle relation - would it be ideal to add $request validation in this function? Or was there a reason why you wouldn't add validation here?

public function toArray($request)
{
$totalPrice = $this->total_price ?? ParkingPriceService::calculatePrice(
$this->zone_id,
$this->start_time,
$this->stop_time
);
 
return [
'id' => $this->id,
'zone' => [
'name' => $this->zone->name,
'price_per_hour' => $this->zone->price_per_hour,
],
'vehicle' => [
'plate_number' => $this->vehicle->plate_number
],
'start_time' => $this->start_time,
'stop_time' => $this->stop_time,
'total_price' => $totalPrice,
];
}
GK
Gavin Kimpson ✓ Link copied!

I added the following code to the top of the toArray() above and it seems to work ok, or would you recommend a different approach?

if (!$this->vehicle) {
abort('400', 'Vehicle missing');
}
 
if (!$this->zone) {
abort('400', 'Zone missing');
}
HN
Huy Nguyen ✓ Link copied!

I don't think we need to add validation in here because at the time the ParkingResource was used, it already has the vehicle and zone. Moreover, if the zone or vehicle which the parking belong to was deleted,I pretty am sure the laravel will throw exception regarding foreign key violation constraint.

A
augusto-dmh ✓ Link copied!

Always before the code reaches 'ParkingResource' a validation like that already exists (in start and stop) or is unnecessary (show) - unnecessary because it gets a Parking record from the database, and we imply the data stored is valid for sure.

KA
Kareem Aladawy ✓ Link copied!

I found that in our ParkingController class's stop() method, we didn't check if the parking stopped before updating its stop_time property, which could result in an incorrect price calculation if the /stop endpoint was triggered multiple times within different intervals.

This situation might be avoided in a frontend client by not allowing the user to click the stop button twice, however it cannot be totally avoided if the user hits the Api endpoint directly.

To resolve this issue, I added the following if statement to the top of ParkingController's stop() method:

if($parking->stop_time) {
return response()->json(['errors' => ['general' => ['Parking already stopped.']], ]
, Response::HTTP_UNPROCESSABLE_ENTITY);
}

I'm not sure if I'm overthinking the situation, but better safe than sorry.

PK
Povilas Korop ✓ Link copied!

Good catch, better safe than sorry indeed!

AA
Arthur Ariza ✓ Link copied!

Nice catch!

NM
Njuguna Mwangi ✓ Link copied!

Brilliant

JO
Josue Oblitas ✓ Link copied!

In the variable $totalTimeByMinutes I had to add abs() so that it returns a positive value because otherwise it would return a negative $totalprice

public static function calculatePrice(int $zone_id, string $startTime, string $stopTime = null): int { $start = new Carbon($startTime); $stop = (!is_null($stopTime)) ? new Carbon($stopTime) : now();

$totalTimeByMinutes = abs($stop->diffInMinutes($start));
 
$priceByMinutes = Zone::find($zone_id)->price_per_hour / 60;
 
$totalPrice = ceil($totalTimeByMinutes * $priceByMinutes);
 
return $totalPrice;
}
M
mrifqyabdallah ✓ Link copied!

Carbon's diffInMinutes() received a second boolean argument of absolute (default to false). So we can just write it as:

$totalTimeByMinutes = $stop->diffInMinutes($start, absolute: true);

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.