1

警告:可能导致 TL:DR

我正在使用 PHP 5.3.10 并遇到以下问题。我确实有一个抽象类DataMapper,它针对DataModel我想要坚持的特定对象进行了扩展。下面的代码可以做到这一点:

abstract class DataMapper {
    public abstract function findById($id);
    public abstract function fetchAll();
    public abstract function save(IModel $model);               // DISCUSSION
    /* more helper functions here */
}

class PersonMapper extends DataMapper {
    public function findById($id) { /* ...magic ... */ }
    public function fetchAll() { /* ...magic ... */ }
    public function save(IModel $model) { /* ...magic ... */ }  // DISCUSSION
}

interface IModel {
    public function setOptions(array $options);
    public function toArray();
}

abstract class Model implements IModel {
    protected $_fields = array();
    protected $_data = array();

    public function setOptions(array $options) { /* ...magic ... */ }
    public function toArray() { /* ...magic ... */ }

    public function __construct(array $options = null) { /* ...magic ... */ }
    public function __set($name, $value) { /* ...magic ... */ }
    public function __get($name) { /* ...magic ... */ }
}

class PersonModel extends Model {
    protected $_fields = array('id', 'name', 'passhash', /*...*/);

    public function setId($value) {
        /* ...Validation happening... */
        $this->_data['id'] = $value;
        return $this;
    }

    public function checkPassword($password) { /* ...magic... */ }
}

这工作正常,但对我的感觉来说真的很古怪。

如您所见,我使用了一个接口IModel来告诉 DataMapper,它确实需要一组特定的参数和方法。但是,某些模型确实具有相应 DataMapper 所需的额外方法 - 在示例中,一种checkPassword()方法用于根据存储的哈希值测试密码。此方法还可以指示 DataMapper 重新散列刚刚测试的密码并根据新要求(例如密码散列函数的难度增加)对其进行更新。

所以我真正想要的是将 PersonMapper 的签名更改为PersonMapper::save(PersonModel $model)- 例如在另一个 DataMapper 中更改为PostMapper::save(PostModel $model)等。这是由于这些 DataMapper 需要某个签名。所以我理想的解决方案是这样的:

abstract class DataMapper {
    public abstract function findById($id);
    public abstract function fetchAll();
    public abstract function save(Model $model);                   // UPDATED
}

class PersonMapper extends DataMapper {
    public function findById($id) { /* ...magic... */ }
    public function fetchAll() { /* ...magic... */ }
    public function save(PersonModel $model) { /* ...magic... */ } // UPDATED
}

abstract class Model { /* ...unchanged... */ }

class PersonModel extends Model { /* ...unchanged... */ }

请注意抽象类中的 Update save-Methods 及其实现。由于PersonModel继承自Model,因此显然具有一组通用的基本签名,我希望这可以正常工作。但事实并非如此——PHP 抱怨子类 PersonMapper 中的接口发生了变化

我的问题:

  • 是否有另一个使用 PHP 5.3.10 的解决方案可以更好地表达这种关系?
  • 它是否可以在更高版本的 PHP 中工作,因此可能值得升级服务器?
4

1 回答 1

1

您可以尝试改用接口。

interface OtherModel {
    public function getThis();
}

interface OtherOtherModel {
    public function getThat();
}

您的模型类可能实现一个或多个接口...

class PersonModel extends Model implements OtherModel {
    protected $_fields = array('id', 'name', 'passhash', /*...*/);

    public function setId($value) {
        /* ...Validation happening... */
        $this->_data['id'] = $value;
        return $this;
    }

    public function checkPassword($password) { /* ...magic... */ }

    public function getThis() {
        // ...
    }
}

您的具体 Mapper 类可以使用 instanceof 来检查此模型是否执行了应有的操作。

class PersonMapper extends DataMapper {
    public function findById($id) { /* ...magic... */ }
    public function fetchAll() { /* ...magic... */ }
    public function save(Model $model) { 
       // verify that certain methods are implemented...
       // throw an exception or reacting accordingly
       print ($model instanceof PersonModel)? 'yes' : 'no';
       print ($model instanceof OtherOtherModel)? 'yes' : 'no';
    } 
}

另一种可能的方法可能如下:

<?php

abstract class DataMapper {
    public abstract function findById($id);
    public abstract function fetchAll();

    public  function save(Model $model) {
        throw new Exception('You have to implement this!');
    }
}

如果在继承类中未覆盖保存方法,则抛出异常。现在你真的可以使用不同的类型提示了。这将起作用:

class PersonMapper extends DataMapper {
    public function findById($id) { /* ...magic... */ }
    public function fetchAll() { /* ...magic... */ }
    public function save(PersonModel $model)  {
        // do something
    }
}

我可以想到另一种可能的方法,即使用接口来定义实现。例如:

interface PersonModelAware {
   public function save(PersonModel $model);
}

interface OtherModelAware {
   public function save(OtherModel $model);
}

等等你的抽象方法可能有一个默认的保存方法或根本没有保存方法。继承类将实现它需要的接口。

总而言之,使您的类型更具体是行不通的,因为抽象方法清楚地表明它需要一个模型。

于 2013-11-06T21:59:08.453 回答