简单的数据库ORM实现

2.1 2023-04-16 20:33 UTC

README

Actions Status Scrutinizer Code Quality

orm 是一个小巧简单的数据库层,实现了 活动记录 模式。它的目标是简单、快速且有用。它依赖于 [clancats hydrahon] 查询构建器进行数据库查询访问。

概述

它能做什么

  • 底层使用PDO,因此应该与任何PDO兼容的数据库兼容
  • 提供使用 clancats/hydrahon 的完整查询构建器接口
  • 支持通过活动记录实现的全局保存、销毁、更新、插入功能
  • 支持模型查找对象以帮助避免 'fat model' 综合征
  • 支持细粒度模型挂钩以精确控制模型生命周期数据
  • 支持使用 respect/validation 进行模型验证
  • 简单的关系处理 - 属于,有多个,有一个
  • 可配置模型属性的数据库类型转换

它不能做什么

  • 迁移 - 您可以使用任何您喜欢的工具来管理您的模式。我们推荐 phinx
  • 多个数据库连接 - 目前 orm 只支持单个PDO连接。

安装

您至少需要PHP7.0才能使用此库。推荐的安装方法是使用 composer

composer install ronanchilvers/orm

配置数据库

由于 orm 使用PDO,因此如何创建和实例化您的PDO对象由您自己决定。这通常在您的引导程序中完成。一旦您的PDO对象可用,您需要将其提供给 orm。以下是一个示例

$pdo = new PDO('sqlite::memory:');

Ronanchilvers\Orm\Orm::setConnection($pdo);

显然,您在实际情况中几乎肯定不会使用 :memory: DSN。然而,无论您如何创建PDO对象,这里的关键点是您需要调用 Orm::setConnection 来为 orm 提供您的连接对象。

基本用法

orm 对您的数据库模式没有任何先入之见。当从数据库构建模型时,它假设它将列映射到属性,当保存时,它假设任何属性都有一个相应的数据库表列。确保数据有意义的是您的责任。

这里我们假设我们有一个如下所示的数据库表。我们在这里使用MySQL / MariaDB语法,但任何PDO支持的语法都应该是可以的。

CREATE TABLE `books` (
  `book_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `book_author` int(11) NOT NULL,
  `book_name` varchar(1024) NOT NULL DEFAULT '',
  `book_created` datetime DEFAULT CURRENT_TIMESTAMP,
  `book_updated` datetime DEFAULT NULL,
  PRIMARY KEY (`book_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

定义模型

首先创建您的模型类。它应该扩展 Ronanchilvers\Orm\Model 类。与许多 活动记录 实现一样,我们假设这里的表名是复数(books),对应的模型类名是单数(book)。

class Book extends Model
{}

如果您的列没有列前缀,那么您只需要做这些。表名将从类名推断出来。但是,如果您像上面的示例那样有列前缀,则可以调整您的模型以适应。

class Book extends Model
{
    static protected $columnPrefix = 'book';
}

同样,如果您的表名不映射到模型名,您也可以指定它。

class Book extends Model
{
    static protected $table        = 'my_books_table';
    static protected $columnPrefix = 'book';
}

现在您可以使用模型了。

查找模型

orm 支持由 clancats/hydrahon 提供的查询构建器接口。为了从数据库检索模型,首先获取查找对象。

$finder = Orm::finder(Book::class);

然后您可以使用查找对象来检索模型。

$books = $finder->all();

您可以使用以下标准查找方法

// Get all the records in one go
$books = $finder->all();

// Get the third page of models when there are 30 records per page
// (10 per page is the default)
$books = $finder->all(3, 30);

// Get a specific model by its primary key, here assumed to be numeric
$book = $finder->one(23);

您可以使用完整的查询构建器来获得更多的查询控制

// Get all the books for author id 20
$books = $finder->select()->where('book_author', 20);

// Get all books added since last week - here we're using the excellent Carbon wrapper
// for DateTime
$recentBooks = $finder->select()->where('book_created', '>', Carbon::now()->subWeek());

您可以在clancats/hydrahon网站(https://clancats.io/hydrahon/master/)了解更多关于查询构建器的功能。

如果您想完全控制执行的SQL,您可以这样做:

$sql = "SELECT *
FROM books
  LEFT JOIN authors ON author_id = book_author
WHERE author_name LIKE :name
  AND author_created < :created";
$params = [
  'name'    => 'Fred%',
  'created' => Carbon::now()->subYear()->format('Y-m-d H:i:s'),
];
$books = $finder->query($sql, $params);

自定义查找器类

默认情况下,Orm::finder将返回一个与给定模型类绑定的纯Ronanchilvers\Orm\Finder对象。但是,您也可以通过在您的模型上设置静态属性$finder来覆盖特定模型的查找器类。如果您想添加自定义查找方法(例如),这会非常有用。

让我们假设您已经创建了一个BookFinder

class BookFinder extends Finder
{
    public function forAuthorId(int $id): array
    {
        return $this
          ->select()
          ->where(Book::prefix('author'), $id)
          ->execute();
    }
}

您可以看到我们已经创建了一个默认查找器的子类,并添加了一个forAuthorId方法,它返回给定作者ID的书籍数组。

然后我们告诉模型我们的新查找器类

class Book extends Model
{
    static protected $finder = BookFinder::class;
}

然后最终,您可以开始使用它了

// $finder will be an instance of BookFinder here
$finder = Orm::finder(Book::class);
$authors = $finder->forAuthorId(2);

持久性

将模型保存回数据库只是调用save方法的问题。

$finder = Orm::finder(Book::class);
$book = $finder->one(23);
$book->save();

同样,销毁模型也很简单

$book->delete();

访问模型数据

在任何活动记录实现中,数据库表列都表示为模型实例上的属性。因此,可以通过访问这些属性来访问数据。

$finder = Orm::finder(Book::class);
$book = $finder->one(72);
echo $book->name;