0

DATA SUMMARY


I have the following data, an $existing_events array of existing events and a request for a new event ($requested_start and $requested_end). Normally pulled from SQL, but I've replicated the issue here w/ static arrays:

$existing_events = array(
    0   => array(
        'time_start' => '2015-09-05 11:30:00',
        'time_end'   => '2015-09-05 18:45:00'
    ),
    1   => array(
        'time_start' => '2015-09-05 07:15:00',
        'time_end'   => '2015-09-05 10:30:00'
    )
);

$requested_start    =   strtotime('2015-09-05 3:30:00 AM');
$requested_end  =   strtotime('2015-09-05 9:30:00 AM');

RESULTS ARRAY


A $results array for tracking how many times the comparison is made ('comparison_counter') and how many conflicts are discovered ('conflicts'):

$results = array(
    'conflicts' => 0,
    'comparison_counter' => 0
);

FOREACH LOOP


And finally, before dumping out json_encode'd results, the foreach loop that compares start & end time for the requested new event, against start & end time for each preexisting event, using this logic. Note the comparison_counter and conflict increments—the former runs every time the loop runs, and the latter is only "reached" in its if-statement, when there is a conflict between the requested event time and the current event time being tested.

if (!empty($existing_events)){
    foreach ($existing_events as $existing_event){
        $existing_start = strtotime($existing_event['time_start']);
        $existing_end = strtotime($existing_event['time_end']);
        $results['comparison_counter']++;
        if ($requested_start > $existing_end) {
            break;
        } elseif ($requested_end < $existing_start) {
            break;
        } else {
            $results["conflicts"]++;
        }
    }
}

echo json_encode($results);
exit;

ACTUAL RESPONSE


What I receive, though, makes it appear that the foreach loop is only executing once, despite the fact that there are two items in my $existing_events array. Here's what I receive with the data above:

comparison_counter: 1
conflicts: 0

vs. what I expect:

comparison_counter: 2
conflicts: 1

Oddly, as soon as I make the following change to 11:30 start-time(to attempt to overlap with both events):

$requested_end  =   strtotime('2015-09-05 11:30:00 AM');

...the comparison_counter now works. I now receive the following:

comparison_counter: 2
conflicts: 2

MY QUESTION


Why does the foreach loop not function properly, based on what's in the array I pass? I would expect it to at least hit its $results['comparison_counter']++; every time it fires.

Community
  • 1
  • 1
cdwyer
  • 589
  • 6
  • 22
  • What specifically are the values of $requested_start and $requested_end? I don't think your comparison is set to do what you're expecting it to. – Patrick Moore Aug 23 '15 at 05:42
  • @SetSailMedia - Form data that's being submitted and then strtotime'd...in this case: requested_start: 1441423800 requested_end: 1441452600 – cdwyer Aug 23 '15 at 05:46
  • Are they date/time stamp, or numeric time() value? nvm just seen your edit – Patrick Moore Aug 23 '15 at 05:47
  • Doesn't the first query take care of all of this (well, with a tiny tweak)? I don't understand the point of the second query – Strawberry Aug 23 '15 at 06:35
  • @Strawberry you're right, it potentially could. Since I originally have both formats in full datetime format, I figured I would just compare that way first, _then_ modify the array directly via reference. In an attempt to isolate the problem, we can probably forget the 2nd foreach loop exists. Is there an issue with the first one, that keeps it from throwing the conflicts flag when only the second existing event has a conflict? Thanks! – cdwyer Aug 23 '15 at 13:03
  • As an aside, I would love for down-voters to comment on why they vote down a question, and how they would suggest improving it. Just a thought. – cdwyer Aug 23 '15 at 13:03
  • I would suggest you forget about the PHP for now, and instead consider following this simple two-step course of action: 1. If you have not already done so, provide proper DDLs (and/or an sqlfiddle) so that we can more easily replicate the problem. 2. If you have not already done so, provide a desired result set that corresponds with the information provided in step 1. – Strawberry Aug 23 '15 at 13:28
  • @Strawberry, I've removed MySQL interaction, isolated the issue and basically rewrote the entire question. Hope that's helpful, and should make more sense now. Thanks! – cdwyer Aug 23 '15 at 18:47
  • @Strawberry About the only thing I can see or come up with...is it possible that the AM/PM in $requsted vs. the 24-hour approach in $existing is causing problems, when they're compared? – cdwyer Aug 23 '15 at 19:20
  • I think your entire approach is wrong. This is (or should be) at heart a database problem, but whatever floats your boat. – Strawberry Aug 23 '15 at 21:46
  • @Strawberry can you elaborate on that a little bit? A database connection is absolutely a necessary part of the project, I agree. But in an effort to isolate the problem, I'm just defining that data directly within an array. It really has nothing to do with what floats my boat, haha...I'm just looking for a solution, and the problem doesn't seem to have anything to do with the SQL component, as I'm having the same issue as before. I rewrote the question after simplifying the problem down and am fairly certain that it has something to do with the data format or the foreach loop itself. – cdwyer Aug 24 '15 at 01:45
  • @Strawberry Let me know if you have any thoughts on what could be causing it...I included a guess and lots of info in the comments I left above, as well as the question (again, thoroughly updated). Thanks! – cdwyer Aug 24 '15 at 01:45
  • @Strawberry as I thought, issue was within the foreach. Just realized that the `break`s needed to be `continue`s. Thanks for your help! – cdwyer Aug 24 '15 at 02:30

2 Answers2

0

I think this might be what you're looking for:

if (!empty($results['same_day'])){
    foreach ($results['same_day'] as $same_day){
        $exist_start = strtotime($same_day->time_start);
        $exist_end = strtotime($same_day->time_end);
        // Check if new meeting plans to start during existing meeting
        if ( ($requested_start > $exist_start) && ($requested_start < $exist_end) ) {
            $results["conflicts"]++;
            break;
        }
        // Check if new meeting plans to end during existing meeting
        if ( ($requested_end > $exist_start) && ($requested_end < $exist_end) ) {
            $results["conflicts"]++;
            break;
        }
        // Check if existing meeting starts during new meeting
        if ( ($exist_start > $requested_start) && ($exist_start < $requested_end) ) {
            $results["conflicts"]++;
            break;
        }
        // Check if existing meeting ends during new meeting
        if ( ($exist_end > $requested_start) && ($exist_end < $requested_end) ) {
            $results["conflicts"]++;
            break;
        }
    }
}

Full code I used to test:

<?php

$a = new stdClass();
$b = new stdClass();

$a->time_start = "2015-09-05 11:30:00";
$a->time_end = "2015-09-05 18:45:00";

$b->time_start = "2015-09-05 07:15:00";
$b->time_end = "2015-09-05 10:30:00";

$results['same_day'] = array( $a, $b );
$results['conflicts'] = 0;

$requested_start = strtotime( "2015-09-05 03:00" );
$requested_end = strtotime( "2015-09-05 11:00" );


if (!empty($results['same_day'])){
    foreach ($results['same_day'] as $same_day){
        $exist_start = strtotime($same_day->time_start);
        $exist_end = strtotime($same_day->time_end);
        // Check if new meeting plans to start during existing meeting
        if ( ($requested_start > $exist_start) && ($requested_start < $exist_end) ) {
            $results["conflicts"]++;
            break;
        }
        // Check if new meeting plans to end during existing meeting
        if ( ($requested_end > $exist_start) && ($requested_end < $exist_end) ) {
            $results["conflicts"]++;
            break;
        }
        // Check if existing meeting starts during new meeting
        if ( ($exist_start > $requested_start) && ($exist_start < $requested_end) ) {
            $results["conflicts"]++;
            break;
        }
        // Check if existing meeting ends during new meeting
        if ( ($exist_end > $requested_start) && ($exist_end < $requested_end) ) {
            $results["conflicts"]++;
            break;
        }
    }
}

var_dump( $results );

?>
Patrick Moore
  • 13,251
  • 5
  • 38
  • 63
  • Appreciate that suggestion! For reference, here's [the logic behind my "start-after OR end-before" approach](http://stackoverflow.com/a/325964/1535999). I feel like that should work/apply in this situation, since I'm only comparing one existing event to one proposed event at a time. Thoughts on that? – cdwyer Aug 23 '15 at 13:08
  • @cdwyer makes sense, but your if() elseif() block does not compare one AND the other together. It compares one separately OR the other separately, which is why it's not failing (reporting conflict). – Patrick Moore Aug 23 '15 at 18:47
  • I _think_ that I'm correct with this written-out summary of what that block does, because those comparisons should be exclusive, as you only need one to verify that you're clear. Like this: (( 1. )) First, if the requested start comes after the existing end, you're fine. New event won't start until the other ends, so new event end time is irrelevant. (( 2. )) Secondly, if the requested end comes before the existing start, you're fine. New event will end before the existing even starts, so new event start time is irrelevant. (( 3. )) If both are false, there has to be overlap somewhere. – cdwyer Aug 23 '15 at 18:54
  • Also, I pretty much overhauled my question above. Have isolated the issue (I think) to the foreach itself, and not what happens inside. The `comparison_counter` isn't showing two loops, despite the fact that the array being passed to foreach **does** have two items. Let me know if you have other thoughts on that! – cdwyer Aug 23 '15 at 18:56
  • Did you try my code? Does it not yield the expected result? – Patrick Moore Aug 23 '15 at 21:31
  • `break` vs. `continue` was the issue within the foreach; the if statements don't really need to be tweaked, as they're ultimately checking the same things. But yes, yours would have worked. Thanks for your help! – cdwyer Aug 24 '15 at 02:29
0

This should have been a much more immediate realization than it was. If you use break, you're basically saying "I found what I want, quit the foreach entirely." If you use a continue, it just drops out of the current iteration (as I understand it) and continues (see what they did, there?) on to the next iteration. Found this "diagram" that explains it nicely and seems to apply to foreach loops, not just while:

while ($foo) {   <--------------------┐
    continue;    --- goes back here --┘
    break;       ----- jumps here ----┐
}                                     |
                 <--------------------┘

Hope this helps somebody else down the line!

Community
  • 1
  • 1
cdwyer
  • 589
  • 6
  • 22