Page 1 sur 1

Différentes méthodes communes a différentes classes

Posté : 07 sept. 2008, 17:28
par savageman
Bonjour,

Je vous expose mon problème.

Je suis en train de faire une petite gestion de formulaire en objet. Je vais expliquer avec un exemple, sinon vous ne comprendrez jamais.
Je me sers de cette page comme référence : http://www.w3.org/TR/html401/interact/f ... edef-INPUT

Comme on peut le voir, les boutons radios et checkboxes peuvent se voir appliquer "checked". Les input text, password et textarea peuvent se voir appliquer "maxlength". De même pour readonly.

Je me demandais comment j'allais faire sans répéter le code de chacune des méthodes utiles à ces fonctions dans chacune des classes.

En fait, j'ai une classe Form_Field.
Form_Input, Form_Textarea héritent de Form_Field.
Form_Text, Form_Checkbox, Form_Password héritent de Form_Input.
etc.

Des méthodes comme setMaxLength() s'appliqueraient à Form_Text, Form_Textarea, Form_Password. D'autres comme setChecked() à Form_Checkbox et Form_Radio. Ces méthodes seraient identiques dans toutes ces classes, d'où je ne veux pas les répéter dans chaque classe...

Y a-t-il un moyen sans passer par ce truc immonde et lent qu'est __call() dans Form_Field.

D'avance merci pour vos réponses.

Posté : 07 sept. 2008, 21:58
par Calimero
Si je comprends bien, ta solution s'appelle l'héritage, non ?

Posté : 08 sept. 2008, 02:50
par savageman
Hum... Oui, ça marcherait bien. Sauf que chaque classe héite déjà de quelque chose...

Posté : 08 sept. 2008, 10:09
par Calimero
Toutes tes classes n'héritent t-elles pas (directement ou indirectement) de Form_Field ? Dans ce cas, qu'est ce qui t'empêche d'ajouter tes méthodes à Form_Field directement ?

Posté : 08 sept. 2008, 11:10
par savageman
Rien, c'est juste que dans ce cas on pourrait cocher un textarea... Pas vraiment valide ni propre. C'est pourquoi je cherche une solution "propre".

Posté : 08 sept. 2008, 12:03
par Calimero
Rien, c'est juste que dans ce cas on pourrait cocher un textarea... Pas vraiment valide ni propre. C'est pourquoi je cherche une solution "propre".
Peut-être que tu te poses trop de questions. Je ne vois pas de lien logique évident entre l'existence d'une méthode setChecked() par exemple et le fait qu'on puisse cocher le textarea en question (une simple propriété binaire de l'objet suffirait à différencier les deux cas).
class Checkbox extends form_field{
  public $checkable=true;
  // [...]
}

class Textarea extends form_field{
  public $checkable=false;
  // [...]
}

if($field->checkable){
  $field->setChecked($value);
} else {
  $field->setValue($value);
}
Et c'est tout à fait propre :-)

Posté : 08 sept. 2008, 12:34
par savageman
Eh bah voilà une solution élégante. :) C'est ce que je cherchais, merci bien.

Je vais juste devoir trouver des jolis noms, parce que maxlengthable ça le fait pas trop. xD

Un grand merci à toi, toutes les 2 solutions que j'avais trouvées passaient par un __call() (que je déteste car c'est super lent).

Posté : 08 sept. 2008, 16:17
par Calimero
Content d'avoir pu t'aider ;-)

Je t'ai donné ici la solution la plus évidente, mais je viens de penser qu'il existe au moins deux autres solutions pour que ton textarea ne soit pas checkable sans même avoir à déclarer de propriété. Tu peux protéger directement la méthode isChecked comme ceci :
class form_field{
  function setChecked($checked){
    //Si l'objet courant n'est pas d'une classe correspondant à un élément cochable,
    //on soulève une exception
    if( ($this instanceof Checkbox) || ($this instanceof Radio) ) {
      $this->checked=$checked;
    } else {
      throw new Exception(__CLASS__.'->'.__METHOD__.'() a été appelée sur un champ non cochable.');
    }
  }

  // [...]
}
Ou bien si l'idée de répertorier les noms de certaines classes filles au niveau de la classe form_field (créant artificiellement des dépendances croisées entre les classes) te parait sale, tu peux aussi utiliser une interface pour déclarer comme cochables uniquement les classes qui le sont et déplacer la condition sur cette interface :
interface Checkable{
    public function setChecked($checked);
}

class Checkbox extends form_field implements Checkable{
  // [...]
}

class Textarea extends form_field{
  // [...]
} 

class form_field{
  public function setChecked($checked){
    //Si l'objet courant n'est pas d'une classe correspondant à un élément cochable,
    //on soulève une exception
    if($this instanceof Checkable) { // instanceof fonctionne aussi avec les interfaces
      $this->checked=$checked;
    } else {
      throw new Exception(__CLASS__.'->'.__METHOD__.'() a été appelée sur un champ non cochable.');
    }
  }

  // [...]
}

Posté : 08 sept. 2008, 16:33
par savageman
J'aime pas trop la première version, un peu verbeuse... Mais le coup de l'interface me plaît vraiment ! Je pense que je vais l'adopter. En plus on peut implémenter plusieurs interfaces, donc c'est cool !

J'avais pensé à un __call() dans Form_Field, mais c'est moche.
J'avais pensé à un genre d'interface, mais plus version décorateur en fait. Une classe Checkable qui prend en paramètre une autre classe et qui implémente setChecked() + toutes les méthodes de la classe passée en paramètre via __call(). Encore une fois, pas super joli...

Posté : 26 nov. 2008, 23:35
par simoh
certes l'héritage multiple n'est pas autorisé en php, mais vous pouvez implémenter une interface en utilisant le mot clé implements, mais sachez que ce n'est valable qu'à partir de php5