vlucas / spot
PHP 5.3+ 的 DataMapper ORM
Requires
- php: >=5.3.2
- doctrine/dbal: ~2.4
- vlucas/valitron: 1.x
This package is auto-updated.
Last update: 2024-09-08 11:06:41 UTC
README
适用于 RDBMS(目前有 MySQL 和 Sqlite 适配器)
弃用通知
本项目的开发已停止。请使用 Spot v2 代替 - 它正在积极维护,并且基于更稳固的 Doctrine DBAL(数据库抽象层,不是 Doctrine ORM)基础,并具有更广泛和更强大的数据库支持(MySQL、SQLite、PostgreSQL、SQLServer、Oracle 等)。
在您的项目中使用 Spot
Spot 是一个独立的 ORM,可以用于任何项目。按照以下说明在您的项目中设置 Spot,或使用您正在使用的框架的预编码插件
连接到数据库
Spot\Config
对象通过名称存储和引用数据库连接。创建一个新的 Spot\Config
实例,并添加带有 DSN 字符串的数据库连接,以便 Spot 可以建立数据库连接。
$cfg = new \Spot\Config(); // MySQL $adapter = $cfg->addConnection('test_mysql', 'mysql://user:password@localhost/database_name'); // Sqlite $adapter = $cfg->addConnection('test_sqlite', 'sqlite://path/to/database.sqlite');
如果您使用 Sqlite,则 Sqlite 文件名必须是数据库的名称后跟扩展名,例如 blogs.sqlite
访问 Mapper
由于 Spot 遵循 DataMapper 设计模式,您将需要一个 mapper 实例来处理对象实体和数据库表。
$mapper = new \Spot\Mapper($cfg);
由于您必须在任何使用数据库的地方访问您的 mapper,大多数人会创建一个辅助方法来创建 mapper 实例一次,然后在需要时再次返回相同的实例。这样的辅助方法可能看起来像这样
function get_mapper() { static $mapper; if($mapper === null) { $mapper = new \Spot\Mapper($cfg); } return $mapper; }
创建实体
实体类可以根据您在项目结构中的设置进行命名和命名空间。以下示例中,实体将仅使用 Entity
命名空间进行前缀,以方便 psr-0 兼容的自动加载。
namespace Entity; class Post extends \Spot\Entity { protected static $_datasource = 'posts'; public static function fields() { return array( 'id' => array('type' => 'int', 'primary' => true, 'serial' => true), 'title' => array('type' => 'string', 'required' => true), 'body' => array('type' => 'text', 'required' => true), 'status' => array('type' => 'int', 'default' => 0, 'index' => true), 'date_created' => array('type' => 'datetime') ); } public static function relations() { return array( // Each post entity 'hasMany' comment entites 'comments' => array( 'type' => 'HasMany', 'entity' => 'Entity_Post_Comment', 'where' => array('post_id' => ':entity.id'), 'order' => array('date_created' => 'ASC') ) ); } }
内置字段类型
所有基本字段类型都内置了所有默认功能,为您提供了便利
string
int
float/double/decimal
boolean
text
date
datetime
timestamp
year
month
day
注册自定义字段类型
如果您想在 get/set 上注册自己的自定义字段类型并添加自定义功能,请查看 Spot\Type
命名空间中的类,创建自己的类,并在 Spot\Config
中注册它
$config->typeHandler('string', '\Spot\Type\String');
迁移/创建和更新表
Spot 提供了一个在实体上运行迁移的方法,它将根据当前实体的 fields
定义自动创建和更改表。
$mapper->migrate('Post');
您的数据库现在应该有一个 posts
表,其中包含您在 Post
实体中描述的所有字段。
查找器(Mapper)
最常用的主要查找器是 all
,用于返回实体集合,以及 first
或 get
,用于返回匹配条件的单个实体。
all(entityName, [conditions])
查找匹配给定条件的所有 entityName
,并返回一个包含已加载 Spot\Entity
对象的 Spot\Entity\Collection
。
// Conditions can be the second argument $posts = $mapper->all('Entity\Post', array('status' => 1)); // Or chained using the returned `Spot\Query` object - results identical to above $posts = $mapper->all('Entity\Post')->where(array('status' => 1));
由于返回了一个 Spot\Query
对象,因此可以根据您希望的任何方式或顺序链式添加条件和其他语句。查询将在迭代或 count
时懒加载执行,或通过调用 execute()
来手动结束链式调用。
first(entityName, [conditions])
查找并返回符合条件的一个Spot\Entity
对象。
$post = $mapper->first('Entity\Post', array('title' => "Test Post"));
或者可以使用first
在先前的查询中使用all
来获取仅有的第一个匹配记录。
$post = $mapper->all('Entity\Post', array('title' => "Test Post"))->first();
条件查询
# All posts with a 'published' status, descending by date_created $posts = $mapper->all('Entity\Post') ->where(array('status' => 'published')) ->order(array('date_created' => 'DESC')); # All posts created before today $posts = $mapper->all('Entity\Post') ->where(array('date_created <' => new \DateTime())); # Posts with 'id' of 1, 2, 5, 12, or 15 - Array value = automatic "IN" clause $posts = $mapper->all('Entity\Post') ->where(array('id' => array(1, 2, 5, 12, 15)));
搜索
Spot支持使用LIKE、REGEX和全文搜索(如果适配器支持)进行搜索查询。
Query#search(text)
Spot\Query
上的search
方法将使用LIKE搜索,如果启用则使用全文搜索。关于使用全文搜索的详细信息,请参阅搜索的维基页面。
$q = "walrus"; // Text to search for $results = $mapper->all('Entity\Post') ->search('body', $q) ->order(array('date_created' => 'DESC'));
LIKE搜索
您可以使用:like
查询运算符来执行LIKE搜索。
$results = $mapper->all('Entity\Post') ->where('body :like', 'walrus%') ->order(array('date_created' => 'DESC'));
REGEX搜索
您可以使用:regex
或~=
查询运算符来执行REGEX搜索。
$results = $mapper->all('Entity\Post') ->where('body ~=', 'walrus.+') ->order(array('date_created' => 'DESC'));
注意:大多数REGEX搜索非常慢,因此请谨慎使用。
关系
关系是从另一个已加载实体对象访问相关、父和子实体的一种便捷方式。一个例子可能是使用$post->comments
来查询与当前$post
对象相关的所有评论。
关系类型
实体关系类型包括:
HasOne
HasMany
HasManyThrough
HasOne
HasOne是最简单的关系 - 例子可能是Post
有一个Author
。
class Entity\Post extends \Spot\Entity { protected static $_datasource = 'posts'; public static function fields() { return array( 'id' => array('type' => 'int', 'primary' => true, 'serial' => true), 'author_id' => array('type' => 'int', 'required' => true), 'title' => array('type' => 'string', 'required' => true), 'body' => array('type' => 'text', 'required' => true), 'status' => array('type' => 'int', 'default' => 0, 'index' => true), 'date_created' => array('type' => 'datetime') ); } public static function relations() { return array( // Each post 'hasOne' author 'author' => array( 'type' => 'HasOne', 'entity' => 'Entity\Author', 'where' => array('id' => ':entity.author_id') ) ); } }
HasMany
HasMany用于单条记录与多条其他记录相关联的情况 - 例子可能是Post
有许多Comments
。
我们首先向我们的Post
对象添加一个comments
关系
class Entity\Post extends \Spot\Entity { protected static $_datasource = 'posts'; public static function fields() { return array( 'id' => array('type' => 'int', 'primary' => true, 'serial' => true), 'author_id' => array('type' => 'int', 'required' => true), 'title' => array('type' => 'string', 'required' => true), 'body' => array('type' => 'text', 'required' => true), 'status' => array('type' => 'int', 'default' => 0, 'index' => true), 'date_created' => array('type' => 'datetime') ); } public static function relations() { return array( // Each post 'hasMany' comments 'comments' => array( 'type' => 'HasMany', 'entity' => 'Entity\Post\Comment', 'where' => array('post_id' => ':entity.id'), 'order' => array('date_created' => 'ASC') ) ); } }
然后添加一个具有“hasOne”关系的Entity\Post\Comment
对象,该关系指向帖子
class Entity\Post\Comment extends \Spot\Entity { // ... snip ... public static function relations() { return array( // Comment 'hasOne' post (belongs to a post) 'post' => array( 'type' => 'HasOne', 'entity' => 'Entity\Post', 'where' => array('id' => ':entity.post_id') ) ); } }
HasManyThrough
HasManyThrough用于多对多关系。一个好的例子是标签。帖子可以有多个标签,标签也可以有多个帖子。这个关系比其他关系更复杂,因为HasManyThrough需要一个连接表和模型。
我们需要向我们的Post
实体添加一个tags
关系,指定关系的两边的查询条件。
class Entity\Post extends \Spot\Entity { // ... snip ... public static function relations() { return array( // Each post 'hasMany' tags `Through` a post_tags table 'tags' => array( 'type' => 'HasManyThrough', 'entity' => 'Entity_Tag', 'throughEntity' => 'Entity_PostTag', 'throughWhere' => array('post_id' => ':entity.id'), 'where' => array('id' => ':throughEntity.tag_id'), ) ); } }
说明
我们想要的结果是ID等于post_tags.tag_id
列的Entity\Tag
对象集合。我们通过使用当前加载的帖子ID与post_tags.post_id
匹配,通过Entity\Post\Tags
实体来获取这个结果。
有关另一个场景和更详细的说明,请参阅HasManyThrough维基页面。