405 Stimmen

Der beste Weg, mehrere Konstruktoren in PHP zu verwenden

Sie können nicht zwei __construct-Funktionen mit eindeutigen Argument-Signaturen in eine PHP-Klasse einfügen. Ich würde das gerne tun:

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.
   }
}

Wie lässt sich dies in PHP am besten bewerkstelligen?

17voto

Sie könnten etwa so vorgehen:

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

16voto

yannis Punkte 5886

Ab der Version 5.4 unterstützt PHP Eigenschaften . Dies ist no genau das, wonach Sie suchen, aber ein einfacher, auf Eigenschaften basierender Ansatz wäre es:

trait StudentTrait {
    protected $id;
    protected $name;

    final public function setId($id) {
        $this->id = $id;
        return $this;
    }

    final public function getId() { return $this->id; }

    final public function setName($name) {
        $this->name = $name; 
        return $this;
    }

    final public function getName() { return $this->name; }

}

class Student1 {
    use StudentTrait;

    final public function __construct($id) { $this->setId($id); }
}

class Student2 {
    use StudentTrait;

    final public function __construct($id, $name) { $this->setId($id)->setName($name); }
}

Am Ende haben wir zwei Klassen, eine für jeden Konstruktor, was ein bisschen kontraproduktiv ist. Um etwas Vernunft zu bewahren, werde ich eine Fabrik einbauen:

class StudentFactory {
    static public function getStudent($id, $name = null) {
        return 
            is_null($name)
                ? new Student1($id)
                : new Student2($id, $name)
    }
}

Es läuft also alles auf Folgendes hinaus:

$student1 = StudentFactory::getStudent(1);
$student2 = StudentFactory::getStudent(1, "yannis");

Dieser Ansatz ist furchtbar langatmig, kann aber äußerst praktisch sein.

9voto

Jed Lynch Punkte 1685

Hier ist ein eleganter Weg, dies zu tun. Erstellen Sie einen Trait, der mehrere Konstruktoren mit einer bestimmten Anzahl von Parametern ermöglicht. Sie würden einfach die Anzahl der Parameter an den Funktionsnamen "__construct" anhängen. Ein Parameter wird also "__construct1" sein, zwei "__construct2"... usw.

trait constructable
{
    public function __construct() 
    { 
        $a = func_get_args(); 
        $i = func_num_args(); 
        if (method_exists($this,$f='__construct'.$i)) { 
            call_user_func_array([$this,$f],$a); 
        } 
    } 
}

class a{
    use constructable;

    public $result;

    public function __construct1($a){
        $this->result = $a;
    }

    public function __construct2($a, $b){
        $this->result =  $a + $b;
    }
}

echo (new a(1))->result;    // 1
echo (new a(1,2))->result;  // 3

7voto

rojoca Punkte 10822

Eine andere Möglichkeit ist die Verwendung von Standardargumenten im Konstruktor wie folgt

class Student {

    private $id;
    private $name;
    //...

    public function __construct($id, $row=array()) {
        $this->id = $id;
        foreach($row as $key => $value) $this->$key = $value;
    }
}

Das bedeutet, dass Sie mit einer Zeile wie dieser instanziieren müssen: $student = new Student($row['id'], $row) sondern hält Ihren Konstruktor schön sauber.

Wenn Sie hingegen die Polymorphie nutzen wollen, können Sie zwei Klassen wie folgt erstellen:

class Student {

    public function __construct($row) {
         foreach($row as $key => $value) $this->$key = $value;
    }
}

class EmptyStudent extends Student {

    public function __construct($id) {
        parent::__construct(array('id' => $id));
    }
}

4voto

David Culbreth Punkte 2307

Ich weiß, ich bin super spät zu der Party hier, aber ich kam mit einem ziemlich flexiblen Muster, das einige wirklich interessante und vielseitige Implementierungen ermöglichen sollte.

Richten Sie Ihre Klasse so ein, wie Sie es normalerweise tun würden, mit beliebigen Variablen.

class MyClass{
    protected $myVar1;
    protected $myVar2;

    public function __construct($obj = null){
        if($obj){
            foreach (((object)$obj) as $key => $value) {
                if(isset($value) && in_array($key, array_keys(get_object_vars($this)))){
                    $this->$key = $value;
                }
            }
        }
    }
}

Wenn Sie Ihr Objekt einfach ein assoziatives Array mit den Schlüsseln des Arrays die gleichen wie die Namen Ihrer vars, wie so...

$sample_variable = new MyClass([
    'myVar2'=>123, 
    'i_dont_want_this_one'=> 'This won\'t make it into the class'
    ]);

print_r($sample_variable);

En print_r($sample_variable); nach dieser Instanziierung ergibt sich folgendes:

MyClass Object ( [myVar1:protected] => [myVar2:protected] => 123 )

Denn wir haben initialisiert $group auf Null in unserem __construct(...) ist es auch möglich, dem Konstruktor überhaupt nichts zu übergeben, etwa so...

$sample_variable = new MyClass();

print_r($sample_variable);

Jetzt ist die Ausgabe genau wie erwartet:

MyClass Object ( [myVar1:protected] => [myVar2:protected] => )

Der Grund, warum ich dies geschrieben habe, war, dass ich die Ausgabe von json_decode(...) zu meinem Konstruktor hinzufügen, ohne sich allzu viele Gedanken darüber zu machen.

Dies wurde in PHP 7.1 ausgeführt. Viel Spaß!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X