模型¶
概览¶
The Phalcon\Mvc\Model是M
在MVC中。它是一个将业务对象和数据库表连接起来的类,以创建一个持久化的领域模型,其中逻辑和数据被封装在一起。它是对象关系映射(ORM)的一种实现。
模型表示应用程序的信息(数据)以及操作这些数据的规则。模型主要用于管理与相应数据库表交互的规则。在大多数情况下,数据库中的每个表都会对应应用程序中的一个模型。大部分应用程序的业务逻辑将集中在模型中。
The Phalcon\Mvc\Model是首个使用Zephir/C语言为PHP编写的ORM,它在保证开发者操作数据库时易用性的同时,也提供了高性能。
注意
模型旨在通过抽象的高层来操作数据库。如果您需要在更底层上操作数据库,请查看Phalcon\Db\Db组件文档。
<?php
use MyApp\Models\Invoices;
/**
* Create an invoice
*/
$invoice = new Invoices();
$invoice->inv_cst_id = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title = 'Invoice for ACME Inc.';
$invoice->inv_total = 100;
$invoice->inv_created_at = '2019-12-25 01:02:03';
$result = $invoice->save();
if (false === $result) {
echo 'Error saving Invoice: ';
$messages = $invoice->getMessages();
foreach ($messages as $message) {
echo $message . PHP_EOL;
}
} else {
echo 'Record Saved';
}
注意
关于如何创建模型的信息,请参阅创建模型部分
常量¶
常量 | 值 |
---|---|
DIRTY_STATE_DETACHED | 2 |
DIRTY_STATE_PERSISTENT | 0 |
DIRTY_STATE_TRANSIENT | 1 |
OP_CREATE | 1 |
OP_DELETE | 3 |
OP_NONE | 0 |
OP_UPDATE | 2 |
TRANSACTION_INDEX | 'transaction' |
方法¶
final public function __construct(
mixed $data = null,
DiInterface $container = null,
ManagerInterface $modelsManager = null
)
assign
。您也可以选择传递DI容器和Models Manager对象。如果未传递,则会使用默认值。 当调用未实现的方法时进行处理。若方法不存在则抛出Phalcon\Mvc\Model\Exception异常 当静态方法未实现时进行处理。若方法不存在则抛出Phalcon\Mvc\Model\Exception异常 使用关联别名作为属性的魔术方法获取相关记录 检查属性是否是有效关联的魔术方法 分配值给模型的魔术方法 设置模型中的行为 <?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Behavior\Timestampable;
class Invoices extends Model
{
public function initialize()
{
$this->addBehavior(
new Timestampable(
[
'onCreate' => [
'field' => 'inv_created_at',
'format' => 'Y-m-d H:i:s',
],
]
)
);
}
}
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
use Phalcon\Messages\Message as Message;
class Invoices extends Model
{
public function beforeSave()
{
if (0 === $this->inv_status_flag) {
$message = new Message(
'Sorry, an invoice cannot be unpaid'
);
$this->appendMessage($message);
}
}
}
public function assign(
mixed $data,
array $whiteList = null,
array $dataColumnMap = null
): ModelInterface
data
参数可以是数组或数据库行。whitelist
是一个包含模型属性的数组,这些属性将在分配过程中被更新。即使数组或数据库行中包含省略的属性也不会被接受;然而,如果模型需要其中一个属性,数据将无法保存并且模型会产生错误。dataColumnMap
是一个将data
的列映射到实际模型中的数组。当您希望将如$_POST
数组中的输入映射到数据库字段时,这非常有用。 从数组向模型赋值
<?php
$invoice->assign(
[
'inv_cst_id' => 1,
'inv_status_flag' => 1,
'inv_title' => 'Invoice for ACME Inc.',
'inv_total' => 100,
'inv_created_at' => '2019-12-25 01:02:03',
]
);
assign
结合数据库行。- 需要列映射
<?php
$invoice->assign(
$row,
null,
[
'inv_cst_id' => 'customerId',
'inv_status_flag' => 'status',
'inv_title' => 'title',
'inv_total' => 'total',
]
);
只更新inv_status_flag
, inv_title
, inv_total
字段。
默认情况下assign
会使用setter方法(如果存在),可以通过使用ini_set
来禁用该功能以直接使用属性
<?php
use MyApp\Models\Invoices;
$average = Invoices::average(
[
'column' => 'inv_total',
]
);
echo 'AVG: ', $average, PHP_EOL;
$average = Invoices::average(
[
'inv_cst_id = 1',
'column' => 'inv_total',
]
);
echo 'AVG [Customer: 1] ', $average, PHP_EOL;
public static function cloneResult(
ModelInterface $base,
array $data,
int $dirtyState = 0
): ModelInterface
<?php
use MyApp\Models\Invoices;
$invoice = Invoices::cloneResult(
new Invoices(),
[
'inv_cst_id' => 1,
'inv_status_flag' => 0,
'inv_title' => 'Invoice for ACME Inc. #2',
'inv_total' => 400,
'inv_created_at' => '2019-12-25 01:02:03',
]
);
public static function cloneResultMap(
mixed $base,
array $data,
array $columnMap,
int $dirtyState = 0,
bool $keepSnapshots = null
): ModelInterface
<?php
use MyApp\Models\Invoices;
$invoice = Invoices::cloneResultMap(
new Invoices(),
[
'customerId' => 1,
'status' => 0,
'title' => 'Invoice for ACME Inc. #2',
'total' => 400,
'created' => '2019-12-25 01:02:03',
]
);
public static function cloneResultMapHydrate(
array $data,
array $columnMap,
int $hydrationMode
): mixed
<?php
use MyApp\Models\Invoices;
$average = Invoices::count();
echo 'COUNT: ', $average, PHP_EOL;
$average = Invoices::count(
'inv_cst_id = 1'
);
echo 'COUNT [Customer: 1] ', $average, PHP_EOL;
create()
会抛出异常。操作成功时返回true
,false
。 <?php
use MyApp\Models\Invoices;
$invoice = new Invoices();
$invoice->assign(
[
'inv_cst_id' => 1,
'inv_status_flag' => 1,
'inv_title' => 'Invoice for ACME Inc.',
'inv_total' => 100,
'inv_created_at' => '2019-12-25 01:02:03',
]
);
$result = $invoice->create();
<?php
use MyApp\Models\Invoices;
$invoice = Invoices::findFirst('inv_id = 4');
$result = $invoice->delete();
$invoices = Invoices::find(
[
'conditions' => 'inv_cst_id = :cst_id:',
'bind' => [
'cst_id' => 1,
]
]
);
foreach ($invoices as $invoice) {
$invoice->delete();
}
var_dump()
<?php
use MyApp\Models\Invoices;
$invoice = Invoices::findFirst('inv_id = 4');
var_dump(
$invoice->dump()
);
find()
is flexible enough to accept a variety of parameters to find the data required. You can check the 查找记录部分以获取更多信息。 查询第一个符合指定条件的记录。它将返回结果集或者null
如果找不到记录。 注意
findFirst()
不再返回false
如果找不到记录。
false
返回变更值列表。 <?php
use MyApp\Models\Invoices;
$invoice = Invoices::findFirst();
print_r(
$invoice->getChangedFields()
);
// []
$invoice->inv_total = 120;;
$invoice->getChangedFields();
print_r(
$invoice->getChangedFields()
);
// ['inv_total']
DIRTY_STATE_*
常量,表明记录是否存在于数据库中 返回验证消息数组 <?php
use MyApp\Models\Invoices;
$invoice = new Invoices();
$invoice->inv_cst_id = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title = 'Invoice for ACME Inc.';
$invoice->inv_total = 100;
$invoice->inv_created_at = '2019-12-25 01:02:03';
$result = $invoice->save();
if (false === $result) {
echo 'Error saving Invoice: ';
$messages = $invoice->getMessages();
foreach ($messages as $message) {
echo $message . PHP_EOL;
}
} else {
echo 'Record Saved';
}
注意
save()
不再接受设置数据的参数。你可以使用assign
替代方法。
OP_*
类常量之一 返回内部旧快照数据 获取用于读取模型数据的连接 返回用于读取模型相关数据的依赖注入连接服务名称 public function getRelated(
string $alias,
mixed $arguments = null
): Phalcon\Mvc\Model\Resultset\Simple | null
null
注意
getRelated()
不再返回false
如果一对一关系中未找到记录。
<?php
use MyApp\Models\Customers;
$customer = Customers::findFirst('cst_id = 1');
$invoices = $customer->getRelated('invoices');
true
如果这些记录是之前通过模型且未带有任何额外参数获取的。 <?php
use MyApp\Models\Customers;
$customer = Customers::findFirst('cst_id = 1');
$invoices = $customer->isRelationshipLoaded('invoices'); // false
$invoices = $customer->getRelated('invoices');
$invoices = $customer->isRelationshipLoaded('invoices'); // true
<?php
use MyApp\Models\Invoices;
$invoice = Invoices::findFirst();
print_r(
$invoice->getChangedFields()
);
// []
$invoice->inv_total = 120;;
$invoice->getChangedFields();
print_r(
$invoice->getChangedFields()
);
// ['inv_total']
$invoice->save();
print_r(
$invoice->getChangedFields()
);
// []
print_r(
$invoice->getUpdatedFields()
);
// ['inv_total']
<?php
use MyApp\Models\Invoices;
$invoice = new Invoices();
$invoice->inv_cst_id = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title = 'Invoice for ACME Inc.';
$invoice->inv_total = 100;
$invoice->inv_created_at = '2019-12-25 01:02:03';
$result = $invoice->create();
$invoice->inv_total = 120;
$hasChanged = $invoice->hasChanged('inv_title');
// false
$hasChanged = $invoice->hasChanged(
[
'inv_total',
]
);
// true
$hasChanged = $invoice->hasChanged(
[
'inv_title',
'inv_total'
],
true
);
// false
<?php
use MyApp\Models\Invoices;
$id = Invoices::maximum(
[
'column' => 'inv_id',
]
);
echo 'MAX: ', $id, PHP_EOL;
$max = Invoices::maximum(
[
'inv_cst_id = 1',
'column' => 'inv_total',
]
);
echo 'MAX [Customer: 1] ', $max, PHP_EOL;
<?php
use MyApp\Models\Invoices;
$id = Invoices::minimum(
[
'column' => 'inv_id',
]
);
echo 'MIN: ', $id, PHP_EOL;
$max = Invoices::minimum(
[
'inv_cst_id = 1',
'column' => 'inv_total',
]
);
echo 'MIN [Customer: 1] ', $max, PHP_EOL;
true
成功时返回false
。 <?php
use MyApp\Models\Invoices;
$invoice = new Invoices();
$invoice->inv_cst_id = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title = 'Invoice for ACME Inc.';
$invoice->inv_total = 100;
$invoice->inv_created_at = '2019-12-25 01:02:03';
$result = $invoice->save();
$invoice = Invoices::findFirst('inv_id = 100');
$invoice->inv_total = 120;
$invoice->save();
注意
save()
不再接受设置数据的参数。你可以使用assign
替代方法。
DIRTY_STATE_*
常量 设置自定义事件管理器 设置用于读取数据的依赖注入连接服务名称 设置记录的旧快照数据。此方法由内部调用,用于当模型配置了保留快照数据时设置旧快照数据 设置记录的快照数据。此方法由内部调用,用于当模型配置了保留快照数据时设置快照数据 设置与模型实例相关联的事务 <?php
use MyApp\Models\Customers;
use MyApp\Models\Invoices;
use Phalcon\Mvc\Model\Transaction\Manager;
use Phalcon\Mvc\Model\Transaction\Failed;
try {
$txManager = new Manager();
$transaction = $txManager->get();
$customer = new Customers();
$customer->setTransaction($transaction);
$customer->cst_name_last = 'Vader';
$customer->cst_name_first = 'Darth';
if (false === $customer->save()) {
$transaction->rollback('Cannot save Customer');
}
$invoice = new Invoices();
$invoice->setTransaction($transaction);
$invoice->inv_cst_id = $customer->cst_id;
$invoice->inv_status_flag = 1;
$invoice->inv_title = 'Invoice for ACME Inc.';
$invoice->inv_total = 100;
$invoice->inv_created_at = '2019-12-25 01:02:03';
if (false === $invoice->save()) {
$transaction->rollback('Cannot save record');
}
$transaction->commit();
} catch (Failed $ex) {
echo 'ERROR: ', $ex->getMessage();
}
<?php
use MyApp\Models\Invoices;
$total = Invoices::sum(
[
'column' => 'inv_total',
]
);
echo 'SUM: ', $total, PHP_EOL;
$total = Invoices::sum(
[
'inv_cst_id = 1',
'column' => 'inv_total',
]
);
echo 'SUM [Customer: 1] ', $total, PHP_EOL;
<?php
use MyApp\Models\Invoices;
$invoice = Invoices::findFirst('inv_id = 4');
print_r(
$invoice->toArray()
);
// [
// 'inv_id' => 4,
// 'inv_cst_id' = $customer->cst_id,
// 'inv_status_flag' = 1,
// 'inv_title' = 'Invoice for ACME Inc.',
// 'inv_total' = 100,
// 'inv_created_at' = '2019-12-25 01:02:03',
// ]
print_r(
$invoice->toArray(
[
'inv_status_flag',
'inv_title',
'inv_total',
]
)
);
// [
// 'inv_status_flag' = 1,
// 'inv_title' = 'Invoice for ACME Inc.',
// 'inv_total' = 100,
// ]
toArray
默认使用 getter 方法,若要禁用此行为,请设置$useGetters
到false
<?php
use MyApp\Models\InvoicesGetters;
$invoice = InvoicesGetters::findFirst('inv_id = 4');
print_r(
$invoice->inv_title
);
// 'Invoice for ACME Inc.'
print_r(
$invoice->getInvTitle()
);
// 'Invoice for ACME Inc. - Status 1'
print_r(
$invoice->toArray()
);
// [
// 'inv_id' => 4,
// 'inv_cst_id' = $customer->cst_id,
// 'inv_status_flag' = 1,
// 'inv_title' = 'Invoice for ACME Inc. - Status 1' ,
// 'inv_total' = 100,
// 'inv_created_at' = '2019-12-25 01:02:03',
// ]
print_r(
$invoice->toArray(
null,
false
)
);
// [
// 'inv_id' => 4,
// 'inv_cst_id' = $customer->cst_id,
// 'inv_status_flag' = 1,
// 'inv_title' = 'Invoice for ACME Inc.' ,
// 'inv_total' = 100,
// 'inv_created_at' = '2019-12-25 01:02:03',
// ]
true
成功时返回false
。 <?php
use MyApp\Models\Invoices;
$invoice = Invoices::findFirst('inv_id = 4');
$invoice->inv_total = 120;
$invoice->update();
注意
当通过findFirst()
查询记录时,你需要获取完整的对象(没有columns
定义),但还需要通过主键进行检索。如果不这样做,ORM 将发出一个INSERT
而不是UPDATE
.
UPDATE
语句中跳过 <?php
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->allowEmptyStringValues(
[
'inv_created_at',
]
);
}
}
protected function belongsTo(
string | array $fields,
string $referenceModel,
string | array $referencedFields,
array options = null
): Relation
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class InvoicesXProducts extends Model
{
public function initialize()
{
$this->belongsTo(
'ixp_inv_id',
Invoices::class,
'inv_id'
);
}
}
protected function hasMany(
string | array $fields,
string $referenceModel,
string | array $referencedFields,
array options = null
): Relation
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Customers extends Model
{
public function initialize()
{
$this->hasMany(
'cst_id',
Invoices::class,
'inv_cst_id'
);
}
}
protected function hasManyToMany(
string | array $fields,
string $intermediateModel,
string | array $intermediateFields,
string | array $intermediateReferencedFields,
string $referenceModel,
string | array $referencedFields,
array $options = null
): Relation
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->hasManyToMany(
'inv_id',
InvoicesXProducts::class,
'ixp_inv_id',
'ixp_prd_id',
Products::class,
'prd_id'
);
}
}
protected function hasOne(
string | array $fields,
string $referenceModel,
string | array $referencedFields,
array options = null
): Relation
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->hasOne(
'inv_cst_id',
Customers::class,
'cst_id'
);
}
}
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->keepSnapshots(true);
}
}
INSERT
/UPDATE
语句中跳过 <?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->skipAttributes(
[
'inv_created_at',
]
);
}
}
INSERT
语句中跳过 <?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->skipAttributesOnCreate(
[
'inv_created_at',
]
);
}
}
UPDATE
语句中跳过 <?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->skipAttributesOnUpdate(
[
'inv_modified_at',
]
);
}
}
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->useDynamicUpdate(true);
}
}
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
use Phalcon\Validation;
use Phalcon\Validation\Validator\ExclusionIn;
class Invoices extends Model
{
public function validation()
{
$validator = new Validation();
$validator->add(
'inv_status_flag',
new ExclusionIn(
[
'domain' => [
0,
1,
],
]
)
);
return $this->validate($validator);
}
}
创建模型¶
模型是一个继承自Phalcon\Mvc\Model的类。其类名应采用驼峰命名法:
默认情况下,模型MyApp\Models\Invoices
将映射到表invoices
。如果你想手动指定其他映射表名,可以使用setSource()
方法:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->setSource('co_invoices');
}
}
模型Invoices
现在映射到co_invoices
表。initialize()
方法有助于为此模型设置自定义行为,比如不同的表。
The initialize()
方法在整个请求期间只调用一次。该方法用于执行适用于应用程序内创建的所有模型实例的初始化操作。如果你想为每个创建的实例执行初始化任务,可以使用onConstruct()
方法:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function onConstruct()
{
// ...
}
}
属性 vs. setter/getter 方法
注意
模型类使用一些内部属性来处理服务。这些属性的名称是保留的,不能用作数据库中的字段名。请记住这一点,并在为你的表命名字段时注意避免冲突。如果存在冲突,你的模型将无法正确更新。
container
, dirtyState
, dirtyRelated
, errorMessages
, modelsManager
, modelsMetaData
, related
, operationMade
, oldSnapshot
, skipped
, snapshot
, transaction
, uniqueKey
, uniqueParams
, uniqueTypes
模型可以用公共属性实现,这意味着每个属性都可以从代码中任何实例化该模型类的地方进行读取和更新:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public $inv_id;
public $inv_cst_id;
public $inv_status_flag;
public $inv_title;
public $inv_total;
public $inv_created_at;
}
另一种实现方式是使用 getter 和 setter 函数,这可以控制哪些属性对该模型公开可用。
使用 getter 和 setter 的好处是开发者可以在设置或获取模型属性值时进行转换和验证检查,而使用公共属性则无法做到这一点。
此外,getter 和 setter 允许在不改变模型类接口的前提下进行未来修改。因此,如果某个字段名称发生变化,唯一需要修改的地方就是模型中私有属性的名字,该名字会被相应的 getter/setter 所引用,而无需在其它代码中做改动。
<?php
namespace MyApp\Models;
use InvalidArgumentException;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
protected $inv_id;
protected $inv_cst_id;
protected $inv_status_flag;
protected $inv_title;
protected $inv_total;
protected $inv_created_at;
public function getId(): int
{
return (int) $this->inv_id;
}
public function getCustomerId(): int
{
return (int) $this->inv_cst_id;
}
public function getStatus(): int
{
return (int) $this->inv_status_flag;
}
public function getTitle(): string
{
return (string) $this->inv_title;
}
public function getTotal(): float
{
return (float) $this->inv_total;
}
public function getCreatedAt(): string
{
return (string) $this->inv_created_at;
}
public function setCustomerId(int $customerId): Invoices
{
$this->inv_cst_id = $customerId;
return $this;
}
public function setStatus(int $status): Invoices
{
$this->inv_status_flag = $status;
return $this;
}
public function setTitle(string $title): Invoices
{
$this->inv_title = $title;
return $this;
}
public function setTotal(float $total): Invoices
{
if ($total < 0) {
throw new InvalidArgumentException(
'Incorrect total'
);
}
$this->inv_total = $total;
return $this;
}
public function setCreatedAt(string $date): Invoices
{
$this->inv_created_at = $date;
return $this;
}
}
公共属性在开发中复杂度较低。但是,使用 getter/setter 可以大幅提高应用的可测试性、可扩展性和可维护性。你需要根据应用的需求来决定哪种策略更适合你。该 ORM 支持两种定义属性的方式。
注意
在使用 getter 和 setter 时,属性名中的下划线可能会带来问题。
注意
当采用 getter/setter 方法时,你需要将属性定义为protected
.
如果你在属性名中使用了下划线,在使用魔法方法时依然要在 getter/setter 声明中使用驼峰命名法。(例如:$model->getPropertyName
而不是$model->getProperty_name
, $model->findByPropertyName
而不是$model->findByProperty_name
等)。
ORM 期望使用驼峰命名法,且通常会移除下划线。因此建议按照文档中的方式给属性命名。你可以使用列映射(如上文所述)以确保属性与其数据库字段正确对应。
记录到对象¶
每个模型实例代表表中的一行记录。你可以通过读取对象属性轻松访问记录数据。例如,对于具有以下记录的 'co_customers' 表:
mysql> select * from co_customers;
+--------+---------------+----------------+
| cst_id | cst_name_last | cst_name_first |
+--------+---------------+----------------+
| 1 | Vader | Darth |
| 2 | Skywalker | Like |
| 3 | Skywalker | Leia |
+--------+---------------+----------------+
3 rows in set (0.00 sec)
你可以通过主键查找特定记录,然后打印其名称:
<?php
use MyApp\Models\Customers;
// cst_id = 3
$customer = Customers::findFirst(3);
// 'Leia'
echo $customer->cst_name_first;
一旦记录加载到内存中,你可以对其数据进行修改并保存更改:
<?php
use MyApp\Models\Customers;
// cst_id = 3
$customer = Customers::findFirst(3);
$customer->cst_name_last = 'Princess';
$customer->save();
如你所见,无需使用原始 SQL 语句。Phalcon\Mvc\Model提供了对 Web 应用程序的高度数据库抽象,简化了数据库操作。
查找记录¶
Phalcon\Mvc\Model还提供了多种查询记录的方法。
find
¶
该方法返回一个Phalcon\Mvc\Model\Resultset, Phalcon\Mvc\Model\Resultset\Complex或Phalcon\Mvc\Model\Resultset\Simple记录集合,即使结果只包含一条记录也是如此。
该方法接受多种参数用于检索数据:
你也可以传入一个带有WHERE
子句的字符串。在上面的例子中,我们获取相同的记录,并指示 ORM 提供一个inv_cst_id = 3
最灵活的语法是传入一个包含不同参数的数组:
数组的第一个参数(没有键的)与上面示例中的处理方式相同(传递字符串)。数组还可以接受额外的参数,提供更多的选项来自定义查找操作。findFirst
¶
你也可以使用findFirst()
方法来仅获取符合指定条件的第一条记录:
findFirst
不带参数调用时将返回 ORM 找到的第一条记录。通常,这是表中的第一条记录。 传递数字时,将使用对应的主键值来查询底层模型。如果未定义主键或存在复合主键,则无法获取任何结果。 你也可以传入一个带有WHERE
子句的字符串。在上面的例子中,我们获取相同的记录,并指示 ORM 提供一个inv_cst_id = 3
注意
如果表的主键不是数字类型,请使用条件查询。查看下面的示例。
$uuid = '5741bfd7-6870-40b7-adf6-cbacb515b9a9';
$invoice = Invoices::findFirst([
'uuid = ?0',
'bind' => [$uuid],
]);
// OR
$uuid = '5741bfd7-6870-40b7-adf6-cbacb515b9a9';
$invoice = Invoices::findFirst([
'uuid = :primary:',
'bind' => ['primary' => $uuid],
]);
注意
如果你的条件中不使用绑定参数,PHQL 将会在内部生成一个新的执行计划,因此会消耗更多内存。强烈建议使用绑定参数!
<?php
use MyApp\Models\Invoices;
$invoice = Invoices::findFirst('uuid = "5741bfd7-6870-40b7-adf6-cbacb515b9a9"');
参数¶
注意
强烈建议使用带有conditions
和bind
的数组语法,以防止 SQL 注入攻击,尤其是当条件来自用户输入时。更多信息请参考绑定参数` 章节。
两个find()
和findFirst()
方法都接受一个关联数组来指定搜索条件。
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find(
[
'inv_cst_id = 3',
'order' => 'inv_total desc'
]
);
你可以(也应该)使用conditions
和bind
数组元素来将参数绑定到查询。使用这种方法可以确保参数被正确绑定,从而减少 SQL 注入的可能性:
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find(
[
'conditions' => 'inv_cst_id = :cst_id:',
'bind' => [
'cst_id' => 3,
],
'order' => 'inv_total desc',
]
);
可用的查询选项包括:
bind
Bind 通常与conditions
一起使用,通过替换占位符并对值进行转义来增强安全性
<?php
use MyApp\Models\Invoices;
$invoice = Invoices::findFirst(
[
'conditions' => 'inv_id = :inv_id:',
'bind' => [
'inv_id' => 3,
],
]
);
bindTypes
在绑定参数时,你可以使用此选项对绑定参数进行额外的类型转换,进一步提升查询安全性。
<?php
use MyApp\Models\Invoices;
use Phalcon\Mvc\Model\Column;
$invoice = Invoices::findFirst(
[
'conditions' => 'inv_id = :inv_id:',
'bind' => [
'inv_id' => 3,
],
'bindTypes' => [
Column::BIND_PARAM_INT,
],
]
);
cache
缓存结果集,减少对关系型系统的持续访问。
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find(
[
'conditions' => 'inv_cst_id = :cst_id:',
'bind' => [
'cst_id' => 3,
],
'cache' => [
'key' => 'customer.3',
'lifetime' => 84600,
],
'order' => 'inv_total desc',
]
);
columns
返回模型中的特定列。
注意
使用这个选项时,将返回一个不完整的对象,因此你不能调用诸如update()
, getRelated()
等等。
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find(
[
'columns' => [
'inv_id',
'total' => 'inv_total'
],
'conditions' => 'inv_cst_id = :cst_id:',
'bind' => [
'cst_id' => 3,
],
]
);
如果数组元素中仅设置了值,则 columns 数组可以直接返回列。但如果指定了键,则该键将作为该字段的别名。在上述示例中,cst_name_first
被别名为first
.
conditions
查找操作的搜索条件。用于提取满足指定条件的记录。默认情况下,Phalcon\Mvc\Model假设第一个参数是条件。
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find(
[
'conditions' => 'inv_cst_id = :cst_id:',
'bind' => [
'cst_id' => 3,
],
]
);
for_update
使用此选项时,Phalcon\Mvc\Model会读取最新的可用数据,并在其读取的每一行上设置排它锁
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find(
[
'conditions' => 'inv_cst_id = :cst_id:',
'bind' => [
'cst_id' => 3,
],
'for_update' => true,
]
);
group
允许跨多条记录收集数据,并按一个或多个列对结果进行分组'group' => 'name, status'
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find(
[
'conditions' => 'inv_cst_id = :cst_id:',
'bind' => [
'cst_id' => 3,
],
'group' => 'inv_status_flag',
]
);
hydration
设置填充策略,以确定如何表示结果中的每条记录
<?php
use MyApp\Models\Invoices;
use Phalcon\Mvc\Model\Resultset;
$invoices = Invoices::find(
[
'conditions' => 'inv_cst_id = :cst_id:',
'bind' => [
'cst_id' => 3,
],
'hydration' => Resultset::HYDRATE_OBJECTS,
]
);
limit
将查询结果限制到某个范围
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find(
[
'conditions' => 'inv_cst_id = :cst_id:',
'bind' => [
'cst_id' => 3,
],
'limit' => 10,
]
);
offset
将查询结果偏移指定数量
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find(
[
'conditions' => 'inv_cst_id = :cst_id:',
'bind' => [
'cst_id' => 3,
],
'limit' => 10,
'offset' => 100,
]
);
order
用于对结果集排序。使用一个或多个逗号分隔的字段。
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find(
[
'conditions' => 'inv_cst_id = :cst_id:',
'bind' => [
'cst_id' => 3,
],
'order' => 'inv_status_flag, inv_total desc',
]
);
shared_lock
使用此选项时,Phalcon\Mvc\Model会读取最新的可用数据,并在其读取的每一行上设置共享锁
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find(
[
'conditions' => 'inv_cst_id = :cst_id:',
'bind' => [
'cst_id' => 3,
],
'shared_lock' => true,
]
);
query
¶
如果你愿意,还可以使用面向对象的方式创建查询,而不是使用参数数组:
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::query()
->where('inv_cst_id = :cst_id:')
->andWhere('inv_total > :total:')
->bind(
[
'cst_id' => 3,
'total' => 1000,
]
)
->orderBy('inv_status_flag, inv_total desc')
->execute()
;
该静态方法query()
返回一个Phalcon\Mvc\Model\Criteria对象,适用于 IDE 自动补全功能。
所有查询在内部都被处理为PHQL查询。PHQL 是一种高级的、面向对象的、类似 SQL 的语言。该语言提供了更多功能以执行查询,例如连接其他模型、分组记录、聚合等。
findBy*
¶
您可以使用findBy<property-name>()
方法的参数相同。此方法扩展了find()
上述方法的功能。它允许你通过在方法本身中使用属性名并传入一个参数来快速地从表中执行 select 查询,该参数包含你想在该列中搜索的数据。
对于以下模型:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public $inv_cst_id;
public $inv_id;
public $inv_status_flag;
public $inv_title;
public $inv_created_at;
}
我们有这些属性inv_cst_id
, inv_id
, inv_status_flag
, inv_title
, inv_created_at
。如果我们想查找所有inv_total = 100
的发票,我们可以使用:
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find(
[
'conditions' => 'inv_total = :total:',
'bind' => [
'total' => 100,
],
]
);
但我们也可以使用:
注意
如果属性名中包含下划线,则会将其转换为驼峰式命名。inv_total
变成了InvTotal
你还可以将参数作为数组传递给第二个参数。这些参数与你可以传递给find
方法设置容器。
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::findByInvTotal(
100,
[
'order' => `inv_cst_id, inv_created_at`
]
);
findFirstBy*
¶
最后,您可以使用findFirstBy<property-name>()
方法的参数相同。此方法扩展了findFirst()
上述方法的功能。它允许你通过在方法本身中使用属性名并传入一个参数来快速地从表中执行 select 查询,该参数包含你想在该列中搜索的数据。
对于以下模型:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Guestbook extends Model
{
public $id;
public $email;
public $name;
public $text;
}
我们有这些属性id
, email
, name
和text
。如果我们想查找访客留言簿中Darth Vader
的条目,我们可以:
<?php
use MyApp\Models\Guestbook;
$guest = Guestbook::findFirst(
[
'conditions' => 'name = :name:',
'bind' => [
'name' => 'Darth Vader',
],
]
);
但我们也可以使用:
<?php
use MyApp\Models\Guestbook;
$name = 'Darth Vader';
$guest = Guestbook::findFirstByName($name);
注意
注意我们在方法调用中使用了Name
并传入了变量$name
,其中包含我们要在表中查找的名称。同时请注意,当我们找到查询匹配项时,所有其他属性也都可以供我们使用。
模型结果集¶
虽然findFirst()
在有数据返回时直接返回被调用类的一个实例,find()
方法返回一个Phalcon\Mvc\Model\Resultset\Simple却不是这样。这是一个封装了结果集所需所有功能(如定位、遍历、计数等)的对象。
这些对象比标准数组更强大。其中最强大的特性之一是Phalcon\Mvc\Model\Resultset在任何时刻内存中只保存一条记录。这在处理大量数据时对内存管理非常有帮助。
遍历结果集的一些示例包括:
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find();
// foreach
foreach ($invoices as $invoice) {
echo $invoice->inv_title, PHP_EOL;
}
// while
$invoices->rewind();
while ($invoices->valid()) {
$invoice = $invoices->current();
echo $invoice->inv_title, PHP_EOL;
$invoices->next();
}
// count
echo count($invoices);
echo $invoices->count();
// seek
$invoices->seek(2);
$invoice = $invoices->current();
// array
$invoice = $invoices[5];
// array - isset
if (true === isset($invoices[3])) {
$invoice = $invoices[3];
}
// First
$invoice = $invoices->getFirst();
// Last
$invoice = $invoices->getLast();
Phalcon 的结果集模拟了可滚动游标。你可以通过访问其位置或移动内部指针到特定位置来获取任意一行记录。
注意
一些数据库系统不支持可滚动游标。这迫使 Phalcon 重新执行查询,将游标重置到开头并获取请求位置的记录。同样地,如果多次遍历一个结果集,则必须执行相同次数的查询。
将大型查询结果存储在内存中会消耗许多资源。但你可以指示 Phalcon 按行块方式抓取数据,从而减少许多情况下的重新执行需求。你可以通过设置orm.resultset_prefetch_records
设置值来实现这一点。这可以在php.ini
中进行,也可以在模型的setup()
中进行。特性章节中可以找到有关此内容的更多信息。
请注意,结果集可以被序列化并存储在缓存后端中。Phalcon\Cache\Cache可以帮助完成此任务。然而,序列化数据会导致Phalcon\Mvc\Model从数据库检索所有数据到一个数组中,因此在此过程中会消耗更多的内存。
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find();
file_put_contents(
'invoices.cache',
serialize($invoices)
);
$invoices = unserialize(
file_get_contents('invoices.cache')
);
foreach ($invoices as $invoice) {
echo $invoice->inv_title;
}
自定义结果集¶
有时应用程序逻辑需要在从数据库中检索数据时对其做额外的处理。以前的做法通常是扩展模型,并将功能封装在模型类或 trait 中,通常返回一个经过转换的数据数组给调用者。
使用自定义结果集后,你不再需要这样做。自定义结果集将封装那些原本放在模型中的功能,并可以被其他模型复用,从而保持代码不重复(DRY)。这样一来,find()
方法将不再返回默认的Phalcon\Mvc\Model\Resultset结果集,而是返回自定义的结果集。Phalcon 允许你通过在模型中使用getResultsetClass()
来实现这一点。
首先,我们需要定义结果集类:
<?php
namespace MyApp\Mvc\Model\Resultset;
use \Phalcon\Mvc\Model\Resultset\Simple;
class Custom extends Simple
{
public function calculate() {
// ....
}
}
在模型中,我们在getResultsetClass()
的组件中访问你的会话,如下所示:
<?php
namespace Phalcon\Test\Models\Statistics;
use MyApp\Mvc\Model\Resultset\Custom;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->setSource('co_invoices');
}
public function getResultsetClass()
{
return Custom::class;
}
}
中设置该类,最终,在你的代码中会有类似如下内容:
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find(
[
'conditions' => 'inv_cst_id = :cst_id:',
'bind' => [
'cst_id' => 3,
],
]
);
$calculated = $invoices->calculate();
过滤结果集¶
过滤数据最有效的方法是设置一些搜索条件,数据库将利用表上的索引更快地返回数据。此外,Phalcon 还允许你使用 PHP 来过滤数据:
<?php
$invoices = Invoices::find();
$invoices = $invoices->filter(
function ($invoice) {
if (1 === $invoice->inv_status_flag) {
return $invoice;
}
}
);
上面的例子将仅从我们的表中返回已支付的发票(inv_status_flag = 1
);
绑定参数¶
在Phalcon\Mvc\Model中也支持绑定参数。建议你使用这种机制来防止代码遭受 SQL 注入攻击。支持使用string
和integer
占位符。
注意
当使用integer
使用?
(?0
, ?1
)。当使用string
占位符时,你必须将字符串包含在:
(:name:
, :total:
)。
一些示例:
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find(
[
'conditions' => 'inv_title LIKE :title: AND ' .
'inv_total > :total:',
'bind' => [
'title' => '%ACME%',
'total' => 1000,
],
]
);
$invoices = Invoices::find(
[
'conditions' => 'inv_title LIKE ?0 = ?0 AND ' .
'inv_total > ?1',
'bind' => [
0 => '%ACME%',
1 => 1000,
],
]
);
$invoices = Invoices::find(
[
'conditions' => 'inv_title = ?0 AND ' .
'inv_total > :total:',
'bind' => [
0 => '%ACME%',
'total' => 1000,
],
]
);
字符串会使用PDO自动转义。此函数会考虑连接的字符集,因此建议在连接参数或数据库配置中定义正确的字符集,因为错误的字符集会在存储或检索数据时产生不良效果。
此外,你还可以设置参数bindTypes
,这允许根据参数的数据类型定义应如何绑定参数:
<?php
use MyApp\Models\Invoices;
use Phalcon\Db\Column;
$parameters = [
'title' => '%ACME%',
'total' => 1000,
];
$types = [
'title' => Column::BIND_PARAM_STR,
'total' => Column::BIND_PARAM_INT,
];
$invoices = Invoices::find(
[
'conditions' => 'inv_title LIKE :title: AND ' .
'inv_total > :total:',
'bind' => $parameters,
'bindTypes' => $types,
]
);
注意
由于默认的绑定类型是Phalcon\Db\Column::BIND_PARAM_STR
,如果你的所有列都是字符串,则无需指定 'bindTypes' 参数。
你还可以在参数中绑定数组,尤其是在使用IN
SQL 关键字时。
注意
数组应使用零开始的索引,并且不能缺少元素
<?php
use MyApp\Models\Invoices;
$customerIds = [1, 3, 4]; // $array: [[0] => 1, [1] => 2, [2] => 4]
$invoices = Invoices::find(
[
'conditions' => 'inv_cst_id IN ({customerId:array})',
'bind' => [
'customerId' => $customerIds,
],
]
);
unset($customerIds[1]); // $array: [[0] => 1, [2] => 4]
$customerIds = array_values($customerIds); // $array: [[0] => 1, [1] => 4]
$invoices = Invoices::find(
[
'conditions' => 'inv_cst_id IN ({customerId:array})',
'bind' => [
'customerId' => $customerIds,
],
]
);
注意
绑定参数适用于所有查询方法,例如find()
和findFirst()
也适用于计算方法,比如count()
, sum()
, average()
等等。
如果你正在使用查询器(finders)例如:find()
, findFirst()
等等,在使用字符串语法作为第一个参数时可以注入绑定参数,而不是使用conditions
数组元素。另外,当使用findFirstBy*
参数会自动绑定。
<?php
use MyApp\Models\Invoices;
$invoices = Invoices::find(
'inv_total > ?0',
'bind' => [
1000,
]
);
$invoices = Invoices::findByInvTotal(1000);
获取前/后操作¶
在某些情况下,我们需要在数据从数据库中获取后进行处理,以便模型包含应用层所需的内容。如事件文档所示,模型可以充当监听器,因此我们可以在模型中实现一些事件方法。
这些方法包括beforeSave
, afterSave
和afterFetch
如下例所示。当数据从数据库填充到模型之后,将立即运行afterFetch
方法。我们可以利用此方法修改或转换模型中的数据。
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public $inv_id;
public $inv_cst_id;
public $inv_status_flag;
public $inv_total;
public $status;
public function beforeSave()
{
$this->status = join(',', $this->status);
}
public function afterFetch()
{
$this->status = explode(',', $this->status);
}
public function afterSave()
{
$this->status = explode(',', $this->status);
}
}
explode
it to an array so that it can be used from our application. After that, you can add or remove elements in the array; before the model saves it, implode
将会被调用,用于把数组以字符串形式存储到数据库。 如果你使用getter/setter替代或者结合公共属性一起使用,则可以在字段被访问时初始化它:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public $inv_id;
public $inv_cst_id;
public $inv_status_flag;
public $inv_total;
public $status;
public function getStatus()
{
return explode(',', $this->status);
}
}
计算¶
计算(或聚合)是数据库系统常见功能的辅助工具,例如COUNT
, SUM
, MAX
, MIN
或AVG
. Phalcon\Mvc\Model允许通过暴露的方法直接使用这些功能。
COUNT
<?php
$rowcount = Invoices::count();
// inv_cst_id = 3
$rowcount = Invoices::count(
[
'inv_cst_id = ?0',
'bind' => [
3,
],
]
);
我们还可以使用group
参数对我们结果进行分组。计数结果显示在返回集合的每个对象的rowcount
属性中。
<?php
$group = Invoices::count(
[
'group' => 'inv_cst_id',
]
);
foreach ($group as $row) {
echo 'Count: ', $row->rowcount, ' - Customer: ', $row->inv_cst_id;
}
$group = Invoices::count(
[
'group' => 'inv_cst_id',
'order' => 'rowcount',
]
);
SUM
<?php
$total = Invoices::sum(
[
'column' => 'inv_total',
]
);
$total = Invoices::sum(
[
'column' => 'total',
'conditions' => 'inv_cst_id = ?0',
'bind' => [
3
]
]
);
sumatory
属性中。 <?php
$group = Invoices::sum(
[
'column' => 'inv_total',
'group' => 'inv_cst_id',
]
);
foreach ($group as $row) {
echo 'Customer: ', $row->inv_cst_id, ' - Total: ', $row->sumatory;
}
$group = Invoices::sum(
[
'column' => 'inv_total',
'group' => 'inv_cst_id',
'order' => 'sumatory DESC',
]
);
AVERAGE
<?php
$average = Invoices::average(
[
'column' => 'inv_total',
]
);
$average = Invoices::average(
[
'column' => 'inv_total',
'conditions' => 'inv_status_flag = ?0',
'bind' => [
0
]
]
);
MAX
- MIN
<?php
$max = Invoices::maximum(
[
'column' => 'inv_total',
]
);
$max = Invoices::maximum(
[
'column' => 'inv_total',
'conditions' => 'inv_status_flag = ?0',
'bind' => [
0
],
]
);
$min = Invoices::minimum(
[
'column' => 'inv_total',
]
);
$min = Invoices::minimum(
[
'column' => 'inv_total',
'conditions' => 'inv_status_flag = ?0',
'bind' => [
0
],
]
);
创建 - 更新¶
The Phalcon\Mvc\Model::save()
方法允许你根据记录是否已存在于与模型关联的表中来创建或更新记录。save 方法由Phalcon\Mvc\Model的 create 和 update 方法在内部调用。为了使其按预期工作,实体中必须正确定义主键,以确定是应创建还是更新记录。
该方法还会执行模型中定义的相关验证器、虚拟外键和事件:
<?php
use MyApp\Models\Invoices;
$invoice = new Invoices();
$invoice->inv_cst_id = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title = 'Invoice for ACME Inc.';
$invoice->inv_total = 100;
$invoice->inv_created_at = '2019-12-25 01:02:03';
$result = $invoice->save();
if (false === $result) {
echo 'Error saving Invoice: ';
$messages = $invoice->getMessages();
foreach ($messages as $message) {
echo $message . PHP_EOL;
}
} else {
echo 'Record Saved';
}
您还可以使用assign()
方法并传入一个包含field => value
元素的数组,以避免手动逐列赋值。Phalcon\Mvc\Model将检查传递进来的数组中的列是否有对应的setter实现,并优先使用它们,而不是直接给属性赋值:
<?php
use MyApp\Models\Invoices;
$invoice = new Invoices();
$invoice->assign(
[
'inv_cst_id' => 1,
'inv_status_flag' => 1,
'inv_title' => 'Invoice for ACME Inc.',
'inv_total' => 100,
'inv_created_at' => '2019-12-25 01:02:03',
]
);
$result = $invoice->save();
直接分配的值或通过属性数组分配的值会根据相关属性的数据类型进行转义/清理。因此,你可以传递一个不安全的数组而不必担心可能的 SQL 注入问题:
<?php
use MyApp\Models\Invoices;
$invoice = new Invoices();
$invoice->assign($_POST);
$result = $invoice->save();
注意
如果没有采取防护措施,批量赋值可能会让攻击者设置任意数据库列的值。只有在你希望允许用户插入或更新模型中的每列时才使用此功能,即使这些字段不在提交的表单中。
你可以在assign
中设置一个额外参数,用以指定在进行批量赋值时仅考虑的字段白名单:
<?php
use MyApp\Models\Invoices;
$invoice = new Invoices();
$invoice->assign(
$_POST,
[
'inv_cst_id',
'inv_status_flag',
'inv_title',
'inv_total',
]
);
$result = $invoice->save();
注意
在非常繁忙的应用程序中,你可以使用create
或update
来执行相应的操作。通过使用这两个方法代替 save,我们可以确保数据一定会保存到数据库或不会保存进去,因为它们会在create
如果记录已经存在时抛出异常,并在update
如果记录不存在时抛出异常。
<?php
use MyApp\Models\Invoices;
$invoice = new Invoices();
$invoice->inv_id = 1234;
$invoice->inv_cst_id = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title = 'Invoice for ACME Inc.';
$invoice->inv_total = 100;
$invoice->inv_created_at = '2019-12-25 01:02:03';
$result = $invoice->update();
if (false === $result) {
echo 'Error saving Invoice: ';
$messages = $invoice->getMessages();
foreach ($messages as $message) {
echo $message . PHP_EOL;
}
} else {
echo 'Record Updated';
}
方法create
和update
同样接受一个值数组作为参数。
删除¶
The delete()
方法允许你删除一条记录。它返回一个布尔值表示成功或失败。
<?php
use MyApp\Models\Invoices;
$invoice = Invoices::findFirst(
[
'conditions' => 'inv_id = :id:',
'bind' => [
'id' => 4,
]
]
);
if (false !== $invoice) {
if (false === $invoice->delete()) {
$messages = $invoice->getMessages();
foreach ($messages as $message) {
echo $message . PHP_EOL;
}
} else {
echo 'Record Deleted';
}
}
你还可以通过使用foreach
:
<?php
use MyApp\Models\Invoices;
$invoice = Invoices::find(
[
'conditions' => 'inv_cst_id = :id:',
'bind' => [
'id' => 3,
]
]
);
foreach ($invoices as $invoice) {
if (false === $invoice->delete()) {
$messages = $invoice->getMessages();
foreach ($messages as $message) {
echo $message . PHP_EOL;
}
} else {
echo 'Record Deleted';
}
}
注意
遍历结果集来删除多条记录。请查看事务章节了解如何在一个循环中通过一次操作删除所有记录。
数据填充模式(Hydration Modes)¶
如前所述,结果集是一系列完整对象的集合。这意味着每个返回的结果都是一个代表数据库中某一行的对象。这些对象可以被修改,并可以在之后保存到数据库以持久化更改。
但是,有时你需要以只读的方式获取数据,例如在单纯查看数据的情况下。在这种情况下,改变记录返回方式是有用的,这有助于节省资源并提高性能。用来表示结果集中返回对象的策略称为数据填充策略。hydration
.
Phalcon 提供了三种填充数据的方式:
结果 | 模式 |
---|---|
数组 | Phalcon\Mvc\Model\Resultset::HYDRATE_ARRAYS |
对象 | Phalcon\Mvc\Model\Resultset::HYDRATE_OBJECTS |
记录 | Phalcon\Mvc\Model\Resultset::HYDRATE_RECORDS |
默认的数据填充模式是返回记录(HYDRATE_RECORDS
)。我们可以轻松地更改数据填充模式以返回数组或对象。将数据填充模式更改为非HYDRATE_RECORDS
以外的模式将返回与数据库断开连接的对象(或数组),即我们不能对这些对象执行任何操作,例如save()
, create()
, delete()
等等。
<?php
use MyApp\Models\Invoices;
use Phalcon\Mvc\Model\Resultset;
$invoices = Invoices::findFirst(
[
'conditions' => 'inv_id = :id:',
'bind' => [
'id' => 4,
]
]
);
// Array
$invoices->setHydrateMode(
Resultset::HYDRATE_ARRAYS
);
foreach ($invoices as $invoice) {
echo $invoice['inv_total'], PHP_EOL;
}
// \stdClass
$invoices->setHydrateMode(
Resultset::HYDRATE_OBJECTS
);
foreach ($invoices as $invoice) {
echo $invoice->inv_total, PHP_EOL;
}
// Invoices
$invoices->setHydrateMode(
Resultset::HYDRATE_RECORDS
);
foreach ($invoices as $invoice) {
echo $invoice->inv_total, PHP_EOL;
}
数据填充模式同样可以作为find
, findFirst
, findFirstBy*
等方法的参数传递:
<?php
use MyApp\Models\Invoices;
use Phalcon\Mvc\Model\Resultset;
$invoices = Invoices::findFirst(
[
'hydration' => Resultset::HYDRATE_ARRAYS,
'conditions' => 'inv_id = :id:',
'bind' => [
'id' => 4,
],
]
);
foreach ($invoices as $invoice) {
echo $invoice['inv_total'], PHP_EOL;
}
表前缀(Table Prefixes)¶
如果你想让你所有的表都有某个特定的前缀而无需在所有模型中设置源表名,你可以使用Phalcon\Mvc\Model\Manager并且使用setModelPrefix()
:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model\Manager;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
}
$manager = new Manager();
$manager->setModelPrefix('co_');
$invoices = new Invoices(null, null, $manager);
echo $invoices->getSource(); // will return co_invoices
标识列(Identity Columns)¶
某些模型可能具有标识列。这些列通常就是映射表的主键。Phalcon\Mvc\Model可以识别标识列并在生成的INSERT
SQL 语句中省略它,从而允许数据库系统正确地为该字段生成新值。创建新记录后,标识字段始终会记录数据库系统为其生成的值:
Phalcon\Mvc\Model尝试从每个表中识别身份列。但是,根据不同的数据库系统,这些列可能是序列列,例如 PostgreSQL 中的情况,或者auto_increment
在 MySQL 情况下的列。
PostgreSQL 使用序列为 主键 自动生成数字值。Phalcon 会尝试从序列中获取生成的值table_field_seq
,例如:co_invoices_id_seq
。如果序列名称不同,你可以始终在模型中使用getSequenceName()
方法,告知 Phalcon 需要为主键使用的序列:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function getSequenceName()
{
return 'invoices_sequence_name';
}
}
忽略列¶
根据你如何在数据库中实现业务规则或模型规则,一些字段在数据库操作中可能会被忽略。例如,如果我们模型中有某个inv_created_date
字段,我们可以指示数据库系统在其中注入当前时间戳:
上面这段代码(针对 MySQL)指示 RDBMS 在创建记录时为inv_created_at
字段分配当前时间戳。因此,在创建记录时我们可以省略这个字段。同样地,在更新记录时我们也可能希望忽略某些字段。
要完成这个任务,我们可以使用skipAttributes
(对任何操作都有效)、skipAttributesOnCreate
(创建操作) 或skipAttributesOnUpdate
(更新操作)
来告诉Phalcon\Mvc\Model总是在创建和/或更新记录时忽略某些字段,以便将值的赋值交由触发器或默认值来完成:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->skipAttributes(
[
'inv_total',
'inv_created_at',
]
);
$this->skipAttributesOnCreate(
[
'inv_created_at',
]
);
$this->skipAttributesOnUpdate(
[
'inv_modified_at',
]
);
}
}
如果你想在你的模型属性中设置默认值(比如inv_created_at
),你可以使用Phalcon\Db\RawValue:
<?php
use MyApp\Models\Invoices;
use Phalcon\Db\RawValue;
$invoice = new Invoices();
$invoice->inv_id = 1234;
$invoice->inv_cst_id = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title = 'Invoice for ACME Inc.';
$invoice->inv_total = 100;
$invoice->inv_created_at = new RawValue('default');
$invoice->create();
我们也可以利用模型中的beforeCreate
事件来在此处分配默认值:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
use Phalcon\Db\RawValue;
class Invoices extends Model
{
public function beforeCreate()
{
$this->inv_created_at = new RawValue('default');
}
}
注意
切勿使用Phalcon\Db\RawValue来分配外部数据(如用户输入)或可变数据。这些字段的值在将参数绑定到查询时会被忽略。因此,它可能被用于 SQL 注入攻击。
动态更新¶
注意
现在动态更新默认是启用状态,你可以通过修改 php.ini 文件中的 "phalcon.orm.dynamic_update" 参数或使用 ini_set 函数来禁用它。
如果全局范围内的动态更新是禁用的,则默认生成的 SQLUPDATE
语句将包含模型中定义的所有列(完整的全字段 SQL 更新语句)。你可以更改特定模型以启用动态更新,在这种情况下,只有发生变更的字段才会用于生成最终的 SQL 语句。
在某些情况下,这可以通过减少应用程序和数据库服务器之间的流量来提升性能,尤其是在目标表中存在 blob/text 字段时:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->useDynamicUpdate(true);
}
}
列映射¶
ORM 支持独立的列映射功能,它允许开发者在模型中使用与数据表中不相同的列名。Phalcon 会识别新的列名,并相应地重命名它们,以匹配数据库中的相应列。当需要在不担心代码中所有查询的情况下重命名数据库字段时,这是一个非常有用的功能。
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public $inv_id;
public $inv_cst_id;
public $inv_status_flag;
public $inv_title;
public $inv_total;
public $inv_created_at;
public function columnMap()
{
return [
'inv_id' => 'id',
'inv_cst_id' => 'customerId',
'inv_status_flag' => 'status',
'inv_title' => 'title',
'inv_total' => 'total',
'inv_created_at' => 'createdAt',
];
}
}
注意
在列映射中定义的数组中,键是数据库中字段的实际名称,值则是虚拟字段,我们可以在代码中使用这些字段
现在我们可以在代码中使用这些虚拟字段(或列映射):
<?php
use MyApp\Models\Invoices;
$invoice = Invoices::findFirst(
[
'conditions' => 'inv_id = :id:',
'bind' => [
'id' => 4,
]
]
);
echo $invoice->customerId, PHP_EOL,
$invoice->total, PHP_EOL,
$invoice->createdAt, PHP_EOL;
$invoices = Invoices::find(
[
'order' => 'createdAt DESC',
]
);
foreach ($invoices as $invoice) {
echo $invoice->customerId, PHP_EOL,
$invoice->total, PHP_EOL,
$invoice->createdAt, PHP_EOL;
}
$invoice = new Invoices();
$invoice->customerId = 1;
$invoice->status = 1;
$invoice->title = 'Invoice for ACME Inc.';
$invoice->total = 100;
$invoice->createdAt = '2019-12-25 01:02:03';
$invoice->save();
注意事项
在重命名列时,请考虑以下几点:
- 对关系/验证器中属性的引用必须使用虚拟名称
- 引用实际列名会导致 ORM 抛出异常
独立列映射允许你:
- 使用自己的约定编写应用程序
- 在代码中去除供应商前缀/后缀
- 更改列名而无需更改应用程序代码
记录快照¶
特定模型可以设置为在查询时维护一条记录的快照。你可以使用此功能来实现审计,或者仅仅为了知道相对于数据库中的数据,模型中的哪些字段发生了变化。
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->keepSnapshots(true);
}
}
当激活此功能时,应用程序会消耗更多的内存,用于跟踪从数据库中获取的原始值。在启用了此功能的模型中,你可以按如下方式检查哪些字段发生了改变:
<?php
use MyApp\Models\Invoices;
$invoice = Invoices::findFirst();
$invoice->inv_total = 120;
var_dump($invoice->getChangedFields()); // ['inv_total']
var_dump($invoice->hasChanged('inv_total')); // true
var_dump($invoice->hasChanged('inv_cst_id')); // false
快照会在模型创建/更新时进行更新。使用hasUpdated()
和getUpdatedFields()
可以检查 create/save/update 后是否有字段更新,但如果执行getChangedFields()
的调用afterUpdate()
, afterSave()
或afterCreate()
.
你可以通过使用以下方法禁用此功能:
或者如果你愿意,可以在你的php.ini
使用此功能将产生以下效果:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public $inv_id;
public $inv_cst_id;
public $inv_status_flag;
public $inv_title;
public $inv_total;
public $inv_created_at;
public function initialize()
{
$this->keepSnapshots(true);
}
}
$invoice = new Invoices();
$invoice->inv_id = 1234;
$invoice->inv_cst_id = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title = 'Invoice for ACME Inc.';
$invoice->inv_total = 100;
$invoice->inv_created_at = '2019-12-25 01:02:03';
$invoice->create();
var_dump(
$invoice->getChangedFields() // []
);
$invoice->inv_total = 120;
var_dump(
$invoice->getChangedFields() // ['inv_total']
);
$invoice->update();
var_dump(
$invoice->getChangedFields() // []
);
getUpdatedFields()
将正确返回已更新的字段,或者如上所述,你可以通过设置相应的 ini 值返回之前的行为。
事件¶
如前所述,Phalcon\Mvc\Model充当一个事件监听器。因此,模型正在监听的所有事件都可以作为方法在模型本身中实现。你可以查阅事件文档了解更多信息。
支持的事件有:
afterCreate
afterDelete
afterFetch
afterSave
afterUpdate
afterValidation
afterValidationOnCreate
afterValidationOnUpdate
beforeDelete
beforeCreate
beforeSave
beforeUpdate
beforeValidation
beforeValidationOnCreate
beforeValidationOnUpdate
notDeleted
notSaved
onValidationFails
prepareSave
validation
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
use Phalcon\Messages\Message as Message;
class Invoices extends Model
{
public function beforeSave()
{
if (0 === $this->inv_status_flag) {
$message = new Message(
'Sorry, an invoice cannot be unpaid'
);
$this->appendMessage($message);
}
}
}
事务¶
事务当我们需要在同一个操作中插入或更新多个表的数据时,事务是确保数据完整性的必要手段。Phalcon\Mvc\Model暴露了setTransaction
方法允许你将每个模型绑定到一个正在进行的事务。
<?php
use MyApp\Models\Customers;
use MyApp\Models\Invoices;
use Phalcon\Mvc\Model\Transaction\Manager;
use Phalcon\Mvc\Model\Transaction\Failed;
try {
$txManager = new Manager();
$transaction = $txManager->get();
$customer = new Customers();
$customer->setTransaction($transaction);
$customer->cst_name_last = 'Vader';
$customer->cst_name_first = 'Darth';
if (false === $customer->save()) {
$transaction->rollback('Cannot save Customer');
}
$invoice = new Invoices();
$invoice->setTransaction($transaction);
$invoice->inv_cst_id = $customer->cst_id;
$invoice->inv_status_flag = 1;
$invoice->inv_title = 'Invoice for ACME Inc.';
$invoice->inv_total = 100;
$invoice->inv_created_at = '2019-12-25 01:02:03';
if (false === $invoice->save()) {
$transaction->rollback('Cannot save record');
}
$transaction->commit();
} catch (Failed $ex) {
echo 'ERROR: ', $ex->getMessage();
}
你还可以在事务中包含查询器结果,甚至同时运行多个事务:
<?php
use MyApp\Models\Customers;
use MyApp\Models\Invoices;
use Phalcon\Mvc\Model\Transaction\Manager;
use Phalcon\Mvc\Model\Transaction\Failed;
try {
$txManager = new Manager();
$transaction = $txManager->get();
$customer = new Customers();
$customer->setTransaction($transaction);
$customer->cst_name_last = 'Vader';
$customer->cst_name_first = 'Darth';
if (false === $customer->save()) {
$transaction->rollback('Cannot save Customer');
}
$average = Invoices::average(
[
Model::TRANSACTION_INDEX => $transaction,
'column' => 'inv_total',
'conditions' => 'inv_cst_id = :customerId:',
'bind' => [
'customerId' => 3,
],
]
);
$invoice = new Invoices();
$invoice->setTransaction($transaction);
$invoice->inv_cst_id = $customer->cst_id;
$invoice->inv_status_flag = 1;
$invoice->inv_title = 'Invoice for ACME Inc.';
$invoice->inv_total = 100 + $average;
$invoice->inv_created_at = '2019-12-25 01:02:03';
if (false === $invoice->save()) {
$transaction->rollback('Cannot save record');
}
$transaction->commit();
} catch (Failed $ex) {
echo 'ERROR: ', $ex->getMessage();
}
更改架构(schema)¶
如果某个模型所映射的表位于不同于默认架构的位置,你可以使用setSchema()
来指向正确的表位置:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->setSchema('invoices');
}
}
多数据库¶
Phalcon模型默认连接到依赖注入容器中定义的相同数据库连接db
服务)。但是,您可能需要将特定模型连接到不同的连接,这些连接可能是连接到不同数据库的连接。
我们可以在每个模型的initialize
方法中定义哪个模型连接到哪个数据库:
<?php
use Phalcon\Di\FactoryDefault;
use Phalcon\Db\Adapter\Pdo\Mysql;
use Phalcon\Db\Adapter\Pdo\PostgreSQL;
$container = new FactoryDefault();
// MySQL
$container->set(
'dbMysql',
function () {
return new Mysql(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'tutorial',
]
);
},
true
);
// PostgreSQL
$container->set(
'dbPostgres',
function () {
return new PostgreSQL(
[
'host' => 'localhost',
'username' => 'postgres',
'password' => '',
'dbname' => 'tutorial',
]
);
}
);
并且在initialize()
方法:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->setConnectionService('dbPostgres');
}
}
关于数据库连接还提供了额外的灵活性。您可以为read
操作指定一个不同的连接,以及为write
操作指定另一个不同的连接。当您有可用于读取操作的内存数据库,以及用于write
操作的不同且更强大的数据库时,这特别有用。
您可以设置两个不同的连接,并在每个模型中透明地使用各自的数据库
<?php
use Phalcon\Di\FactoryDefault;
use Phalcon\Db\Adapter\Pdo\Mysql;
use Phalcon\Db\Adapter\Pdo\PostgreSQL;
$container = new FactoryDefault();
// MySQL - read
$container->set(
'mysqlRead',
function () {
return new Mysql(
[
'host' => '10.0.4.100',
'username' => 'root',
'password' => 'secret',
'dbname' => 'tutorial',
]
);
},
true
);
// MySQL - write
$container->set(
'mysqlWrite',
function () {
return new Mysql(
[
'host' => '10.0.4.200',
'username' => 'root',
'password' => 'secret',
'dbname' => 'tutorial',
]
);
},
true
);
并且在initialize()
方法:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->setReadConnectionService('mysqlRead');
$this->setWriteConnectionService('mysqlWrite');
}
}
ORM还提供了水平分片功能,允许您根据查询条件实现shard
选择:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
/**
* Dynamically selects a shard
*
* @param array $intermediate
* @param array $bindParams
* @param array $bindTypes
*
* @return Phalcon\Db\Adapter\AdapterInterface
*/
public function selectReadConnection(
array $intermediate,
array $bindParams,
array $bindTypes
) {
if (true === isset($intermediate['where'])) {
$conditions = $intermediate['where'];
if ($conditions['left']['name'] === 'id') {
$id = $conditions['right']['value'];
if ($id > 0 && $id < 10000) {
return $this->getDI()->get('dbShard1');
}
if ($id > 10000) {
return $this->getDI()->get('dbShard2');
}
}
}
return $this->getDI()->get('dbShard0');
}
}
在上面的例子中,我们检查了$intermediate
数组,这是一个Phalcon内部构造的数组,提供查询的中间表示形式。我们检查是否有任何where
条件。如果没有,我们就使用默认的分片dbShard0
.
如果已定义条件,我们会检查条件中是否有id
作为字段,并获取其值。如果id
在0
和100000
之间,则使用dbShard1
,否则的话使用dbShard2
.
The selectReadConnection()
方法在每次我们需要从数据库获取数据时都会被调用,并返回要使用的正确连接。
依赖注入¶
Phalcon\Mvc\Model紧密绑定到DI容器。您可以通过使用getDI
方法来获取容器。因此,您可以访问注册在DI容器中的所有服务。
以下示例向您展示如何打印模型中不成功的save
操作生成的任何消息,并将这些消息显示在flash消息传递器中。为此,我们使用notSaved
事件:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function notSaved()
{
$flash = $this->getDI()->getFlash();
$messages = $this->getMessages();
foreach ($messages as $message) {
$flash->error($message);
}
}
}
模型特性¶
ORM有几个选项可以全局控制特定行为。您可以通过在您的php.ini
文件中添加特定行或使用模型上的setup
静态方法来启用或禁用这些功能。您可以在代码中临时启用或禁用这些功能,也可以永久启用或禁用。
或者通过使用Model
:
可用选项包括:
选项 | 默认值 | 描述 |
---|---|---|
caseInsensitiveColumnMap | false | 大小写不敏感的列映射 |
castLastInsertIdToInt | false | 将lastInsertId 转换为整数 |
castOnHydrate | false | 在填充时自动转换为原始类型 |
columnRenaming | true | 列重命名 |
disableAssignSetters | false | 禁用设置器 |
enableImplicitJoins | true | 启用隐式连接 |
events | true | 所有模型的回调、钩子和事件通知 |
exceptionOnFailedMetaDataSave | false | 当元数据保存失败时抛出异常 |
exceptionOnFailedSave | false | 当save() |
forceCasting | false | 强制将绑定参数转换为其原生类型 |
ignoreUnknownColumns | false | 忽略模型上的未知列 |
lateStateBinding | false | 延迟绑定状态Phalcon\Mvc\Model::cloneResultMap() 方法可以实现反向操作。 |
notNullValidations | true | 自动验证非null 存在的列 |
phqlLiterals | true | PHQL解析器中的字面量 |
prefetchRecords | 0 | 从ORM获取数据时预取的记录数 |
updateSnapshotOnSave | true | 更新快照save() |
virtualForeignKeys | true | 虚拟外键 |
ini
选项:
; phalcon.orm.cache_level = 3
; phalcon.orm.case_insensitive_column_map = false
; phalcon.orm.cast_last_insert_id_to_int = false
; phalcon.orm.cast_on_hydrate = false
; phalcon.orm.column_renaming = true
; phalcon.orm.disable_assign_setters = false
; phalcon.orm.enable_implicit_joins = true
; phalcon.orm.enable_literals = true
; phalcon.orm.events = true
; phalcon.orm.exception_on_failed_metadata_save = true
; phalcon.orm.exception_on_failed_save = false
; phalcon.orm.force_casting = false
; phalcon.orm.ignore_unknown_columns = false
; phalcon.orm.late_state_binding = false
; phalcon.orm.not_null_validations = true
; phalcon.orm.resultset_prefetch_records = "0"
; phalcon.orm.unique_cache_id = 3
; phalcon.orm.update_snapshot_on_save = true
; phalcon.orm.virtual_foreign_keys = true
; phalcon.db.escape_identifiers = On
; phalcon.db.force_casting = Off
注意
Phalcon\Mvc\Model::assign()
(在创建/更新/保存模型时也会使用)始终使用设置器(如果存在)处理传递的数据参数,即使不必要或不需要也是如此。这会为您的应用增加一些额外开销。您可以通过在ini文件中添加phalcon.orm.disable_assign_setters = 1
来更改此行为,它将直接使用$this->property = value
.
整数与字符串¶
如果您希望从相关数据库字段获取整数值,则需要执行以下操作:- 确保int
related database fields, you will need to do the following: - Make sure that the castOnHydrate
(或设置ini_set('phalcon.orm.cast_on_hydrate', 'on')
)已启用
- 确保您在服务器上使用的是
mysqlnd
驱动程序。您可以使用phpinfo()
(pdo_mysql > 客户端API版本)进行检查 - 在您的数据库连接提供者中,您需要传递以下选项:
注册提供者应如下所示:
<?php
$parameters = [
// ....
];
/** @var Manager $eventsManager */
$eventsManager = $container->getShared('eventsManager');
$container->setShared(
'db',
function () use ($eventsManager, $parameters) {
$options = [
'host' => $parameters['host'] ?? 'localhost',
'dbname' => $parameters['dbname'] ?? 'phalcon',
'username' => $parameters['user'] ?? 'root',
'password' => $parameters['pass'] ?? 'secret',
'encoding' => $parameters['encoding'] ?? 'utf8',
'options' => [
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_STRINGIFY_FETCHES => false,
]
];
$connection = new Mysql($options);
$connection->setEventsManager($eventsManager);
return $connection;
);
}
无效的参数编号¶
在v5.6中,实例化PDO时使用的参数已恢复为默认设置。因此,PDO::ATTR_EMULATE_PREPARES
默认为true
.
但是,如果您在代码中遇到以下信息,当绑定参数在查询中多次使用时:
错误
无效的参数编号
您可以检查PDO参数并确保在注册数据库提供者时设置了PDO::ATTR_EMULATE_PREPARES
被设置为true
, when registering your database provider.
独立组件¶
你可以使用Phalcon\Mvc\Model可以单独使用,如果您愿意,可自行进行必要的设置。下面的示例演示了如何实现这一点。
<?php
use Phalcon\Di\Di;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Manager;
use Phalcon\Db\Adapter\Pdo\Sqlite;
use Phalcon\Mvc\Model\Metadata\Memory;
$container = new Di();
$container->set(
'db',
new Sqlite(
[
'dbname' => 'sample.db',
]
)
);
$container->set(
'modelsManager',
new Manager()
);
$container->set(
'modelsMetadata',
new Memory()
);
class Invoices extends Model
{
}
echo Invoices::count();