【问题标题】:Superclass static var shared over children子类共享的超类静态变量
【发布时间】:2014-05-23 08:25:20
【问题描述】:

标题有点误导,因为我知道不可能在子级上共享静态 var,但我正在寻找一种方法来解决我的问题,而无需使用共享静态 var。

我有一个名为“Model”的超类和一些名为“User”和“Company”的底层子类。我想尽可能地自动化,以便子类只有字段,仅此而已。

我想在超类中创建 static 'GetAll' 和 'GetByKey' 方法,以便可以使用以下代码:

 $users = User::getAll();
 $company = Company::getByKey(34); // Where 34 is the ID.

就我而言,Company 类应该知道他的主键,而无需我每次都告诉他。我不想在子类中避免这样的方法:

public static function getByUserId($userId)
{
    return static::find(array('userId' => $userId));
}

为此,我在超类中创建了一个静态变量“primaryKey”。问题是,没有完美的地方来“设置”变量。如果我唯一要做的就是获取所有用户,则不会使用构造函数。接下来,静态变量属于模型,这意味着它将被覆盖,如果我有这样的代码:

 $company = new Company(); // Model sets 'primaryKey' to 'id'
 $user = User::GetByKey('myUsername'); // The 'primaryKey' is still 'id', but it should be 'username'

确实想要保存 $primaryKey,因为每次我请求某些模型时从 MySQL 获取主键只是开销。

有没有办法给子类自己的静态变量值而不在特定类中创建它们?我想尽量减少为模型所做的工作量。使用反射或魔术方法完全没有问题。在最荒谬的情况下,我必须创建一个 DatabaseManager,它有一个包含所有模型的所有信息的数组。

注意:我问的是是否有解决我的问题的方法,即使不推荐或丑陋的代码也是如此。我知道这个问题已经被问过很多次了。

提前致谢,

【问题讨论】:

  • 如果我正确理解了您的问题,如果您在没有静态的情况下设计应用程序逻辑,您就不会遇到这个问题。
  • 如何在每个类中设置主键为常量,然后通过常量([称为类].'primary_key')获取值。很抱歉无法提供一些代码,但我正在浏览 SO,同时接听一些支持电话@work。
  • 你为什么不简单地实例化你的类?!请看How Not To Kill Your Testability Using Statics
  • illuzive:我有更多这样的变量,这样可以解决问题。 @deceze,Ben-beri:您的意思是: $modelManager->GetByKey('User', 'myUsername'); ?
  • @ErwinOkken 看看我的回答。

标签: php inheritance static


【解决方案1】:

您可以在此处使用 Services 和 DataMapper 模式,而不是使用 dirty statics

根据How should a model be structured in MVC?

另外,您似乎无法理解模型是什么,请查看上面的链接,仔细阅读。

我无法真正将您的代码想法完全实现到这个示例中 - 因为我并没有真正理解那里发生了什么(静态的力量)

我将尝试向您展示一个非常相似的示例:

class CompanyBusiness {

    private $mapper;
    private $domain;

    public function __construct(CompanyMapper $mapper, CompanyDomain $domain) {
        $this->mapper = $mapper;
        $this->domain = $domain;
    }
}

在此示例中,您有一个 Comapany 服务,它现在处理公司工作人员(雇主)。您想从向您的公司添加新工作人员开始,服务将从您处理输入/输出的地方接收数据(即控制器),然后将数据初始化到DomainObject .注意:您的公司仅允许该员工的电子邮件有效且他至少 23 岁,因此您必须在您的 DomainObject 中验证他的数据,请参见以下示例:

/**
 * Adds a new worker to the comapny, following the company rules.
 * @param $firstname    string  First name of the worker
 * @param $lastname     string  Last name of the worker
 * @param $age          int     Age of the worker
 * @param $email        string  E-mail of the worker
 * @return bool         Business action succeed
 */
public function addWorker($firstname, $lastname, $age, $email) {
    $this->domain->setFirstname($firstname);
    $this->domain->setLastname($lastname);
    $this->domain->setAge($age);
    $this->domain->setEmail($email);

    if ($this->domain->validated()) {
        $this->mapper->addNewWorker($this->domain);
        return true;
    }
    else {
        // error has occured, maybe set error message?
    }
    return false;
}

还有你的域方法:

/**
 * Checks if worker follows the requirements/valid
 * @return bool valid
 */
public function validated() {
    return $this->age >= self::MINIMUM_AGE && filter_var($this->email, FILTER_VALIDATE_EMAIL);
}

注意:self::MINIMUM_AGE 是域类中的常量。

DataMapper 是有趣的部分,它是处理数据结构(即 MySQL 数据库)的类。

这就是我在上面的实现中使用它的方式:

class CompanyMapper extends Mapper {

    private $tables = array(
        'workers'   => 'company_workers'
    );

    public function addNewWorker(CompanyDomain $company) {
        parent::getDB()->insert($this->tables['workers'], array(
            "first_name"    => $company->getFirst(),
            "last_name"     => $company->getLast(),
            "age"           => $company->getAge(),
            "email"         => $company->getEmail()));
    }

    public function getWorkerProfileByID($id) {
        /** @var $query PDOStatement */
        $query = parent::getDB()->fetch($this->tables['workers'], array("worker_id" => $id));
        return $query->fetch();
    }
}

基本上 DataMappers 应该包含数据库连接,不管在哪里 - 我已经使用父类将它存储在那里。

再举一个例子,获取特定工人的个人资料数据(通过他的 ID)

/**
 * @param $id       int ID of the worker
 * @return array    The data of user's profile
 */
public function getWorkerProfile($id) {
    $data = $this->mapper->getWorkerProfileByID($id);
    return $data; // check for nulls after receiving return or set error messages
}

另外,如果你打算像这样使用依赖注入,你最终会厌倦注入和关心这些构造函数中的依赖,你可以使用一个 DI 容器,它会自动为你创建和注入这些对象,@ 987654322@看一下自述文件,它将解释和展示有用的东西。

【讨论】:

    猜你喜欢
    • 2013-07-12
    • 2011-01-31
    • 1970-01-01
    • 2017-03-22
    • 1970-01-01
    • 2015-10-14
    • 1970-01-01
    • 2016-04-17
    • 1970-01-01
    相关资源
    最近更新 更多