How to check if exists overlaped dates?

Created on 9 March 2023, about 2 years ago
Updated 1 June 2023, almost 2 years ago

I have a Smart Date Recurring field into a type of content.
Users are allowed to set recurrent dates.

I'm doing a custom module and I'm trying to obtain an array for all occurrences dates (start and end timestamps) of a node, for then generate a query (Dynamic Queries) to check if exists some overlap with other nodes.

$node_dates = $node->get('field_reserva_sala_data_i_hora')->getValue();
...
$orGroup = $query->orConditionGroup();

foreach($node_dates as $date) {
	
	$data_start = $date['value'];
	$date_end = $date['end_value'];
	
	$andGroup = $query->andConditionGroup();
	$andGroup->condition('field_reserva_sala_data_i_hora.end_value', $data_start, '>');
	$andGroup->condition('field_reserva_sala_data_i_hora.value', $date_end, '<');
	
	$orGroup->condition($andGroup);
	
}

$query->condition($orGroup);

Exists an easy way to get it?

The objective is to prevent overlap the dates between nodes.

I use:
- Drupal 10.0.3
- smart_date_recur 3.7.1

💬 Support request
Status

Active

Version

3.7

Component

Smart Date Recur

Created by

🇪🇸Spain intersarsi

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @intersarsi
  • 🇨🇦Canada mandclu

    The easiest way that comes to mind is to look for an existing field value that has a start or end between the start and end of each value being saved in your foreach loop.

    That said, it looks like you're working on some kind of reservations system. Have you looked at the Bookable Calendar module? It was built specifically for that.

  • 🇪🇸Spain intersarsi

    Firstable, thank you for the suggested module, although not fits for our requeriments. We are using FullCalendar module.

    I'm sorry, I'm not able to understand what you are trying to suggest me.

    For example, I have an entity with a smart_date field filled with the information that shows the "smart_date_field.png" capture.

    The result is that I have an event for each day (five "Prova" events), as it shows on "smart_date_calendar.png" capture.

    When I save that node (that it contains the smart_date field) I do a validation to check if in these five days already exists other events (same day and time).

    To get it I added a constraint on the entity using the hook "hook_entity_type_alter".

    Into the constraint I'm trying to generate a query that retrieves me all events that already exists into that dates:

    - If it retrives 0 elements, this it means it doesn't exist overlapped events, and it pass the constraint.
    - If it retrives more than 0 elements, this it means that exists at least one overlap event, and don't pass the constraint.

    When I validate, I get the value of the smart_date field with:
    $node_dates = $node->get('field_reserva_sala_data_i_hora')->getValue();

    With the instruction above I can retrieve only the first timestamp ocurrence with "field_reserva_sala_data_i_hora.value" and "field_reserva_sala_data_i_hora.end_value".

    This it means that the loop iterates only one time, as a result, only one query condition has been added.

    How I can get the other 4 ocurrences?

    To get something like this:

    WHERE 
    ...
        (
    		("node__field_reserva_sala_data_i_hora"."field_reserva_sala_data_i_hora_end_value" > '1678102200') and 
            ("node__field_reserva_sala_data_i_hora"."field_reserva_sala_data_i_hora_value" < '1678105800')
    	)
        OR
    	(
    		("node__field_reserva_sala_data_i_hora"."field_reserva_sala_data_i_hora_end_value" > '1688380200') and 
            ("node__field_reserva_sala_data_i_hora"."field_reserva_sala_data_i_hora_value" < '1688383800')
    	) 
        OR
    	(
    		("node__field_reserva_sala_data_i_hora"."field_reserva_sala_data_i_hora_end_value" > '1691058600') and 
            ("node__field_reserva_sala_data_i_hora"."field_reserva_sala_data_i_hora_value" < '1691062200')
    	) 
        OR
    	(
    		("node__field_reserva_sala_data_i_hora"."field_reserva_sala_data_i_hora_end_value" > '1693737000') and 
            ("node__field_reserva_sala_data_i_hora"."field_reserva_sala_data_i_hora_value" < '1693740600')
    	) 
        OR
    	(
    		("node__field_reserva_sala_data_i_hora"."field_reserva_sala_data_i_hora_end_value" > '1696329000') and 
            ("node__field_reserva_sala_data_i_hora"."field_reserva_sala_data_i_hora_value" < '1696332600')
    	)
        ...
    
  • 🇨🇦Canada mandclu

    Thanks for the additional explanation. When I initially read the issue it wasn't clear to me that the problem is not being able to validate the recurring values, even though you did clearly mention that it was a recurring date.

    It seems like an issue with when your validation is running. I suspect that the most reliable fix would be to resolve Provide a simple method to expand recurring events Active but I know that doesn't solve your short term problem.

    If you can't find a way to fire your validation after the extra values have been generated, theoretically an alternative would be to run smart_date_recur_generate_rows() from smart_date_recur.module to generate the additional values. The catch is that this would also create a rule entity, so you would have to delete that entity when you're done your validation. Or, you could use the code in that function as a guide for how to generate those other instances in a more custom way.

  • 🇪🇸Spain intersarsi

    Thank you!

    The code below seems to work to check if it exists overlapped events:

    $query = $this->entityTypeManager->getStorage('node')->getQuery();
    $query->condition(...);
    
    $recurrences = smart_date_recur_generate_rows($smart_date_field_value, $node_entity_type, $node_bundle, $smart_date_field_name, $month_limit);
    
    $orGroup = $query->orConditionGroup();
    
    foreach($recurrences as $recurrence){
    
    	$date_start = $recurrence['value'];
    	$date_end = $recurrence['end_value'];
    	
    	$andGroup = $query->andConditionGroup();
    	
    	$andGroup->condition($smart_date_field_name . '.end_value', $date_start, '>');
    	$andGroup->condition($smart_date_field_name . '.value', $date_end, '<');
    	
    	$orGroup->condition($andGroup);
    	
    }
    
    $query->condition($orGroup);
    
    $nodes_id = $query->execute(); //overlapped events/nodes
    

    In other hand, you commented that every time that I execute smart_date_recur_generate_rows() function it creates/generates a new rule entity that I have to delete when I have finished my validation.

    Do you mean that the rule is saved into a database table? I have searched into the database table "smart_date_rule". Is here where it will be set/saved? I'm not able to find here.

  • 🇨🇦Canada mandclu

    I'm glad you got it working!

    As for the extra rules, it probably wouldn't happen when resaving values that were previously saved as repeating, but the first time you would save a recurring date. If, on the other hand, you're not seeing that problem, then even better. :)

  • 🇪🇸Spain intersarsi

    Hi,

    I have developed a module that it checks overlapping reservations.

    Cool Calendar Extras

    I hope it would be useful and you can take some idea.

  • 🇨🇦Canada mandclu

    Interesting, thanks for sharing! Out of curiosity, the module mentions an ability to reminds users about their reservations, but it isn't how a user creates a reservation. Or is that just a reminder for all events on that day?

    In terms of the sending of the emails, did you consider compatibility with the Easy Email module, which allows for HTML-formatted emails?

  • 🇪🇸Spain intersarsi

    Hi!

    The module doesn't create a type of content for you.
    You can enable the reminders for each type of content that you have.

    For example, if you create a "room reservation" type of content, into the Drupal configuration section "/admin/config/regional/cool_calendar_extras" you will find a "room reservation" tab and here you can enable a check (the type of content must have a smart date field).

    The user don't have the ability to set if he wants to recieve the remminder (only administrators).

    It is a functionality on the type of content level, no for each node that user creates.

    When cron's executes, it checks which nodes have a current date.
    It sends an email for each node, it doesn't group all user remminders into a same email.

    I will take a look the Easy Email module. It works with Drupal 10?

    Thank you!

Production build 0.71.5 2024