ikkez / f3-cortex
PHP Fat-Free 框架的多引擎 ORM / ODM
Requires
README
PHP Fat-Free 框架的通用数据映射器
Cortex 是一个多引擎 ActiveRecord ORM / ODM,提供简单的对象持久性。其主要特性包括:
- 处理 SQL、Jig 和 MongoDB 数据库引擎
- 使用熟悉的 SQL 语法编写查询,它们可以转换为 Jig 和 Mongo
- 自动从定义的模式配置中创建 SQL 表和扩展列
- 使用 SQL Fluid 模式轻松进行原型设计,这使得您的 RDBMS 模式无模式,并自动添加新表列
- 支持模型和集合
- 关系:将多个模型链接到一对一、一对多和多对多关联
- 智能加载相关模型(智能懒加载和预加载,无需配置)
- 用于通过关系进行嵌套过滤的有用方法
- 为所有字段提供大量事件处理器和自定义设置器/获取器预处理程序
- 为 NoSQL 定义默认值和可空字段
- 还提供了额外的 验证插件
使用 Cortex,您可以创建通用的应用程序,这些应用程序可以与用户选择的任何数据库一起工作,无论它是 SQlite、PostgreSQL、MongoDB 还是其他数据库。您还可以混合使用多个引擎或同时使用它们。
它非常适合快速轻松地进行数据抽象,并提供了一组有用的过滤器选项。
目录
快速入门
系统需求
Cortex 至少需要 Fat-Free v3.4 和 PHP 5.4。对于一些功能,它还需要 F3 SQL 模式插件。
安装
要安装 Cortex,只需将 /lib/db/cortex.php
文件复制到您的库中。对于 SQL 模式插件,也要复制 lib/db/sql/schema.php
。
如果您使用 composer,您只需要运行 composer require ikkez/f3-cortex:1.*
,它将 Cortex 及其依赖项包含到您的包中。
设置数据库
创建您选择的数据库对象。您可以选择 SQL、Jig 或 MongoDB。以下是一些示例
// SQL - MySQL $db = new \DB\SQL('mysql:host=localhost;port=3306;dbname=MyAppDB','user','pw'); // SQL - SQlite $db = new \DB\SQL('sqlite:db/database.sqlite'); // SQL - PostgreSQL $db = new \DB\SQL('pgsql:host=localhost;dbname=MyAppDB','user','pw'); // SQL - SQL Server $db = new \DB\SQL('sqlsrv:SERVER=LOCALHOST\SQLEXPRESS2012;Database=MyAppDB','user','pw'); // Jig $db = new \DB\Jig('data/'); // Mongo $db = new \DB\Mongo('mongodb://localhost:27017','testdb');
让我们开始吧
如果你熟悉F3自带的Data-Mappers,那么你对Cortex可以执行的基本CRUD操作也应该很清楚。它实现了ActiveRecord Cursor类及其所有方法。因此,你可以将Cortex用作F3映射器的直接替换
,并且它的基本用法将保持简单
$user = new \DB\Cortex($db, 'users'); $user->name = 'Jack Ripper'; $user->mail = 'jacky@email.com'; $user->save();
好吧,这并不太令人印象深刻。但现在是时候再次找到这个人了
$user->load(['mail = ?','jacky@email.com']); echo $user->name; // shouts out: Jack Ripper
正如你所看到的,过滤器数组是纯SQL语法,你已经在F3 SQL Mapper中使用过。在Cortex中,这将与所有3个数据库引擎一起工作。这里是一个更复杂的where
条件
$user->load(['name like ? AND (deleted = 0 OR rights > ?]', 'Jack%', 3));
无需复杂的条件对象或令人困惑的Mongo where-array结构。它就像你习惯的那样简单。使用Jig DB将自动将查询转换为适当的Jig过滤器
Array ( [0] => (isset(@name) && preg_match(?,@name)) AND ( (isset(@deleted) && (@deleted = 0)) OR (isset(@rights) && @rights > ?) ) [1] => /^Jack/ [2] => 3 )
而对于MongoDB,它转换为这样
Array ( [$and] => Array ( [0] => Array ( [name] => MongoRegex Object ( [regex] => ^Jack ) ) [1] => Array ( [$or] => Array ( [0] => Array ( [deleted] => 0 ) [1] => Array ( [rights] => Array ( [$gt] => 3 ) )))))
你可以使用Cursor的所有花哨方法,如load
、find
、cast
、next
或prev
。关于过滤和其他方法的更多信息稍后介绍。
SQL Fluid 模式
当你正在为一些新的对象进行原型设计或者不想在同时使用Cortex和SQL数据库后端时麻烦创建表模式,你可以启用SQL Fluid Mode。这样,Cortex将自动创建所有必要的表和列,这样你就可以专注于编写应用程序代码。它将尝试根据提供的样本数据猜测正确的数据类型。要启用流体模式,只需向对象的构造函数传递第三个参数
$user = new \DB\Cortex($db, 'users', TRUE); $user->name = 'John'; // varchar(256) $user->age = 25; // integer $user->active = true; // boolean|tinyint $user->lastlogin = '2013-08-28'; // date
这样,它还创建了datetime、float、text(当strlen > 255
)和double数据类型。
注意:流体模式会禁用底层SQL表模式的缓存。这可能会稍微影响性能,所以请记住在完成后将其禁用。此外,请记住,你无法加载或从不存在表中查找任何记录 - 考虑首先创建并保存一些样本数据,以便Cortex可以创建表。
Cortex 模型
直接使用Cortex类对于某些CRUD操作来说很容易,但要启用一些更高级的功能,你需要将Cortex包装在一个像这样的Model类中
// file at app/model/user.php namespace Model; class User extends \DB\Cortex { protected $db = 'AppDB1', // F3 hive key of a valid DB object $table = 'users'; // the DB table to work on }
现在你可以这样轻松地创建你的映射器对象
$user = new \Model\Users();
这是最小的模型配置。Cortex至少需要一个工作的数据库对象。你也可以通过构造函数传递它(new \Model\Users($db);
)并将其放入设置中。$db
必须是一个hive键的字符串,其中存储着数据库对象,或者数据库对象本身。如果没有提供$table
,Cortex将使用类名作为表名。
配置
Cortex不需要太多的配置。但至少设置字段配置会很有用。这样,它能够遵循数据实体的定义架构,并使你能够使用一些自动安装程序(见设置)。它看起来像这样
// file at app/model/user.php namespace Model; class User extends \DB\Cortex { protected $fieldConf = [ 'name' => [ 'type' => 'VARCHAR256', 'nullable' => false, ], 'mail' => [ 'type' => 'VARCHAR128', 'index' => true, 'unique' => true, ], 'website' => [ 'type' => 'VARCHAR256' ], 'rights_level' => [ 'type' => 'TINYINT', 'default' => 3, ], ], $db = 'DB', $table = 'users', $primary = 'id'; // name of the primary key (auto-created), default: id }
在$fieldConf
数组中,你可以设置列的数据类型(type
)、nullable
标志和default
值。通过index
和unique
,你甚至可以为列设置索引。这样做可以使你在SQL数据库中安装新的模型,添加一些可空验证检查和为NoSQL引擎设置默认值的能力。这使得使用这种松散耦合的字段定义在不同数据库之间交换模型变得容易。
你不需要以这种方式配置所有字段。如果你正在处理现有的表,底层的SQL Mapper公开了现有的表模式。因此,如果你不需要自动安装程序功能,你只需跳过这些字段的配置即可,或者只需设置你需要的那些(例如,对于具有关系的字段)。
由于列数据类型目前仅用于设置SQL中的表,因此它遵循所需的SQL数据类型表和SQL模式插件。
您也可以扩展此配置数组,为其添加自定义验证规则或其他您需要的功能。
数据类型值是由 Schema 插件定义的常量。如果您想在您的 IDE 中使用自动补全来查找正确的值,请输入常量的较长的路径。
'type' => \DB\SQL\Schema::DT_TIMESTAMP, 'default' => \DB\SQL\Schema::DF_CURRENT_TIMESTAMP,
附加数据类型
Cortex 为处理字段中的数组值提供了两种自己的数据类型。尽管 Jig 和 Mongo 可以自然地支持它们,但大多数 SQL 引擎尚不支持。因此,Cortex 引入了
DT_SERIALIZED
DT_JSON
例如
'colors' => [ 'type' => self::DT_JSON, ],
现在您可以在模型字段中保存数组数据,这些数据在幕后被 json_encoded 到一个 text
字段中(当使用 SQL 后端时)。
$mapper->colors = ['red','blue','green'];
备用配置
如果您需要更灵活的配置并且不想硬编码它,您可以通过重载 Model 类构造函数来从 ini
-文件或其他地方加载其配置。例如
class User extends \DB\Cortex { function __construct() { // get the DB from elsewhere $this->db = \Registry::get('DB'); $f3 = \Base::instance(); // load fields from .ini file if (!$f3->exists('usermodel')) $f3->config('app/models/usermodel.ini'); foreach ($f3->get('usermodel') as $key => $val) $this->{$key} = $val; parent::__construct(); } }
并在您的 usermodel.ini
文件中
[usermodel] table = users [usermodel.fieldConf] name.type = VARCHAR256 name.nullable = FALSE mail.type = VARCHAR128 website.type = VARCHAR256 rights_level.type = TINYINT rights_level.default = 3
黑名单字段
fields()
方法可以用来返回当前模型上的可用字段。如果用一个简单的数组参数调用,如 $news->fields(['title']);
,它将把提供的元素作为白名单应用到整个映射器上。在其余的生命周期中,它将只激活您在此处允许的字段。如果用第二个参数调用,如 $news->fields(['author']),true);
,该数组将被用作黑名单,并限制对提供的字段的访问。您还可以使用 点 作为分隔符定义深层嵌套字段:$news->fields(['tags.title']);
将仅激活新闻模型中的标签标题,而不会加载或保存您标签模型中存在的任何其他字段。后续对 fields
方法的调用将合并到所有已定义的黑名单/白名单定义中。
设置
此方法创建您运行 Cortex 模型所需的 SQL 数据库表。 它还会添加已存在的表中缺少的字段。
如果您的模型有有效的字段配置,您就可以运行此安装方法
\Model\User::setup();
如果您没有模型类,您需要提供设置方法的所有参数。
$fields = [ 'name' => ['type' => \DB\SQL\Schema::DT_TEXT], 'mail' => ['type' => \DB\SQL\Schema::DT_INT4], 'website' => ['type' => \DB\SQL\Schema::DT_INT4], ]; \DB\Cortex::setup($db, 'users', $fields);
放下
此方法会完全从使用的数据库中删除指定的表。因此请小心处理。
// With Model class \Model\User::setdown(); // Without Model class \DB\Cortex::setdown($db, 'users');
关系
使用 Cortex,您可以在多个模型之间创建关联。通过将它们链接起来,您可以创建所有常见的关联,这些关联对于智能和简单的持久化非常有用。
设置链接
为了使关系正常工作,您需要使用具有字段配置的模型类。Cortex 提供以下类型的关联,这些关联通常必须在关系的两个类中 定义。
这是带有关系的字段配置的示例
这将在作者和新闻之间创建聚合,因此
一条新闻属于一个作者。
一个作者写了多条新闻。
作为旁注:belongs-to-*
定义将在该表中创建一个新的列,用于保存对应模型的 id(外键字段)。而 has-*
定义只是虚拟字段,它们将根据对方的 id(反向方式)查询链接的模型。这导致以下配置架构
对于 belongs-to-one 和 belongs-to-many
'realTableField' => [ 'relationType' => '\Namespace\ClassName', ],
为 belongs-to-*
定义外键是可选的。默认方式是使用标识字段。对于 SQL 引擎,这要么是默认的主键 id
,要么是可以使用 $primary
类属性设置的自定义主键。NoSQL 引擎将使用 _id
。如果您需要定义另一个非主键字段以进行连接,请使用 ['\Namespace\ClassName','cKey']
。
对于 has-one 和 has-many
'virtualField' => [ 'relationType' => ['\Namespace\ClassName','foreignKey'], ],
外键是您在对应模型中用于定义 belongs-to-one
连接的字段名称。
多对多
对于多对多关系有一个特殊情况:在这里你需要在两个模型上都使用一个 has-many
类型,这意味着必须有一个 3rd 交叉表来保存绑定一切的键。通常 Cortex 会在设置方法上自动创建该表,并使用自动生成的表名。如果你想为该连接表使用自定义名称,请在 两个 模型的配置数组中添加一个 3rd 参数,例如:
'tags' => [ 'has-many' => [\Model\Tag::class,'news','news_tags'], ],
默认情况下,主键用作交叉表中记录的参考。如果你需要使用不同的字段作为主键,则可以设置一个自定义的 localKey
。
'tag_key' => [ 'type' => \DB\SQL\Schema::DT_VARCHAR128, ], 'tags' => [ 'has-many' => [\Model\Tag::class,'news','news_tags', 'localKey' => 'tag_key' ], ],
对于自定义关系键(外键)使用 relPK
'tags' => [ 'has-many' => [\Model\Tag::class,'news','news_tags', 'relPK'=> 'news_id' ], ],
自定义交叉列名称
如果你正在处理现有的数据库表,或者想在交叉表中使用自己的字段名称,你可以使用 relField
选项设置这些:
例如,在新闻模型中
'tags' => [ 'has-many' => [\Model\Tag::class,'news','news_tags', 'relField' => 'news_id' ], ],
和在标签模型中
'news' => [ 'has-many' => [\Model\News::class,'tags','news_tags', 'relField' => 'tag_id' ], ],
这意味着第三个交叉表包含 news_id
和 tag_id
字段。
处理关系
好吧,最后我们来到了最有趣的部分。当配置完成并执行设置后,你就准备好开始使用了。
一对一
要创建一个新的关系
// load a specific author $author = new \AuthorModel(); $author->load(['_id = ?', 2]); // create a new profile $profile = new ProfileModel(); $profile->status_message = 'Hello World'; // link author and profile together, just set the foreign model to the desired property $profile->author = $author; // OR you can also just put in the id instead of the whole object here // (means you don't need to load the author model upfront at all) $profile->author = 23; $profile->save();
当然,你也可以从作者模型开始,反过来操作
// create a new profile $profile = new ProfileModel(); $profile->status_message = 'Hello World'; $profile->save(); // load a specific author and add that profile $author = new \AuthorModel(); $author->load(['_id = ?', 2]); $author->profile = $profile; $author->save();
然后再加载它
$author->load(['_id = ?', 23]); echo $author->profile->status_message; // Hello World $profile->load(['_id = ?', 1]); echo $profile->author->name; // Johnny English
一对一,一对多,多对一
将一个作者保存到一个新闻记录中。
$author->load(['name = ?','Johnny English']); $news->load(['_id = ?',42]); $news->author = $author; // set the object or the raw id $news->save();
现在你可以获取
echo $news->author->name; // 'Johnny English'
字段 author
现在持有整个 AuthorModel 的映射对象。因此,你也可以更新、删除或转换它。
通过作者在对应模型中获取所有新闻的例子如下
$author->load(['_id = ?', 42]); $author->news; // is now an array of NewsModel objects // if you like to cast them all you can use $allNewsByAuthorX = $author->castField('news'); // is now a multi-dimensional array
多对多,双向
当关系的两个模型在其链接字段上都有 has-many
配置时,Cortex 在设置时创建一个新的参考表,其中包含两个模型的外键。这样,你可以查询模型 A 来获取模型 B 的相关模型,反之亦然。
要保存多个集合到一个模型,你有几种方法
$news->load(['_id = ?',1]); // array of IDs from TagModel $news->tags = [12, 5]; // OR a split-able string $news->tags = '12;5;3;9'; // delimiter: [,;|] // OR an array of single mapper objects $news->tags = [$tag,$tag2,$tag3]; // OR a hydrated mapper that may contain multiple results $tag->load(['_id != ?',42]); $news->tags = $tag; // you can also add a single tag to your existing tags $tag->load(['_id = ?',23]); $news->tags[] = $tag; $news->save();
现在你可以获取一个新闻条目的所有标签
$news->load(['_id = ?',1]); echo $news->tags[0]['title']; // Web Design echo $news->tags[1]['title']; // Responsive
以及所有带有 响应式 标签的新闻
$tags->load(['title = ?','Responsive']); echo $tags->news[0]->title; // '10 Responsive Images Plugins'
这个例子展示了查询的逆向方式(使用 TagModel 找到相应的新闻)。当然,你也可以使用更直接的方式,这提供了更多的可能性,因此请检查 has() 方法。
多对多,单向
你可以使用一个 belongs-to-many
字段配置来定义一个单向 m:m 关系。这是一种特殊的许多到多类型,因为它不会使用第三张表作为参考,而只是将 ID 列表放入表字段中,这是 NoSQL 解决方案中常用的做法。这是一种单向绑定,因为对应方不会知道其关系,而且查询反向方式更困难,但它仍然是在某些情况下的一种轻量级且有用的解决方案。
保存的方式与上面描述的其他 m:m 类型相同
$news->tags = [4,7]; // IDs of TagModel $news->save();
并获取它们
$news->load(['_id = ?', 77]); echo $news->tags[0]->title; // Web Design echo $news->tags[1]->title; // Responsive
多对多,自引用
如果你想要将多对多关系绑定到自身,即你想将它添加到相同模型的自身属性中,你也可以这样做,因为这些现在被检测为自引用字段。
一个常见的场景是 User
有朋友,而这个关系的目标也是 User
。因此,它会将关系绑定到自身
namespace Model; class User extends \DB\Cortex { protected $fieldConf = [ 'friends' => [ 'has-many' => [\Model\User::class,'friends'] ] ]; }
要使用不同的字段名作为交叉表中的参考字段,使用 selfRefField
选项
'friends' => [ 'has-many' => [\Model\User::class,'friends', 'selfRefField' => 'friends_ref' ] ]
因为这也是一个多对多关系,所以也需要一个交叉表。其名称基于表和字段名称生成,但也可以作为第三个数组参数定义,例如 ['\Model\User','friends','user_friends']
。
通常情况下,这是一个双向关系,意味着您将直接链接到您的朋友(friends
),另一个链接到反向链接(与我有关的朋友,friends_ref
)。由于这对进一步的工作和筛选非常不便,因此这两个字段在内部相互链接,并将始终代表所有关系,无论关系是由UserA还是UserB添加的。
$userA = new \Model\User(); $userA->load(['_id = ?', 1]); $userB = new \Model\User(); $userB->load(['_id = ?', 2]); $userC = new \Model\User(); $userC->load(['_id = ?', 3]); if ($userA->friends) $userA[] = $userB; else $userA = [$userB]; $userA->save(); $userC->friends = [$userA,$userB]; $userC->save();
唯一的例外是,当前记录本身始终被排除,因此您不会得到UserA是UserA的朋友。
$userA->load(['_id = ?', 1]); $userA->friends->getAll('_id'); // [2,3] $userB->load(['_id = ?', 2]); $userB->friends->getAll('_id'); // [1,3] $userC->load(['_id = ?', 3]); $userC->friends->getAll('_id'); // [1,2]
事件处理器
Cortex继承了所有来自Cursor事件处理器的设置器,并添加了自定义字段处理器(设置器/获取器)。这些可以在执行某些操作之前或之后执行一些额外的代码。这可以直接在您的模型中进行验证,或者进行扩展的保存、加载或删除级联。
支持以下事件
onload
onset
onget
beforeerase
aftererase
beforeinsert
afterinsert
beforeupdate
afterupdate
您可以通过这种方式设置自己的处理器来处理这些事件
$mapper->onload(function($self){ // custom code }); // or $mapper->onload('App/Foo/Bar::doSomething');
您可以将接受Base->call方法作为处理器函数的任何内容。请注意,如果在Cortex的子类(即您的模型类中的扩展__construct
)中定义处理器,请使用$self->set('field','val')
而不是$self->field=val
。
如果任何before*
事件返回false
结果,将要执行的操作将被中止,并且将跳过after*
事件。
自定义字段处理器
onset
和onget
事件具有略微不同的参数
$mapper->onset('field',function($self, $val){ return md5($val); });
您还可以将这些自定义字段预处理程序定义为类中的方法,方法名称为set_*
或get_*
,其中*
是您的字段名称。例如
class User extends \DB\Cortex { // [...] // validate email address public function set_mail($value) { if (\Audit::instance()->email($value) == false) { // no valid email address // throw exception or set an error var and display a flash message $value = null; } return $value; } // hash a password before saving public function set_password($value) { return \Bcrypt::instance()->hash($value); } public function get_name($value) { return ucfirst($value); } }
因此,在您的模型中设置这些字段,如
$user->password = 'secret'; $user->mail = 'foo@bar.com';
现在将触发您的自定义设置器,执行您想要的任何操作。
过滤查询语法
基本上,Cortex查询的$filter
语法很简单,类似于SQL。但是,您应该阅读以下附加说明中的某些细微修改。
运算符
支持以下常见过滤器运算符
- 关系运算符:
<
、>
、<=
、>=
、==
、=
、!=
、<>
- 搜索运算符:
LIKE
、NOT LIKE
、IN
、NOT IN
(不区分大小写) - 逻辑运算符:
(
、)
、AND
、OR
、&&
、(||
仅MySQL和JIG)
比较
使用比较运算符,您可以执行以下操作
-
比较字段与其他字段
['foo < bar']
-
比较字段与值
['foo >= 1']
或['foo == 'bar'']
特别建议,对于值比较,您强烈建议在您的过滤器中使用占位符,并相应地绑定它们的值。这确保了数据映射器使用参数化查询以提高安全性。占位符如下所示
- 位置绑定参数
['foo = ?', 1]
或['foo = ? AND bar < ?', 'baz', 7]
-
命名绑定参数
['foo = :foo', ':foo'=>1]
['foo = :foo AND bar < :bar', ':foo'=>'hallo', ':bar'=>7]
糖
-
Cortex中的特殊糖是,您还可以将这两种类型混合在一起
['foo = ? AND bar < :bar', 'bar', ':bar'=>7]
-
您还可以重用命名参数(在原始PDO中不可能)
['min > :num AND max < :num', ':num' => 7]
-
与
NULL
(可空字段)的比较这样简单['foo = ?', NULL]
或['foo != ?', NULL]
搜索
-
LIKE
运算符的工作方式与F3 SQL搜索语法相同。搜索通配符(%
)属于绑定值,而不是查询字符串。例如:
['title LIKE ?', '%castle%']
或['email NOT LIKE ?', '%gmail.com']
-
在原始PDO中,
IN
运算符通常需要多个占位符(如foo IN (?,?,?)
)。在Cortex查询中,您只需使用一个数组即可完成此操作,QueryParser将完成其余工作。例如:
['foo IN ?', [1,2,3]]
您还可以将CortexCollection用作绑定参数。在这种情况下,主键将自动用于匹配。
$fruits = $fruitModel->find(['taste = ?','sweet']); $result = $userModel->find(['favorite_fruit IN ?',$fruits])
选项
加载操作中的$options
数组尊重以下键:
- 排序
- 限制
- 偏移
使用DESC
和ASC
标志对排序字段进行排序,就像在SQL中一样。当前的额外group
设置只是绕过到底层映射器,并应根据所选的数据库引擎工作。对此的任何统一可能在未来的版本中得到处理。
关系排序
注意:此功能目前为v1.7的实验性功能。
对于1-n关系,您可以根据关系的字段对排序选项应用排序规则。您需要在您的排序定义中,对在$fieldConf
中为该关系使用的字段前面加上一个@
。
给定以下字段配置
// Contracts fieldConf: 'user' => ['belongs-to-one' => UserModel::class] // User fieldConf: 'contracts' => ['has-many' => [ContractsModel::class,'user']]
此示例将通过按用户名排序的所有合同记录进行分页。
$contracts = new Contracts(); $res = $contracts->paginate(0,10,null, ['order'=>'@user.name ASC']);
高级过滤技术
当您的应用程序达到所有基本CRUD操作都正常工作的点时,您可能需要更多关于根据条件查找记录的控制。这时,has()
和filter()
方法就派上用场了。
has
has
方法向相关字段添加一些条件,这些条件必须在触发其父对象的下一个find()
或load()
方法时满足。因此,这是为了限制主要结果。
换句话说:让我们查找所有被标记为“Responsive”的新闻记录。
$news->has('tags', ['title = ?','Responsive']); $results = $news->find(); echo $results[0]->title; // '10 Responsive Images Plugins'
当然,您也可以使用查询的反向方法,使用TagModel,按标题加载它们,并访问共享的$tags->news
属性以查找记录。使用“has”方法的优点是可以向父对象添加条件。这样,您可以将加载行编辑如下:$news->find(['published = ?', 1]);
。现在,您可以基于两个不同的模型限制结果 - 您只加载被标记为“Responsive”的已发布新闻。
您还可以向不同的关系添加多个has
条件。
$news->has('tags', ['title = ?','Responsive']); $news->has('author', ['username = ?','ikkez']); $results = $news->find(['published = ?', 1], ['limit'=>3, 'order'=>'date DESC']);
现在,您只加载最后3篇由我撰写的、被标记为“Responsive”、按发布日期排序的新闻。
如果您喜欢,也可以以流畅的方式调用它们:$news->has(...)->load(...);
。
orHas
与has
方法类似,但使用OR运算符添加has
条件。
filter
filter
方法旨在限制关系的查询结果。例如:加载作者x及其2014年的所有新闻。
$author->filter('news', ['date > ?','2014-01-01']); $author->load(['username = ?', 'ikkez']);
与has
方法类似,您可以为多个filter
条件添加条件。您也可以混合使用filter和has条件。一旦执行了load
或find
函数,filter(和has)条件就会被清除,以便进行下一个查询。
filter条件目前不能继承。这意味着如果您递归地访问关系的字段(如$author->news[0]->author->news),它们不会被过滤,而是再次完全懒加载。
传播
您还可以使用点样式语法(.
)过滤深层嵌套的关系。以下示例找到所有作者,并仅加载其标记为“Responsive”的新闻。
$author->filter('news.tags', ['title = ?','Responsive']); $author->find();
同样适用于has
过滤器。下一个示例类似于上一个示例,但这次,而不是找到所有作者,它只返回写过被标记为“Responsive”的新闻条目的作者。
$author->has('news.tags', ['title = ?','Responsive']); $author->find();
注意:这些嵌套过滤技术仍然是实验性的,所以请谨慎处理,并充分测试您的应用程序。
聚合分析
Cortex提供了一些方便的快捷方式,可用于基本字段聚合。
关系计数
有时您需要知道一条记录有多少关系 - 例如,用于某些统计或用于显示前10个列表视图的排序。
因此,请查看countRel方法,它允许您为结果记录设置一个新的临时字段,以计数has-many
字段上的相关记录。
// find all tags with the sum of all news that uses the tag, ordered by the top occurring tags first. $tag = new \Model\Tag(); $tag->countRel('news'); $result = $tag->find(null,['order'=>'count_news DESC, title']);
新字段命名为count_{$key}
,但您也可以设置一个自定义别名。正如您所看到的,您还可以使用该字段进行结果的附加排序。您还可以结合使用has()
和filter()
方法,并使用.
分隔符将关系计数器设置为嵌套关系。请注意,countRel()
仅适用于下一个调用的find()
操作。目前,您不能在$filter
查询中使用这些虚拟计数字段。
虚拟字段
Cortex有一些自定义虚拟字段的能力。这些可能有助于添加可能包含不在实际数据库表中存储的数据的附加字段,或从其他字段或函数计算其值,类似于自定义字段设置器和获取器。
// just set a simple value $user->virtual('is_online', TRUE); // or use a callback function $user->virtual('full_name', function($this) { return $this->name.' '.$this->surname; });
您还可以使用此方法对字段进行计数或求和,甚至可以使用$collection->orderBy('foo DESC, bar ASC')
在此字段上重新排序您的集合。请注意,这些虚拟字段仅适用于您最终接收到的集合 - 您不能在实际的查找之前在过滤查询或排序条件中使用这些字段。
但如果您使用SQL引擎,您可以使用虚拟临时字段的底层映射器功能 -只需在执行任何加载或查找操作之前设置即可
$mapper->newField = 'SQL EXPRESSION';
映射器 API
$db
数据库对象
可以是\DB\SQL、\DB\Jig或\DB\Mongo的对象,或者是一个包含实际数据库对象存储位置的HIVE键的字符串。
$table
要工作的表,字符串
如果未设置表,Cortex将使用strtolower(get_class($this))
作为表名。
$fluid
触发SQL流体模式,布尔值 = false
$fieldConf
字段配置,数组
数组方案是
protected $fieldConf = [ 'fieldName' => [ 'type' => string 'nullable' => bool 'default' => mixed 'index' => bool 'unique' => bool ] ]
从数据类型表获取可能的整个类型列表。
注意:您还可以添加'passThrough' => true
,以便在需要不在数据类型表中可用的自定义类型时,将type中的原始值用作数据类型。
$ttl
默认映射器模式ttl,整数 = 60
这仅影响SQL映射器的模式缓存。
$rel_ttl
默认映射器关系ttl,整数 = 0
此设置将在您的模型中为所有关系查询添加缓存。
$primary
SQL表主键,字符串
定义表使用的键。对于SQL引擎,默认是id
,对于JIG和Mongo引擎,始终是_id
。设置方法尊重此值以在您的数据库中创建新的SQL表,并且必须是一个整数列。
load
检索满足条件的第一条记录
bool load([ array $filter = NULL [, array $options = NULL [, int $ttl = 0 ]]])
简单的示例用于加载用户
$user->load(['username = ?','jacky']); if (!$user->dry()) { // user was found and loaded echo $user->username; } else { // user was not found }
如果没有参数调用,它将从数据库中加载第一条记录。如果加载操作成功,方法返回TRUE
。
loaded
计数当前加载的记录数
int loaded()
示例
$user->load(['last_name = ?','Johnson']); echo $user->loaded(); // 3
first, last, next, prev, skip
导航光标位置和映射记录的方法
参见 http://fatfreeframework.com/cursor#CursorMethods。
$user->load(['last_name = ?','Johnson']); echo $user->loaded(); // 3 echo $user->_id; // 1 $user->last(); echo $user->_id; // 3 echo $user->prev()->_id; // 2 echo $user->first()->_id; // 1 echo $user->skip(2)->_id; // 3
cast
将映射对象的字段作为关联数组返回
array cast ([ Cortex $obj = NULL [, int $rel_depths = 1]])
字段掩码
注意:由于配置关系深度似乎越来越不实用,引入了一种新的关系转换方法:“字段掩码”。这是未来的发展方向,并将取代旧版“关系深度配置”。
您还可以使用$rel_depths
来定义映射器的一个掩码,这样您就可以限制转换返回的字段
$data = $item->cast(null,[ '_id', 'order.number', 'product._id', 'product.title', 'product.features._id', 'product.features.title', 'product.features.icon', ]);
关系深度(旧方法)
一个简单的cast示例。如果模型包含关系,默认情况下它们也将以1级深度进行转换
$user->load(['_id = ?',3]); var_dump($user->cast()); /* Array ( [_id] => 3 [first_name] => Steve [last_name] => Johnson [comments] => Array( [1] => Array ( [_id] = 23 [post] => 2 [message] => Foo Bar ), [2] => Array ( [_id] = 28 [post] => 3 [message] => Lorem Ipsun ) ) )*/
如果您增加$rel_depths
的值,您还可以解决更深层的关系
var_dump($user->cast(NULL, 2)); /* Array ( ... [comments] => Array( [1] => Array ( [_id] = 23 [post] => Array( [_id] => 2 [title] => Kittenz [text] => ... ) [message] => Foo Bar ), ... ) )*/
关系深度配置
如果您只想解析特定的关系字段,可以将一个数组设置为$rel_depths
参数,其结构如下
$user->cast(NULL, [ '*' => 0, // cast all own relations to the given depth, // 0 doesn't cast any relation (default if this key is missing) 'modelA' => 0,// if a relation key is defined here, modelA is being loaded and casted, // but not its own relations, because the depth is 0 for it 'modelB' => 1,// modelB and all its 1st level relations are loaded and casted 'modelC' => [...] // you can recursively extend this cast array scheme ]); // simple sample: only cast yourself and the author model without its childs $news->cast(NULL,[ '*'=>0, 'author'=>0 ]); // nested sample: only cast yourself, // your own author relation with its profile and all profile relations $news->cast(NULL,[ '*'=>0, 'author'=>[ '*'=>0, 'profile'=>1 ] ]);
如果您不想解析和转换任何关系,只需将$rel_depths
设置为0
。任何一对一关系字段仅包含外键记录的_id
(或从$fieldConf绑定的任何其他自定义字段)的值,多对一和多对多字段为空。
castField
转换映射的相关集合
array|null castField( string $key [, int $rel_depths = 0 ])
find
返回匹配条件的对象集合
CortexCollection|false find([ array $filter = NULL [, array $options = NULL [, int $ttl = 0 ]]])
结果CortexCollection实现了ArrayIterator,可以像普通数组一样处理。在调用find
之前设置的任何过滤器或计数器都将被使用
// find published #web-design news, sorted by approved user comments $news->has('tags',['slug = ?','web-design']); $news->filter('comments', ['approved = ?',1]); $news->countRel('comments'); $records = $news->find( ['publish_date <= ? and published = ?', date('Y-m-d'), true], ['order' => 'count_comments desc'] );
findByRawSQL
使用原始SQL查询查找结果并将其工厂化到模型中
CortexCollection findByRawSQL( string|array $query [, array $args = NULL [, int $ttl = 0 ]])
如果您想编写自己的SQL查询并将结果工厂化到适当的模型中,可以使用此方法。例如。
$news_records = $news->findByRawSQL('SELECT * from news where foo <= ? and active = ?',[42, 1]);
findone
返回第一个符合条件记录(映射对象)
Cortex|false findone([ array $filter = NULL [, array $options = NULL [, int $ttl = 0 ]]])
此方法继承自Cursor类。
afind
返回匹配条件的数组数组
array|null find([ array $filter = NULL [, array $options = NULL [, int $ttl = 0 [, int|array $rel_depths = 1 ]]]])
查找整个集合,匹配条件,并根据$rel_depths
配置将所有映射器转换为数组。
addToCollection
给这个模型一个指向它所属的集合的引用
null addToCollection( CortexCollection $cx )
onload, aftererase, afterinsert, aftersave, afterupdate, beforeerase, beforeinsert, beforesave, beforeupdate
定义事件触发器
callback onload( callback $func )
有关事件处理器的更多信息,请参阅指南。
onget, onset
定义自定义字段获取器/设置器
callback onget( string $field, callback $func )
有关自定义字段处理器的更多信息,请参阅指南。
clear
清除任何映射字段或关系
null clear( string $key )
cleared
返回字段是否被清除
mixed initial( string $key )
如果字段最初有数据,但数据已从字段中清除,则返回旧清除数据。如果没有初始数据或字段没有更改(清除),则返回FALSE
。
clearFilter
移除一个或所有关系过滤器
null clearFilter([ string $key = null ])
仅移除给定的$key
过滤器或全部,如果没有给出。
compare
将新数据与某些字段的现有初始值进行比较
null compare( array $fields, callback $new [, callback $old = null ])
此方法将新数据(形式为[field => value]的关联数组)与初始字段值进行比较,并为$new和$old值调用回调函数,这些回调函数可以用于准备新数据/清理旧数据。
更新字段已设置,$new回调函数必须返回一个值。
$uploads=[ 'profile_image' => 'temp_uploads/thats_me.jpg', 'pictures' => ['7bbn4ksw8m5', 'temp_uploads/random_pic.jpg'] ]; $this->model->compare($uploads,function($filepath) { // new files return $this->handleFileUpload($filepath); }, function($fileId){ // old files $this->deleteFile($fileId); });
在上面的例子中,我们处理多个字段,并将它们的值与传入的新数据数组进行比较。对于每个新的字段值或更改/添加的数组项值,都会调用 $new
函数。对于在新数据中不再存在的现有数据,将调用 $old
函数。
copyfrom
从hive键或给定数组中初始化mapper
null copyfrom( string|array $key [, callback|array|string $fields = null ])
使用此方法一次设置mapper的多个值。$key
参数必须是包含实际数组的hive键的数组或字符串。
$fields
参数可以是一个可分割的字符串
$news->copyfrom('POST','title;text');
或者一个数组
$news->copyfrom('POST',['title','text']);
或者一个回调函数,该函数用于过滤输入数组
$news->copyfrom('POST',function($fields) { return array_intersect_key($fields,array_flip(['title','text'])); });
copyto
将mapper值复制到hive键
null copyto( string $key [, array|string $relDepth = 0 ])
copyto_flat
将mapper值复制到hive键,关系是键的简单数组
null copyto_flat( string $key )
所有 has-many
关系都以它们的主键的简单数组列表形式返回。
count
计算符合标准的记录数
null count([ array $filter [, array $options = NULL [, int $ttl = 60 ]]])
与 find()
类似,但它只执行计数查询而不是真正的选择。
countRel
添加一个虚拟字段,用于计算出现的关系
null countRel( string $key [, string $alias [, array $filter [, array $option]]])
$key
参数必须是一个现有的关系字段名称。这将向您的结果添加一个虚拟计数器字段,其中包含与当前记录匹配的关系的计数/总和,默认名称为 count_{$key}
,除非您为它定义了一个自定义的 $alias
。
您还可以定义用于计数关系的查询的 $filter
和 $options
。
您还可以使用此计数器进行排序,例如在这个标签云示例中
$tags = new \Model\Tag(); $tags->filter('news',['published = ? and publish_date <= ?', true, date('Y-m-d')]); $tags->countRel('news'); $result = $tags->find(['deleted = ?',0], ['order'=>'count_news desc']);
此方法还支持传播,因此您可以非常直接地在嵌套关系上定义计数器
// fetch all posts, with comments and count its likes (reactions of type "like") on each comment $post->countRel('comments.reaction','count_likes', ['type = ?', 'like']); $results = $post->find();
dbtype
返回当前使用的db类型
string dbtype()
类型是 SQL
、Mongo
或 Jig
。
defaults
从模式配置返回默认值
array defaults([ bool $set = FALSE ])
返回一个 $key
=> $value
数组,其中包含具有不同于 NULL
的默认值的字段。
dry
如果当前光标位置没有映射到任何记录,则返回 TRUE
bool dry()
示例
$mapper->load(['_id = ?','234']); if ($mapper->dry()) { // not found } else { // record was loaded }
erase
删除对象/并重置ORM
null erase([ array $filter = null ])
当设置 $filter
参数时,它将删除所有匹配的记录
$user->erase(['deleted = ?', 1]);
当在未设置 $filter
参数的初始化mapper上调用时,它将删除加载的记录
$user->load(['_id = ?',6]); $user->erase();
这也会调用 beforeerase
和 aftererase
事件。
exists
检查某个字段是否存在于mapper中或是否为虚拟关系字段
bool exists( string $key [, bool $relField = false ])
如果 $relField
为 true,它还会检查定义的关系字段 $fieldConf。
fields
获取字段或设置字段的白名单/黑名单
array fields([ array $fields = [] [, bool $exclude = false ])
如果您不提供任何参数调用此方法,它将返回模式中可用的字段列表。
var_dump( $user->fields() ); /* Array( '_id' 'username' 'password' 'email' 'active' 'deleted' )*/
如果您设置了一个 $fields
数组,它将启用字段白名单,并将给定的字段放入该白名单。加载的记录上所有非白名单字段都不可用、不可见或不可访问。这对于您不想在返回的转换数组中包含某些字段非常有用。
$user->fields(['username','email']); // only those fields $user->load(); var_dump($user->cast()); /* Array( '_id' => 5 'username' => joe358 'email' => joe@domain.com )*/
调用此方法将重新初始化mapper,并在任何进一步的加载或查找操作上生效,因此首先运行此操作。
如果您将 $exclude
参数设置为 true
,它也将启用白名单,但将所有可用的字段(除去给定的字段)放入白名单。换句话说,给定的 $fields 成为黑名单,只有剩余的字段保持可见。
$user->fields(['email'], true); // all fields, but not these $user->load(); var_dump($user->cast()); /* Array( '_id' => 5 'username' => joe358 'password' => $18m$fsk555a3f2f08ff28 'active' => 1 'deleted' => 0 )*/
如果您的模型上配置了关系字段,您还可以禁止访问该关系的字段。为此请使用点符号
$comments->fields(['user.password'], true); // exclude the password field in user model $comments->load(); var_dump($comments->cast()); /* Array( '_id' => 53 'message' => .... 'user' => Array( '_id' => 5 'username' => joe358 'active' => 1 'deleted' => 0 ) )*/
您可以多次调用此方法,并结合使用。它将始终与之前设置的白色和黑名单字段合并。_id
总是存在的。
filter
添加加载相关模型过滤器
Cortex filter( string $key [, array $filter = null [, array $options = null ]])
参见高级过滤技术。
get
检索键的值
mixed get( string $key [, bool $raw = false ])
如果$raw
为true
,它将直接返回字段中的原始数据,不进行任何进一步处理,不解析关系,不触发获取事件。如果您只想获取关系字段的原始外键值,则非常有用。
getRaw
检索键的原始内容
mixed getRaw( string $key )
这是$mapper->get('key', TRUE)
的快捷方法。
getFieldConfiguration
返回模型$fieldConf
数组
array|null getFieldConfiguration()
getTable
返回模型表名
string getTable()
如果没有定义表,则使用当前类名的小写作为表名。
has
将条件过滤添加到下一个查找调用
Cortex has( string $key [, array $filter = null [, array $options = null ]])
参见高级过滤技术。
orHas
将具有条件过滤的OR操作符添加到先前条件
与has过滤相同,但通过逻辑OR与先前条件连接。
initial
返回初始字段值
mixed initial( string $key )
返回字段初始数据,就像它是从数据库中检索出来的一样,即使字段之后已更改。在返回之前,数组字段被正确解码/反序列化。
mergeFilter
将多个过滤数组粘合为一个
array mergeFilter( array $filters [, string $glue = 'and' ])
当您想向过滤数组添加更多条件或将多个过滤数组合并在一起时,这非常有用,例如,当您组装基于条件的复杂搜索功能时。使用$glue
参数定义用于合并两个过滤器的部分(通常是AND
或OR
)。
$filter1 = ['_id = ?', 999]; $filter2 = ['published = ? or active = ?', true, false]; $new_filter = $mapper->mergeFilter([$filter1, $filter2]); // array('(_id = ?) and (published = ? or active = ?)', 999, true, false)
paginate
返回包含匹配条件的记录子集的数组
array paginate([ int $pos = 0 [, int $size = 10 [, array $filter = NULL [, array $options = NULL [, int $ttl = 0 ]]]]])
参见Cursor->paginate。任何has和filter过滤器都可以与paginate一起使用。
rel
从一个关系返回干净的/干燥的模型
Cortex rel( string $key )
例如,如果comments
是一个与\Model\Comment
的一对多关系
$user->load(); var_dump($user->comments); // array of comments $new_comment = $user->rel('comments'); // $new_comment is a new empty \Model\Comment
reset
重置并重新初始化映射器
null reset([ bool $mapper = true ])
如果$mapper
是false,它只重置过滤器、默认值和映射器的内部缓存,但保留填充的记录不受影响。
resetFields
重置特定字段并返回到其默认值
null resetFields( array $fields )
如果任何字段没有默认值,则将其重置为NULL
。
resolveConfiguration
启动映射器以获取其配置
array resolveConfiguration()
返回一个数组,该数组公开映射器配置。该数组包括
- table
- fieldConf
- db
- fluid
- primary
save
保存映射记录
建议始终使用save方法。它将自动检查您是要保存新记录还是更新已加载的现有记录。
$user->username = 'admin' $user->email = 'admin@domain.com'; $user->save(); // insert $user->reset(); $user->load(['username = ?','admin']); $user->email = 'webmaster@domain.com'; $user->save(); // update
save方法还触发beforeinsert
、beforeupdate
、afterinsert
和afterupdate
事件。还有insert
和update
方法,但直接使用这些方法将跳过事件和任何级联操作。
set
将值绑定到键
mixed set( string $key, mixed $val )
setdown
删除所有模型数据,请谨慎处理
null setdown([ object|string $db = null [, string $table = null ]])
此方法完全删除自己的表,并使用数据库中的多对多交叉表。
setFieldConfiguration
设置模型定义
null setFieldConfiguration( array $config )
用于设置$fieldConf
数组。
setup
设置/更新表模式
bool setup([ object|string $db = null [, string $table = null [, array $fields = null ]]])
此方法为模型本身创建所需表,以及额外所需的交叉表。它使用内部模型属性$db、$table和fieldConf,但也可以提供方法参数,这些参数将具有优先级。
touch
使用当前时间更新给定日期或时间字段
null touch( string $key [, int $timestamp = NULL ])
如果$key
是数组$fieldConf中定义的字段,并且是日期、日期时间或时间戳类型,此方法将字段更新为当前时间/日期的适当格式。
如果提供了$timestamp
,则使用该值而不是当前时间。
有效
返回当前迭代器位置是否有效。
bool valid()
它是dry()的对立面。
$mapper->load(['_id = ?','234']); if ($mapper->valid()) { // record was loaded } else { // not found }
虚拟
虚拟映射字段设置器
null virtual( string $key, mixed $val )
这将为映射设置自定义虚拟字段。对于某些按需操作很有用。
$user->virtual('pw_unsecure', function($this) { return \Bcrypt::instance()->needs_rehash($this->password, 10); });
可以在选定的集合上进行排序后使用虚拟字段,请参阅虚拟字段。
集合 API
每次使用find
方法时,它都会返回新的CortexCollection类的一个实例。这样我们就能从结果中的每个映射器内部确定整个集合,这为我们提供了一些更高级的功能,如智能加载关系。CortexCollection实现了ArrayIterator
接口,所以它可以像普通数组一样访问。以下是Cortex集合提供的最有用的方法
添加
将单个模型添加到集合中
null add( Cortex $model )
也可以使用数组表示法来添加模型
$news->load(); $new_comment = $news->rel('comments'); $new_comment->text = 'Foo Bar'; $news->comments[] = $new_comment; $news->save();
castAll
将所有包含的映射器转换为嵌套数组
array castAll([ $reldepths = 1 ])
类似于对单个映射器的Cortex->cast
方法,此方法自动将所有包含的映射器转换为简单的嵌套数组。
$result = $news->find(['published = ?',true]); if ($result) $json = json_encode($result->castAll());
使用$reldepths
参数定义要转换的内容,请参阅cast方法的详细信息。
compare
compare collection with a given ID stack
array compare( array|CortexCollection $stack [, string $cpm_key = '_id'])
此方法非常有用,可以比较当前集合与另一个集合或值列表,该列表在集合记录中被检查是否存在。
例如,您有一个即将更新的关系集合,您想知道哪些记录将要被删除或在集合中是新的
$res = $user->friends->compare($newFriendIds); if (isset($res['old'])) { // removed friends } if (isset($res['new'])) { // added friends foreach($res['new'] as $userId) { // do something with $userId } }
比较结果$res
是一个数组,可以包含数组键old
和new
,它们都代表一个$cpm_key
值的数组。
注意:这只是一种比较 - 实际上它不会更新任何集合。比较后,添加简单的$user->friends = $newFriendIds;
以更新集合。
包含
检查集合是否包含具有给定键值集的记录
bool contains( mixed|Cortex $val [, string $key = '_id' ])
此方法可以方便地检查集合是否包含给定的记录,或者是否有具有给定值的记录
if ($user->friends && $user->friends->contains($newFriend)) { $f3->error(400,'This user is already your friend'); return; }
使用自定义比较键
if ($user->blocked_users->contains($currentUserId,'target')) { // this user has blocked you }
公开
返回内部数组表示形式
array expose()
工厂
从给定的记录创建一个新的集合实例
CortexCollection factory( array $records )
$records
必须是一个数组,包含Cortex映射器对象。
getAll
返回所有模型指定属性的值
array getAll( string $prop [, bool $raw = false ])
您可以使用getAll()
从所有包含的映射器中检索特定键的所有值。将第二个参数设置为true
以获取原始数据库结果,而不是配置为关系的字段上的已解析映射器。
$users = $user->find(['active = ?',1]); $mails = $users->getAll('email'); /* Array( 'user1@domain.com', 'user2@domain.com', 'user3@domain.com' )*/
getBy
array getBy( string $index [, bool $nested = false ])
您可以使用getBy()
按定义的键转换结果。因此,您需要提供一个现有的字段,如下所示;
$pages = $page->find(); $pages_by_slug = $pages->getBy('slug');
这将根据每个映射器的电子邮件字段对结果数组进行排序,这会给您一个结果数组,如array("foo@domain.com"=>array(...))
。如果您将第二个参数提供为true
,则将记录排序到另一个数组深度,以跟踪每个键的多个结果。
hasChanged
返回true,如果任何模型在添加到集合后被修改
bool hasChanged()
orderBy
使用类似SQL的语法重新排列当前集合
null orderBy( string $cond )
如果您需要将结果集合再次重新排序到另一个键,请像这样使用此方法:$results->orderBy('name DESC');
。这也适用于多个排序键。
setModels
设置模型集合
array setModels( array $models [, bool $init = true ])
此方法将多个Cortex对象添加到自己的集合中。当$init
为true
时,使用此方法添加的模型不会影响changed
状态。
slice
截取集合
null slice( int $offset [, int $limit = null ])
这将从集合中删除一部分。
附加说明
-
要释放任何关系,只需将字段设置为
NULL
并保存映射器。 -
所有关系都是延迟加载以节省性能。这意味着它们将在您通过链接属性访问它们或转换整个父模型时才会加载。
-
结果集合内的延迟加载将
自动
触发该属性的预加载到整个集合。结果保存到Identity Map以减轻对后续调用的压力。我称之为智能加载,并用于解决1+N查询问题,无需额外配置。 -
如果您需要使用与
id
不同的SQL主键(由于任何遗留原因),您可以使用$primary
类属性将其设置为其它的值。现在您应该在查询中使用新的自定义主键。这样做将限制您的应用程序使用SQL引擎。 -
要获取任何记录的id,请使用
$user->_id;
。即使您已设置自定义主键,这也适用。 -
要按其
id
查找任何记录,请使用过滤器数组中的字段_id
,例如:['_id = ?', 123]
。 -
主字段不应包含在
$fieldConf
数组中。它们可能会干扰设置过程。 -
您可以通过这些蜂巢键来控制Cortex的一些小行为
-
CORTEX.queryParserCache
:如果TRUE
,所有查询字符串都将被缓存(可能会添加很多缓存条目)。默认:FALSE
-
CORTEX.smartLoading
:触发智能延迟预加载。默认为TRUE
,但如果您认为某些东西工作不正常,请将其关闭。如果禁用,可能会向您的数据库发送大量额外的查询。 -
CORTEX.standardiseID
:默认TRUE
。这会将任何定义的主键移动到返回数组中的_id
字段。 -
CORTEX.quoteConditions
:默认TRUE
。默认情况下,根据使用的数据库引擎,所有where条件中的字段名称都会自动引用。这有助于解决SQL中的保留名称问题。然而,字段的检测还不够完美,因此如果您想自己添加正确的反引号或其他引用,请将其设置为FALSE
。
-
已知问题
- 这实际上不是一个错误,但返回的集合(来自关系、find或paginate方法)是不可克隆的,因为它们需要保持对其关系的身份映射的唯一引用。这导致所有包含的映射器在模板中都不会自动转义,无论
ESCAPE
设置如何。请记住为您的令牌添加| esc
过滤器。
如果您发现任何问题或错误,请在github上提交新问题或写邮件。谢谢。
路线图
如果您有任何想法、建议或改进,请随时在github上为这个问题添加问题。
Cortex 目前仅反映最常见的使用案例。如果您需要更广泛的查询或数据库控制,您可以考虑使用底层的映射器或直接使用数据库。这可以在您的模型类中的自定义方法或字段预处理中进行。
无论如何,希望您觉得这很有用。如果您喜欢这个插件,为什么不考虑捐赠一下呢?
如果您想看到 Cortex 在实际中的使用,请查看 fabulog。
许可协议
GPLv3