基于 Nette\Database 的轻量级 ORM

10.1.2 2018-02-21 08:05 UTC

README

Build Status Code coverage Total downloads Latest stable

基于 Nette\Database 的轻量级 ORM

Buy me a Coffee

快速入门

考虑以下数据库模式

Database schema

实体

首先,我们将根据上述模式创建实体类。定义实体属性有两种方式 - 通过 @property[-read] 注解,或者简单通过 getter 和 setter。

标签

/**
 * @property-read int $id
 * @property string $name
 */
class Tag extends YetORM\Entity
{}

作者

/**
 * @property-read int $id
 * @property string $name
 * @property string $web
 * @property \DateTime $born
 */
class Author extends YetORM\Entity
{}

书籍

Book 实体中存在一些关系 - 两个 N:1 的 Author 和 M:N 的 Tag 关系。每个 YetORM\Entity 中都有一个 YetORM\Record 实例,它是一个简单的 Nette\Database\Table\ActiveRow 包装器。这意味着我们可以通过它访问相关记录或列值。

/**
 * @property-read int $id
 * @property string $title
 * @property string $web
 * @property string $slogan
 */
class Book extends YetORM\Entity
{
	function getAuthor()
	{
		return new Author($this->record->ref('author', 'author_id'));
	}

	function getMaintainer()
	{
		return new Author($this->record->ref('author', 'maintainer_id'));
	}

	function getTags()
	{
		$selection = $this->record->related('book_tag');
		return new YetORM\EntityCollection($selection, 'Tag', 'tag');
	}
}

使用 $record->ref($table, $column) 我们可以通过列 $column 访问表 $table 中的相关行 - 非常简单。

M:N 关系是通过 YetORM\EntityCollection 实例实现的 - 这是一个实体惰性集合。在这种情况下,它遍历来自 book_tag 表的所有相关行(第一个参数),创建 Tag(第二个参数)的实例,并在每个相关的 book_tag 表行中访问相关的 tag 表行(第三个参数),然后将其传递给 Tag 实体构造函数 :-)

这听起来很疯狂,但实际上很容易习惯。

有了这些知识,我们现在可以简单地为 Author 实体添加一些有用的方法

// class Author
function getBooksWritten()
{
	$selection = $this->record->related('book', 'author_id');
	return new YetORM\EntityCollection($selection, 'Book');
}

function getBooksMaintained()
{
	$selection = $this->record->related('book', 'maintainer_id');
	return new YetORM\EntityCollection($selection, 'Book');
}

仓库

每个仓库都必须定义表和实体类名称 - 通过 @table@entity 注解,或者通过受保护的 $table$entity 类属性。

/**
 * @table  book
 * @entity Book
 */
class BookRepository extends YetORM\Repository
{}

获取集合

YetORM\Repository 提供了基本的 findAll()findBy($criteria) 方法,两者都返回已提到的 EntityCollection

我们可以简单地遍历所有书籍

$books = new BookRepository($connection); // $connection instanceof Nette\Database\Context

foreach ($books->findAll() as $book) { // $book instanceof Book
	echo $book->title;
	echo $book->getAuthor()->name;
	foreach ($book->getTags() as $tag) { // $tag instanceof Tag
		echo $tag->name;
	}
}

获取单个实体

$book = $books->getByID(123); // instanceof Book or NULL if not found

魔法 findBy<Property>()getBy<Property>() 方法

我们不需要手动编写 findByTitle($title) 方法,如下所示

function findByTitle($title)
{
	return $this->findBy(array(
		'title' => $title,
	));
}

我们可以直接调用

$books->findByTitle($title); // without having the method implemented

这将返回具有该确切标题的书籍集合。

要获取单个实体,请使用魔法 getBy<Property>($value) 方法

$book = $books->getByIsbn('<isbn_code>'); // instanceof Book or NULL if not found

为了与 IDE 代码补全一起使用这些魔法方法,我们可以使用 @method 注解

/**
 * @table  book
 * @entity Book
 * @method YetORM\EntityCollection|Book[] findByTitle(string $title)
 * @method Book|NULL getByIsbn(string $isbn)
 */
class BookRepository extends YetORM\Repository
{}

/**
 * @property-read int $id
 * @property string $title
 * @property string $isbn
 */
 class Book extends Entity
 {}

重要:使用魔法 findBy<Property>()getBy<Property>() 方法时,请确保您已经通过 @property 注解定义了该属性!

注意:魔法 findBy<Property>()getBy<Property>() 无法在类型为 Entity 的关系属性上使用。

持久化

要持久化更改,我们只需调用 $repository->persist($entity)

$book->web = 'http://example.com';
$books->persist($book);

就是这样!

附加说明

  • 无标识映射
  • 查询效率 - 集合(或 YetORM\Record)使用 Nette\Database 的效率
  • 集合操作 - 集合可以通过 $coll->orderBy($column, $dir) 排序,并通过 $coll->limit($limit, $offset) 限制

更多

有关更多示例,请参阅 测试