Using PHP Traits to access Eloquent Events

Object Oriented programming languages, barring one or two exceptions, do not generally allow multiple inheritance. That is to say, if a class is inheriting from another class, it can't then also inherit from a third class or any subsquent classes.

In cases where objects need to use methods contained in classes that they are not already inheriting, they use other techniques to get around this restriction. Java uses interfaces. In version 5.4 PHP introduced the concept of traits. This technique allow programmers to indentify "traits" which are common to multiple classes and make these traits available to be shared across a range of classes. As Laravel Models are already inheriting from the Model class, (e.g class Booking extends Model - at the top of the Laravel Model class definition) they cannot then inherit (share) functionality from other classes - so they use traits.

Laravel Models have the ability to share a range of traits from the Events. These Events fire various traits during the lifecycle of the model class. The following is the main list of events that will fire as the model is accessed in various ways - retrieved, creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored. More detail on these is available here https://laravel.com/docs/7.x/eloquent#events.

Laravel has an additional feature for model classes which makes it easier to access the traits. The laravel boot( ) function will allow us to easily hook into the Laravel events.

We know how to retrieve the cost per hour depending on the membershiptype, now all we need to do is to calculate how many hours the booking is for. To do this we will instantiate PHP Datetime objects using the Datetime class. The Datetime class has a diff( ) function which will allow us to calculate the interval between the two datatime objects. This interval can be expressed in a range of different time formats such as years, months, weeks, days, hours, minutes or seconds. In our case, we need to figure out the number of hours between the two date-times.

We need to bring all this together in the booking.php model class. We need to add some intelligence this class so that any time a new booking is created or an existing booking is updated the "saving" event will fire before the information is persisted to the database. The saving event should perform the following steps:

  1. retrieve the cost per hour based on the membership typeinstantiate two datetime objects for the start time and end time
  2. calculate the interval between the start and end and return it as a number of hours
  3. calculate the booking fee based on the hours and the cost per hour
  4. assign this value to the booking fee in the booking table

To get all this working add the following boot( ) function to app\models\booking.php

public static function boot()
{
    parent::boot();
    self::saving(function($model)
    {
        //figure out who the logged in user is
        $loggedInUser = Auth::user();
        //retrieve the costPerHour based on members of that type
        $costPerHour = $loggedInUser->member->membershiptype->courthourlyfee;
        //get datetime objects for the starttime and endtime - calculate the interval between them
        $startTime = \DateTime::createFromFormat("H:i",$model->starttime);
        $endTime = \DateTime::createFromFormat("H:i",$model->endtime);
        $interval = $endTime->diff($startTime);
        //add up the number of hours, minutes and seconds - express this as a decimal of hours
        //which is rounded to 2 decimal places
        $totalHours = round($interval->s / 3600 + $interval->i / 60 + $interval->h, 2);
        //set the booking fee to be the cost per hour times the totalHours(decimal)
        $model->fee = $costPerHour * $totalHours;    
    });
}

Leave a Reply