ekhaled/f3-cortex

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

v1.7.3 2021-03-15 10:49 UTC

This package is auto-updated.

Last update: 2024-09-20 21:22:19 UTC


README

一旦相关PR合并到原始仓库,此分支将被废弃

Cortex

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

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

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

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

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

目录

  1. 快速入门
  2. SQL 流体模式
  3. Cortex 模型
    1. 配置
      1. 附加数据类型
      2. 替代配置
      3. 黑名单字段
    2. 设置
    3. 撤消
  4. 关系
    1. 设置链接
    2. 处理关系
      1. 一对一
      2. 多对多,双向
      3. 多对多,单向
      4. 多对多,自引用
  5. 事件处理程序
    1. 自定义字段处理程序
  6. 过滤查询语法
    1. 运算符
    2. 选项数组
  7. 高级过滤技术
    1. 过滤
  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 文件复制到您的 libs 中。对于 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://:27017','testdb');

让我们开始吧

如果您熟悉F3的Data-Mapper,那么您已经知道Cortex也支持所有基本的CRUD操作。它实现了ActiveRecord的Cursor类及其所有方法。因此,您可以将Cortex作为F3 Mapper的即插即用替代品,其基本用法依然简单。

$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 流体模式

当您正在构建一些新的对象或只是不想在使用Cortex的同时处理表模式时,您可以在使用SQL数据库后端时启用SQL Fluid模式。这样,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包装到像这样的模型类中

// 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数据类型表

您还可以扩展此配置数组,以便为自定义验证规则或其他内容留出空间。

数据类型值是来自模式插件的定义常量。如果您想在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,
],

现在您可以在模型字段中保存数组数据,这些数据在幕后(在使用SQL后端时)被json_encoded到text字段中。

$mapper->colors = ['red','blue','green'];

替代配置

如果您需要更多灵活的配置并且不想硬编码,可以覆盖模型类构造函数以从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

黑名单字段

field()方法可以用来返回当前模型上的可用字段。如果用一个简单的数组参数如$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 类型,这意味着必须有一个第三个枢纽表来保持绑定一切的外键。通常,Cortex 将在 setup 方法上自动创建该表,使用自动生成的表名。如果您想为该连接表使用自定义名称,请将第三个参数添加到 两个 模型的配置数组中,例如

'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事件处理器的所有setter,并额外添加了自定义字段处理器(setter/getter)。这些可以在执行某些操作之前或之后执行一些额外的代码。这可以在您的模型中直接进行验证,或者进行扩展的保存、加载或删除级联。

支持以下事件

  • 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';

现在将触发您的自定义setter,执行您想要的任何操作。

过滤查询语法

基本上,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']

  • IN运算符在原始PDO中通常需要多个占位符(如foo IN (?,?,?))。在Cortex查询中,您只需使用一个数组即可,查询解析器会处理其余部分。

    ['foo IN ?', [1,2,3]]

    您还可以使用CortexCollection作为绑定参数。在这种情况下,主键将自动用于匹配

    $fruits = $fruitModel->find(['taste = ?','sweet']);
    $result = $userModel->find(['favorite_fruit IN ?',$fruits])

选项

用于加载操作的$options数组尊重以下键

  • order
  • limit
  • offset

使用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方法向相关字段添加一些条件,这些条件必须在它的父级下一个find()或load()方法被触发时满足。所以这是用来限制主要结果的。

换句话说:让我们找到所有被标记为“响应式”的新闻记录。

$news->has('tags', ['title = ?','Responsive']);
$results = $news->find();
echo $results[0]->title; // '10 Responsive Images Plugins'

当然,您也可以使用查询的相反方式,使用TagModel,通过标题加载它们,并访问共享的$tags->news属性来找到您的记录。has方法的优势在于您也可以向父级添加一个条件。这样,您可以将加载行编辑成如下:$news->find(['published = ?', 1]);。现在,您可以根据两个不同的模型来限制结果 - 您只加载被标记为“响应式”的已发布新闻。

您还可以向不同的关系添加多个has条件

$news->has('tags', ['title = ?','Responsive']);
$news->has('author', ['username = ?','ikkez']);
$results = $news->find(['published = ?', 1], ['limit'=>3, 'order'=>'date DESC']);

现在,您只加载我写的最后3篇被标记为“响应式”的新闻,按发布日期排序。 ;)

如果您愿意,也可以用流畅的风格调用它们:$news->has(...)->load(...);

过滤

filter方法是为了限制关系的查询结果。例如:加载作者x及其2014年的新闻。

$author->filter('news', ['date > ?','2014-01-01']);
$author->load(['username = ?', 'ikkez']);

has()方法类似,您可以为查询添加多个过滤条件。您还可以混合使用过滤和has条件。一旦执行了loadfind函数,过滤(和has)条件就会清除,为下一个查询做准备。

当前过滤条件不支持继承。这意味着如果您递归访问一个关系的字段(例如$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对象

可以是\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',
]);

关系深度(旧方法)

一个简单的转换示例。如果模型包含关系,它们也将默认转换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调用。

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

此方法将新数据(以 [字段 => 值] 的关联数组形式)与初始字段值进行比较,并为 $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 键或给定数组中填充映射器

null copyfrom( string|array $key [, callback|array|string $fields = null ])

使用此方法一次设置映射器的多个值。$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

将映射器值复制到 hive 键

null copyto( string $key [, array|string $relDepth = 0 ])

copyto_flat

将映射器值复制到 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 参数的填充映射器上调用时,它将删除加载的记录

$user->load(['_id = ?',6]);
$user->erase();

这也会调用 beforeeraseaftererase 事件。

exists

检查映射器中是否存在特定字段或是否为虚拟关系字段

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
)*/

调用此方法将重新初始化映射器,并对后续的加载或查找操作生效,因此首先运行此方法。

如果您将 $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 总是存在。

过滤

为加载相关模型添加过滤器

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

如果没有定义表,则使用当前类名的小写作为表名。

为下一个查找调用添加具有条件的过滤器

Cortex has( string $key [, array $filter = null [, array $options = null ]])

高级过滤技术

orHas

将具有条件的过滤器(使用 OR 运算符)添加到之前条件中

与具有过滤器相同,但它通过逻辑 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 过滤器都可以与分页一起使用。

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

此方法将完全删除自己的表,并从数据库中删除许多多对多枢纽表。

设置字段配置

设置模型定义

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,则使用该值而不是当前时间。

valid

返回当前迭代器位置是否有效。

bool valid()

它是 dry() 的对应物。

$mapper->load(['_id = ?','234']);
if ($mapper->valid()) {
    // record was loaded
} else {
    // not found
}

virtual

虚拟映射字段设置器

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 集合提供的一些最有用的方法

add

将单个模型添加到集合中

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; 以更新集合。

contains

检查集合是否包含具有给定键值集的记录

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
}

expose

返回内部数组表示

array expose()

factory

从给定的记录创建新的集合实例

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 时,使用此方法添加的模型不会影响 更改 状态。

slice

截取集合

null slice( int $offset [, int $limit = null ])

此操作从集合中删除一部分。

附加说明

  • 要释放任何关系,只需将字段设置为 NULL 并保存映射器。

  • 所有关系都采用延迟加载以节省性能。这意味着它们将在您通过链接属性或将整个父模型转换为其他类型时才加载。

  • 在结果集合中进行延迟加载将 自动 启用该属性的 预加载 到整个集合。结果被保存到一个 Identity Map 中,以减轻对后续调用的压力。我将其称为 智能加载,用于解决 1+N 查询问题,无需额外配置。

  • 如果您需要使用不同于 id 的 SQL 主键(出于任何遗留原因),您可以使用 $primary 类属性将其设置为您想要的内容。您现在应该使用新的自定义 pkey 在查询中使用。这样做将限制您的应用程序使用 SQL 引擎。

  • 要获取任何记录的 id,请使用 $user->_id;。即使您已设置自定义主键,这也一样有效。

  • 要通过 id 查找任何记录,请在您的过滤器数组中使用字段 _id,例如 ['_id = ?', 123]

  • 主字段不应包含在 $fieldConf 数组中。它们可能会干扰 设置 例程。

  • 您可以通过这些 hive 键控制 Cortex 的一些小行为

    • CORTEX.queryParserCache:如果 TRUE,所有查询字符串也将被缓存(可能增加很多缓存条目)。默认:FALSE

    • CORTEX.smartLoading:触发智能延迟预加载。默认是 TRUE,但如果您认为某些事情工作不正确,请将其关闭。如果禁用,可能会向您的数据库发送大量的额外查询。

    • CORTEX.standardiseID:默认 TRUE。这会将定义的任何主键移动到返回数组中的 _id 字段。

    • CORTEX.quoteConditions:默认 TRUE。默认情况下,所有在 where 条件中的字段名都会根据使用的数据库引擎自动引号化。这有助于解决 SQL 中的保留名称问题。但是,字段检测还不够完美,因此如果您想自己添加正确的反引号或其他引号,请将其设置为 FALSE

已知问题

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

如果您发现任何问题或错误,请在网上提交一个 新问题 或发送电子邮件。谢谢。

路线图

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

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

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

buy me a Beer

如果您想看看Cortex的实际应用,可以看看fabulog

许可证

GPLv3