Calendrier des événements récurrents / répétitifs - Meilleure méthode de stockage


311

Je crée un système d'événements personnalisé, et si vous avez un événement répétitif qui ressemble à ceci:

L'événement A se répète tous les 4 jours à partir du 3 mars 2011

ou

L'événement B se répète toutes les 2 semaines le mardi à partir du 1er mars 2011

Comment puis-je stocker cela dans une base de données d'une manière qui rendrait la recherche simple. Je ne veux pas de problèmes de performances s'il y a un grand nombre d'événements, et je dois passer en revue chacun d'eux lors du rendu du calendrier.


Pouvez-vous expliquer pourquoi 1299132000 est codé en dur? Que cela fera-t-il si j'ai besoin d'obtenir les dates d'occurrence et l'utilisateur pour la date de fin donnée?
Murali Murugesan

@Murali Boy c'est vieux, mais je suis presque sûr que 1299132000 est censé être la date actuelle.
Brandon Wamboldt

@BrandonWamboldt, j'ai essayé votre idée avec SQL Server. stackoverflow.com/questions/20286332/display-next-event-date . Je veux trouver tous les prochains articles comme la version c #
Billa

Réponses:


211

Stockage de motifs répétitifs "simples"

Pour mon calendrier basé sur PHP / MySQL, je voulais stocker les informations sur les événements récurrents / récurrents aussi efficacement que possible. Je ne voulais pas avoir un grand nombre de lignes et je voulais facilement rechercher tous les événements qui auraient lieu à une date spécifique.

La méthode ci-dessous est idéale pour stocker des informations répétitives qui se produisent à intervalles réguliers, tels que tous les jours, tous les n jours, chaque semaine, chaque mois chaque année, etc. etc. Cela inclut également les modèles de type tous les mardis et jeudis, car ils sont stockés séparément comme chaque semaine à partir d'un mardi et chaque semaine à partir d'un jeudi.

En supposant que j'ai deux tables, une appelée eventscomme ceci:

ID    NAME
1     Sample Event
2     Another Event

Et une table appelée events_metacomme ceci:

ID    event_id      meta_key           meta_value
1     1             repeat_start       1299132000
2     1             repeat_interval_1  432000

Avec repeat_start étant une date sans heure comme horodatage Unix, et repeat_interval un montant en secondes entre les intervalles (432000 est de 5 jours).

repeat_interval_1 va avec repeat_start de l'ID 1. Donc, si j'ai un événement qui se répète tous les mardis et tous les jeudis, le repeat_interval serait 604800 (7 jours), et il y aurait 2 repeat_starts et 2 repeat_intervals. Le tableau ressemblerait à ceci:

ID    event_id      meta_key           meta_value
1     1             repeat_start       1298959200 -- This is for the Tuesday repeat
2     1             repeat_interval_1  604800
3     1             repeat_start       1299132000 -- This is for the Thursday repeat
4     1             repeat_interval_3  604800
5     2             repeat_start       1299132000
6     2             repeat_interval_5  1          -- Using 1 as a value gives us an event that only happens once

Ensuite, si vous avez un calendrier qui passe tous les jours, saisissant les événements du jour où il se trouve, la requête ressemblera à ceci:

SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
RIGHT JOIN `events_meta` EM2 ON EM2.`meta_key` = CONCAT( 'repeat_interval_', EM1.`id` )
WHERE EM1.meta_key = 'repeat_start'
    AND (
        ( CASE ( 1299132000 - EM1.`meta_value` )
            WHEN 0
              THEN 1
            ELSE ( 1299132000 - EM1.`meta_value` )
          END
        ) / EM2.`meta_value`
    ) = 1
LIMIT 0 , 30

Remplacement {current_timestamp}par l'horodatage Unix pour la date actuelle (moins l'heure, de sorte que les valeurs d'heure, de minute et de seconde soient définies sur 0).

J'espère que cela aidera quelqu'un d'autre aussi!


Stockage de motifs répétitifs "complexes"

Cette méthode est mieux adaptée au stockage de motifs complexes tels que

Event A repeats every month on the 3rd of the month starting on March 3, 2011

ou

Event A repeats Friday of the 2nd week of the month starting on March 11, 2011

Je recommanderais de combiner cela avec le système ci-dessus pour plus de flexibilité. Les tableaux pour cela devraient comme:

ID    NAME
1     Sample Event
2     Another Event

Et une table appelée events_metacomme ceci:

ID    event_id      meta_key           meta_value
1     1             repeat_start       1299132000 -- March 3rd, 2011
2     1             repeat_year_1      *
3     1             repeat_month_1     *
4     1             repeat_week_im_1   2
5     1             repeat_weekday_1   6

repeat_week_imreprésente la semaine du mois en cours, qui pourrait être comprise entre 1 et 5 potentiellement. repeat_weekdayle jour de la semaine, 1-7.

En supposant maintenant que vous parcourez les jours / semaines pour créer une vue mensuelle dans votre calendrier, vous pouvez composer une requête comme celle-ci:

SELECT EV . *
FROM `events` AS EV
JOIN `events_meta` EM1 ON EM1.event_id = EV.id
AND EM1.meta_key = 'repeat_start'
LEFT JOIN `events_meta` EM2 ON EM2.meta_key = CONCAT( 'repeat_year_', EM1.id )
LEFT JOIN `events_meta` EM3 ON EM3.meta_key = CONCAT( 'repeat_month_', EM1.id )
LEFT JOIN `events_meta` EM4 ON EM4.meta_key = CONCAT( 'repeat_week_im_', EM1.id )
LEFT JOIN `events_meta` EM5 ON EM5.meta_key = CONCAT( 'repeat_weekday_', EM1.id )
WHERE (
  EM2.meta_value =2011
  OR EM2.meta_value = '*'
)
AND (
  EM3.meta_value =4
  OR EM3.meta_value = '*'
)
AND (
  EM4.meta_value =2
  OR EM4.meta_value = '*'
)
AND (
  EM5.meta_value =6
  OR EM5.meta_value = '*'
)
AND EM1.meta_value >= {current_timestamp}
LIMIT 0 , 30

Ceci combiné avec la méthode ci-dessus pourrait être combiné pour couvrir la plupart des modèles d'événements répétitifs / récurrents. Si j'ai oublié quelque chose, veuillez laisser un commentaire.


1
J'essaye vos modèles répétitifs «simples» de stockage. si j'ai besoin qu'il se répète chaque semaine le mardi, dois-je modifier la fonction repeat_start ou créer un nouvel enregistrement avec la dernière date. ou existe-t-il un moyen de répéter chaque semaine en fonction du premier repeat_start ???
loo

1
Votre réponse a été d'une grande aide @roguecoder mais cela n'a pas tout à fait fonctionné au départ ... J'ai trouvé ma réponse après ce post: stackoverflow.com/questions/10545869/…
Ben Sinclair

1
En AND ( ( CASE ( 1299132000 - EM1.meta_value ) WHEN 0 THEN 1 ELSE ( 1299132000 - EM1.meta_value) END ) / EM2.meta_value ) = 1est-il / EM2.meta_valuemal placé?
Murali Murugesan

1
Ceci est d'une grande aide. Comment suggéreriez-vous d'y accéder en tant qu'enregistrements individuels, par exemple, si vous vouliez avoir des commentaires ou des enregistrements sur des événements individuels?
johnrees

26
Il convient de noter que vous ne devez pas utiliser de valeurs codées en dur pour les intervalles de répétition, c'est-à-dire les 86400secondes dans une journée, car cela ne prend pas en compte l'heure d'été. Il est plus approprié de calculer ces choses dynamiquement à la volée et de stocker à la place interval = dailyet interval_count = 1ou interval = monthlyet interval_count = 1.
Corey Ballou

185

Bien que la réponse actuellement acceptée m'a été d'une grande aide, je voulais partager quelques modifications utiles qui simplifient les requêtes et augmentent également les performances.


Événements de répétition "simples"

Pour gérer des événements qui se reproduisent à intervalles réguliers, tels que:

Repeat every other day 

ou

Repeat every week on Tuesday 

Vous devez créer deux tables, une appelée eventscomme ceci:

ID    NAME
1     Sample Event
2     Another Event

Et une table appelée events_metacomme ceci:

ID    event_id      repeat_start       repeat_interval
1     1             1369008000         604800            -- Repeats every Monday after May 20th 2013
1     1             1369008000         604800            -- Also repeats every Friday after May 20th 2013

Étant repeat_startune date d'horodatage Unix sans heure (1369008000 correspond au 20 mai 2013), et repeat_intervalun montant en secondes entre les intervalles (604800 est 7 jours).

En parcourant chaque jour du calendrier, vous pouvez obtenir des événements répétés à l'aide de cette simple requête:

SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
WHERE  (( 1299736800 - repeat_start) % repeat_interval = 0 )

Remplacez simplement l'horodatage unix (1299736800) pour chaque date de votre calendrier.

Notez l'utilisation du modulo (signe%). Ce symbole est comme une division régulière, mais renvoie le '' reste '' au lieu du quotient, et en tant que tel est 0 chaque fois que la date actuelle est un multiple exact de repeat_interval de repeat_start.

Comparaison des performances

Ceci est beaucoup plus rapide que la réponse basée sur les "méta-clés" précédemment suggérée, qui était la suivante:

SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
RIGHT JOIN `events_meta` EM2 ON EM2.`meta_key` = CONCAT( 'repeat_interval_', EM1.`id` )
WHERE EM1.meta_key = 'repeat_start'
    AND (
        ( CASE ( 1299132000 - EM1.`meta_value` )
            WHEN 0
              THEN 1
            ELSE ( 1299132000 - EM1.`meta_value` )
          END
        ) / EM2.`meta_value`
    ) = 1

Si vous exécutez EXPLAIN cette requête, vous remarquerez qu'elle nécessitait l'utilisation d'un tampon de jointure:

+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref              | rows | Extra                          |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
|  1 | SIMPLE      | EM1   | ALL    | NULL          | NULL    | NULL    | NULL             |    2 | Using where                    |
|  1 | SIMPLE      | EV    | eq_ref | PRIMARY       | PRIMARY | 4       | bcs.EM1.event_id |    1 |                                |
|  1 | SIMPLE      | EM2   | ALL    | NULL          | NULL    | NULL    | NULL             |    2 | Using where; Using join buffer |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+

La solution avec 1 jointure ci-dessus ne nécessite pas un tel tampon.


Modèles "complexes"

Vous pouvez ajouter la prise en charge de types plus complexes pour prendre en charge ces types de règles de répétition:

Event A repeats every month on the 3rd of the month starting on March 3, 2011

ou

Event A repeats second Friday of the month starting on March 11, 2011

Votre table d'événements peut avoir exactement la même apparence:

ID    NAME
1     Sample Event
2     Another Event

Ensuite, pour ajouter la prise en charge de ces règles complexes, ajoutez des colonnes events_metacomme ceci:

ID    event_id      repeat_start       repeat_interval    repeat_year    repeat_month    repeat_day    repeat_week    repeat_weekday
1     1             1369008000         604800             NULL           NULL            NULL          NULL           NULL             -- Repeats every Monday after May 20, 2013
1     1             1368144000         604800             NULL           NULL            NULL          NULL           NULL             -- Repeats every Friday after May 10, 2013
2     2             1369008000         NULL               2013           *               *             2              5                -- Repeats on Friday of the 2nd week in every month    

Notez que vous simplement devez spécifier un repeat_interval ou un ensemble de repeat_year, repeat_month, repeat_day, repeat_weeket les repeat_weekdaydonnées.

Cela rend la sélection des deux types simultanément très simple. Parcourez chaque jour et remplissez les valeurs correctes (1370563200 pour le 7 juin 2013, puis l'année, le mois, le jour, le numéro de semaine et le jour de la semaine comme suit):

SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
WHERE  (( 1370563200 - repeat_start) % repeat_interval = 0 )
  OR ( 
    (repeat_year = 2013 OR repeat_year = '*' )
    AND
    (repeat_month = 6 OR repeat_month = '*' )
    AND
    (repeat_day = 7 OR repeat_day = '*' )
    AND
    (repeat_week = 2 OR repeat_week = '*' )
    AND
    (repeat_weekday = 5 OR repeat_weekday = '*' )
    AND repeat_start <= 1370563200
  )

Cela renvoie tous les événements qui se répètent le vendredi de la 2e semaine, ainsi que tous les événements qui se répètent tous les vendredis, donc il renvoie les ID d'événement 1 et 2:

ID    NAME
1     Sample Event
2     Another Event

* Sidenote dans le SQL ci-dessus, j'ai utilisé les index de jour par défaut de PHP Date , donc "5" pour vendredi


J'espère que cela aide les autres autant que la réponse originale m'a aidé!


6
C'est incroyable, merci! Avez-vous une idée de comment vous encoderiez "tous les 2 mois le premier lundi" ou "tous les 3 mois le premier lundi", etc.?
Jordan Lev

6
Je suis d'accord, c'est incroyable. Cependant, je suis tombé sur le même dilemme que Jordan Lev. Le champ repeat_interval n'est pas bon pour répéter des mois car certains mois sont plus longs que d'autres. En outre, comment limiter la durée d'un événement récurrent. C'est-à-dire, tous les 2 mois le premier lundi pendant 8 mois. Le tableau doit avoir une sorte de date de fin.
Abinadi

11
Ceci est une excellente réponse. J'ai supprimé le repeat_interval et ajouté la date de repeat_end, mais cette réponse a énormément aidé.
Iain Collins

3
Conseil: pour les modèles complexes, on pourrait éliminer la repeat_intervalcolonne et la représenter dans les colonnes suivantes (c.-à repeat_year- d. , Etc.) Pour la première ligne, la situation de répétition tous les lundis après le 20 mai 2013 peut être représentée en plaçant un 1 dans le repeat_weekdayet un *dans les autres colonnes.
musubi

3
@OlivierMATROT @milos L'idée est de définir explicitement le champ que vous souhaitez corriger, et le reste sur le caractère générique *. Donc, pour "chaque mois le 3", vous venez de définir repeat_dayà 3, le reste des repeatchamps à * (laissez repeat_intervalnull), et définissez repeat_start sur le timecode unix du 3 mars 2011 pour être votre date d'ancrage.
ahoffner

28

Amélioration: remplacer l'horodatage par la date

Comme une petite amélioration à la réponse acceptée qui a ensuite été affinée par ahoffner - il est possible d'utiliser un format de date plutôt qu'un horodatage. Les avantages sont:

  1. dates lisibles dans la base de données
  2. aucun problème avec les années> 2038 et l'horodatage
  3. les suppressions doivent être prudentes avec les horodatages basés sur des dates corrigées des variations saisonnières, c'est-à-dire qu'au Royaume-Uni, le 28 juin commence une heure plus tôt que le 28 décembre.

pour ce faire, modifiez la base de données repeat_startà stocker en tant que type 'date' et repeat_intervalmaintenez maintenant les jours plutôt que les secondes. soit 7 pour une répétition de 7 jours.

changer la ligne sql:

WHERE (( 1370563200 - repeat_start) % repeat_interval = 0 )

à:

WHERE ( DATEDIFF( '2013-6-7', repeat_start ) % repeat_interval = 0)

tout le reste reste le même. Simples!


Et si je veux que mon événement se répète d'année en année? repeat_interval devrait stocker 365 jours? Et si leur année compte 366 jours?
TGeorge

3
@ George02 si l'événement est annuel, vous laissez repeat_interval NULL et repeat_year est *, puis en fonction de la récurrence, vous pouvez définir repeat_month et repeat_day, par exemple le 11 mars ou repeat_month, repeat_week et repeat_weekday pour définir le 2e mardi d'avril.
jerrygarciuh

25

Je suivrais ce guide: https://github.com/bmoeskau/Extensible/blob/master/recurrence-overview.md

Assurez-vous également que vous utilisez le format iCal afin de ne pas réinventer la roue et n'oubliez pas la règle n ° 0: Ne stockez PAS les instances d'événements récurrents individuels sous forme de lignes dans votre base de données!


2
Comment modéliseriez-vous un suivi des utilisateurs qui ont assisté à une instance spécifique? Est-il judicieux de rompre avec la règle n ° 0 dans ce cas?
Danny Sullivan

2
@DannySullivan Du haut de ma tête, j'aurais une autre entité attendedEventavec baseInstanceIdet instanceStartDate- C'est par exemple l'événement de base à partir duquel vous avez créé la vue de calendrier des règles récurrentes et utilisez la date de début pour spécifier des informations sur cette instance spécifique. Cette entité pourrait également avoir quelque chose comme attendedListIdqui mène à une autre table de id,attendedUserId
Gal Bracha

@DannySullivan Je sais que ça fait un moment que tu ne l'as pas demandé. Mais en dehors du commentaire précédent, vous pouvez toujours effectuer une recherche inversée pour voir si cet utilisateur faisait partie du modèle de récurrence des événements. Cela vous dirait s'ils étaient au moins programmés pour l'événement. Qu'ils aient réellement assisté ou non est une histoire différente qui serait plus dans le sens du commentaire de DannySullivan.
BRogers

24

Pour tous ceux qui sont intéressés par cela, il vous suffit maintenant de copier-coller pour commencer en quelques minutes. J'ai pris les conseils dans les commentaires aussi bien que possible. Faites-moi savoir si je manque quelque chose.

"VERSION COMPLEXE":

événements

+ ---------- + ---------------- +
| ID | NOM |
+ ---------- + ---------------- +
| 1 | Exemple d'événement 1 |
| 2 | Deuxième événement |
| 3 | Troisième événement |
+ ---------- + ---------------- +

events_meta

+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +
| ID | event_id | repeat_start | repeat_interval | repeat_year | repeat_month | repeat_day | repeat_week | repeat_weekday |
+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +
| 1 | 1 | 2014-07-04 | 7 | NULL | NULL | NULL | NULL | NULL |
| 2 | 2 | 2014-06-26 | NULL | 2014 | * | * | 2 | 5 |
| 3 | 3 | 2014-07-04 | NULL | * | * | * | * | 5 |
+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +

Code SQL:

CREATE TABLE IF NOT EXISTS `events` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `NAME` varchar(255) NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;

--
-- Dumping data for table `events`
--

INSERT INTO `events` (`ID`, `NAME`) VALUES
(1, 'Sample event'),
(2, 'Another event'),
(3, 'Third event...');

CREATE TABLE IF NOT EXISTS `events_meta` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `event_id` int(11) NOT NULL,
  `repeat_start` date NOT NULL,
  `repeat_interval` varchar(255) NOT NULL,
  `repeat_year` varchar(255) NOT NULL,
  `repeat_month` varchar(255) NOT NULL,
  `repeat_day` varchar(255) NOT NULL,
  `repeat_week` varchar(255) NOT NULL,
  `repeat_weekday` varchar(255) NOT NULL,
  PRIMARY KEY (`ID`),
  UNIQUE KEY `ID` (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;

--
-- Dumping data for table `events_meta`
--

INSERT INTO `events_meta` (`ID`, `event_id`, `repeat_start`, `repeat_interval`, `repeat_year`, `repeat_month`, `repeat_day`, `repeat_week`, `repeat_weekday`) VALUES
(1, 1, '2014-07-04', '7', 'NULL', 'NULL', 'NULL', 'NULL', 'NULL'),
(2, 2, '2014-06-26', 'NULL', '2014', '*', '*', '2', '5'),
(3, 3, '2014-07-04', 'NULL', '*', '*', '*', '*', '1');

également disponible en exportation MySQL (pour un accès facile)

Exemple de code PHP index.php:

<?php
    require 'connect.php';    

    $now = strtotime("yesterday");

    $pushToFirst = -11;
    for($i = $pushToFirst; $i < $pushToFirst+30; $i++)
    {
        $now = strtotime("+".$i." day");
        $year = date("Y", $now);
        $month = date("m", $now);
        $day = date("d", $now);
        $nowString = $year . "-" . $month . "-" . $day;
        $week = (int) ((date('d', $now) - 1) / 7) + 1;
        $weekday = date("N", $now);

        echo $nowString . "<br />";
        echo $week . " " . $weekday . "<br />";



        $sql = "SELECT EV.*
                FROM `events` EV
                RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
                WHERE ( DATEDIFF( '$nowString', repeat_start ) % repeat_interval = 0 )
                OR ( 
                    (repeat_year = $year OR repeat_year = '*' )
                    AND
                    (repeat_month = $month OR repeat_month = '*' )
                    AND
                    (repeat_day = $day OR repeat_day = '*' )
                    AND
                    (repeat_week = $week OR repeat_week = '*' )
                    AND
                    (repeat_weekday = $weekday OR repeat_weekday = '*' )
                    AND repeat_start <= DATE('$nowString')
                )";
        foreach ($dbConnect->query($sql) as $row) {
            print $row['ID'] . "\t";
            print $row['NAME'] . "<br />";
        }

        echo "<br /><br /><br />";
    }
?>

Exemple de code PHP connect.php:

<?
// ----------------------------------------------------------------------------------------------------
//                                       Connecting to database
// ----------------------------------------------------------------------------------------------------
// Database variables
$username = "";
$password = "";
$hostname = ""; 
$database = ""; 

// Try to connect to database and set charset to UTF8
try {
    $dbConnect = new PDO("mysql:host=$hostname;dbname=$database;charset=utf8", $username, $password);
    $dbConnect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

} catch(PDOException $e) {
    echo 'ERROR: ' . $e->getMessage();
}
// ----------------------------------------------------------------------------------------------------
//                                      / Connecting to database
// ----------------------------------------------------------------------------------------------------
?>

Le code php est également disponible ici (pour une meilleure lisibilité):
index.php
et
connect.php
Maintenant, la configuration devrait prendre quelques minutes. Pas des heures. :)


2
comment puis-je interroger pour obtenir tous les événements répétés dans une plage de dates .. c'est-à-dire pour obtenir tous les événements répétés entre le 2014-10-01 et le 2014-12-30. merci pour votre message
Well Wisher

@Wellwisher - répéter ... jusqu'à et table temporaire stackoverflow.com/questions/34407833/…
Brad Kent

1
@Alex Comment puis-je supprimer une occurrence d'instance d'un événement répété.
Pugazhenthi

1
Je sais que c'est un vieux fil mais pourquoi le type varchar sur les colonnes repeat_ *? Ne pourriez-vous pas utiliser un entier et une valeur négative au lieu de '*'?
Olivier MATROT

1
Merci pour le code. Je dois cependant remarquer que votre implémentation db / queries est un peu dérangeante et très inefficace. Par exemple, pourquoi utiliser varchar (255) pour de telles colonnes simples (comme @OlivierMATROT l'a mentionné, vous pouvez utiliser des entiers, et même si ce n'est pas le cas, pourquoi 255?). Et si vous répétez la requête 30 fois, pourquoi ne pas utiliser des instructions ou des procédures? Je dis juste pour le bien si quelqu'un est sur le point de mettre en œuvre cela.
Rony

15

Alors que les solutions proposées fonctionnent, j'essayais de l'implémenter avec le calendrier complet et cela nécessiterait plus de 90 appels de base de données pour chaque vue (car il charge le mois actuel, précédent et suivant), ce qui, je n'étais pas trop ravi.

J'ai trouvé une bibliothèque de récursivité https://github.com/tplaner/Lorsque vous stockez simplement les règles dans la base de données et une requête pour extraire toutes les règles pertinentes.

J'espère que cela aidera quelqu'un d'autre, car j'ai passé tant d'heures à essayer de trouver une bonne solution.

Edit: Cette bibliothèque est pour PHP


Je veux également utiliser fullcalendar. Comment Quand la bibliothèque pourrait-elle m'aider? Comment extraire des événements propoer?
piernik

@piernik - Je configurerais la bibliothèque comme dans la documentation et si vous rencontrez des problèmes spécifiques, ouvrez une nouvelle question sur stackoverflow avec le code que vous avez configuré et les problèmes que vous rencontrez. Je suis sûr que si vous mettez autant d'efforts chez certains membres, cela vous aidera.
Tim Ramsey

Je veux dire que l'utilisation de WhenVous devez stocker toutes les dates de recurcissement dans la base de données ou obtenir tous les événements de recurcissement et générer des dates dans php no dans la base de données. Ai-je raison?
piernik

@piernik Vous stockeriez la date initiale et la / les règle (s) dans la base de données et l'utiliser Whenpour générer toutes les dates - qui sont remplies à partir de la date / des règles stockées initiales.
Tim Ramsey

Ce n'est pas bon non plus - Vous ne pouvez pas en une seule commande mysql obtenir des événements corrects - Vous devez utiliser PHP pour cela. Merci quand même
piernik

14

Pourquoi ne pas utiliser un mécanisme similaire aux tâches cron d'Apache? http://en.wikipedia.org/wiki/Cron

Pour le calendrier \ la planification, j'utiliserais des valeurs légèrement différentes pour les "bits" pour tenir compte des événements de réapparition du calendrier standard - au lieu de [jour de la semaine (0 - 7), mois (1 - 12), jour du mois (1 - 31), heure (0 - 23), min (0 - 59)]

- J'utiliserais quelque chose comme [Année (répéter tous les N ans), mois (1 - 12), jour du mois (1 - 31), semaine du mois (1-5), jour de la semaine (0 - 7) ]

J'espère que cela t'aides.


6
Je pense que c'est trop d'options de jour de la semaine. 1-7 ou 0-6 semble plus précis.
Abinadi

2
Il est bon d'utiliser cron pour stocker la répétition. mais le problème est qu'il est très difficile à rechercher.
Stony

@Vladimir comment stockez-vous, tous les deux mardis (toutes les deux semaines le mardi)
julestruong

@julestruong ce site Web semble avoir la réponse: coderwall.com/p/yzzu5a/running-a-cron-job-every-other-week
Ashton Wiersdorf

cron a une expressivité limitée, car il est apatride (comparant simplement la date / heure actuelle / hypothericale à un modèle), en tant que tel, il ne peut pas représenter certains modèles commerciaux / humains courants tels que "tous les trois jours" ou "toutes les 7 heures", qui nécessitent de se souvenir de la dernière occurrence. Ce n'est pas évident; vous pourriez penser que vous dites simplement jour / 3 ou heure / 7 dans crontab, mais à la fin du mois / jour, vous avez des jours / heures "restants" qui sont inférieurs à 3 ou 7; avec des résultats catastrophiques possibles.
Jaime Guerrero

5

J'ai développé un langage de programmation ésotérique juste pour ce cas. La meilleure partie à ce sujet est qu'il est sans schéma et indépendant de la plate-forme. Il vous suffit d'écrire un programme de sélection, pour votre emploi du temps, dont la syntaxe est contrainte par l'ensemble de règles décrites ici -

https://github.com/tusharmath/sheql/wiki/Rules

Les règles sont extensibles et vous pouvez ajouter n'importe quel type de personnalisation en fonction du type de logique de répétition que vous souhaitez effectuer, sans vous soucier des migrations de schéma, etc.

Il s'agit d'une approche complètement différente et qui pourrait présenter certains inconvénients.


4

Cela ressemble beaucoup aux événements MySQL qui sont stockés dans les tables système. Vous pouvez regarder la structure et déterminer quelles colonnes ne sont pas nécessaires:

   EVENT_CATALOG: NULL
    EVENT_SCHEMA: myschema
      EVENT_NAME: e_store_ts
         DEFINER: jon@ghidora
      EVENT_BODY: SQL
EVENT_DEFINITION: INSERT INTO myschema.mytable VALUES (UNIX_TIMESTAMP())
      EVENT_TYPE: RECURRING
      EXECUTE_AT: NULL
  INTERVAL_VALUE: 5
  INTERVAL_FIELD: SECOND
        SQL_MODE: NULL
          STARTS: 0000-00-00 00:00:00
            ENDS: 0000-00-00 00:00:00
          STATUS: ENABLED
   ON_COMPLETION: NOT PRESERVE
         CREATED: 2006-02-09 22:36:06
    LAST_ALTERED: 2006-02-09 22:36:06
   LAST_EXECUTED: NULL
   EVENT_COMMENT:


3

@Rogue Coder

C'est bien!

Vous pouvez simplement utiliser l'opération modulo (MOD ou% dans mysql) pour rendre votre code simple à la fin:

Au lieu de:

AND (
    ( CASE ( 1299132000 - EM1.`meta_value` )
        WHEN 0
          THEN 1
        ELSE ( 1299132000 - EM1.`meta_value` )
      END
    ) / EM2.`meta_value`
) = 1

Faire:

$current_timestamp = 1299132000 ;

AND ( ('$current_timestamp' - EM1.`meta_value` ) MOD EM2.`meta_value`) = 1")

Pour aller plus loin, on pourrait inclure des événements qui ne se reproduisent pas pour toujours.

Quelque chose comme "repeat_interval_1_end" pour indiquer la date du dernier "repeat_interval_1" pourrait être ajouté. Cependant, cela rend la requête plus compliquée et je ne peux pas vraiment comprendre comment le faire ...

Peut-être que quelqu'un pourrait aider!


1

Les deux exemples que vous avez donnés sont très simples; ils peuvent être représentés comme un simple intervalle (le premier étant de quatre jours, le second de 14 jours). La façon dont vous modélisez cela dépendra entièrement de la complexité de vos récurrences. Si ce que vous avez ci-dessus est vraiment aussi simple que cela, enregistrez une date de début et le nombre de jours dans l'intervalle de répétition.

Si, toutefois, vous devez prendre en charge des éléments tels que

L'événement A se répète chaque mois le 3 du mois à partir du 3 mars 2011

Ou

L'événement A se répète le deuxième vendredi du mois à partir du 11 mars 2011

C'est un schéma beaucoup plus complexe.


1
J'ai fait ajouter les règles plus complexes que vous venez d'énoncer plus tard, mais pas pour l'instant. Comment pourrais-je modéliser la requête SQL pour obtenir des événements le 7 mars 2011, par exemple, afin qu'elle reçoive mon événement récurrent?
Brandon Wamboldt
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.