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();
}

モデル クラスは 1 つ以上のインターフェイスを実装する可能性があります...

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() {
        // ...
    }
}

具体的なマッパー クラスは、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!');
    }
}

save メソッドが継承クラスでオーバーライドされていない場合は、例外をスローします。これで、実際に別のタイプヒントを使用できるようになりました。これはうまくいきます:

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 に答える