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?

4voto

paishin Punkte 51

Sie könnten etwas wie das Folgende machen, was wirklich einfach und sehr sauber ist:

public function __construct()    
{
   $arguments = func_get_args(); 

   switch(sizeof(func_get_args()))      
   {
    case 0: //No arguments
        break; 
    case 1: //One argument
        $this->do_something($arguments[0]); 
        break;              
    case 2:  //Two arguments
        $this->do_something_else($arguments[0], $arguments[1]); 
        break;            
   }
}

4voto

stanley mbote Punkte 563

Ich stand vor dem gleichen Problem bei der Erstellung mehrerer Konstruktoren mit unterschiedlichen Signaturen, aber leider bietet PHP keine direkte Methode, dies zu tun. Ich habe jedoch einen Trick gefunden, um das Problem zu lösen. Ich hoffe, er funktioniert auch bei Ihnen.

    <?PHP

    class Animal
    {

      public function __construct()
      {
        $arguments = func_get_args();
        $numberOfArguments = func_num_args();

        if (method_exists($this, $function = '__construct'.$numberOfArguments)) {
            call_user_func_array(array($this, $function), $arguments);
        }
    }

    public function __construct1($a1)
    {
        echo('__construct with 1 param called: '.$a1.PHP_EOL);
    }

    public function __construct2($a1, $a2)
    {
        echo('__construct with 2 params called: '.$a1.','.$a2.PHP_EOL);
    }

    public function __construct3($a1, $a2, $a3)
    {
        echo('__construct with 3 params called: '.$a1.','.$a2.','.$a3.PHP_EOL);
    }
}

$o = new Animal('sheep');
$o = new Animal('sheep','cat');
$o = new Animal('sheep','cat','dog');

// __construct with 1 param called: sheep
// __construct with 2 params called: sheep,cat
// __construct with 3 params called: sheep,cat,dog

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ß!

4voto

Waku-2 Punkte 966

Diese Frage wurde bereits mit sehr intelligenten Wegen zur Erfüllung der Anforderung beantwortet, aber ich frage mich, warum nicht einen Schritt zurückgehen und die grundlegende Frage stellen, warum wir eine Klasse mit zwei Konstruktoren benötigen? Wenn meine Klasse zwei Konstruktoren benötigt, dann muss ich wahrscheinlich die Art und Weise, wie ich meine Klassen entwerfe, ein wenig mehr überdenken, um ein saubereres und besser testbares Design zu finden.

Wir versuchen, die Instanziierung einer Klasse mit der eigentlichen Klassenlogik zu verwechseln.

Wenn sich ein Schülerobjekt in einem gültigen Zustand befindet, spielt es dann eine Rolle, ob es aus einer DB-Zeile oder aus Daten eines Webformulars oder einer KI-Anfrage erstellt wurde?

Um nun die Frage zu beantworten, die hier auftauchen könnte, wenn wir die Logik der Erstellung eines Objekts aus einer Datenbankzeile nicht hinzufügen, wie erstellen wir dann ein Objekt aus den Datenbankdaten, können wir einfach eine weitere Klasse hinzufügen, nennen Sie sie StudentMapper, wenn Sie mit dem Daten-Mapper-Muster vertraut sind, in einigen Fällen können Sie StudentRepository verwenden, und wenn nichts Ihren Bedürfnissen entspricht, können Sie eine StudentFactory erstellen, um alle Arten von Objektkonstruktionsaufgaben zu erledigen.

Das Wichtigste ist, dass wir die Persistenzschicht aus unserem Kopf heraushalten, wenn wir an den Domänenobjekten arbeiten.

3voto

lukas.j Punkte 3655

Ab PHP 8 können wir benannte Argumente verwenden:

class Student {

  protected int $id;
  protected string $name;

  public function __construct(int $id = null, string $name = null, array $row_from_database = null) {
    if ($id !== null && $name !== null && $row_from_database === null) {
      $this->id = $id;
      $this->name = $name;
    } elseif ($id === null && $name === null
        && $row_from_database !== null
        && array_keys($row_from_database) === [ 'id', 'name' ]
        && is_int($row_from_database['id'])
        && is_string($row_from_database['name'])) {
      $this->id = $row_from_database['id'];
      $this->name = $row_from_database['name'];
    } else {
      throw new InvalidArgumentException('Invalid arguments');
    }
  }

}

$student1 = new Student(id: 3, name: 'abc');
$student2 = new Student(row_from_database: [ 'id' => 4, 'name' => 'def' ]);

Mit einer angemessenen Prüfung ist es möglich, ungültige Kombinationen von Argumenten auszuschließen, so dass die erzeugte Instanz am Ende des Konstruktors eine gültige Instanz ist (Fehler werden jedoch erst zur Laufzeit erkannt).

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