Précédent   Forum des professionnels en informatique > PHP > Bibliothèques et frameworks > symfony
symfony Forum d'entraide sur le framework PHP symfony. Avant de poster : cours symfony et FAQ symfony
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 07/07/2011, 15h49   #1
Nouveau Membre du Club
 
Homme
Étudiant
Inscription : août 2006
Messages : 49
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Étudiant
Secteur : Santé

Informations forums :
Inscription : août 2006
Messages : 49
Points : 25
Points : 25
Par défaut save fait une boucle infinie.

Bonjour,

Dans le cadre d'un projet symfony avec doctrine, j'utilise sfDoctrineGuardUser et définis donc mes utilisateurs sur 2 tables jointes : GessehEtudiant et sfGuardUser.
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
GessehEtudiant:
actAs:
Timestampable:
created: { disabled: true }
updated: { format: Y-m-d }
columns:
promo_id: { type: integer, notnull: true }
classement: { type: integer, notnull: true }
tel: { type: string(14), notnull: false }
naissance: { type: date, notnull: false }
anonyme: { type: boolean, default: false }
utilisateur: { type: integer, notnull: true }
relations:
GessehPromo: { onDelete: CASCADE, local: promo_id, foreign: id }
sfGuardUser: { onDelete: CASCADE, local: utilisateur, type: one, foreign: id }
(le schéma de sfGuardUser est celui du plugin sans le moindre changement).

Dans le backend, j'utilises l'admin generator :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
generator:
class: sfDoctrineGenerator
param:
model_class: GessehEtudiant
theme: admin
non_verbose_templates: true
with_show: false
singular: ~
plural: ~
route_prefix: gesseh_etudiant
with_doctrine_route: true
actions_base_class: sfActions
 
config:
actions: ~
fields:
utilisateur: { label: Utilisateur, help: Utilisateur correspondant }
promo_id: { label: Promotion }
sfGuardUser: { label: Étudiant, help: Informations du compte utilisateur correspondant }
gesseh_promo: { label: Promotion }
tel: { label: Téléphone }
naissance: { label: Date de naissance }
list:
title: Liste des étudiants
display: [=sfGuardUser, gesseh_promo, tel, naissance]
sort: [promo_id, asc]
max_per_page: 50
actions:
_new: { label: Ajouter }
autoUpdatePromo: { label: Passage à la promo supérieure }
deleteAncien: { label: Maintenance (hors promo) }
# importNew: { label: Importer }
batch_actions: {}
# horsPromo: { label: Sortir des promos }
object_actions:
_edit: { label: Modifier }
table_method: retrieveEtudiant
filter:
display: [promo_id]
form:
class: GessehEtudiantForm
display: [promo_id, sfGuardUser, tel, naissance, classement]
fields:
promo_id: { help: Hors Promo désactive automatiquement le compte utilisateur }
classement: { label: Classement (choix), help: Ordre de classement pour les choix dans la promo }
edit:
title: Modifier %%sfGuardUser%%
actions:
_delete: { label: Supprimer }
_list: { label: Retour à la liste }
_save: { label: Enregistrer }
new:
title: Nouvel étudiant
actions:
_list: { label: Retour à la liste }
_save: { label: Enregistrer }
_save_and_add: { label: Ajouter un autre }
et j'ai un peu modifié le save de sfGuardUser :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class sfGuardUser extends PluginsfGuardUser
{
/* Enregistrement personnalisé d'un utilisateur */
public function save (Doctrine_Connection $conn = null)
{
/* Paramètres prédéfinis pour l'enregistrement d'un étudiant */
if ($this->getId() != null) {
if ($etudiant = Doctrine::getTable('GessehEtudiant')->findOneByUtilisateur($this->getId())) {
if ($etudiant->getPromoId() == Doctrine::getTable('GessehPromo')->findOneByActive(false)->getId()) {
$this->setIsActive(false);
} else {
$this->setIsActive(true);
}
if (!$this->hasGroup('etudiant')) {
$this->addGroupByName('etudiant');
}
}
}
 
if ($this->getPassword() == null) {
$this->setPassword($this->generatePassword(8));
}
 
return parent::save($conn);
}
 
/* Auto-génération d'un mot de passe */
private function generatePassword ($length)
{
$characters = array ('a','z','e','r','t','y','u','p','q','s','d','f','g','h','j','k','m','w','x','c','v','b','n','2','3','4','5','6','7','8','9','A','Z','E','R','T','Y','U','P','S','D','F','G','H','J','K','L','M','W','X','C','V','B','N');
 
$password = '';
 
for ($i = 0 ; $i < $length ; $i++) {
$rand = array_rand($characters);
$password .= $characters[$rand];
}
 
return $password;
}
}
Or, si la procédure fonctionne parfaitement lorsqu'il s'agit d'éditer un utilisateur-étudiant, je me retrouve avec un "Fatal error: Maximum execution time of 30 seconds exceeded in ..." lors de la création d'un nouvel utilisateur-étudiant.


En creusant les logs, je me suis rendu compte que c'était la fonction save qui bouclait à l'infini.


Je ne comprends pas pourquoi d'autant que je suis persuadé d'avoir réussi à créer des utilisateurs il y a 10 jours au moment où j'ai procédé à la modification du save (pas re-vérifié depuis, je m'en suis rendu compte lors du test en "prod" juste avant de lancer l'appli réellement en production).
CaraG33k est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/07/2011, 15h57   #2
Membre éclairé
 
Inscription : août 2007
Messages : 360
Détails du profil
Informations forums :
Inscription : août 2007
Messages : 360
Points : 334
Points : 334
Bonjour,

Y a une condition qui me fait mal aux yeux :

Code :
1
2
 
$etudiant->getPromoId() == Doctrine::getTable('GessehPromo')->findOneByActive(false)->getId()
Effectivement ce code marchera pour la création de nouveaux utilisateurs, mais jamais pour la modification...

Je te laisse regarder ce que te renvoie :

Code :
1
2
 
Doctrine::getTable('GessehPromo')->findOneByActive(false)
Parce que je pense que si tu as plus d'une "GessehPromo" dont le champ "active" est à false, tu n'auras jamais le résultat que tu veux...

Pour le reste, on verra plus tard...

Cordialement,

Mathieu
mathieu44800 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/07/2011, 20h13   #3
Nouveau Membre du Club
 
Homme
Étudiant
Inscription : août 2006
Messages : 49
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Étudiant
Secteur : Santé

Informations forums :
Inscription : août 2006
Messages : 49
Points : 25
Points : 25
Je suis conscient que ce bout de code manque d'élégance mais il fait son travail (que ce soit dans l'édition ou dans la création) dans la mesure où, comme tu le précises bien, il n'y a bien qu'une seule promo inactive (davantage serait superflu) créée par défaut, l'administrateur ne pouvant ajouter que des promos actives.

Par contre ça ne m'explique pas pourquoi le "return parent::save($conn)" boucle à l'infini sur une création et pas sur une édition : j'ai dû rater un truc.

Edit: Quoique, à la lumière de ta remarque, j'ai fait quelques tests supplémentaires et il semble en effet que ce problème de boucle infini n'apparaisse que lorsqu'on ajoute un étudiant dans cette seule promo inactive (ce qui n'arrive jamais en théorie en utilisation quotidienne et qui explique pourquoi je n'avais pas noté ce bug auparavant : cas limite jusqu'alors non testé). Je vais creuser de ce côté, bien que la logique de cette boucle ne m'apparaisse pas évidente, pour le moment.
CaraG33k est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 09/07/2011, 10h35   #4
Membre éclairé
 
Inscription : août 2007
Messages : 360
Détails du profil
Informations forums :
Inscription : août 2007
Messages : 360
Points : 334
Points : 334
Bonjour,

Tout d'abord, je n'aurais pas réécrit cette méthode, pour ne pas interférer avec le comprtement de base du plugin, qui je te le rappelle, garanti la sécurité de ton application...

Tu aurais du avoir recours à une méthode save() ou doSave() d'un formulaire que tu aurais hérité du formulaire de création d'utilisateurs du plugin !!!

Ceci dit :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
 
public function save (Doctrine_Connection $conn = null)
  {
     // Je me souviens plus si le sfGuardUser dispose d'une PK 'id'
     if (strlen(trim($this->get('id'))) !== 0) {
 
       // Pourquoi il n'y a pas de PK sur cette table ??
       $etudiant = GessehEtudiant::getInstance()->findOneByUtilisateur($this->get('id'));
 
       if ($etudiant instanceof GessehEtudiant) 
      {
         // Tu sais déjà ce que j'en pense...
         $promoid = Doctrine::getTable('GessehPromo')->findOneByActive(false)->get('id');
 
        $this->setIsActive(true);
 
        if ($etudiant->get('promo_id') === $promoid) 
        {
          $this->setIsActive(false);
        } 
 
        if (! $this->hasGroup('etudiant')) 
        {
           // TU créele groupe, mais tu ne l'affecte pas à l'utilisateur...
           $this->addGroupByName('etudiant');
        }
      }
    }
 
    if (strlen(trim($this->get('password'))) === 0) 
    {
       $this->setPassword($this->generatePassword(8));
    }
 
    // As-tu été regardé dans la classe parente cette méthode ??
    return parent::save($conn);
  }
Voila dans le détail ce que j'en pense...

Cordialement,

Mathieu
mathieu44800 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/07/2011, 18h17   #5
Modérateur
 
Avatar de Michel Rotta
 
Homme Michel Rotta
Responsable d'exploitation informatique
Inscription : septembre 2005
Messages : 4 913
Détails du profil
Informations personnelles :
Nom : Homme Michel Rotta
Âge : 49
Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

Informations professionnelles :
Activité : Responsable d'exploitation informatique
Secteur : Distribution

Informations forums :
Inscription : septembre 2005
Messages : 4 913
Points : 7 505
Points : 7 505
Citation:
Or, si la procédure fonctionne parfaitement lorsqu'il s'agit d'éditer un utilisateur-étudiant, je me retrouve avec un "Fatal error: Maximum execution time of 30 seconds exceeded in ..." lors de la création d'un nouvel utilisateur-étudiant.
L'Id n'existe pas pour un objet non sauvegadé. Dans le cycle de vie, l'Id utilisateur n'est récupérer qu'après le cycle de la sauvegarde. Donc vouloir te base sur la valeur de cet Id avant l'action de sauvegarde semble provoquer cette action de sauvegarde qui va essayer de trouver l'Id utilisateur et donc générer une action de sauvegarde qui va essayer de trouver l'Id... et tu es mort !

Il faut sortir cette récupération de la boucle save(). Ou tester si l'utilisateur est isNew().

Et effectivement, il ne faut pas, sauf exception exceptionnellement exceptionnel toucher le code d'un plugin et à fortiori celui de sfGuard plus que tout autre.
__________________
Si tu donnes un poisson à un homme, il mangera un jour. Si tu lui apprends à pêcher, il mangera toujours (Lao Tseu).
  • Pensez à valoriser les réponses pertinantes, cliquez sur le bouton vert +1 pour indiquer votre accord avec la solution proposée.
  • Pensez à utiliser la balise [code] pour afficher du code, elle est cachée sous le bouton [#] dans l'éditeur.
  • Une discussion est terminée ? Alors le bouton est votre ami !
Michel Rotta est déconnecté   Envoyer un message privé Réponse avec citation 10
Réponse Proposer ce sujet en actualité
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 21h54.


 
 
 
 
Partenaires

Hébergement Web