模型关系¶
概览¶
数据库规范化是一个将数据拆分到不同表中并在这些表之间创建链接的过程,目的是提升灵活性、减少数据冗余和提高数据完整性。关系在每个模型的initialize
方法中定义。
可用的关系类型如下:
一对一
hasOne(
string|array $fields,
string $referenceModel,
string|array $referencedFields,
array $options = null
)
hasOneThrough(
string|array $fields,
string $intermediateModel,
string|array $intermediateFields,
string|array $intermediateReferencedFields,
string $referenceModel,
string|array $referencedFields,
array $options = null
)
一对多
hasMany(
string|array $fields,
string $referenceModel,
string|array $referencedFields,
array options = null
)
多对一
belongsTo(
string|array $fields,
string $referenceModel,
string|array $referencedFields,
array options = null
)
多对多
hasManyToMany(
string|array $fields,
string $intermediateModel,
string|array $intermediateFields,
string|array $intermediateReferencedFields,
string $referenceModel,
string|array $referencedFields,
array $options = null
)
关系可以是单向或双向的,每个关系可以是简单的(一对一模型)也可以是更复杂的(多个模型组合)。模型管理器会处理这些关系中的外键约束,定义这些约束有助于参照完整性以及模型相关记录的便捷快速访问。通过实现关系,可以轻松且统一地从源模型访问相关模型的数据。
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->hasOne(
'inv_cst_id',
Customers::class,
'cst_id',
[
'alias' => 'customers',
'reusable' => true,
]
);
}
}
单向¶
单向关系是那些在一个方向上生成但反向不存在的关系。
双向¶
双向关系在两个模型中都建立关联,每个模型都定义对方的逆向关系。
设置¶
在 Phalcon 中,关系必须在模型的initialize()
方法中定义。使用belongsTo()
, hasMany()
, hasManyToMany()
, hasOne()
和hasOneThrough()
方法,可以定义当前模型的一个或多个字段与另一个模型字段之间的关系。每个方法需要三个参数:
- 本地字段
- 引用模型
- 引用字段
方法 | 描述 |
---|---|
belongsTo | 定义一个 n-1 的关系 |
hasMany | 定义一个 1-n 的关系 |
hasManyToMany | 定义一个 n-n 的关系 |
hasOne | 定义一个 1-1 的关系 |
hasOneThrough | 定义一个 1-1 的关系 |
下面的模式展示了 3 个表及其关系,作为我们示例的基础:
create table co_invoices
(
inv_id int(10) auto_increment primary key,
inv_cst_id int(10) null,
inv_status_flag tinyint(1) null,
inv_title varchar(100) null,
inv_total float(10, 2) null,
inv_created_at datetime null
);
create table co_invoices_x_products
(
ixp_inv_id int(10),
inv_prd_id int(10)
);
create table co_products
(
prd_id int(10) auto_increment primary key,
prd_title varchar(100) null,
prd_price float(10, 2) null
);
- 模型
Invoices
拥有多个InvoicesProducts
. - 模型
Products
拥有多个InvoicesProducts
. - 模型
InvoicesProducts
属于这两个Invoices
和Products
模型,并以多对一的关系。 - 模型
Invoices
与Products
存在多对多关系,InvoicesProducts
.
通过
<?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->hasManyToMany(
'inv_id',
InvoicesProducts::class,
'ixp_inv_id',
'ixp_prd_id',
Products::class,
'prd_id',
[
'reusable' => true,
'alias' => 'products',
]
);
$this->hasMany(
'inv_id',
InvoicesProducts::class,
'ixp_inv_id',
[
'reusable' => true,
'alias' => 'invoicesProducts'
]
);
}
}
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class InvoicesProducts extends Model
{
public $ixp_inv_id;
public $ixp_prd_id;
public function initialize()
{
$this->belongsTo(
'ixp_inv_id',
Invoices::class,
'inv_id',
[
'reusable' => true,
'alias' => 'invoice'
]
);
$this->belongsTo(
'ixp_prd_id',
Products::class,
'prd_id',
[
'reusable' => true,
'alias' => 'product'
]
);
}
}
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Products extends Model
{
public $prd_id;
public $prd_title;
public $prd_price;
public $prd_created_at;
public function initialize()
{
$this->hasMany(
'prd_id',
InvoicesProducts::class,
'ixp_prd_id'
);
// Many to many -> Invoices
$this->hasManyToMany(
'prd_id',
InvoicesProducts::class,
'ixp_prd_id',
'ixp_inv_id',
Invoices::class,
'inv_id',
[
'reusable' => true,
'alias' => 'invoices',
]
);
}
}
The first parameter indicates the field of the local model used in the relationship; the second indicates the name of the referenced model, and the third is the field name in the referenced model. You could also use arrays to define multiple fields in the relationship.
Many-to-many relationships require 3 models and define the attributes involved in the relationship:
<?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->hasManyToMany(
'inv_id',
InvoicesProducts::class,
'ixp_inv_id',
'ixp_prd_id',
Products::class,
'prd_id',
[
'reusable' => true,
'alias' => 'products',
]
);
}
}
参数¶
根据应用的需求,您可能希望将描述不同行为的数据存储在一张表中。例如,您可能只想有一个名为co_customers
的表,并包含一个cst_status_flag
描述状态的字段(例如活跃、非活跃等)。
使用关系,您可以只获取那些Customers
与我们的Invoices
具有某个cst_status_flag
的数据。在关系中定义这个约束可以让模型完成所有工作。
它还接受闭包函数,在每次访问相关记录之前都会重新计算该条件。这使得可以在查询之间自动更新条件。
<?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->hasMany(
'inv_cst_id',
Customers::class,
'cst_id',
[
'reusable' => true,
'alias' => 'customersActive',
'params' => [
'conditions' => 'cst_status_flag = :status:',
'bind' => [
'status' => 1,
]
]
]
);
$container = $this->getDI();
$this->hasMany(
'inv_cst_id',
Customers::class,
'cst_id',
[
'reusable' => true,
'alias' => 'customersNearby',
'params' => function() use ($container) {
return [
'conditions' => 'cst_location = :location:',
'bind' => [
'location' => $container->getShared('myLocationService')->myLocation,
]
];
}
]
);
}
}
多字段¶
有时关系需要根据多个字段而非单一字段来定义。请看下面的例子:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Products extends Model
{
public $prd_id;
public $prd_type_flag;
public $prd_name;
}
和
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Parts extends Model
{
public $par_id;
public $par_prd_id;
public $par_par_id;
public $par_type_flag;
public $par_name;
}
上述情况下,我们有一个Products
模型,它具有prd_id
, prd_type_flag
和prd_name
字段。Parts
模型包含par_id
, par_prd_id
, par_type_flag
和par_name
。关系基于产品的唯一 ID 以及类型。
使用如上所示的关系选项,在两个模型之间绑定一个字段不会返回我们需要的结果。我们可以使用数组指定必要的字段来定义关系。
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Products extends Model
{
public $prd_id;
public $prd_type_flag;
public $prd_name;
public function initialize()
{
$this->hasOne(
[
'prd_id',
'prd_type_flag'
],
Parts::class,
[
'par_prd_id',
'par_type_flag'
],
[
'reusable' => true, // cache
'alias' => 'parts',
]
);
}
}
注意
关系中的字段映射是一一对应的,即源模型数组的第一个字段匹配目标数组的第一个字段,依此类推。源模型和目标模型中的字段数量必须相同。
访问¶
我们可以通过多种方式访问模型的关系。
- 魔术方法
__get
,__set
- 魔术方法
get*
getRelated
__get()
¶
您可以使用魔术方法访问关系。为关系赋值给alias
简化了相关数据的访问。属性名称与alias
.
<?php
$customer = Customers::findFirst(
[
'conditions' => 'cst_id = :customerId:',
'bind' => [
'customerId' => 1,
],
]
);
foreach ($customer->invoices as $invoice) {
echo $invoice->inv_title;
}
或者对于多对多关系(参见上面的模型):
<?php
$invoice = Invoices::findFirst(
[
'conditions' => 'inv_cst_id = :customerId:',
'bind' => [
'customerId' => 1,
],
]
);
foreach ($invoice->invoicesProducts as $product) {
echo $invoice->product->prd_name;
}
foreach ($invoice->products as $product) {
echo $invoice->prd_name;
}
使用魔术__get
可以直接访问关系,但不提供额外功能,如过滤或排序。
get*()
¶
您还可以通过使用以get开头的 getter 方法访问同样的关系,并使用关系名。
<?php
$customer = Customers::findFirst(
[
'conditions' => 'cst_id = :customerId:',
'bind' => [
'customerId' => 1,
],
]
);
foreach ($customer->getInvoices() as $invoice) {
echo $invoice->inv_title;
}
或者对于多对多关系(参见上面的模型):
<?php
$invoice = Invoices::findFirst(
[
'conditions' => 'inv_cst_id = :customerId:',
'bind' => [
'customerId' => 1,
],
]
);
foreach ($invoice->getInvoiceProducts() as $product) {
echo $invoice->product->prd_name;
}
foreach ($invoice->getProducts() as $product) {
echo $invoice->prd_name;
}
<?php
$invoice = Invoices::findFirst(
[
'conditions' => 'inv_cst_id = :customerId:',
'bind' => [
'customerId' => 1,
],
]
);
$products = $invoice->getProducts(
[
'order' => 'prd_name',
]
);
foreach ($products as $product) {
echo $invoice->prd_name;
}
<?php
$invoice = Invoices::findFirst(
[
'conditions' => 'inv_cst_id = :customerId:',
'bind' => [
'customerId' => 1,
],
]
);
$products = $invoice->getProducts(
[
'prd_created_at = :date:',
'bind' => [
'date' => '2019-12-25',
],
]
);
foreach ($products as $product) {
echo $invoice->prd_name;
}
手动获取相同记录的方式为:
<?php
$invoice = Invoices::findFirst(
[
'conditions' => 'inv_cst_id = :customerId:',
'bind' => [
'customerId' => 1,
],
]
);
$invoicesProducts = InvoicesProducts::find(
[
'conditions' => 'ixp_inv_id = :invoiceId:',
'bind' => [
'invoiceId' => $invoice->inv_id,
],
]
);
$productIds = [];
foreach ($invoicesProducts as $intermediate) {
$productIds[] = $intermediate->ixp_prd_id;
}
$products = Products::find(
[
'conditions' => 'prd_id IN ({array:productIds})',
'bind' => [
'productIds' => $productIds,,
],
]
);
foreach ($products as $product) {
echo $invoice->prd_name;
}
前缀get
用于find()
/findFirst()
获取相关记录。
类型 | 隐式方法 | 返回 |
---|---|---|
属于 | findFirst | 直接引用相关记录的模型实例 |
拥有一个 | findFirst | 直接引用相关记录的模型实例 |
Has-One-Through | findFirst | 直接引用相关记录的模型实例 |
拥有多个 | find | 引用模型的模型实例集合 |
Has-Many-to-Many | (复杂查询) | 引用模型的模型实例集合inner join ) |
您还可以使用count
前缀用于返回一个整数,表示相关记录的数量:
<?php
$invoice = Invoices::findFirst(
[
'conditions' => 'inv_cst_id = :customerId:',
'bind' => [
'customerId' => 1,
],
]
);
echo $invoice->countProducts();
getRelated()
¶
你可以通过使用以下方式访问相同的关系:getRelated()
并定义你想要获取的关联关系。
<?php
$customer = Customers::findFirst(
[
'conditions' => 'cst_id = :customerId:',
'bind' => [
'customerId' => 1,
],
]
);
foreach ($customer->getRelated('invoices') as $invoice) {
echo $invoice->inv_title;
}
或者对于多对多关系(参见上面的模型):
<?php
$invoice = Invoices::findFirst(
[
'conditions' => 'inv_cst_id = :customerId:',
'bind' => [
'customerId' => 1,
],
]
);
foreach ($invoice->getRelated('products') as $product) {
echo $invoice->prd_name;
}
getRelated()
是一个数组,提供了额外可设置的选项,比如过滤和排序。 <?php
$invoice = Invoices::findFirst(
[
'conditions' => 'inv_cst_id = :customerId:',
'bind' => [
'customerId' => 1,
],
]
);
$products = $invoice->getRelated(
'products',
[
'prd_created_at = :date:',
'bind' => [
'date' => '2019-12-25',
],
]
);
foreach ($products as $product) {
echo $invoice->prd_name;
}
setRelated()
¶
你可以通过使用以下方式设置相同的关联关系:setRelated()
并定义你想要设置的关联关系。
<?php
$customer = Customers::findFirst(
[
'conditions' => 'cst_id = :customerId:',
'bind' => [
'customerId' => 1,
],
]
);
$invoice = new Invoice();
$customer->setRelated('invoices', [$invoice]);
别名¶
可以通过使用远程表的名称来访问关联关系。由于命名约定的原因,这可能并不容易,并可能导致混淆。如上所示,你可以定义一个alias
到该关联关系。
<?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->hasManyToMany(
'inv_id',
InvoicesProducts::class,
'ixp_inv_id',
'ixp_prd_id',
Products::class,
'prd_id'
);
}
}
使用别名:
<?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->hasManyToMany(
'inv_id',
InvoicesProducts::class,
'ixp_inv_id',
'ixp_prd_id',
Products::class,
'prd_id',
[
'reusable' => true,
'alias' => 'products',
]
);
}
}
如果你的表结构有自连接,则在没有别名的情况下将无法访问这些关联关系,因为你会使用相同的模型。
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Parts extends Model
{
public $par_id;
public $par_prd_id;
public $par_par_id;
public $par_type_flag;
public $par_name;
public function initialize()
{
$this->hasMany(
'par_id',
Parts::class,
'par_par_id',
[
'reusable' => true,
'alias' => 'children',
]
);
$this->belongsTo(
'par_par_id',
Parts::class,
'par_id',
[
'reusable' => true,
'alias' => 'parent',
]
);
}
}
Part
与一个或多个Part
对象有关联关系。每个Part
可能由其他构成它的部分组成。因此,我们最终得到一个自连接关系。对于一部电话Part
我们有以下子部件: <?php
$phone = Parts::findFirst(....);
echo $phone->getChildren();
// --- Cover
// --- Battery
// --- Charger
而这些部件中的每一个都有该电话作为父级:
缓存¶
访问相关数据可能会显著增加数据库中的查询数量。你可以尽可能地利用关系中的reusable
选项来减少这种负载。将此选项设置为true
将指示 Phalcon 在第一次访问关联关系时缓存其结果,以便后续对该关联关系的调用可以使用缓存的结果集,而不再次从数据库请求数据。此缓存仅在同一个请求期间有效。
注意
推荐你在关系中尽可能多地使用reusable
选项
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Invoices extends Model
{
public function initialize()
{
$this->hasOne(
'inv_cst_id',
Customers::class,
'cst_id',
[
'alias' => 'customers',
'reusable' => true,
]
);
}
}
自动补全¶
大多数具有自动补全功能的 IDE 和编辑器在使用魔术方法(包括方法和属性)时都无法检测正确的类型。为了解决这个问题,你可以在类文档块中指明可用的魔术操作,帮助 IDE 实现更好的自动补全功能:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
/**
* Invoices model
*
* @property Simple|Products[] $products
* @method Simple|Products[] getProducts($parameters = null)
* @method integer countProducts()
*/
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->hasManyToMany(
'inv_id',
InvoicesProducts::class,
'ixp_inv_id',
'ixp_prd_id',
Products::class,
'prd_id',
[
'reusable' => true,
'alias' => 'products',
]
);
}
}
条件语句¶
你也可以根据条件创建关联关系。当基于关联进行查询时,条件会自动附加到查询中:
<?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->hasManyToMany(
'inv_id',
InvoicesProducts::class,
'ixp_inv_id',
'ixp_prd_id',
Products::class,
'prd_id',
[
'reusable' => true,
'alias' => 'products',
]
);
}
}
class Companies extends Model
{
public function initialize()
{
$this->hasMany(
'id',
Invoices::class,
'inv_id',
[
'alias' => 'Invoices',
]
);
$this->hasMany(
'id',
Invoices::class,
'inv_id',
[
'alias' => 'InvoicesPaid',
'params' => [
'conditions' => "inv_status = 'paid'",
],
]
);
$this->hasMany(
'id',
Invoices::class,
'inv_id',
[
'alias' => 'InvoicesUnpaid',
'params' => [
'conditions' => "inv_status <> :status:",
'bind' => [
'status' => 'unpaid',
],
],
]
);
}
}
此外,你可以在模型上的getInvoices()
或getRelated()
参数中使用它们,以进一步过滤或排序你的关联关系:
<?php
$company = Companies::findFirst(
[
'conditions' => 'id = :id:',
'bind' => [
'id' => 1,
],
]
);
*
$unpaidInvoices = $company->InvoicesUnpaid;
$unpaidInvoices = $company->getInvoicesUnpaid();
$unpaidInvoices = $company->getRelated('InvoicesUnpaid');
$unpaidInvoices = $company->getRelated(
'Invoices',
[
'conditions' => "inv_status = 'paid'",
]
);
$unpaidInvoices = $company->getRelated(
'Invoices',
[
'conditions' => "inv_status = 'paid'",
'order' => 'inv_created_date ASC',
]
);
虚拟外键¶
默认情况下,关联关系没有任何约束,不会在添加、更新或删除记录时检查相关数据。然而,你可以向你的关联关系添加验证,以确保数据的完整性。这可以通过关联方法的最后一个参数完成。
交叉表InvoicesProducts
可以稍作修改以演示此功能:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class InvoicesProducts extends Model
{
public $ixp_inv_id;
public $ixp_prd_id;
public function initialize()
{
$this->belongsTo(
'ixp_inv_id',
Invoices::class,
'inv_id',
[
'alias' => 'invoice',
'foreignKey' => true,
'reusable' => true,
]
);
$this->belongsTo(
'ixp_prd_id',
Products::class,
'prd_id',
[
'alias' => 'product',
'foreignKey' => [
'message' => 'The prd_id does not exist ' .
'in the Products model',
],
'reusable' => true,
]
);
}
}
如果你更改了belongsTo()
关系以使其表现得像外键一样,它会在插入/更新这些字段的值时验证是否存在对应模型中的有效 ID。同样地,如果一个hasMany()
/hasOne()
被更改为定义了foreignKey
,它将在记录具有相关数据时验证是否允许或禁止删除记录。
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class Products extends Model
{
public $prd_id;
public $prd_title;
public $prd_price;
public $prd_created_at;
public function initialize()
{
$this->hasMany(
'prd_id',
Products::class,
'ixp_prd_id',
[
'foreignKey' => [
'message' => 'The product cannot be deleted ' .
'because there are invoices ' .
'attached to it',
],
]
);
}
}
虚拟外键也可以设置为允许空值,如下所示:
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
class InvoicesProducts extends Model
{
public $ixp_inv_id;
public $ixp_prd_id;
public function initialize()
{
$this->belongsTo(
'ixp_inv_id',
Invoices::class,
'inv_id',
[
'alias' => 'invoice',
'foreignKey' => true,
'reusable' => true,
]
);
$this->belongsTo(
'ixp_prd_id',
Products::class,
'prd_id',
[
'alias' => 'product',
'foreignKey' => [
'allowNulls' => true,
'message' => 'The prd_id does not exist ' .
'in the Products model',
],
'reusable' => true,
]
);
}
}
级联 / 限制¶
默认情况下,作为虚拟外键的关联关系会限制创建 / 更新 / 删除记录,以保持数据完整性。你可以使用CASCADE
和RESTRICT
中的action
选项来定义模仿 RDBMS 功能的这些约束:foreignKey
。该Phalcon\Mvc\Model\Relation底层对象提供了两个常量:
Relation::ACTION_CASCADE
Relation::ACTION_RESTRICT
<?php
namespace MyApp\Models;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Relation;
class Products extends Model
{
public $prd_id;
public $prd_title;
public $prd_price;
public $prd_created_at;
public function initialize()
{
$this->hasMany(
'prd_id',
Products::class,
'ixp_prd_id',
[
'foreignKey' => [
'action' => Relation::ACTION_CASCADE,
],
]
);
}
}
上面的代码允许你在主记录被删除时删除所有相关的记录(级联删除)。
操作¶
如果结果集返回完整的对象,则可以使用关联执行操作。
保存¶
魔术属性可用于存储记录及其相关属性:
<?php
$artist = new Artists();
$artist->name = 'Shinichi Osawa';
$artist->country = 'Japan';
$album = new Albums();
$album->name = 'The One';
$album->artist = $artist;
$album->year = 2008;
$album->save();
保存具有“一对多”关联的记录及其相关记录:
<?php
$customer = Customers::findFirst(
[
'conditions' => 'cst_id = :customerId:',
'bind' => [
'customerId' => 1,
]
]
);
$invoice1 = new Invoices();
$invoice1-> inv_status_flag = 0;
$invoice1-> inv_title = 'Invoice for ACME Inc. #1';
$invoice1-> inv_total = 100;
$invoice1-> inv_created_at = time();
$invoice2 = new Invoices();
$invoice2-> inv_status_flag = 0;
$invoice2-> inv_title = 'Invoice for ACME Inc. #2';
$invoice2-> inv_total = 200;
$invoice2-> inv_created_at = time();
$customer->invoices = [
$invoice1,
$invoice2
];
$customer->save();
上面的代码从我们的数据库中获取了一个客户。创建了两张发票并分配给客户的invoices
关系作为数组。然后保存客户记录,这也同时保存了两张发票并将它们链接到客户。
尽管上述语法非常方便,但在某些情况下并不理想,尤其是在更新相关记录时。Phalcon 不知道需要使用更新添加或删除哪些记录,因此它会执行替换操作。在更新的情况下,最好自行控制数据,而不是留给框架处理。
使用上述语法保存数据将隐式创建一个事务并在一切正常的情况下提交它。在整个事务保存过程中生成的消息将传递回用户以提供更多相关信息。
注意
通过重载以下方法 / 事件来添加相关实体是不可行的:
Phalcon\Mvc\Model::beforeSave()
Phalcon\Mvc\Model::beforeCreate()
Phalcon\Mvc\Model::beforeUpdate()
要使这项工作在模型内部运行,你需要重载Phalcon\Mvc\Model::save()
方法。
更新¶
而不是这样做:
<?php
$invoices = $customer->getInvoices();
foreach ($invoices as $invoice) {
$invoice->inv_total = 100;
$invoice->inv_updated_at = time();
if (false === $invoice->update()) {
$messages = $invoice->getMessages();
foreach ($messages as $message) {
echo $message;
}
break;
}
}
你可以这样做:
update
还接受一个匿名函数,用于过滤必须更新的记录:
<?php
$data = [
'inv_total' => 100,
'inv_updated_at' => time(),
];
$customer->getInvoices()->update(
$data,
function ($invoice) {
return ($invoice->inv_cst_id !== 1);
}
);
删除¶
而不是这样做:
<?php
$invoices = $customer->getInvoices();
foreach ($invoices as $invoice) {
if (false === $invoice->delete()) {
$messages = $invoice->getMessages();
foreach ($messages as $message) {
echo $message;
}
break;
}
}
你可以这样做:
delete()
还接受一个匿名函数,用于过滤必须删除的记录:
<?php
$customer->getInvoices()->delete(
function ($invoice) {
return ($invoice->inv_total >= 0);
}
);
消息¶
你可以在另一个模型中追加消息。
<?php
$invoices = $customer->getInvoices();
foreach ($invoices as $invoice) {
if ( false === $invoice->save() ) {
$customer->appendMessagesFrom($invoice);
}
}
$messages = $customer->getMessages();
foreach ($messages as $message) {
echo $message;
$metaData = $message->getMetadata();
if ( true === isset($metaData['model']) ) {
echo $metaData['model'];
}
}
为了获得更好的错误报告,你可以从消息元数据中检索模型和引用模型的名称:
<?php
$invoices = $customer->getInvoices();
if ( false === $customer->save() ) {
$messages = $customer->getMessages();
foreach ($messages as $message) {
echo $message;
$metaData = $message->getMetadata();
if ( true === isset($metaData['model']) ) {
echo $metaData['model'];
}
if ( true === isset($metaData['referenceModel']) ) {
echo $metaData['referenceModel'];
}
}
}