381 votes

Meilleure façon d'utiliser des constructeurs multiples en PHP

Vous ne pouvez pas mettre deux fonctions __construct avec des signatures d'arguments uniques dans une classe PHP. J'aimerais faire ceci :

class Student 
{
   protected $id;
   protected $name;
   // etc.

   public function __construct($id){
       $this->id = $id;
      // other members are still uninitialized
   }

   public function __construct($row_from_database){
       $this->id = $row_from_database->id;
       $this->name = $row_from_database->name;
       // etc.
   }
}

Quelle est la meilleure façon de faire cela en PHP ?

113 votes

Je rêve aussi de constructeurs nommés et de surcharge de méthodes +1

0 votes

Dans mon cas, je veux avoir un constructeur protégé qui a un argument requis de moins que le constructeur public - dans le but de standardiser sa méthode de fabrication. J'ai besoin d'une classe capable de créer des copies d'elle-même, et la fabrique est dans une classe abstraite, mais les classes concrètes peuvent avoir des constructeurs qui nécessitent un deuxième argument - dont la classe abstraite n'a aucune idée.

0 votes

Ce n'est pas vraiment quelque chose d'utile mais quelque chose que j'ai découvert par hasard il y a quelque temps : la classe DatePeriod dans date_c.php a plusieurs constructeurs. Mais je ne sais pas ce que PHP en fait en interne.

526voto

Kris Points 11892

Je ferais probablement quelque chose comme ça :

<?php

class Student
{
    public function __construct() {
        // allocate your stuff
    }

    public static function withID( $id ) {
        $instance = new self();
        $instance->loadByID( $id );
        return $instance;
    }

    public static function withRow( array $row ) {
        $instance = new self();
        $instance->fill( $row );
        return $instance;
    }

    protected function loadByID( $id ) {
        // do query
        $row = my_awesome_db_access_stuff( $id );
        $this->fill( $row );
    }

    protected function fill( array $row ) {
        // fill all properties from array
    }
}

?>

Ensuite, si je veux un étudiant dont je connais l'identité :

$student = Student::withID( $id );

Ou si j'ai un tableau de la ligne de la base de données :

$student = Student::withRow( $row );

Techniquement, vous ne construisez pas de constructeurs multiples, juste des méthodes d'aide statiques, mais vous évitez ainsi beaucoup de code spaghetti dans le constructeur.

2 votes

On dirait que vous venez de répondre à la question que j'ai posée à gpilotino. Merci. Très clair.

1 votes

@JannieT, pas vraiment, les usines sont un peu plus compliquées que cela, et à mon avis probablement trop pour ce problème.

1 votes

Pouvez-vous élaborer davantage, je ne pense pas que ce soit exagéré.

93voto

timaschew Points 3444

La solution de Kris est vraiment sympa, mais je préfère un mélange de style factory et fluent :

<?php

class Student
{

    protected $firstName;
    protected $lastName;
    // etc.

    /**
     * Constructor
     */
    public function __construct() {
        // allocate your stuff
    }

    /**
     * Static constructor / factory
     */
    public static function create() {
        return new self();
    }

    /**
     * FirstName setter - fluent style
     */
    public function setFirstName($firstName) {
        $this->firstName = $firstName;
        return $this;
    }

    /**
     * LastName setter - fluent style
     */
    public function setLastName($lastName) {
        $this->lastName = $lastName;
        return $this;
    }

}

// create instance
$student= Student::create()->setFirstName("John")->setLastName("Doe");

// see result
var_dump($student);
?>

2 votes

+1 ; Ce type de solution peut donner un code très agréable. Bien que j'opterais pour setLastName (ou plutôt tous les setters) dans cette solution pour retourner $this au lieu d'avoir effectivement deux setters sur la même propriété.

4 votes

En tant qu'habitué des langages compilés et typés statiquement comme C#, cette façon de faire me convient parfaitement.

0 votes

En quoi le fait de fournir une méthode de création statique diffère-t-il de la simple utilisation du constructeur de la même manière ? $student = new Student()->setFirstName("John")->setLastName("Doe");

44voto

Daff Points 22358

PHP est un langage dynamique, vous ne pouvez donc pas surcharger les méthodes. Vous devez vérifier les types de vos arguments comme ceci :

class Student 
{
   protected $id;
   protected $name;
   // etc.

   public function __construct($idOrRow){
    if(is_int($idOrRow))
    {
        $this->id = $idOrRow;
        // other members are still uninitialized
    }
    else if(is_array($idOrRow))
    {
       $this->id = $idOrRow->id;
       $this->name = $idOrRow->name;
       // etc.  
    }
}

23 votes

Tout ce qui en résulte est un code spaghetti génial. Mais c'est en effet probablement la façon la plus simple de le faire.

0 votes

Si vous créez vos constructeurs comme vous le feriez dans un langage à typage statique, cela deviendra du code spaghetti. Mais vous ne le faites pas. Créer deux constructeurs avec un paramètre et aucun type (aucun type == n'importe quel type) pour ce paramètre ne fonctionnera dans aucun langage, de toute façon (par exemple, cela ne fonctionnera pas non plus d'avoir deux constructeurs Java avec un paramètre Object chacun dans une classe).

1 votes

Ce que je voulais dire, c'est que vous faites des choses différentes dans le même champ d'application en fonction de l'influence extérieure. Ce n'est pas une mauvaise solution (puisqu'elle fonctionnera), mais ce n'est pas celle que je choisirais.

26voto

Björn Points 15485
public function __construct() {
    $parameters = func_get_args();
    ...
}

$o = new MyClass('One', 'Two', 3);

Maintenant, $paramters sera un tableau avec les valeurs 'One', 'Two', 3.

Edit,

Je peux ajouter que

func_num_args()

vous donnera le nombre de paramètres de la fonction.

3 votes

Comment cela résout-il le problème de savoir ce qui a été voté ? Je pense que cela complique le problème car au lieu de devoir vérifier le type du paramètre, vous devez vérifier si x paramètre est défini et ensuite son type.

0 votes

Cela ne résout pas le problème de savoir quel type a été passé, mais c'est la solution pour les "constructeurs multiples" en PHP. La vérification du type est à la charge de l'OP.

23 votes

Je me demande ce qui se passe lorsqu'un nouveau développeur est ajouté à un projet contenant beaucoup de code comme celui-ci.

16voto

Andrei Serdeliuc Points 3327

Vous pourriez faire quelque chose comme ça :

public function __construct($param)
{
    if(is_int($param)) {
         $this->id = $param;
    } elseif(is_object($param)) {
     // do something else
    }
 }

0 votes

+1 pour une solution très praticable. Cependant, pour la classe en question, je vais utiliser la méthode de @Kris.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X