ikkez/f3-cortex

PHP Fat-Free 框架的多引擎 ORM / ODM

v1.7.7 2024-03-06 16:05 UTC

This package is auto-updated.

Last update: 2024-09-06 17:09:37 UTC


README

Cortex

PHP Fat-Free 框架的通用数据映射器

Cortex 是一个多引擎 ActiveRecord ORM / ODM,提供简单的对象持久性。其主要特性包括:

  • 处理 SQL、Jig 和 MongoDB 数据库引擎
  • 使用熟悉的 SQL 语法编写查询,它们可以转换为 Jig 和 Mongo
  • 自动从定义的模式配置中创建 SQL 表和扩展列
  • 使用 SQL Fluid 模式轻松进行原型设计,这使得您的 RDBMS 模式无模式,并自动添加新表列
  • 支持模型和集合
  • 关系:将多个模型链接到一对一、一对多和多对多关联
  • 智能加载相关模型(智能懒加载和预加载,无需配置)
  • 用于通过关系进行嵌套过滤的有用方法
  • 为所有字段提供大量事件处理器和自定义设置器/获取器预处理程序
  • 为 NoSQL 定义默认值和可空字段
  • 还提供了额外的 验证插件

使用 Cortex,您可以创建通用的应用程序,这些应用程序可以与用户选择的任何数据库一起工作,无论它是 SQlite、PostgreSQL、MongoDB 还是其他数据库。您还可以混合使用多个引擎或同时使用它们。

它非常适合快速轻松地进行数据抽象,并提供了一组有用的过滤器选项。

目录

  1. 快速入门
  2. SQL Fluid 模式
  3. Cortex 模型
    1. 配置
      1. 附加数据类型
      2. 备用配置
      3. 黑名单字段
    2. 设置
    3. 拆卸
  4. 关系
    1. 设置链接
    2. 处理关系
      1. 一对一
      2. 多对多,双向
      3. 多对多,单向
      4. 多对多,自引用
  5. 事件处理器
    1. 自定义字段处理器
  6. 过滤查询语法
    1. 运算符
    2. 选项数组
  7. 高级过滤技术
    1. has
    2. orHas
    3. filter
  8. 聚合分析
    1. 计数关系
    2. 虚拟字段
  9. 映射器 API
  10. 集合 API
  11. 附加说明
  12. 已知问题
  13. 路线图
  14. 许可协议

快速入门

系统需求

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 及其依赖项包含到您的包中。

设置数据库

创建您选择的数据库对象。您可以选择 SQLJigMongoDB。以下是一些示例

// 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的所有花哨方法,如loadfindcastnextprev。关于过滤和其他方法的更多信息稍后介绍。

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值。通过indexunique,你甚至可以为列设置索引。这样做可以使你在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 提供以下类型的关联,这些关联通常必须在关系的两个类中 定义

这是带有关系的字段配置的示例

Cortex Rel 1

这将在作者和新闻之间创建聚合,因此

一条新闻属于一个作者。

一个作者写了多条新闻。

UML 1

作为旁注:belongs-to-* 定义将在该表中创建一个新的列,用于保存对应模型的 id(外键字段)。而 has-* 定义只是虚拟字段,它们将根据对方的 id(反向方式)查询链接的模型。这导致以下配置架构

对于 belongs-to-onebelongs-to-many

'realTableField' => [
    'relationType' => '\Namespace\ClassName',
],

belongs-to-* 定义外键是可选的。默认方式是使用标识字段。对于 SQL 引擎,这要么是默认的主键 id,要么是可以使用 $primary 类属性设置的自定义主键。NoSQL 引擎将使用 _id。如果您需要定义另一个非主键字段以进行连接,请使用 ['\Namespace\ClassName','cKey']

对于 has-onehas-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_idtag_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']

Cortex m:m self-ref

通常情况下,这是一个双向关系,意味着您将直接链接到您的朋友(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*事件。

自定义字段处理器

onsetonget事件具有略微不同的参数

$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。但是,您应该阅读以下附加说明中的某些细微修改。

运算符

支持以下常见过滤器运算符

  • 关系运算符:<><=>====!=<>
  • 搜索运算符:LIKENOT LIKEINNOT IN(不区分大小写)
  • 逻辑运算符:()ANDOR&&、(||仅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数组尊重以下键:

  • 排序
  • 限制
  • 偏移

使用DESCASC标志对排序字段进行排序,就像在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条件。一旦执行了loadfind函数,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()

类型是 SQLMongoJig

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();

这也会调用 beforeeraseaftererase 事件。

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 ])

如果$rawtrue,它将直接返回字段中的原始数据,不进行任何进一步处理,不解析关系,不触发获取事件。如果您只想获取关系字段的原始外键值,则非常有用。

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参数定义用于合并两个过滤器的部分(通常是ANDOR)。

$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。任何hasfilter过滤器都可以与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 ])

如果$mapperfalse,它只重置过滤器、默认值和映射器的内部缓存,但保留填充的记录不受影响。

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方法还触发beforeinsertbeforeupdateafterinsertafterupdate事件。还有insertupdate方法,但直接使用这些方法将跳过事件和任何级联操作。

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$tablefieldConf,但也可以提供方法参数,这些参数将具有优先级。

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是一个数组,可以包含数组键oldnew,它们都代表一个$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对象添加到自己的集合中。当$inittrue时,使用此方法添加的模型不会影响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

已知问题

  • 这实际上不是一个错误,但返回的集合(来自关系、findpaginate方法)是不可克隆的,因为它们需要保持对其关系的身份映射的唯一引用。这导致所有包含的映射器在模板中都不会自动转义,无论ESCAPE设置如何。请记住为您的令牌添加| esc过滤器。

如果您发现任何问题或错误,请在github上提交新问题或写邮件。谢谢。

路线图

如果您有任何想法、建议或改进,请随时在github上为这个问题添加问题。

Cortex 目前仅反映最常见的使用案例。如果您需要更广泛的查询或数据库控制,您可以考虑使用底层的映射器或直接使用数据库。这可以在您的模型类中的自定义方法或字段预处理中进行。

无论如何,希望您觉得这很有用。如果您喜欢这个插件,为什么不考虑捐赠一下呢?

buy me a Beer

如果您想看到 Cortex 在实际中的使用,请查看 fabulog

许可协议

GPLv3