域对象管理器


Domain Object Manager?

我目前正在探索一个框架,并在其中对服务进行编码。服务本身有越来越多的方法(这还不错,因为它是一个服务)。这种不安来自于Service用于将DB数据转换为Domain对象的映射器,因为该类正在发展到我感到不舒服的地方(每个Service方法至少需要2个mapper方法和一个属性(一个用于获取数据,一个用于映射数据和SQL属性)。

所以我的问题是,是否建议为每个服务方法创建一个映射器类(例如,使用工厂)?

所以,这就是有问题的类:

<?php
import('sites::foo::biz', 'Held');
import('sites::foo::biz', 'Attribut');
import('sites::foo::biz', 'Generierung');
import('sites::foo::biz', 'Talent');
import('sites::foo::biz', 'Kampftalent');
class HeldenblattMapper extends APFObject
{
    const SQL_GET_HELD = <<<SQL
    SELECT
        name,
        geschlecht,
        groesse,
        gewicht,
        tsatag,
        haare,
        augen,
        aussehen,
        stand,
        titel,
        SO,
        hintergrund,
        AP_frei   AS freieAP,
        AP_gesamt AS alleAP
    FROM
        helden
    WHERE
        id = ?
    ;
SQL;
    const SQL_GET_ATTR = <<<SQL
    SELECT
        Attribut,
        Wert
    FROM
        held_attribute
    WHERE
        HeldenID = ?
    ;
SQL;
    const SQL_GET_TALENTE = <<<SQL
    SELECT
        Talent,
        Probe,
        Attribute,
        Wert,
        Kategorie
    FROM
        held_talente
    WHERE
        HeldenID = ? AND Kategorie = ?
    ORDER BY
        Kategorie, Talent
    ;
SQL;
    const SQL_GET_RKP = <<<SQL
    SELECT
        RKP,
        Typ
    FROM
        held_rkp
    WHERE
        HeldenID = ? AND pri_sec = ?
    ;
SQL;
    const SQL_GET_KAMPF = <<<SQL
    SELECT
        Talent,
        Spezialisierung,
        AT_PA,
        Wert,
        Kategorie,
        Attacke(HeldenID)   AS AT,
        Parade(HeldenID)    AS PA,
        Fernkampf(HeldenID) AS FK
    FROM
        held_kampftalente
    WHERE
        HeldenID = ? AND Kategorie = ?
    ORDER BY
        Kategorie DESC, Talent ASC
    ;
SQL;
    public function getHeld($id)
    {
        $PDOHeld = $this->getPDOById(self::SQL_GET_HELD, $id);
        return $this->mapPDOHeld2DomainObject($PDOHeld);
    }
    public function getAttributeByHeld($id)
    {
        $PDOAttr = $this->getPDOById(self::SQL_GET_ATTR, $id);
        return $this->mapPDOAttribute2DomainObjects($PDOAttr);
    }
    public function getRKPByHeld($id)
    {
        $sql = 'SELECT RKP FROM held_rkp WHERE HeldenID = ? AND Typ = ? ORDER BY pri_sec ASC;';
        $rkp = $this->getPDOConnection()->prepareStatement($sql);
        $rkp->bindParam(1, $id,  PDO::PARAM_INT);
        $rkp->bindParam(2, $typ, PDO::PARAM_STR);
        $arr   = array();
        $types = array('Rasse', 'Kultur', 'Profession');
        foreach ($types as $typ)
        {
            $rkp->execute();
            $arr[$typ] = $this->mapPDORKP2DomainObject($rkp);
        }
        return $arr;
    }
    public function getTalenteByHeld($id)
    {
        $sql = 'SELECT DISTINCT Kategorie FROM held_talente WHERE HeldenID = ?';
        $PDOKat = $this->getPDOById($sql, $id);
        $PDOKat->setFetchMode(PDO::FETCH_COLUMN, 0);
        $PDOTal = $this->getPDOConnection()->prepareStatement(self::SQL_GET_TALENTE);
        $PDOTal->bindParam(1, $id,  PDO::PARAM_INT);
        $PDOTal->bindParam(2, $kat, PDO::PARAM_STR);
        $arr = array();
        foreach ($PDOKat as $kat)
        {
            $PDOTal->execute();
            $arr[$kat] = $this->mapPDOTalente2DomainObjects($PDOTal);
        }
        return $arr;
    }
    public function getKampftalenteByHeld($id)
    {
        $sql = 'SELECT DISTINCT Kategorie FROM held_kampftalente WHERE HeldenID = ?';
        $PDOKat = $this->getPDOById($sql, $id);
        $PDOKat->setFetchMode(PDO::FETCH_COLUMN, 0);
        $PDOTal = $this->getPDOConnection()->prepareStatement(self::SQL_GET_KAMPF);
        $PDOTal->bindParam(1, $id,  PDO::PARAM_INT);
        $PDOTal->bindParam(2, $kat, PDO::PARAM_STR);
        $arr = array();
        foreach ($PDOKat as $kat)
        {
            $PDOTal->execute();
            $arr[$kat] = $this->mapPDOKampf2DomainObjects($PDOTal);
        }
        return $arr;
    }
    private function mapPDOHeld2DomainObject(PDOStatement $ps)
    {
        $row = $ps->fetch(PDO::FETCH_ASSOC);
        if (!$row)
        {
            throw new DomainException('No Character data found.');
        }
        $held = new Held();
        $held->setName($row['name']);
        $held->setGeschlecht($row['geschlecht']);
        $held->setAlleAP($row['alleAP']);
        $held->setFreieAP($row['freieAP']);
        return $held;
    }
    private function mapPDOAttribute2DomainObjects(PDOStatement $ps)
    {
        $ps->setFetchMode(PDO::FETCH_ASSOC);
        $arr = array();
        foreach ($ps as $row)
        {
            $attribut = new Attribut();
            $attribut->setName($row['Attribut']);
            $attribut->setWert($row['Wert']);
            $arr[] = $attribut;
        }
        return $arr;
    }
    private function mapPDORKP2DomainObject(PDOStatement $ps)
    {
        $rkp = new Generierung();
        $rkp->setRKPName($ps->fetchColumn());
        $mod = $ps->fetchColumn();
        if ($mod)
        {
            $rkp->setModifikation($mod);
        }
        return $rkp;
    }
    private function mapPDOTalente2DomainObjects(PDOStatement $ps)
    {
        $ps->setFetchMode(PDO::FETCH_ASSOC);
        $arr = array();
        foreach ($ps as $row)
        {
            $talent = new Talent();
            $talent->setName($row['Talent']);
            $talent->setWert($row['Wert']);
            $talent->setAttribute($row['Attribute']);
            $talent->setProbe($row['Probe']);
            $talent->setTyp($row['Kategorie']);
            $arr[] = $talent;
        }
        return $arr;
    }
    private function mapPDOKampf2DomainObjects(PDOStatement $ps)
    {
        $ps->setFetchMode(PDO::FETCH_ASSOC);
        $arr = array();
        foreach ($ps as $row)
        {
            $talent = new Kampftalent();
            $talent->setName($row['Talent']);
            $talent->setWert($row['Wert']);
            $talent->setSpezialisierung($row['Spezialisierung']);
            $talent->setAtPaFromDBValue($row['AT_PA'], '_');
            if ('Nahkampf' == $row['Kategorie'])
            {
                $at = (int) $row['AT'] + $talent->getAT();
                $talent->setAT($at);
                $pa = (int) $row['PA'] + $talent->getPA();
                $talent->setPA($pa);
            }
            elseif ('Fernkampf' == $row['Kategorie'])
            {
                $fk = (int) $row['FK'] + $talent->getAT();
                $talent->setAT($fk);
            }
            $arr[] = $talent;
        }
        return $arr;
    }
    private function getPDOConnection()
    {
        $cm = $this->getServiceObject('core::database', 'ConnectionManager');
        return $cm->getConnection('PDO');
    }
    private function getPDOById($sql, $id)
    {
        $ps = $this->getPDOConnection()->prepareStatement($sql);
        $ps->bindValue(1, (int) $id, PDO::PARAM_INT);
        $ps->execute();
        return $ps;
    }
}

相关服务

<?php
class HeldenblattService extends APFObject
{
    private $id;
    public function init($param)
    {
        if (!is_int($param))
        {
            throw new InvalidArgumentException('Cannot create a Character Sheet without a valid Character ID.');
        }
        $this->id = $param;
    }
    public function getHeld()
    {
        $mapper = $this->getMapper();
        return $mapper->getHeld($this->id);
    }
    public function heldGetRKP()
    {
        $mapper = $this->getMapper();
        return $mapper->getRKPByHeld($this->id);
    }
    public function heldGetAttribute()
    {
        $mapper = $this->getMapper();
        return $mapper->getAttributeByHeld($this->id);
    }
    public function heldGetTalente()
    {
        $mapper = $this->getMapper();
        return $mapper->getTalenteByHeld($this->id);
    }
    public function heldGetKampf()
    {
        $mapper = $this->getMapper();
        return $mapper->getKampftalenteByHeld($this->id);
    }
    private function getMapper()
    {
        return $this->getDIServiceObject(
            'sites::foo::data', 
            'HeldenblattMapper'
        );
    }
}

看起来它们也一样,但一个是业务类,另一个是模型类(关注点分离)。

我更喜欢一个带有多个映射器的单一服务,因为域数据库映射的实现需要更多的代码,而不仅仅是服务的加载/保存调用。

如果服务正在增长(目前没有太多),我可以考虑为每个部分/主题制作一个服务映射器对。否则mapper类将容易地扩展500行。