Permettre de discriminer la création de l'édition d'un objet dans $flux['args'] #4939

Open
opened 4 weeks ago by bricebou · 12 comments
bricebou commented 4 weeks ago

Bonjour,

Je propose ce ticket suite à des échanges sur IRC ces derniers jours.

Au cours du développement d'un plugin, je cherche à remplir une table de la base de données une fois la création d'un objet éditorial effectuée.
J'appelle donc le pipeline post_edition, histoire de récupérer aisément les valeurs saisie dans le formulaie d'édition de l'objet.

(Peut-être me rétorquerez-vous que je peux passer par le pipeline post_insertion puis effectuer une requête fetsel pour récupérer les champs souhaités -- sous réserve que j'ai bien saisi la documentation...)

Par contre, et si je ne m'abuse, il n'y a aucun moyen direct de détecter si l'objet existe déjà ou s'il s'agit d'une modification d'un objet existant.

Merci encore pour tout et par avance pour les retours ;)

Bonjour, Je propose ce ticket suite à des échanges sur IRC ces derniers jours. Au cours du développement d'un plugin, je cherche à remplir une table de la base de données une fois la création d'un objet éditorial effectuée. J'appelle donc le pipeline `post_edition`, histoire de récupérer aisément les valeurs saisie dans le formulaie d'édition de l'objet. (Peut-être me rétorquerez-vous que je peux passer par le pipeline post_insertion puis effectuer une requête fetsel pour récupérer les champs souhaités -- sous réserve que j'ai bien saisi la documentation...) Par contre, et si je ne m'abuse, il n'y a aucun moyen direct de détecter si l'objet existe déjà ou s'il s'agit d'une modification d'un objet existant. Merci encore pour tout et par avance pour les retours ;)

Faut préciser je pense :

  • le pipeline post_insertion est normalement fait pour ça : il est appelé justement pour chaque création d'un objet
  • SAUF QUE, depuis le départ, 99,9% des utilisations c'est qu'il est utilisé pour la création d'un objet totalement VIDE, juste pour récupérer le id_patate ! Et donc jamais avec les champs complets lors du premier remplissage à la création
  • et donc quand on veut les champs de la création et bah on est obligé de s'insérer toujours dans post_edition, et non pas dans post_insertion qui ne sert à rien du tout ou peu souvent

Sauf que, l'objet de ce ticket, c'est que lors du premier passage dans post_edition c'est-à-dire uniquement celui qui suit immédiatement post_insertion, et bah on a pas moyen de savoir que c'est la première fois.

Faut préciser je pense : - le pipeline post_insertion est normalement fait pour ça : il est appelé justement pour chaque *création* d'un objet - SAUF QUE, depuis le départ, 99,9% des utilisations c'est qu'il est utilisé pour la création d'un objet totalement VIDE, juste pour récupérer le id_patate ! Et donc jamais avec les champs complets lors du premier remplissage à la création - et donc quand on veut les champs *de la création* et bah on est obligé de s'insérer toujours dans post_edition, et non pas dans post_insertion qui ne sert à rien du tout ou peu souvent Sauf que, l'objet de ce ticket, c'est que lors du **premier passage** dans post_edition c'est-à-dire uniquement celui qui suit immédiatement post_insertion, et bah on a pas moyen de savoir que c'est la première fois.
Owner

C'est une bonne question, en fait actuellement je crois pas qu'on ait un point d'entrée où l'on soit sûr d'être tout à la fin du processus de création d'un objet.

Je ne sais pas si une indication supplémentaire dans post_edition ferait le job, puisque selon les objets, des fois ça ne passe pas là-dedans.

Le problème c'est en effet que la création d'un objet peut se faire en plusieurs étapes, avec des appels successifs à objet_inserer() et objet_modifier().
Pour la création d'un auteur, ça passe même plusieurs fois pas objet_modifier(), ce qui rend la détection encore plus ardue.

Pour reprendre et compléter les exemples de @rastapopoulos, du plus simple au plus complexe, selon les objets on peut avoir cela :

  1. un seul appel à objet_inserer() avec un $set complet : ok avec post_insertion
  2. appel à objet_inserer() vide puis un seul appel à objet_modifier() : possiblement ok avec post_insertion
  3. appel à objet_inserer() vide puis plusieurs appels à objet_modifier() : on l'a dans l'os :)

Bref, à mon avis conceptuellement il faudrait un point d'entrée complémentaire au pipeline post_insertion, mais où l'on soit vraiment sûr d'être à la fin du processus.

C'est une bonne question, en fait actuellement je crois pas qu'on ait un point d'entrée où l'on soit sûr d'être tout à la fin du processus de création d'un objet. Je ne sais pas si une indication supplémentaire dans post_edition ferait le job, puisque selon les objets, des fois ça ne passe pas là-dedans. Le problème c'est en effet que la création d'un objet peut se faire en plusieurs étapes, avec des appels successifs à objet_inserer() et objet_modifier(). Pour la création d'un auteur, ça passe même plusieurs fois pas objet_modifier(), ce qui rend la détection encore plus ardue. Pour reprendre et compléter les exemples de @rastapopoulos, du plus simple au plus complexe, selon les objets on peut avoir cela : 1. un seul appel à `objet_inserer()` avec un `$set` complet : ok avec post_insertion 2. appel à `objet_inserer()` vide puis **un seul** appel à `objet_modifier()` : possiblement ok avec post_insertion 3. appel à `objet_inserer()` vide puis **plusieurs** appels à `objet_modifier()` : on l'a dans l'os :) Bref, à mon avis conceptuellement il faudrait un point d'entrée complémentaire au pipeline post_insertion, mais où l'on soit vraiment sûr d'être à la fin du processus.

Un pipeline "post_creation"

Un pipeline "post_creation"

Je ne sais pas si tout est comparable. Dans les quelques utilisations personnalisées, bah oui on peut faire tout et n'importe quoi avec objet_inserer() et objet_modifier(), et donc il est, il me semble, parfaitement impossible de regrouper tout ça dans une même organisation. Il est impossible de savoir quand ça se termine "vraiment" dans ce cas là. (D'ailleurs pour un auteur, c'est pas pareil quand c'est un admin qui crée avec le editer_auteur et l'inscription publique, ça ne peut pas vouloir dire la même chose.)

Je crois que le cas à gérer vraiment, le cas le plus courant, 99% du temps, c'est la création depuis le CVT d'un objet, c'est-à-dire 1) création d'une ligne vide (et donc post_insertion ne sert que dans peu de cas) puis 2) appel à objet_modifier() pour remplir avec le tout premier remplissage des champs, quand les gens on enregistré alors que id_patate=new/oui.

Je ne sais pas si tout est comparable. Dans les quelques utilisations personnalisées, bah oui on peut faire tout et n'importe quoi avec objet_inserer() et objet_modifier(), et donc il est, il me semble, parfaitement impossible de regrouper tout ça dans une même organisation. Il est impossible de savoir quand ça se termine "vraiment" dans ce cas là. (D'ailleurs pour un auteur, c'est pas pareil quand c'est un admin qui crée avec le editer_auteur et l'inscription publique, ça ne peut pas vouloir dire la même chose.) Je crois que le cas à gérer vraiment, le cas le plus courant, 99% du temps, c'est la création depuis le CVT d'un objet, c'est-à-dire 1) création d'une ligne vide (et donc post_insertion ne sert que dans peu de cas) puis 2) appel à objet_modifier() pour remplir avec le tout premier remplissage des champs, quand les gens on enregistré alors que id_patate=new/oui.
Owner

En général, je résouds par un petit hack dans nomplugin_administrations.php fonction nomplugin_upgrade qui permet de gérer les deux étapes

include_spip('base/peupler_base');

	$maj['1.0.0'] = array(
		installation_des tables();
	);
    
	$maj['1.0.1'] = array(
		array('peupler_base')
	);

et peupler_base est une fonction qui appelle sql_insertq_multi

En général, je résouds par un petit hack dans nomplugin_administrations.php fonction nomplugin_upgrade qui permet de gérer les deux étapes ``` include_spip('base/peupler_base'); $maj['1.0.0'] = array( installation_des tables(); ); $maj['1.0.1'] = array( array('peupler_base') ); ``` et peupler_base est une fonction qui appelle sql_insertq_multi
Owner

Je pense que le problème est mal posé au départ.

La notion de "exite déjà" ou "est une nouvelle création d'objet" n'est pas très claire, et il n'y a en effet pas de frontière claire entre "je suis dans l'insertion d'un nouvel objet" et "je suis en train de modifier un objet existant".

La seule frontière claire c'est le sql_insert_q() fait dans objet_inserer(), et il est en effet parfois vide, parfois pas, sans que pour autant ça signifie quoi que ce soit sur le fait qu'il y aura derrière un ou plusieurs appel à objet_modifier() pour complèter la création d'un objet.

N'oubliez notamment pas que des plugins s'insèrent peut-être déjà dans les pipelines et ajoutent eux-même des modification complémentaires. On a donc aucune garantie d'atomicité d'une ecriture de "création d'un nouvel objet".

Le plus robuste est donc de ne pas se reposer sur cette notion de création, mais simplement sur l'état de l'objet quand il est modifié via post_edition (ou inséré dans post_insertion) et d'en déterminer les actions à faire, en prenant notamment soin de modifier en conséquence les états pour qu'un nouvel appel identique ne reproduise pas l'action par erreur.

Il ne faut jamais oublier que sur un serveur web la notion de transition ou de parcours n'a pas de sens : un même hit peut-être réjoué plusieurs fois, ou annulé en plein milieu, ou se dérouler en concurrences avec un autre hit identique etc.

Si vraiment il est nécessaire de savoir qu'on est dans le hit de création, il y a plusieurs méthode pour ça:

  • s'insérer dans post_insertion() et lever un flag pour dire "hé on vient de créer cet objet", information utilisable ensuite dans tous les appels à post_edition sur le même objet du hit
  • voire, au lieu de lever un flag, lancer une fonction via register-shutdown-function qui sera appelée en fin de hit, (donc après tous les objet_modifier() qui suivent un objet_inserer() pour la création d'un nouvel objet editorial)

Mais donc du point de vue du core, il n'y a pas vraiment de moyen de déterminer qu'on est "à la fin" de la création d'un objet. On pourrait tout au plus flaguer tous les appels à objet_modifier() du même hit que les objet_inserer(), sans pour autant jamais pouvoir assurer que c'est le dernier.

Donc il faut voir au cas par cas comment procéder le plus judicieusement

Je pense que le problème est mal posé au départ. La notion de "exite déjà" ou "est une nouvelle création d'objet" n'est pas très claire, et il n'y a en effet pas de frontière claire entre "je suis dans l'insertion d'un nouvel objet" et "je suis en train de modifier un objet existant". La seule frontière claire c'est le `sql_insert_q()` fait dans `objet_inserer()`, et il est en effet parfois vide, parfois pas, sans que pour autant ça signifie quoi que ce soit sur le fait qu'il y aura derrière un ou plusieurs appel à `objet_modifier()` pour complèter la création d'un objet. N'oubliez notamment pas que des plugins s'insèrent peut-être déjà dans les pipelines et ajoutent eux-même des modification complémentaires. On a donc aucune garantie d'atomicité d'une ecriture de "création d'un nouvel objet". Le plus robuste est donc de ne pas se reposer sur cette notion de création, mais simplement sur l'état de l'objet quand il est modifié via post_edition (ou inséré dans post_insertion) et d'en déterminer les actions à faire, en prenant notamment soin de modifier en conséquence les états pour qu'un nouvel appel identique ne reproduise pas l'action par erreur. Il ne faut jamais oublier que sur un serveur web la notion de transition ou de parcours n'a pas de sens : un même hit peut-être réjoué plusieurs fois, ou annulé en plein milieu, ou se dérouler en concurrences avec un autre hit identique etc. Si *vraiment* il est nécessaire de savoir qu'on est dans le hit de création, il y a plusieurs méthode pour ça: * s'insérer dans post_insertion() et lever un flag pour dire "hé on vient de créer cet objet", information utilisable ensuite dans tous les appels à post_edition sur le même objet du hit * voire, au lieu de lever un flag, lancer une fonction via `register-shutdown-function` qui sera appelée en fin de hit, (donc après tous les `objet_modifier()` qui suivent un `objet_inserer()` pour la création d'un nouvel objet editorial) Mais donc du point de vue du core, il n'y a pas vraiment de moyen de déterminer qu'on est "à la fin" de la création d'un objet. On pourrait tout au plus flaguer tous les appels à `objet_modifier()` du même hit que les `objet_inserer()`, sans pour autant jamais pouvoir assurer que c'est le dernier. Donc il faut voir au cas par cas comment procéder le plus judicieusement

Pour une bonne partie des cas d'utilisation, je pense effectivement qu'avoir dans le "args" un tableau des champs en base tels qu'ils étaient AVANT la modification demandée (et donc tableau qui serait VIDE la première fois), ça résoudrait déjà beaucoup de chose.

Qu'on soit dans pre_edition ou dans post_edition, on devrait avoir un tableau "champs" avant l'état précédent.

Et d'ailleurs lors de l'appelle cette clé existe mais n'est jamais vraiment remplie. Il ne reste donc plus qu'à la remplir de manière systèmatique, automatique. :)

Ainsi une personne dans ce pipeline pourra tester si avant la modif (qui est alors dans "data" ou si on fait un nouveau fetsel), tel ou tel champ n'était pas encore rempli, et faire des choses en conséquence lors de ce "premier remplissage". (Et je pense que c'est plus ça le concept le plus important "premier remplissage", plutôt que la création qui est plus floue)

Pour une bonne partie des cas d'utilisation, je pense effectivement qu'avoir dans le "args" un tableau des champs en base tels qu'ils étaient AVANT la modification demandée (et donc tableau qui serait VIDE la première fois), ça résoudrait déjà beaucoup de chose. Qu'on soit dans pre_edition ou dans post_edition, on devrait avoir un tableau "champs" avant l'état précédent. Et d'ailleurs lors de l'appelle cette clé existe mais n'est jamais vraiment remplie. Il ne reste donc plus qu'à la remplir de manière systèmatique, automatique. :) Ainsi une personne dans ce pipeline pourra tester si avant la modif (qui est alors dans "data" ou si on fait un nouveau fetsel), tel ou tel champ n'était pas encore rempli, et faire des choses en conséquence lors de ce "premier remplissage". (Et je pense que c'est plus ça le concept le plus important "premier remplissage", plutôt que la création qui est plus floue)
b_b added the
amélioration
label 3 weeks ago
b_b added this to the 4.1 milestone 3 weeks ago
b_b commented 5 days ago
Owner

Et d'ailleurs lors de l'appelle cette clé existe mais n'est jamais vraiment remplie. Il ne reste donc plus qu'à la remplir de manière systèmatique, automatique. :)

Le plan semble défini, tu payes ta PR @rastapopoulos ?

> Et d'ailleurs lors de l'appelle cette clé existe mais n'est jamais vraiment remplie. Il ne reste donc plus qu'à la remplir de manière systèmatique, automatique. :) Le plan semble défini, tu payes ta PR @rastapopoulos ?
Owner

Like this ? !4953

Like this ? !4953
b_b commented 5 days ago
Owner

Super @rastapopoulos il ne reste "plus qu'à" valider le nom champs_anciens dans les commentaires de la PR ^^

Super @rastapopoulos il ne reste "plus qu'à" valider le nom `champs_anciens` dans les commentaires de la PR ^^
JLuc commented 4 days ago

Je dirais que je préfèrerais champs_avant :-)...
Et possiblement j'aimerais être rassuré sur le coût de de ce sql_select supplémentaire dont l'intérêt n'est que potentiel.

Je dirais que je préfèrerais `champs_avant` :-)... Et possiblement j'aimerais être rassuré sur le coût de de ce sql_select supplémentaire dont l'intérêt n'est que potentiel.
Owner
  1. Tous les trucs existants utilisent le mot "ancien" : "statut_ancien", "date_ancienne", etc (dans le pipeline instituer) => cohérence
  2. on parle d'une opération qui se fait 98% du temps lors de la validation par un admin/rédac dans un form d'édition dans l'admin, donc bon, le coup d'un fetsel ici va pas changer la face du monde… Là où ça pourrait c'est pour des opérations en masse utilisant l'API, mais dans ce cas de toute façon t'es censé gérer le temps (soit parce que c'est en cli donc temps infini, soit en génie faut penser à relancer en X fois)
1) Tous les trucs existants utilisent le mot "ancien" : "statut_ancien", "date_ancienne", etc (dans le pipeline instituer) => cohérence 2) on parle d'une opération qui se fait 98% du temps lors de la validation par un admin/rédac dans un form d'édition dans l'admin, donc bon, le coup d'un fetsel ici va pas changer la face du monde… Là où ça pourrait c'est pour des opérations en masse utilisant l'API, mais dans ce cas *de toute façon* t'es censé gérer le temps (soit parce que c'est en cli donc temps infini, soit en génie faut penser à relancer en X fois)
Sign in to join this conversation.
No Milestone
No project
No Assignees
7 Participants
Notifications
Due Date

No due date set.

Dependencies

This issue currently doesn't have any dependencies.

Loading…
There is no content yet.