computools / clight-orm
轻量级ORM
Requires
- php: ^7.1
Requires (Dev)
- phpunit/phpunit: >=4.8 < 6.0
This package is not auto-updated.
Last update: 2024-10-02 09:03:36 UTC
README
这个库被设计为常规的ORM。
ORM开发的目的在于创建一个快速且方便的工具,用于处理数据库和关系映射。ORM允许你将实体与一对一、一对多、多对一、多对多关系类型关联起来。
安装
composer require computools/clight-orm
结构元素
-
实体 - 数据库表的表示。这些对象必须具有属性获取器和设置器,并扩展库中的 AbstractEntity。
-
仓库 - 基于 Repository 模式,这些对象必须具有实体对象定义,并扩展自 AbstractRepository。抽象仓库包含所有常见的(如 find、findBy 等)数据搜索和保存方法。
示例
示例请参考 tests 目录。
让我们看看
实体
AbstractEntity 继承涉及实现 getTable() 方法,该方法将定义数据库表名,以及 getFields() 方法,该方法返回规则数组。
此外,您可以提供一些可选字段,如果查询结果中存在字段,则将进行映射。例如,我们可以添加一些 count 字段,该字段不会与表相关联,但将在查询结果中呈现。
为此,您可以使用 getOptionalFields() 方法。
键必须指定为表列名。
允许的字段类型
- IdType 带有可选参数 'identifierField',如果表的标识符列名不是 'id',则需要指定。
- IntegerType - 整数,带有可选参数 'columnName',如果映射的字段与数据库字段不同。
- StringType - 字符串或文本,带有可选参数 'columnName',如果映射的字段与数据库字段不同。
- FloatType - 浮点数或小数,带有可选参数 'columnName',如果映射的字段与数据库字段不同。
- DateTimeType - 日期时间,列名作为第一个参数,格式作为第二个参数。
- CreatedAtType - 日期时间,值将在实体创建时自动定义。第一个参数是 columnName,第二个是 datetime 格式,两者都是可选的。
- UpdatedAtType - 日期时间,值将在实体更新时自动定义。第一个参数是 columnName,第二个 - datetime 格式,两者都是可选的。
- BooleanType - 布尔值,第一个参数是 column name 如果与表字段名不同,第二个参数定义字段是否必须保存为 int(默认为 true)。
- JsonType - json,将 json 保存到数据库,并将数组转换为实体。第一个参数 - 表字段名。
允许的关系类型
-
OneToOne - 第一个参数是相关实体实例,第二个是主表字段名
-
ManyToOne - 第一个参数是相关实体实例,第二个是主表字段名。
-
OneToMany - 第一个参数是相关实体实例,第二个是相关表字段。
-
ManyToMany - 带参数
- entity - 相关实体实例
- table - 多对多关系表名
- columnName - 关系表列名,对应主表
- referencedColumnName - 关系表列名,对应引用表
use Computools\CLightORM\Entity\AbstractEntity;
class Book extends AbstractEntity { public function getTable(): string { return 'books'; }
public function getFields(): array { return [ 'id' => new IdType(), 'name' => new StringType('title'), 'price' => new FloatType(), 'authors' => new ManyToMany(new Author(), 'authors_books', 'book_id', 'author_id'), 'themes' => new ManyToMany(new Theme(), 'books_theme', 'book_id', 'theme_id') ]; } public functnion getOptionalFields(): array { return [ 'themes_count' => new IntegerType() ]; } private $id; private $name; private $authors = []; private $themes = []; private $price; private $themesCount; public function getThemesCount() { return $this->themesCount; } public function getId(): ?int { return $this->id; } public function getName(): ?tring { return $this->name; } public function setName(?string $name): void { $this->name = $name; } public function getAuthors(): array { return $this->authors; } public function getThemes(): array { return $this->themes; } public function getPrice(): ?float { return $this->price; } public function setPrice(float $price) { $this->price = $price; }
}
标识字段必须能够在新实体(尚未保存到数据库)中接受 null。
另一种选择是使用公共属性而不是getter和setter。这个库支持这种类型的实体。
use Computools\CLightORM\Entity\AbstractEntity;
class Book extends AbstractEntity
{
public $id;
public $name;
public $authors;
public $themes;
public $price;
}
如果您想将null设置为一对一或多对一关系,可以使用destroyToOneRelation(string $field)方法
$post->destroyToOneRelation('author');
$postRepository->save();
此操作将null保存到post记录的author_id字段。无论使用何种方法接收实体,都可以使用此功能。所以两者都有效
$postRepository->find(1, ['author']);
$post->destroyToOneRelation('author');
$postRepository->find(1);
$post->destroyToOneRelation('author');
如果您想为两个实体添加多对多关系,可以调用addRelation(EntityInterface $entity)方法,并调用removeRelation(EntityInterface $entity)来移除。
$post->addRelation($user);
$post->removeRelation($user);
ORM可以执行实体的批量赋值。因此,您可以使用此结构
$book = new Book();
$book->fill([
'name' => 'Book name',
'price' => 10.99
]);
$bookRepository->save($book);
代替
$book = new Book();
$book->setName('Book name');
$book->setPrice(10.99);
$bookRepository->save($book);
如果为实体设置了$allowedFields属性,则此类操作将被允许。所以它看起来像
class Book
{
protected $allowedFields = [
'name',
'price'
];
public $name;
public $price;
}
这是可以使用fill()方法设置的字段列表。如果指定的字段不在列表中,则将其跳过。
仓库
use Computools\CLightORM\Repository\AbstractRepository;
use Computools\CLightORM\Test\Entity\Book;
class BookRepository extends AbstractRepository
{
public function getEntityClass(): string
{
return Book::class;
}
public function findByUser(User $user): ?Book
{
return $this->findBy(['user_id' => $user->getId()]);
}
}
这是实现仓库的方式。您可以编写自己的方法或使用现有方法。
要调用仓库,可以使用
Computools\CLightORM\CLightORM
以下是一个示例
$pdo = new \PDO(
sprintf(
'%s:host=%s;port=%s;dbname=%s',
'mysql',
'127.0.0.1',
'3306',
'test'
),
'user',
'password'
);
$clightORM = new CLightORM($pdo);
要获取特定的实体仓库,只需使用类字符串作为参数调用create方法
$repository = $clightORM->createRepository(PostRepository::class);
$repository->find(2);
仓库方法
- find(int $id, array $with, $expiration = 0)
- findBy(array $citeria, ?Order $order, array $with, ?Pagination $pagination, $expiration = 0)
- findOneBy(array $criteria, ?Order $order = null, array $with, $expiration = 0)
- findFirst($with)
- findLast($with)
- save(EntityInterface $entity, array $with, $relationExistsCheck = false)
- remove(EntityInterface $entity)
Computools\CLightORM\Tools\Order对象可以用于对查询结果进行排序。
$repository->findBy([
'name' => 'test'
],
new Order('name', 'DESC')
)
expiration参数可以用于将搜索结果存储到缓存中。如果不等于0,则第一次调用结果将存储到缓存中。然后方法调用将返回缓存中的数据,直到过期。有关详细信息,请参阅缓存部分。
With参数提供您将相关实体包含到结果中的可能性。您还可以获取相关实体的相关实体等。例如
$book = $bookRepository->findLast(['themes', 'authors']);
这将找到具有相关主题和作者的最后本书(您必须指定与关系对应的实体字段名称)
$book = $bookRepository->findLast(
[
'themes' => [
'posts'
],
'authors'
]
);
这将找到具有主题和作者的最后本书。此外,还会找到所有主题的相关帖子。对于save方法,您也可以定义$with参数,以在结果中获取相关实体。
嵌套级别不受限制,因此您可以使用此结构
$book = $bookRepository->findLast(
[
'themes' => [
'posts' => [
'editors' => [
'userProfile'
]
]
],
'authors'
]
);
仓库'save'方法的第一个参数是对象链接,因此您可能无法使用方法结果覆盖对象变量。
$post = new Post();
$post->setUser($user);
$postRepository->save($post);
return $post;
但是,当然,您可以使用返回值
$post = new Post();
$post->setUser($user);
return $postRepository->save($post);
如果仓库结果给出的是一个集合,则它将是实体数组。
您可以使用Computools\CLightORM\Tools\Pagination作为findBy的第三个参数来分页结果。
$posts = $repository->findByUser($this->getUser(), ['theme'], (new Pagination())->setPagination(1, 20));
或者
$posts = $repository->findByUser($this->getUser(), ['theme'], (new Pagination())->setLimitOffset(20, 0));
您还可以在仓库类内部使用orm对象来执行一些自定义查询。
查询
您可以使用内置的查询对象来完成一些自定义逻辑。
CLightORM对象可以创建任何类型的查询。此对象在任何一个仓库中都是可访问的。
class PostRepository extends AstractRepository
{
public function getEntityClass(): string
{
return Post::class;
}
public function findPostsCustom()
{
$query = $this->orm->createQuery();
$query
->select('id, name')
->from($this->table)
->where('title', 'test')
->where('type', 'test')
->whereExpr('id < 5')
->whereExpr('id > 2')
->whereArray([
'title' => 'test',
'type' => 'test'
])
->groupBy('id')
->order('id', 'DESC')
->limit(10, 5)
->execute();
return $query->getResult();
}
}
上面的方法演示了查询的可能方法。它返回数组作为结果。如果您想将结果映射到某个实体,则不得指定选择字段,并调用mapToEntity或mapToEntities方法。
$query
->from($this->table);
->whereExpr('id < 5')
->whereExpr('id > 2')
->execute();
return $this->mapToEntities($query, ['author', 'editor']);
要使用JOIN命令,可以使用Computools\CLightORM\Database\Query\Structure\Join。构造函数参数是
-
string $type (LEFT, RIGHT, INNER)
-
string $table (表名)
-
string $condition (例如,on p.user_id = u.id)
-
string $alias = null
$query ->from('post', 'p') ->join(new Join('INNER', 'user', 'ON p.author_id = u.id', 'u')) ->where('p.id', 5) ->execute()
多对多关系保存可以检查已存在的关联。因此,如果您想避免关系重复,可以提供第三个参数作为true。
$post = $postRepository->find(1);
$user = $userRepository->find(1);
$post->addRelation($user);
$postRepository->save($post, [], true);
此调用还将调用重复检查,并且不会添加相同的关联。如果您提供第三个参数为false,则检查将不会执行,并且如果数据库表有唯一索引,则会抛出异常。
缓存
缓存机制可以用来存储一些搜索结果。要使用它,您需要在创建CLightORM实例时指定缓存类型。有两种不同的选项来存储结果 - memcached和文件系统。
Computools\CLightORM\Cache\Memcache接受两个参数
- host(string) - memcached服务器主机,默认为'localhost',
- port(int) - memcached服务器端口,默认为'11211'
Computools\CLightORM\Cache\Filecache接受缓存目录作为参数,默认为'cache'。
因此,要使用缓存,您需要编写类似以下内容
$pdo = new \PDO(
sprintf(
'%s:host=%s;port=%s;dbname=%s',
'mysql',
'127.0.0.1',
'3306',
'test'
),
'user',
'password'
);
$clightORM = new CLightORM($pdo, new Filecache('response/cache'));
或者
$clightORM = new CLightORM($pdo, new Memcache('localhost', 11211));
然后所有您的仓库都将创建具有缓存作为私有属性。您可以为findBy等提供expiration参数。
$repository->findBy(array $criteria, null, array $with = [], null, $expiration = 3600)
如果expiration = 0,则不会使用缓存。如果不为0 - 如果未过期,则从缓存中获取数据。