Fragen zur Objektorientierung - Wo die Logik unterbringen?

Trash

Erfahrenes Mitglied
Hallo,
ich arbeite derzeit mit PHP und habe vor das Ganze vor allem objektorientiert und sauber zu programmieren. Deshalb habe ich nun dazu einige Fragen.

Bei mir können sich z.B. User registrieren. Bisher mache ich das folgendermaßen, nachdem das Formular abgeschickt wurde:

PHP:
$user = new user();
$user->addUser($_POST['vorname'], $_POST['nachname'], $_POST['email']);

Die Prüfung, ob Vorname/Nachname gefüllt sind und ob die E-Mail überhaupt eine gültige E-Mail Adresse ist, mache ich nun in der Klasse:

PHP:
class user {

var $vorname, $nachname,  $email;

public function addUser($vorname, $nachname, $email) {
	global $dbConnect;
	$this->vorname = mysql_real_escape_string($vorname);
	$this->nachname = mysql_real_escape_string($nachname);
	$this->email = mysql_real_escape_string($email);

$errorsReported = $this->validateUserinput();
if ($errorsReported == false) {
$upassword = md5($this->createhash(7));
$dbConnect->query("INSERT INTO "._USERS." VALUES ('','$this->nachname', '$this->vorname', '$upassword','$this->email')");
}

Meine Frage ist nun, in Java würde man eher mit Getter/Settern arbeiten, also vermutlich so:

PHP:
$user = new user();
$user->setName($_POST['nachname']);
$user->setVorname($_POST['vorname']);
$user->setEmail($_POST['email']);
$user->setPassword($hash=new hash());
$user->persist();

Aber wie löse ich dieses in PHP? Heißt es auch hier generell: Sowenig Logik wie möglich in die Klasse? D.h. die Validierung der Daten und die Abfrage, ob die Validierung erfolgreich war hat eigentlich auch nichts in der Klasse zu suchen, oder?

Danke!
 
Zuletzt bearbeitet:
Diese Validierung hat etwas mit der Klasse zu tun. Die Getter und Setter kannst du problemlos von Hand machen. Das ist mMn ziemlich sauber so. Diese Logik ist ja direkt mit dem User verbunden. Auslagern macht da nur mässig Sinn.

PHP erlaubt es halt auch Properites zu setzen die die Klasse nicht definiert hat. Mit protected und privat kannst du die Variablen schützen.
mit __get() und __set() kannst du auch automatisierte Getter und Setter erstellen

ein kleines Beispiel
PHP:
<?php 
class User{
    protected $vorname;
    protected $nachname;
    protected $emailValid;

    public function __construct($vorname = false, $nachname = false){
        if($vorname) $this->vorname = $vorname;
        if($nachname) $this->vorname = $vorname;
    }

    /**
     * Setter und Getter für Email
     */
    public function setEmail($email){
        //TODO: Spezailprüfung für Email
        $this->emailValid = "mailto:{$email}";
    }    
    public function getEmail(){
        return $this->emailValid;
    }

    /**
     * Set und Get für alle definierten Properties
     */
    public function __set($name, $value){
        if(property_exists(__CLASS__, $name)){
            //TODO: Standartprüfung
            $this->$name = $value;
        }else{
            throw new Exception("{$name} ist kein gültiges Property");
        }
    } 
    public function __get($name){
        if(property_exists(__CLASS__, $name)){
            return $this->$name;
        }else{
            throw new Exception("{$name} ist kein gültiges Property");
        }        
    }
   
}

try{
    $u = new user();
    $u->nachname = 'Muster';
    $u->vorname = 'Hans';
    $u->setEmail('hans.muster@muster.com');
    
    $email = $u->getEmail();
    echo "<a href='{$mail}'>{$u->vorname} {$u->nachname}</a><hr>";
    //Und noch einen Fehler provuzieren
    $u->Strasse = 'Musterstrase';
}catch(Exception $e){
    echo $e->getMessage();
}

?>
 
yaslaw, danke für die Antwort. Aber meinst Du jetzt mein derzeitiger Stand ist "sauber" oder mein Beispiel wie ich es an Java angelehnt machen würde ?!

Im Prinzip lässt mir PHP ja freie Wahl, ob ich z.B. die objektspezifischen Variablen folgendermaßen setze:

1.
PHP:
$user->new user("Hans", "Mustermann", "test@test.de");

2.
PHP:
$user->new user();
$user->addUser("Hans", "Mustermann", "test@test.de");

3.
PHP:
$user->new user();
$user->setVorname("Hans");
$user->setNachname("Mustermann");
$user->setEmail("test@test.de");

Dies sind ja drei Varianten. Im Prinzip setzt die Klasse dann ja eh mit $this->vorname=$vorname; die Variable an das Objekt. Nur zu einem anderen Zeitpunkt. Wie ich das löse, ist also eher "Geschmackssache", oder? Ein generelles: "Variante 3 ist die sauberste", gibt es also nicht?!
 
Das ist von Aussen schön so. Doch wie du das in der Klasse umsetzt, dort musst du greiffen.
Schau dir mal mein Beispiel an. Darun unterbinde ich das Setzen von Parametern einfach so. Auch wenn man die Paramter über den Constructeur setzt, geht er intern auf __set().
Was nun schöner ist von aussen betrachtet, hängt wohl sehr vom Einsatz ab. Ich persönlich versuche dei Getter und Setter als Funktionen so gut es ghet nicht einzusetzen. Grad der Getter lässt sich nicht direkt innerhalb eines Strings setzen und somit muss der String immer mit . zusammengesetzt werden.
PHP:
//geht
echo "User: {$user->vorname}";
//geht nicht
echo "User: {$user->getVorname()}";

Eien Validierung in __set() macht Sinn, da man so das beliebige Setzen von Paramtern verhindern kann. Grad wenn du mit Settern arbeitest, solltest du über __set() verhindern, dass jemand den Wert direkt setzt und somit deine Validierung umgeht.

Über den Construkteur die Variablen zu setzen macht Sinn, wenn es ncith zu viele sind. Ansonsten verliert man die Übersicht. Meistens habe ich eine Kombination deines Fall 1) und 2)
 
Hmm,
ich denke ich werde meinen Code noch etwas umbauen. Ob ich nun die setter/getter benutze, weiß ich noch nicht genau.

Aber nun weiß ich, dass ich durchaus in der Klasse eine Validierung machen kann. Ansonsten kann man die Klasse ja "dumm" halten, und die Vailidierung und ihre nötigen Abfragen (ist das Ergebnis valide oder nicht) auch außerhalb der Klasse machen.

Ich dachte, dass es dort irgendnwie einen "Standard" gibt, der z.B. besagt, dass die Klasse eigentlich nur Variablen an die Objekte binden soll und das Objekt dann wegspeichert... aber wenn das nicht so ist, kann ich die Logik auch mit in die Klasse bauen und so weniger Code außerhalb der Klasse haben...

Edit:
Noch eine blöde Frage:

PHP:
__construct($vorname = false, $nachname = false){

Was macht das $vorname=false? Für mich ließt sich das so, als würde die Variable $vorname in der Fkt immer mit false belegt sein..******!
 
Zuletzt bearbeitet:
Wie willst du objektorientiert programmieren wenn du die ganzen Logiken aus den Objekte herausnehmen willst? Wenn du nur Variablen binden willst, dann kannst du das über die Standartklasse stdclass machen und musst dir keine Klassen programmieren.

Dass die Klasse ihre Logik gegen Aussen abschottet ist ein wesentlicher Bestandteil des Objektorientierten Programmierens. Denn grad so kannst du mit Interfaces verscheidene Klassen gegen Aussen zusammenfassen und jede Klasse har für siech die Logik die es braucht. Ein einfaches Besipiel: In der jeweiligen Methode getSql ist die logik gespeichert
PHP:
<?php 
/**
 * Interface für die Values under MySQL
 */
interface ISqlValue{
    public function getSql();
}

/**
 * Abstrakte Klasse die alle gemeisammen Funktionen enthält
 * Ind iesem Beispeil der Konstrukteur 
 */
abstract class ASqlValue implements ISqlValue{
    public function __construct($value){$this->value = $value;}
}

/**
 * Klasse für Nummern
 */
class SqlValueNumber extends ASqlValue implements ISqlValue{
    public function getSql(){return $this->value;}
}

/**
 * Klasse für Strings
 */
class SqlValueString extends ASqlValue implements ISqlValue{
    public function getSql(){return "'{$this->value}'";}
}

/**
 * Klasse für Datum
 */
class SqlValueDate extends ASqlValue implements ISqlValue{
    public function getSql(){return "'".date( 'Y-m-d H:i:s', $this->value )."'";}
}

/**
 * Funktion zur Erstellen eines Where-Strings
 * @param $varobj       Objekt mit dem Interface ISqlValue 
 * @param $varname      Name des Feldes in der DB
 */
 function getWhere(ISqlValue $varobj, $varname){
     //Da lle Obejkte dem Interface ISqlValue entsprechen, 
     //können wir hier auf die mehtode getSql() zugreiffen, egal welche
     //der Klassen dahintersteckt
     return "{$varname} = ".$varobj->getSql();
 }

//Testcode: Erstellen der Objekte
$wheres[] = getWhere(new SqlValueNumber(123), 'zahl');
$wheres[] = getWhere(new SqlValueString('abc'), 'text');
$wheres[] = getWhere(new SqlValueDate(time()), datum);
//Ausgeben des WHERE-Strings
echo implode(' AND ', $wheres);

?>

Was macht das $vorname=false? Für mich ließt sich das so, als würde die Variable $vorname in der Fkt immer mit false belegt sein..******!
false ist der Standartwert, falls die Klasse ohne Paramter eröffnet wird.
 
Zuletzt bearbeitet:
yaslaw, ich habe doch noch eine Frage.

Wenn ich z.B. einen User angelegt habe und sich dieser nun einloggen möchte. Wo ordne ich dann z.B. die Funktionen: verifyEmail() / verifyPassword() ein?

Sind das Funktionen der Userklasse? Es wäre ja eigentlich nicht richtig, das Folgende zu tun, oder?

PHP:
$user = new user();
$user->verifyEmail($_POST['email']);

Ich würde eher folgendes machen: Eine validator-klasse bauen, die eben diese Funktionen beinhaltet oder ist da der generelle Ansatz so, dass das verifizieren der Mail/des PW in die Userklasse gehört, da es User-spezifische Attribute sind?
 
Also die Validation in die Validator-Class und nicht in die User-Class...

Wie meinst Du das mit dem Return-Object?

Wenn ich eine verifyEmail/verifyPW Methode baue, greift diese auf die DB zu, verifiziert und gibt doch nur true/false zurück?!

Hast Du ein Beispiel? ;-)
 
Zuletzt bearbeitet:
Das hängt nun mal von deiner Architektur ab. Es gibt nicht Richtig oder Falsch.

Auf die Schnelle hät ich sowas gemacht, ist jedoch total aus dem Konzept gerissen
PHP:
class UserValidator{
    static function getUser($name, $password){
        //TODO: prüfen ob es valid ist
        return $valid ? new User($name) : false;
    }
}

if(!$user = UserValidator::getUser($name, $passwod)){
    //TODO User ist gültig und wir haben grad das User-Objekt
    echo $user->getReadableName();
} else {
    throw new Exception('User abgelehnt');
}
 
Zurück