jasny / db
通过扩展 PHP 扩展,添加现代面向对象数据库模式
Requires
- php: >=5.6.0
- ext-mongodb: *
- jasny/meta: ^3.0.0
- jasny/typecast: ^2.1.0
- jasny/validation-result: ~1.0.0
- sebastian/comparator: ~1.2 || ~2.0 || ~3.0
Requires (Dev)
- jasny/php-code-quality: ^1.1
- dev-master
- v2.4.17
- v2.4.16
- v2.4.15
- v2.4.14
- v2.4.13
- v2.4.12
- v2.4.11
- v2.4.10
- v2.4.9
- v2.4.8
- v2.4.7
- v2.4.6
- v2.4.5
- v2.4.4
- v2.4.3
- v2.4.2
- v2.4.1
- v2.4.0
- v2.3.0
- v2.2.2
- v2.2.1
- v2.2.0
- v2.1.7
- v2.1.6
- v2.1.5
- v2.1.4
- v2.1.3
- v2.1.2
- v2.1.1
- v2.1.0
- v2.0.2
- v2.0.1
- v2.0.0
- v2.0.0-beta3
- v2.0.0-beta2
- v2.0.0-beta1
- v2.0.0-beta0
- v1.1.0
- v1.0.0
This package is auto-updated.
Last update: 2020-09-11 10:12:01 UTC
README
Jasny DB 将面向对象设计模式添加到 PHP 的数据库扩展中。
Jasny DB 是一个 PHP 的数据访问层 (不是 数据库抽象层)。它允许您正确地构建模型,同时仍然使用 PHP 原生数据库扩展的方法和功能。
安装
该库不是直接安装的。Jasny DB 库包含设计模式定义和实现。它作为为特定 PHP 扩展实现的实际库的抽象基类。
实现
- Jasny\DB\MySQL 扩展 mysqli
- Jasny\DB\Mongo 扩展 mongo
- Jasny\DB\REST 用于实现 REST 的数据源
服务定位器
静态 Jasny\DB
是一个 服务定位器。它提供对 工厂、注册表 和 构建器 的访问。
connectionFactory()
- 数据库连接工厂connectionRegistry()
- 数据库连接注册表entitySetFactory()
- 实体集工厂
连接
连接对象用于与数据库交互。其他对象必须使用连接对象执行获取、保存和删除数据库中的数据等操作。
注册表
要注册连接,请使用 Jasny\DB::connectionFactory()->register($name, $connection)
。要获取已注册的连接,请使用 Jasny\DB::connectionFactory()->get($name)
或快捷方式 Jasny\DB::conn($name)
。可以通过 Jasny\DB::connectionFactory()->unregister($name|$connection)
从注册表中删除连接。
$db = new Jasny\DB\MySQL\Connection(); Jasny\DB::connectionFactory()->register('foo'); Jasny\DB::conn('foo')->query(); Jasny\DB::connectionFactory()->unregister('foo');
相同的连接可以在不同的名称下注册多次。
命名连接
实现 Namable
接口的连接可以使用 useAs($name)
方法向 Jasny\DB
注册自己。通过 getConnectionName()
可以获取连接的名称。
$db = new Jasny\DB\MySQL\Connection(); $db->useAs('foo'); Jasny\DB::conn('foo')->query();
如果只有一个数据库连接名称,默认为 'default',因为 $name
默认为 'default'。
$db = new Jasny\DB\MySQL\Connection(); $db->useAs('default'); Jasny\DB::conn()->query();
配置
您可以使用 Jasny\DB::configure()
来配置连接,而不是手动创建和配置。这个静态属性可以保存每个连接的配置。当使用 conn()
方法时,Jasny DB 会根据配置设置自动创建一个新的连接。
Jasny\DB::configure([ 'default' => [ 'driver' => 'mysql', 'database' => 'database', 'host' => 'localhost', 'username' => 'root', 'password' => 'secure', 'charset' => 'utf8' ], 'external' => [ 'driver' => 'rest', 'host' => 'api.example.com', 'username' => 'user', 'password' => 'secure' ] ]); Jasny\DB::conn()->query(); Jasny\DB::conn('external')->get("/something");
Jasny\DB::$drivers
包含了带有驱动名称的 Connection
类列表。createConnection($settings)
方法使用 driver
设置来选择连接类。其他设置会被传递给连接构造函数。
实体
实体是在数据库或其他数据存储中想要表示的“事物”。它可以是博客上的新文章、论坛中的用户或权限管理系统中的权限。
实体对象的属性是数据的表示。实体通常也携带业务逻辑。
设置值
setValues()
方法是一个设置所有属性从数组中的辅助函数,它的工作方式类似于 流畅接口。
$foo = new Foo(); $foo->setValues(['red' => 10, 'green' => 20, 'blue' => 30])->doSomething();
实例化
使用 new
关键字是为创建新实体预留的。
当获取实体的数据时,使用 __set_state()
方法来创建实体。该方法在调用构造函数之前设置实体对象的属性。
活动记录
实体可以实现 活动记录模式。活动记录将数据和数据库访问合并到一个单独的对象中。
获取
可以使用 fetch($id)
从数据库中加载实体。
$foo = Foo::fetch(10); // id = 10 $foo = Foo::fetch(['reference' => 'myfoo']);
保存
实现 ActiveRecord 接口的对象有一个 save()
方法,用于将实体存储在数据库中。
$foo->save(); $foo->setValues($data)->save();
删除
可以使用 delete()
方法从数据库中删除实体。
$foo->delete();
可选地可以实施 软删除,这样被删除的实体可以恢复。
$foo->undelete();
数据映射器
您可以选择通过使用 数据映射器 将数据库逻辑与业务逻辑分离。数据映射器负责从数据库加载实体并将它们存储在数据库中。
您应该使用数据映射器或活动记录,但不能同时使用两者。当使用数据映射器时,实体不应了解数据库,并且不应包含任何数据库代码(例如 SQL 查询)。
获取
可以使用 fetch($id)
从数据库中加载实体。
$foo = FooMapper::fetch(10); // id = 10 $foo = FooMapper::fetch(['reference' => 'myfoo']);
保存
数据映射器实现 save($entity)
方法来存储实体。
FooMapper::save($foo); FooMapper::save($foo->setValues($data));
删除
可以使用 delete($entity)
方法从数据库中删除实体。
FooMapper::delete($foo);
可选地可以实施 软删除,这样被删除的实体可以恢复。
FooMapper::undelete($foo);
数据集
实体往往是数据集中的一部分,如表格或集合。如果可以从该集中加载多个实体,则活动记录或数据映射器实现 Dataset
接口。
fetch()
方法返回单个实体。 fetchAll()
方法返回多个实体。fetchList()
以键/值对的形式加载列表,其中键是 ID 和描述。 count()
方法计算集合中实体的数量。
fetch 方法仅支持简单情况。对于特定情况,应添加特定方法,而不是覆盖基本 fetch 方法。
过滤器
fetch 方法接受一个 $filter
参数。过滤器是一个关联数组,包含字段名和相应的值。请注意,fetch()
方法要么接受一个唯一 ID,要么接受过滤器。
过滤器应始终返回与调用不带过滤器的方法相同或更少的结果。
$foo = Foo::fetch(['reference' => 'zoo']); $foos = Foo::fetchAll(['bar' => 10]); $list = Foo::fetchList(['bar' => 10]); $count = Foo::count(['bar' => 10]);
过滤器键可以包含指令。以下指令受到支持
键 | 值 | 描述 |
---|---|---|
"field" | 标量 | 字段是值 |
"field (not)" | 标量 | 字段不是值 |
"field (min)" | 标量 | 字段等于或大于值 |
"field (max)" | 标量 | 字段等于或小于值 |
"field (any)" | 数组 | 字段是数组中的其中一个值 |
"field (none)" | 数组 | 字段不是数组中的任何一个值 |
如果字段是数组,可以使用以下指令
键 | 值 | 描述 |
---|---|---|
"field" | 标量 | 值是字段的一部分 |
"field (not)" | 标量 | 值不是字段的一部分 |
"field (any)" | 数组 | 任何一个值都是字段的一部分 |
"field (all)" | 数组 | 所有值都是字段的一部分 |
"field (none)" | 数组 | 没有值是字段的一部分 |
过滤器应与业务逻辑保持一致,这可能不会直接与检查字段值相一致。记录集应实现一个名为 filterToQuery
的方法,该方法将过滤器转换为依赖于数据库的查询语句。您可以覆盖此方法以支持自定义过滤器键。
直接使用查询参数($_GET
)和输入数据($_POST
)是安全的。
// -> GET /foos?color=red&date(min)=2014-09-01&tags(not)=abandoned&created.user=12345
$result = Foo::fetchAll($_GET);
实体集
每当返回实体数组时,Jasny DB 将返回一个 EntitySet
对象。实体集可以用作数组或对象。
需要更多文档
元数据
实体表示模型中的一个元素。元数据(Metadata)包含有关实体结构的详细信息。元数据应被视为静态的,因为它描述了特定类型的所有实体。
类的元数据可能包含应存储数据的数据表名称。属性的元数据可能包含数据类型、是否必需以及属性描述。
Jasny DB 支持通过使用 Jasny\Meta 通过注释定义元数据。
/** * User entity * * @entitySet UserSet */ class User { /** * @var string * @required */ public $name; }
类注解
* @entitySet - Default entity set for this class of Entities
特定的Jasny DB驱动器可能使用额外的类注解。
属性注解
* @var - (type casting) - Value type or class name
* @type - (validation) - Value (sub)type
* @required (validation) - Value should not be blank at validation.
* @min (validation) - Minimal value
* @max (validation) - Maximal value
* @minLength (validation) - Minimal length of a string
* @maxLength (validation) - Maximal length of a string
* @options _values_ (validation) - Value should be one the the given options.
* @pattern _regex_ (validation) - Value should match the regex pattern.
* @immutable (validation) - Property can't be changed after it is created.
* @unique (validation) - Entity should be unique accross it's dataset.
* @unique _field_ (validation) - Entity should be unique for a group. The group is identified by _field_.
* @censor (redact) - Skip property when outputting the entity.
特定的Jasny DB驱动器可能使用额外的属性注解。
注意事项
元数据在泛化和抽象代码方面非常有用。然而,您可能会很快陷入通过元数据编写的陷阱。这往往会导致难以阅读和维护的代码。
仅使用元数据来抽象广泛使用的功能,并使用重载来实施特殊情况。
类型转换
实体支持类型转换。这是基于元数据进行的。类型转换由Jasny\Meta库实现。
内部类型
对于PHP内部类型,使用正常的类型转换。值不会被盲目地转换。例如,将“foo”转换为整数将触发警告并跳过转换。
对象
将值转换为支持懒加载的Identifiable
实体将创建一个幽灵对象。实现ActiveRecord
或拥有DataMapper
但不支持LazyLoading
的实体将从数据库中获取。
将值转换为非可识别实体将调用Entity::fromData()
方法。
将值转换为任何其他类型的对象将创建一个新的对象。例如,将“bar”转换为Foo
将产生new Foo("bar")
。
验证
实现Validatable接口的实体在保存之前可以进行一些基本的验证。这包括检查所有必需的属性是否有值,检查变量类型是否匹配,以及检查值是否在数据库中唯一。
validate()
方法将返回一个Jasny\ValidationResult
。
$validation = $entity->validate(); if ($validation->failed()) { http_response_code(400); // Bad Request json_encode($validation->getErrors()); exit(); }
懒加载
Jasny DB支持通过允许它们作为幽灵创建来懒加载实体。幽灵仅持有实体数据的有限集合,通常仅是标识符。当访问其他属性时,它会加载其余的数据。
当一个值被转换为支持懒加载的实体时,会创建该实体的幽灵。
软删除
支持软删除的实体以可以恢复的方式进行删除。
可以使用undelete()
恢复已删除的实体,或者可以使用purge()
永久删除它们。
isDeleted()
方法检查该文档是否已被删除。
获取方法不会返回已删除的实体。相反,使用fetchDeleted($filter)
加载一个已删除的实体。使用fetchAllDeleted($filter)
从数据库中获取所有已删除的实体。
可维护的代码
要创建可维护的代码,您应该至少遵守以下规则
- 不要在模型类之外访问数据库。
- 使用特性或多个类来分离数据库逻辑(例如查询)和业务逻辑。
- 尽量减少
if
语句的数量。通过重载实现特殊情况。
SOLID原则
SOLID原则包含5个原则,当一起使用时,可以使代码库在长时间内更易于维护。虽然Jasny DB并不强制要求,但它支持构建符合SOLID原则的代码库。
方法保持小巧,并预期通过扩展类来实现每个方法的overloading
。
Jasny DB的功能通过接口定义,并在单个功能或设计模式周围使用特性定义。使用特定接口将触发行为。特性可能或不用于实现接口,而不会产生后果。
Active Record模式和SRP原则
使用Active Record模式被认为违反了SRP原则。Active Record通常将数据库逻辑和业务逻辑组合在一个类中。
然而,这种模式生成的代码更易于阅读和理解。因此,它仍然非常受欢迎。
最终,选择权在你。使用Active Record模式在Jasny DB中始终是可选的。或者,你也可以选择使用数据映射器进行数据库交互。
// Active Record $user = User::fetch(10); $user->setValues($data); $user->save(); // Data Mapper $user = DB::mapper('User')->fetch(10); $user->setValues($data); DB::mapper('User')->save($user);
代码生成
存在于版本1中,但尚未提供版本2