phplaravellaravel-validationoverlapping-matches

Efficient way to check for overlapping ranges in a database, Laravel validation


I have a model named ClientDiscountRate, which naturally corresponds to a table.

I am developing a method to allow the creation of a new ClientDiscountRate. However, I am facing a problem: the client_discount_rates table has from and to columns, and I need to ensure that the new rate does not overlap with any existing rates.

Both from and to must be integers, with from being smaller than to.

For instance, if I already have a range from 4 to 6, no overlapping ranges means the new rate cannot have a from or to value of 4, 5, or 6. It also means the new rate cannot encompass 4-6, such as having from: 3, to: 7. The new range can be before or after the existing range, but it cannot contain, be contained by, or intersect with the existing range in any way.

Moreover, I could have multiple existing ranges. So, if I have ranges 4-6 and 10-20, the overlapping check must apply the same logic to both ranges - the new range cannot overlap, contain, or be contained by either existing range.

I have written the following code, but I am unsure if it is the most efficient approach:

$from = $requestData['from'];
$to = $requestData['to'];
$matching = ClientDiscountRate::where('client_id', $client->id)
    ->where('from', '<=', $from)
    ->where('to', '>=', $from)
    ->first();
if ($matching) return response()->json(['error' => 'this range overlaps an existing one - from'], 400);
$matching = ClientDiscountRate::where('client_id', $client->id)
    ->where('from', '<=', $to)
    ->where('to', '>=', $to)
    ->first();
if ($matching) return response()->json(['error' => 'this range overlaps an existing one - to'], 400);
$matching = ClientDiscountRate::where('client_id', $client->id)
    ->where('from', '>=', $from)
    ->where('from', '<=', $to)
    ->first();
if ($matching) return response()->json(['error' => 'this range contains an existing one'], 400);
$matching = ClientDiscountRate::where('client_id', $client->id)
    ->where('to', '>=', $from)
    ->where('to', '<=', $to)
    ->first();
if ($matching) return response()->json(['error' => 'this range contains an existing one'], 400);

Is the above a sensible way to check for undesirable overlaps in the from and to fields?


Solution

  • The rule to respect is that for all the from in the database that is less or equal your $to, you have to find that all the corresponding to for those from have to be less than your $from, if it's not the case then you have an overlapping ranges.

    $from = $requestData['from'];
    $to = $requestData['to'];
    
    $matching = ClientDiscountRate::where('client_id', $client->id)
        ->where('from', '<=', $to)
        ->where('to', '>=', $from)
        ->exists();
    
    if ($matching) {
        return response()->json(['error' => 'this range overlaps an existing one'], 400);
    }