在PHP应用程序和ORM中构建数据访问层

时间:2016-07-16 03:00:23

标签: php oop orm

我在确定如何从数据库表中访问数据时遇到了一些麻烦。我知道有很多框架可以促进这一点,例如Yii,但我试图避免这条路线,因为它与我团队中的其他人的学习曲线和时间限制。

假设我有一个名为File的表,其中包含一个包含其名称,上传日期,上传用户等的文件列表。我还有另一个名为Versions的表,它基本上包含每个文件' s version:当用户上传同一个文件时,会将其视为修订版。

文件

fid | name  | uploadDate 
1   | test  | 2017-01-01
2   | test2 | 2017-01-01

版本

vid | fid   | user
1   | 1     | a
2   | 1     | b

我最初的想法是为每个表创建一个类,我将在fid构造函数中放入Files并检索该单行并对Versions执行相同的操作,其中vid将放在构造函数中并检索特定版本。

然而,这意味着我将有两个单独的查询,每个类一个。所以我想的可能是将它们合二为一。这意味着我将在Files类中的一个查询(使用LEFT JOIN)下检索文件信息和版本信息,然后使用Versions类来处理数据而不是查询数据库并处理数据。

出于某种原因,我觉得这样有更好的表现,因为我使用JOINS来帮助我,但另一方面,如果我只需要特定版本文件的信息,我需要创建File的实例,这意味着使用fid THEN从内部检索我想要的版本。 (< - 措辞不佳)。

也许有人可以给我一些关于这种方式的见解,以及在无法访问框架时该怎么做。

1 个答案:

答案 0 :(得分:1)

ORM无法解决您的所有问题

在许多情况下,使用模型访问数据库很方便,但并非总是如此。您可能更好地保持ORM模型不受需要复杂查询和高性能的任务的影响。

正确使用ORM模式

ORM只是您可以在代码中使用的众多编码模式之一。

阅读此主题以了解有关它的更多信息What is an ORM and where can I learn more about it?

您不应将ORM视为隔离数据库访问的应用程序层。它只是一种以面向对象的方式操作数据的技术。

在每个流行的框架中,您都会看到ORM是一个选项,而不是强制层。

考虑您希望获取特定文件的所有版本的情况。使用方法FileVersionManager创建类似getAllVersions singleton的内容,该方法将fid执行JOIN查询。这里也可以是checkoutToVersiongetPreviousVersion。此方法可以返回一个或一组Version ORM模型。重要的是 - 这种方式在语义上更有意义,因此更容易阅读和理解其他程序员。

或者如果您需要在每个文件更改上生成版本,您可以将此逻辑委托给类似Versionable行为或类似的东西。

我更喜欢将ORM视为某种应用程序域定义层。

所以,回答你的问题

如果您想要自己的ORM实现,那么不要太复杂。使用CRUD方法和模式定义为每个表创建单独的ORM模型。并使用其他模式执行复杂查询。

示例(应该实现Query.selectWhere以使示例正常工作):

<?php


/**
 * @property $name
 **/
class File extends ORMModel
{
    public function tableName()
    {
        return 'files';
    }

    public function properties()
    {
        return array_merge(parent::properties(), ['name']);
    }
}

/**
 * @property $number
 * @property $file_id
 **/
class Version extends ORMModel
{
    public function tableName()
    {
        return 'versions';
    }

    public function properties()
    {
        return array_merge(parent::properties(), ['file_id', 'number']);
    }

    public function getFile()
    {
        return new File($this->file_id);
    }
}

class FileVersionManager
{
    public static function getLatestVersion($file)
    {
        $query = new Query();
        $rows = $query->selectWhere((new Version())->tableName(), ['file_id' => $file->id]);

        $max = 0;
        $maxId = null;
        foreach ($rows as $row) {
            if ($row['number'] > $max) {
                $maxId = $row['id'];
                $max = $row['number'];
            }
        }

        return new Version($maxId);
    }

    public static function createNewVersion($file)
    {
        $latestVersion = static::getLatestVersion($file);
        $newVersion = new Version();
        $newVersion->file_id = $file->id;
        $newVersion->number = $latestVersion->number + 1;
        $newVersion->insert();

        return $newVersion;
    }
}

class Query
{

    private static $datasource = [
        'files' => [
            1 => [
                'name' => 'file1',
            ],
            2 => [
                'name' => 'file1',
            ]
        ],
        'versions' => [
            1 => [
                'file_id' => 1,
                'number' => 1
            ],
            2 => [
                'file_id' => 1,
                'number' => 2
            ],
            3 => [
                'file_id' => 2,
                'number' => 1
            ],
            4 => [
                'file_id' => 2,
                'number' => 2
            ],
        ]
    ];

    public function select($tablename) {
        return static::$datasource[$tablename];
    }

    public function selectOne($tablename, $id) {
        return @static::$datasource[$tablename][$id];
    }

    public function selectWhere($tableName, $condition) {
        //@todo: implement selection assuming that condition is ['propertyName1' => 'propertyValue1', 'propertyName2' => 'propertyValue2',  ]
        //should retun array of properties including id for above example
        return [];
    }

    public function update($tableName, $id, $values) {
        if (empty(static::$datasource[$tableName][$id])) return false;
        static::$datasource[$tableName][$id] = $values;
        return true;
    }

    public function insert($tableName,  $values) {
        $id = max(array_keys(static::$datasource[$tableName])) + 1;
        static::$datasource[$tableName][$id] = $values;
        return $id;
    }

    public function delete($tableName, $id)
    {
        unset(static::$datasource[$tableName][$id]);
        return true;
    }

}

/**
 * @property $id
 **/
abstract class ORMModel
{
    private $properties;

    public abstract function tableName();
    public function properties() {
        return ['id'];
    }

    public function __get($name) {
        $propertyNames = $this->properties();

        if (in_array($name, $propertyNames)) {
            return @$this->properties[$name];
        }
    }

    public function __set($name, $value) {
        $propertyNames = $this->properties();

        if (in_array($name, $propertyNames)) {
            $this->properties[$name] = $value;
        }
    }

    public function __construct($id = null)
    {
        if (!empty($id)) {
            $query = new Query();
            if ($this->properties = $query->selectOne($this->tableName(), $id)) {
                $this->properties['id'] = $id;
            }
        }
    }

    public function insert()
    {
        $query = new Query();
        $id = $query->insert($this->tableName(), $this->properties);
        $this->properties['id'] = $id;
        return $this;
    }

    public function update()
    {
        if (empty($this->properties['id'])) return false;

        $query = new Query();
        return $query->update($this->tableName(), $this->id, array_diff($this->properties, ['id']));
    }

    public function delete()
    {
        if (empty($this->properties['id'])) return false;

        $query = new Query();
        return $query->delete($this->tableName(), $this->id);
    }
}


$version = new Version(1);
$file = $version->getFile();
var_dump($file->name);

$file = new File(2);
$version = FileVersionManager::getLatestVersion($file);
var_dump($version->number);

FileVersionManager::createNewVersion($file);
$version = FileVersionManager::getLatestVersion($file);
var_dump($version->number);

P.S。您可以在这里定义自己的Query实施,这应该适用于您的(任意)数据源

PPS 我仍然建议您学习一些流行的框架(Yii2,Laravel,Symfony或其他任何框架),因为这是学习构建应用程序架构的最佳实践的好方法