1

PHP 和 OOP 的新手,所以请多多包涵……我正处于设计和编写我的第一个 PHP 和 OOP 网站的乐观早期阶段,因为我一生都在写蹩脚的 M$ VBA 垃圾。

我有一个“用户”类,它有一个带有相关数据库调用等的保存方法......(实际上我有处理连接和 CRUD 的 DB 和 DBUtils 类 - 我的业务类只是调用 DB Utils 上的选择、更新、删除方法并传递关联的数据数组)

Anywho...我的“用户”类由一个“管理员”类扩展,它在“用户”yada yada 之外具有一些附加属性...

处理“管理员”上的保存方法的最佳方法是什么?我知道如果我向 Admin 类添加一个保存方法,它将取代 User 上的方法,但我不希望它这样做。我想在 Admin 上编写 save 方法,只处理特定于 Admin 对象的属性等,以及从“User”继承的属性在 User save 方法中处理。

那有意义吗?它是我正在寻找的特定 OOP 模式吗?任何有关我应该如何设计和构建此代码的帮助或指导将不胜感激。

编辑:哇!感谢以下所有答案。不确定哪个是我的首选。我将不得不做一些玩...

4

5 回答 5

5

您的主要问题源于您一直忽略 OOP 中的核心思想之一:单一责任原则..再说一次,似乎每个提供“答案”的人都不知道 SRP 是什么。

您所说的“业务逻辑”应该与存储相关的操作分开。no的实例User都不Admin应该知道存储是如何执行的,因为它不是业务逻辑。

$entity = new Admin;
$mapper = new AdminMapper( $db );

$entity->setName('Wintermute');
$mapper->fetch( $entity ); 
//retrieve data for admin with that name

$entity->setName('Neuromancer');
$mapper->store( $entity );
// rename and save

您在上面看到的是数据映射器模式的一个极其简化的应用。这种模式的目标是分离业务和存储逻辑。

如果不是直接实例化映射器$mapper = new AdminMapper( $db ),而是由注入的工厂提供$mapper = $this->mapperFactory->build('Admin')(因为它应该在适当的代码库中),那么您将没有关于存储介质的指示。数据可以存储在 SQL DB 或文件中,也可以存储在某个远程 REST API 中。如果映射器的界面保持不变,您可以随时更换它。

使用工厂可以避免与特定类名的紧密耦合,并且对于 SQL 数据库的映射器,可以让您在每个实例中注入相同的数据库连接。

要了解更多关于它的信息,您可以阅读thisthisthis

但是,如果您正在认真考虑学习 OOP,那么阅读这本书是必须的

于 2012-09-24T21:46:35.200 回答
3

您正在尝试实现活动记录模式

对象持久化的一种方法是提供一个共同的祖先类[例如BasicEntity]每个子类都扩展,它基于给定的数据模式构建查询:

class BasicEntity
{

    protected $tablename;

    protected $schema;

    public function update()
    {

        $fields = "";
        $placeholders = "";

        foreach($this -> schema as $field => $type)
        {

            // you join the fields here to get something like ('username', 'email', 'enabled', 'createdAt', 'password')
            // then you write your PDO statement providing placeholders like (:?, :?, :?, :?, :?)
            // you'll have to bind parameters based on their $type [int, string, date]

        }

        $query = sprintf(
            "UPDATE %s SET VALUES(%s) = %s",
            $this -> tablename,
            $fields,
            $placeholders
        );

        // execute statement here, handle exceptions, and so...

    }

}

所以你的User课会是这样的:

class User extends BasicEntity
{

    protected $id;
    protected $username;
    protected $email;
    protected $password;
    protected $enabled;
    protected $createdAt;

    public function __construct()
    {

        $this -> tablename = '_user';
        $this -> schema = array(
            'id'        => 'int',
            'username'  => 'string',
            'email'     => 'string',
            'password'  => 'string',
            'enabled'   => 'int',
            'createdAt' => 'datetime'
        );

    }

}

还有你的Admin课:

class Admin extends User
{

    protected $additionalProperty;

    public function __construct()
    {

        parent::__construct();

        $this -> schema['additionalProperty'] = 'string';

    }

}

调用update()将基于类模式构建正确的查询。这种方法适用于低复杂度级别,因为您会注意到:

  • 如果您扩展实体 [在同一张表上!],您需要提供空表字段,即使对于没有此类字段的行 [在这种情况下,additionalProperty];
  • 如果你的模式改变了[例如你改变了一个变量名],你必须把它硬编码到类构造函数中,使它更难维护;
  • 如果你想处理实体之间的关系,那么在每个 SELECT 语句中编写正确的连接将是一个很大的痛苦,除非你只是编写大量单独的查询,从而降低性能。

要解决第一个问题,您需要组合对象,因此您不会使主表增长太多[AdditionalPropertyList例如,它只是获取对外部实体的引用]。

要解决第二个问题,您必须将架构保存在外部文件中或使用内联注释

要解决第三个问题,您必须编写自己的 ORM [对象关系映射],或者更好地切换到现有的

无论如何,出于学习的好处,我会站在巨人的肩膀上,如果您计划构建可扩展且可维护的应用程序,我会选择一个框架。

于 2012-09-24T12:13:06.537 回答
3

当您在 Admin 类中编写保存方法时,您可以在推迟到标准保存的父对象之后执行额外的工作......

class Admin extends User
{
    // Other methods

    public function save()
    {
        parent::save();
        // now save your additional fields wherever they need to go
    }
}

附带说明一下,虽然使用对象来表示域,例如 User 和 Admin,但很好 - 在其他地方使用持久性逻辑有时是值得的,例如通过使用The Repository Pattern。这可以防止您的对象被绑定到持久性机制。

于 2012-09-24T11:46:27.923 回答
2

有很多方法可以解决这个问题。其中之一是有一个受保护的方法,可以在扩展类中重写以提供来自该扩展类的数据;

一个例子:

class User
{
  public function Save()
  {
    $savestuff = $this->GetProperties();
    // do save acctions
  }

  protected function GetProperties()
  {
    return 'user prop';
  }
}

class Admin Extends User
{
  protected function GetProperties()
  {
    return  'admin prop';
  }
} 
于 2012-09-24T11:46:47.990 回答
1

尽管是 OO 的新手,但听起来您确实遇到了 PHP 对象模型的主要缺点之一。您想要做的事情可以通过方法重载轻松实现(具有多个具有相同名称但采用不同参数的方法的能力,调用的实际成员函数取决于参数)。

save您可以通过检查类中方法中的参数来解决此问题Admin

class Admin
{
    public function save(Model_Abstract $model = null)//type hinting
    {
        if ($model instanceof Model_Admin)
        {
            //do admin insert/update stuff here
            return;
        }
        parent::save($model);//call regular method
    }
}
于 2012-09-24T11:51:28.510 回答