6 Stimmen

Wie entwirft man Domain-Layer-Objekte, um mehrere Objekte und ein einzelnes Objekt im Zend Framework darzustellen?

Ich arbeite an der Erstellung einer Domänenschicht in Zend Framework, die von der Datenzugriffsschicht getrennt ist. Der Data Access Layer besteht aus zwei Hauptobjekten, einem Table Data Gateway und einem Row Data Gateway. Wie in Bill Karwin's Antwort auf diese frühere Frage Ich habe jetzt den folgenden Code für mein Domain-Person-Objekt:

class Model_Row_Person
{
    protected $_gateway;

    public function __construct(Zend_Db_Table_Row $gateway)
    {
        $this->_gateway = $gateway;
    }

    public function login($userName, $password)
    {

    }

    public function setPassword($password)
    {

    }
}

Dies funktioniert jedoch nur bei einer einzelnen Zeile. Ich muss auch ein Domänenobjekt erstellen, das die gesamte Tabelle darstellen kann und (vermutlich) verwendet werden kann, um durch alle Personen in der Tabelle zu iterieren und die entsprechende Art von Person (Admin, Käufer, etc.) Objekt für die Verwendung zurückzugeben. Im Grunde stelle ich mir so etwas wie das Folgende vor:

class Model_Table_Person implements SeekableIterator, Countable, ArrayAccess
{
    protected $_gateway;

    public function __construct(Model_DbTable_Person $gateway)
    {
        $this->_gateway = $gateway;
    }

    public function current()
    {
        $current = $this->_gateway->fetchRow($this->_pointer);

        return $this->_getUser($current);
    }

    private function _getUser(Zend_Db_Table_Row $current)
    {
        switch($current->userType)
        {
            case 'admin':
                return new Model_Row_Administrator($current);
                break;
            case 'associate':
                return new Model_Row_Associate($current);
                break;
        }
    }
}

Ist dies ein guter/schlechter Weg, um dieses spezielle Problem zu lösen? Welche Verbesserungen oder Anpassungen sollte ich an der Gesamtkonstruktion vornehmen?

Vielen Dank im Voraus für Ihre Kommentare und Kritiken.

9voto

Bill Karwin Punkte 493880

Ich hatte mir vorgestellt, dass Sie die Domänenmodellklasse verwenden würden, um die Tatsache, dass Sie eine Datenbanktabelle für die Persistenz verwenden, vollständig zu verbergen. Die Übergabe eines Table-Objekts oder eines Row-Objekts sollte also völlig unauffällig sein:

<?php
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();

$db = Zend_Db::factory('mysqli', array('dbname'=>'test',
    'username'=>'root', 'password'=>'xxxx'));
Zend_Db_Table_Abstract::setDefaultAdapter($db);

class Table_Person extends Zend_Db_Table_Abstract
{
    protected $_name = 'person';
}

class Model_Person
{
    /** @var Zend_Db_Table */
    protected static $table = null;

    /** @var Zend_Db_Table_Row */
    protected $person;

    public static function init() {
        if (self::$table == null) {
            self::$table = new Table_Person();
        }
    }

    protected static function factory(Zend_Db_Table_Row $personRow) {
        $personClass = 'Model_Person_' . ucfirst($personRow->person_type);
        return new $personClass($personRow);
    }

    public static function get($id) {
        self::init();
        $personRow = self::$table->find($id)->current();
        return self::factory($personRow);
    }

    public static function getCollection() {
        self::init();
        $personRowset = self::$table->fetchAll();
        $personArray = array();
        foreach ($personRowset as $person) {
            $personArray[] = self::factory($person);
        }
        return $personArray;
    }

    // protected constructor can only be called from this class, e.g. factory()
    protected function __construct(Zend_Db_Table_Row $personRow) {
        $this->person = $personRow;
    }

    public function login($password) {
        if ($this->person->password_hash ==
            hash('sha256', $this->person->password_salt . $password)) {
            return true;
        } else {
            return false;
        }

    }

    public function setPassword($newPassword) {
        $this->person->password_hash = hash('sha256',
            $this->person->password_salt . $newPassword);
        $this->person->save();
    }
}

class Model_Person_Admin extends Model_Person { }
class Model_Person_Associate extends Model_Person { }

$person = Model_Person::get(1);
print "Got object of type ".get_class($person)."\n";
$person->setPassword('potrzebie');

$people = Model_Person::getCollection();
print "Got ".count($people)." people objects:\n";
foreach ($people as $i => $person) {
    print "\t$i: ".get_class($person)."\n";
}

"Ich dachte, statische Methoden seien schlecht deshalb habe ich versucht, eine die Methoden auf Tabellenebene als Instanz Methoden zu machen."

Ich glaube nicht an eine pauschale Aussage, dass static ist immer schlecht, oder Singletons sind immer schlecht, oder goto ist immer schlecht, oder was auch immer. Menschen, die solche eindeutigen Aussagen machen, versuchen, die Dinge zu sehr zu vereinfachen. Verwenden Sie die sprachlichen Mittel angemessen, und sie werden Ihnen gut tun.

Allerdings ist die Wahl eines Sprachkonstrukts oft mit einem Kompromiss verbunden: Manche Dinge sind einfacher, andere schwieriger zu bewerkstelligen. Die Leute verweisen oft auf static was es schwierig macht, Unit-Test-Code zu schreiben, und auch PHP hat einige ärgerliche Unzulänglichkeiten in Bezug auf statische und Subklassifizierung. Aber es gibt auch Vorteile, wie wir in diesem Code sehen. Sie müssen selbst beurteilen, ob die Vorteile die Nachteile überwiegen, und zwar von Fall zu Fall.

"Würde Zend Framework eine Finder Klasse?"

Ich glaube nicht, dass das notwendig ist.

"Gibt es einen bestimmten Grund dafür, dass Sie die find-Methode umbenannt haben, damit sie in der der Model-Klasse umbenannt haben?"

Ich habe die Methode get() nur um sich zu unterscheiden von find() . Das "Getter"-Paradigma wird mit OO-Schnittstellen assoziiert, während "Finder" traditionell mit Datenbanken in Verbindung gebracht werden. Wir versuchen, das Domänenmodell so zu gestalten, dass es so tut, als ob keine Datenbank beteiligt wäre.

"Und würden Sie weiterhin die Logik verwenden, um die spezifischen getBy und getCollectionBy-Methoden zu implementieren?"

Ich würde mich dagegen sträuben, eine generische getBy() Methode, weil es verlockend ist, sie einen allgemeinen SQL-Ausdruck akzeptieren zu lassen und ihn dann wortwörtlich an die Datenzugriffsobjekte weiterzugeben. Dadurch wird die Verwendung unseres Domänenmodells mit der zugrunde liegenden Datenbankdarstellung gekoppelt.

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