2016-07-16 70 views
1

我在计算如何从数据库表中访问数据时遇到了一些问题。我知道有许多框架可以促进这一点,例如Yii,但是我试图通过与团队中其他人的学习曲线和时间限制来避开这条路线。在PHP应用程序和ORM中构建数据访问层

假设我有一个名为File的表格,其中包含文件列表及其名称,上传日期,上传用户等。我还有另一个名为Versions的表,它基本上保存每个文件的版本:当用户上传同一个文件时,它认为这是一个修订。

文件

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

版本

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

我最初的想法是创建一个类为每个表在那里我会扔在一个fidFiles构造和检索单排,并为Versions做相同的处理,其中vid将被放入构造函数中,并检索e特定版本。

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

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

也许有人可以让我对这种方式有所了解,以及在无法访问框架时该怎么做。

+0

如果您和您的团队都是PHP开发人员并且不愿意没有时间学习框架,我建议使用redbean http://www.redbeanphp.com/index.php快速且简单的orm准备好使用 –

回答

1

ORM不会解决所有的问题

在很多情况下是很方便的使用模型来访问数据库,但并非总是如此。如果你需要复杂的查询和高性能,可能会让你的ORM模型免于任务。

使用ORM模式正确

ORM只是众多编码模式,你可以在你的代码中使用的一个。

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

你不应该考虑ORM作为隔离访问数据库的应用层。这只是一种以面向对象的方式操作数据的技术。

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

考虑一下您希望获取特定文件的所有版本的情况。使用方法getAllVersions创建类似FileVersionManager的单身人士,其将通过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的实现,这应该适用于你的(任何)数据源

P.P.S.我仍然会建议你学习一些流行的框架(Yii2,Laravel,Symfony或任何其他),因为它是学习构建应用程序体系结构的最佳实践的好方法

+0

好吧。在这种情况下,查询结果会传递到它将处理数据的类模型中? – Dimitri

+0

@Dimitri如果你看看框架,那么你会发现ORM通常不会直接查询数据库。通常情况下,这是额外的层(如Yii中的QueryBuilder),而ORM只是提供一种透明的方式来处理数据库模式。你想要将你的ORM构建为严格在SQL查询之上的层,你很快就会陷入灵活性限制的陷阱--ORM将成为你的“薄弱的地方”,你会恨他们并且想要执行直接构建SQL查询而不是使用ORM。 – oakymax

+1

@Dimitri我已经更新了我的答案。包含ORM说明主题的链接以及一些建议代码结构的示例。希望这有助于 – oakymax

相关问题