Eventos recurrentes en FullCalendar

Resuelto Junnel Gallemaso asked hace 11 años • 0 respuestas

Estoy usando jQuery FullCalendar como el calendario que uso en mi sitio web para la agenda de disponibilidad.

¿Existe alguna función/método/opción en el calendario completo que maneje mis eventos recurrentes por días? Por ejemplo, los lunes solo de 7:00 a. m. a 9:00 a. m., martes, de 4:00 p. m. a 9:00 p. m., ¿algo así?

Junnel Gallemaso avatar Mar 01 '13 22:03 Junnel Gallemaso
Aceptado

Eventos repetitivos simples

Para agregar una alternativa simple a las enumeradas aquí, Fullcalendar ahora admite (en cierto modo) eventos recurrentes semanales. Entonces, si solo necesitas algo como: [Every Monday and Thursday from 10:00am to 02:00pm], puedes usar lo siguiente:

events: [{
    title:"My repeating event",
    start: '10:00', // a start time (10am in this example)
    end: '14:00', // an end time (2pm in this example)
    dow: [ 1, 4 ] // Repeat monday and thursday
}],

JSFiddle

Esto está documentado en Eventos en segundo plano , pero también funciona para eventos regulares.

Guardar esto en una base de datos no sería difícil.

Añade algunas restricciones

Si no desea que se repitan infinitamente, deberá agregar algunas fechas de inicio y finalización.

Entonces, en la base de datos:

  • Deje que el evento que se muestra arriba represente el registro principal
  • Tenga otra tabla con fechas de inicio/finalización.
  • Ejemplo de tabla unida:

eventId  timeStart  timeEnd   dow    dateStart      dateEnd
     1      10:00    12:00  [1,4]  2015/03/01   2015/04/01  // Month of March
     1      10:00    12:00  [1,4]  2015/05/01   2015/06/01  // Month of May
     1      10:00    12:00  [1,4]  2016/01/01   2017/01/01  // Year of 2017

Pase esto al cliente como JSON:

{ id:1, start:"10:00", end:"12:00", dow:[1,4],
  ranges[{start:"2015/03/01", end:"2015/04/01"},
         {start:"2015/05/01", end:"2015/06/01"},
         {start:"2016/01/01", end:"2017/01/01"},]
}

Y del lado del cliente, use eventRender de fullcalendar para representar eventos solo cuando estén dentro de uno de los rangos de tiempo. Algo como esto debería funcionar:

eventRender: function(event){
    return (event.ranges.filter(function(range){ // test event against all the ranges

        return (event.start.isBefore(range.end) &&
                event.end.isAfter(range.start));

    }).length)>0; //if it isn't in one of the ranges, don't render it (by returning false)
},

Eso supone que sus eventos están estructurados como:

var repeatingEvents = [{
    title:"My repeating event",
    id: 1,
    start: '10:00', 
    end: '14:00', 
    dow: [ 1, 4 ], 
    ranges: [{ //repeating events are only displayed if they are within at least one of the following ranges.
        start: moment().startOf('week'), //next two weeks
        end: moment().endOf('week').add(7,'d'),
    },{
        start: moment('2015-02-01','YYYY-MM-DD'), //all of february
        end: moment('2015-02-01','YYYY-MM-DD').endOf('month'),
    },/*...other ranges*/],
},/*...other repeating events*/];

JSFiddle


Durante la noche

En caso de que desee que se repitan eventos durante la noche ( como aquí ), simplemente consulte 24:00la hora de finalización. Por ejemplo:

{
  start: '10:00', //starts at 10 on monday
  end:   '27:00', //24+3 is handled correctly.
  dow: [1]
}

JSFiddle

DanielST avatar Apr 01 '2015 14:04 DanielST

Echa un vistazo a este sitio... http://fajitanachos.com/Fullcalendar-and-recurring-events/

Ofrece mucha información útil sobre eventos recurrentes. FullCalendar admite eventos recurrentes con respecto a la identificación. Puede manejar los eventos en el lado del servidor o en el lado del cliente, pero la preferencia sería el lado del servidor. Te daré algunas ideas, pero no lo incluye todo. Como he aprendido, los eventos recurrentes son difíciles de mantener.

Si quisiera manejarlos del lado del cliente, tendría que recorrer la frecuencia del evento repetido y la lógica de qué días. Probablemente necesite usar la devolución de llamada eventRender y luego representar cada evento en bucle usando la devolución de llamada de opciones. El problema con esto será que aún tendrás que guardar la frecuencia recurrente y un operador lógico para tu opción de frecuencia en tu base de datos...

(columna1:frecuencia=(int)8, columna2:tipo=enum(a'b'c), a=diario, b=semanal, c=mensual, etc.).

...y luego, cada vez que editaras ese evento, se editarían todos los eventos. Si necesitara eliminar solo un evento, se encontraría con una serie de problemas dentro de su lógica y fácilmente podría convertirse en un desastre GIGANTE.

La segunda opción era hacer todo esto del lado del servidor. Creando dos tablas, una con el evento padre y la segunda con todas sus recurrencias. En la tabla principal, almacenaría la información general, como una identificación única, color, color de fondo, título, todo el día, es recurrente, frecuencia, tipo, etc. En la tabla secundaria, usaría la identificación única de la tabla principal para asociar cada recurrencia (tenga en cuenta que si desea eliminar/editar eventos individuales, las filas de la tabla secundaria también deben tener su propia identificación única y una columna que etiquete en qué tabla se encuentra). Cuando agrega un evento recurrente, necesita agregar un campo de enumeración que etiquete si es un evento recurrente o no, también conocido como...

columna:recurrente=enum('0','1')---verdadero/falso

... y luego debe agregar cada recurrencia en la tabla secundaria con su información específica como inicio y fin, etc. Cuando consulta el evento, puede consultarlo desde el padre y luego, si el evento es recurrente, obtener esos eventos asociados en una segunda consulta, o puede usar INNER JOIN en table1.id=table2.parentID en una sola consulta.

Como puede ver, un evento recurrente puede volverse muy detallado muy rápidamente. Descubra qué lógica necesita y espero que esto le ayude a usted o a alguien al menos a empezar. Salud.

Juan Gonzales avatar Mar 04 '2013 16:03 Juan Gonzales

No es necesario establecer una relación entre padres e hijos. Aquí está el código que proporciona una solución simple para eventos recurrentes en jquery. El calendario completo usa estas funciones a continuación en su archivo php que usará más para llamar a todos sus eventos.

function render_fccalendar_events() {
        $_POST['start'] = strtotime('2013-05-01');
        $_POST['end'] = strtotime('2013-05-31');
        $start = date('Y-m-d',$_POST['start']);
        $end = date('Y-m-d', $_POST['end']);
        $readonly = (isset($_POST['readonly'])) ? true : false;    
        $events = fcdb_query_events($start, $end);       
        render_json(process_events($events, $start, $end, $readonly));
}

function process_events($events, $start, $end, $readonly) {
    if ($events) {
        $output = array();
        foreach ($events as $event) {
            $event->view_start = $start;
            $event->view_end = $end;
            $event = process_event($event, $readonly, true);
            if (is_array($event)) {
                foreach ($event as $repeat) {
                    array_push($output, $repeat);
                }
            } else {
                array_push($output, $event);
            }
        }
        return $output;
    }
}

function process_event($input, $readonly = false, $queue = false) {
    $output = array();
    if ($repeats = generate_repeating_event($input)) {
        foreach ($repeats as $repeat) {
            array_push($output, generate_event($repeat));
        }
    } else {
        array_push($output, generate_event($input));
    }

    if ($queue) {
        return $output;
    }
    render_json($output);
}


function generate_event($input) {
    $output = array(
        'id' => $input->id,
        'title' => $input->name,
        'start' => $input->start_date,
        'end' => $input->end_date,
        'allDay' => ($input->allDay) ? true : false,
        //'className' => "cat{$repeats}",
        'editable' => true,
        'repeat_i' => $input->repeat_int,
        'repeat_f' => $input->repeat_freq,
        'repeat_e' => $input->repeat_end
    );
    return $output;
}



function generate_repeating_event($event) {

    $repeat_desk = json_decode($event->repeat_desk);
    if ($event->repeat == "daily") {
        $event->repeat_int =0;
        $event->repeat_freq = $repeat_desk->every_day;
    }
    if ($event->repeat == "monthly") {
        $event->repeat_int =2;        
        $event->repeat_freq = $repeat_desk->every_month;
    }
    if ($event->repeat == "weekly") {
        $event->repeat_int =1;                
       $event->repeat_freq = $repeat_desk->every_weak;
    }
    if ($event->repeat == "year") {
        $event->repeat_int =3;                        
        $event->repeat_freq = $repeat_desk->every_year;
    }

    if ($event->occurrence == "after-no-of-occurrences") {
        if($event->repeat_int == 0){
            $ext = "days";
        }
        if($event->repeat_int == 1){
            $ext = "weeks";
        }
        if($event->repeat_int == 2){
            $ext = "months";
        }
        if($event->repeat_int == 3){
            $ext = "years";
        }
       $event->repeat_end =  date('Y-m-d',strtotime("+" . $event->repeat_int . " ".$ext));
    } else if ($event->occurrence == "no-end-date") {
        $event->repeat_end = "2023-04-13";
    } else if ($event->occurrence == "end-by-end-date") {
        $event->repeat_end = $event->end_date;
    }



    if ($event->repeat_freq) {

        $event_start = strtotime($event->start_date);
        $event_end = strtotime($event->end_date);
        $repeat_end = strtotime($event->repeat_end) + 86400;
        $view_start = strtotime($event->view_start);
        $view_end = strtotime($event->view_end);
        $repeats = array();

        while ($event_start < $repeat_end) {
            if ($event_start >= $view_start && $event_start <= $view_end) {
                $event = clone $event; // clone event details and override dates
                $event->start_date = date(AEC_DB_DATETIME_FORMAT, $event_start);
                $event->end_date = date(AEC_DB_DATETIME_FORMAT, $event_end);
                array_push($repeats, $event);
            }
            $event_start = get_next_date($event_start, $event->repeat_freq, $event->repeat_int);
            $event_end = get_next_date($event_end, $event->repeat_freq, $event->repeat_int);
        }
        return $repeats;
    }
    return false;
 }

function get_next_date($date, $freq, $int) {
    if ($int == 0)
        return strtotime("+" . $freq . " days", $date);
    if ($int == 1)
        return strtotime("+" . $freq . " weeks", $date);
    if ($int == 2)
        return get_next_month($date, $freq);
    if ($int == 3)
        return get_next_year($date, $freq);
}

function get_next_month($date, $n = 1) {
    $newDate = strtotime("+{$n} months", $date);
    // adjustment for events that repeat on the 29th, 30th and 31st of a month
    if (date('j', $date) !== (date('j', $newDate))) {
        $newDate = strtotime("+" . $n + 1 . " months", $date);
    }
    return $newDate;
}

function get_next_year($date, $n = 1) {
    $newDate = strtotime("+{$n} years", $date);
    // adjustment for events that repeat on february 29th
    if (date('j', $date) !== (date('j', $newDate))) {
        $newDate = strtotime("+" . $n + 3 . " years", $date);
    }
    return $newDate;
}

function render_json($output) {
    header("Content-Type: application/json");
    echo json_encode(cleanse_output($output));
    exit;
}


function cleanse_output($output) {
    if (is_array($output)) {
        array_walk_recursive($output, create_function('&$val', '$val = trim(stripslashes($val));'));
    } else {
        $output = stripslashes($output);
    }
    return $output;
}

function fcdb_query_events($start, $end) {
    global $wpdb;
    $limit = ($limit) ? " LIMIT {$limit}" : "";
    $result = $wpdb->get_results("SELECT id, name,start_date,end_date,repeat_desk,`repeat`,occurrence,occurrence_desk

                                        FROM " . 

$wpdb->prefix . "lgc_events
                                        WHERE (
                                        (start_date >= '{$start}' AND start_date < '{$end}')
                                        OR (end_date >= '{$start}' AND end_date < '{$end}')
                                        OR (start_date <= '{$start}' AND end_date >= '{$end}')
                                        OR (start_date < '{$end}' AND (`repeat`!= ''))


                            )
                                        ORDER BY start_date{$limit};");

    return return_result($result);
}


function return_result($result) {
    if ($result === false) {
        global $wpdb;
        $this->log($wpdb->print_error());
        return false;
    }
    return $result;
}

en el código anterior utilicé repetir_desk en el que almaceno código json para la frecuencia de repeticióningrese la descripción de la imagen aquí

y jquery para llamar a tu archivo

 events:  {
                url: '<?php echo $lgc_plugindir; ?>includes/imagerotator.php',
                data: {
                    action: 'get_events'
                },
                type: 'POST'
            }

Usé esto para WordPress, puedes usar este código según tus requisitos.

Akshay Jindal avatar May 21 '2013 15:05 Akshay Jindal

Para las personas que tienen eventos recurrentes más complejos de los que FullCalendar puede manejar de forma integrada ( consulte la respuesta de slicedtoad ), pueden usar rSchedule .

Por ejemplo, lunes solo de 7:00 a. m. a 9:00 a. m., martes, de 4:00 p. m. a 9:00 p. m.

import { Schedule } from '@rschedule/rschedule';
import { StandardDateAdapter } from '@rschedule/standard-date-adapter';

const mondayDate = new Date(2019, 6, 15);
const tuesdayDate = new Date(2019, 6, 16);

const schedule = new Schedule({
  // add specific dates
  dates: [
    new StandardDateAdapter(mondayDate, {duration: 1000 * 60 * 60 * 2})
  ],
  // add recurrence rules
  rrules: [{
    start: tuesdayDate,
    duration: 1000 * 60 * 60 * 5, // duration is expressed in milliseconds
    frequency: 'WEEKLY'
  }],
});

const firstFiveEvents = schedule
  .occurrences({ take: 5 })
  .toArray()
  .map(adapter => 
    ({title: 'My event title', start: adapter.date, end: adapter.end})
  );

// You can then pass `firstFiveEvents` to fullcalendar for rendering

rSchedule también admite moment/ luxon, así como zonas horarias. Para obtener más información, puede consultar los documentos de rSchedule .

John avatar Jul 16 '2019 08:07 John