Eventos recurrentes en FullCalendar
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í?
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:00
la 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
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.
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ón
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.
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 .