【问题标题】:A PHP design pattern for the model part [PHP Zend Framework]模型部分的 PHP 设计模式 [PHP Zend Framework]
【发布时间】:2010-03-23 10:38:11
【问题描述】:

我有一个使用 Zend 框架的 PHP MVC 应用程序。如快速入门中所述,我使用 3 层作为模型部分:

  • 模型(业务逻辑)
  • 数据映射器
  • 表数据网关(或数据访问对象,即每个 SQL 表一个类)

该模型是 UML 设计的,完全独立于 DB。

我的问题是:我不能拥有同一“实例/记录”的多个实例。

例如:例如,如果我得到 id=5 的用户“Chuck Norris”,这将创建一个新的模型实例,其中成员将被填充由数据映射器(数据映射器查询查询数据库的表数据网关)。然后,如果我将名称更改为“Duck Norras”,不要立即将其保存在数据库中,而是在另一个变量中重新加载相同的用户,我会遇到“同步”问题......(不同的实例相同的“记录

现在,我使用 Multiton / Identity Map 模式:类似于 Singleton,但多个实例由一个键索引(在我们的示例中是用户 ID)。但这让我的开发和测试变得非常复杂。

怎么做才对?

【问题讨论】:

    标签: php design-patterns zend-framework oop


    【解决方案1】:

    Identity Map

    编辑

    回应此评论:

    如果我有“select * from X”,如何跳过获取已加载的记录?

    您不能在查询本身中,但您可以在将行加载到实体对象的逻辑中。在伪代码中:

    class Person {}
    
    class PersonMapper {
      protected $identity_map = array();
      function load($row) {
        if (!isset($this->identity_map[$row['id']])) {
          $person = new Person();
          foreach ($row as $key => $value) {
            $person->$key = $value;
          }
          $this->identity_map[$row['id']] = $person;
        }
        return $this->identity_map[$row['id']];
      }
    }
    
    class MappingIterator {
      function __construct($resultset, $mapper) {
        $this->resultset = $resultset;
        $this->mapper = $mapper;
      }
      function next() {
        $row = next($this->resultset);
        if ($row) {
          return $this->mapper->load($row);
        }
      }
    }
    

    实际上,您可能希望您的 MappingIterator 实现 Iterator,但为简洁起见,我跳过了它。

    【讨论】:

    • ...问题是当我需要执行特定查询(选择哪里...)时,我必须绕过身份映射,这可能会导致同一“身份”的多个实例跨度>
    • 听起来你正在以一种奇怪的方式实现它。如果你在水合阶段使用恒等映射,你可以跳过那些已经在映射中的记录。
    • 如果我有“select * from X”,如何跳过获取已加载的记录?
    【解决方案2】:

    将所有加载的模型实例保存在“实时模型池”中。当您加载/查询模型时,首先检查它是否已经加载到池中(使用主键或类似概念)。如果是,则从池中返回对象(或引用)。这样,您的所有引用都指向同一个对象。我的术语可能不正确,但希望您能理解。基本上,池充当业务逻辑和数据库之间的缓存。

    【讨论】:

    • 是的,我明白了,这正是我一直在做的。但是问题是当你想要所有用户的列表时,你不能使用“池”,所以你必须做一个 SQL 查询,但是你会有多个实例用于同一个用户。解决方案是始终检查池,但这需要做很多工作,不切实际......而且您必须始终牢记这一点,否则您将面临调试噩梦
    • 所以基本上我们在谈论 ORM 的优点/缺点。如果实施得当,它不一定是调试的噩梦,但总会对性能造成影响。池检查部分当然是在您的 ORM 层中透明地完成的,因此您不必担心业务逻辑层中的这一点。一个相关的[问题][1]。 [1]:stackoverflow.com/questions/108699/good-php-orm-library
    • 您的链接将我指向 Xyster (xyster.libreworks.net/documentation/guide/…),这正是我正在尝试开发的,但当然更好。谢谢你
    • 我很高兴听到这个消息。就个人而言,我多年来一直在寻找 PHP ORM 的圣杯,并且开发了一些,但仍然没有找到它;)
    • 好吧,看了 Xyster 之后,它似乎是一个“单人”项目。但我读到 Doctrine 2 会做到这一点,所以我会等待......
    【解决方案3】:

    多吨

    如果您想在项目中使用各种单例,最好的选择。

    <?php
    abstract class FactoryAbstract {
        protected static $instances = array();
        public static function getInstance() {
            $className = static::getClassName();
            if (!(self::$instances[$className] instanceof $className)) {
                self::$instances[$className] = new $className();
            }
            return self::$instances[$className];
        }
        public static function removeInstance() {
            $className = static::getClassName();
            if (array_key_exists($className, self::$instances)) {
                unset(self::$instances[$className]);
            }
        }
        final protected static function getClassName() {
            return get_called_class();
        }
        protected function __construct() { }
        final protected function __clone() { }
    }
    abstract class Factory extends FactoryAbstract {
        final public static function getInstance() {
            return parent::getInstance();
        }
        final public static function removeInstance() {
            parent::removeInstance();
        }
    }
    // using:
    class FirstProduct extends Factory {
        public $a = [];
    }
    class SecondProduct extends FirstProduct {
    }
    FirstProduct::getInstance()->a[] = 1;
    SecondProduct::getInstance()->a[] = 2;
    FirstProduct::getInstance()->a[] = 3;
    SecondProduct::getInstance()->a[] = 4;
    print_r(FirstProduct::getInstance()->a);
    // array(1, 3)
    print_r(SecondProduct::getInstance()->a);
    // array(2, 4)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-25
      • 1970-01-01
      • 2012-03-06
      • 2012-01-07
      • 2012-05-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多