3

我们有一个基于 Yii 的 PHP 应用程序,它对不同类型的对象执行一些通用操作(例如与社交网络“分享”或在内部网上商店中“购买”)。

现在开发人员使用类常量来分隔不同类型的对象。像这样:

模型中:

class Referral extends CActiveRecord {
    //... a lot more stuff here...
    const ITEM_TYPE_PRODUCT = 'product';
    const ITEM_TYPE_DESIGN = 'design';
    const ITEM_TYPE_BRAND = 'brand';
    const ITEM_TYPE_INVITE = 'invite';
    const ITEM_TYPE_DESIGNER = 'designer';
    //... a lot more stuff here...
}

然后在一些控制器中:

// calling static method of Referral and pass a type IDs to change it's behavior
$referral_params = Referral::buildReferallParams(
    Referral::TYPE_PINTEREST, 
    Referral::ITEM_TYPE_PRODUCT
)

类常量的这种使用导致了我想摆脱的主要代码重复。

我个人认为我们应该为类型添加一个查找表,如下所示:

CREATE TABLE `item_type` (
    id int primary key auto_increment,
    name varchar(255)
);

并创建一个名为ItemType从表中提取 ID 的 Yii 模型类。

但这会导致另一个问题:无论如何我们都需要在代码中使用这种类型 ID,并且需要这样做很长时间。我们需要以某种方式为 ID 创建一个符号名称。现在调用likeReferral::ITEM_TYPE_PRODUCT很方便,改成Yii-flavoredItemType::model()->findByAttributes(array('name' => 'Product'))是完全不能接受的。

我想避免在其中维护相同的类常量列表(现在通过我们的代码库复制),ItemType因为每次添加新类型都需要添加新常量,这只是等待发生的不同步问题。

因此,问题如下:如何使查找表的 ActiveRecord 与类似“枚举”的数字常量集同样有用?

我使用 ItemType 的子类提供了以下一些漂亮的解决方案:

ProductItemType::id(); // returns lazy-loaded ID for 'Product' item type
BrandItemType::id();   // the same for 'Brand' item type
// ... and so on

但是如果现在需要单个基类的五个子类,以后可能需要另外六个。有没有可行的架构解决方案?

编辑

重复的问题是:声明类型 ID 的类常量在每个需要以某种方式区分不同项目类型的类中重新定义。我知道最“真实”的解决方案是将其从里到外反转并将取决于对象类型的功能移动到对象本身,但是我们有没有任何测试覆盖的遗留代码,这个解决方案现在只是一个梦想. 现在我想做的就是删除这个重复。还有常数。

4

1 回答 1

1

好吧,如果您想一次性定义所有这些,并从数据库中获取。这意味着你需要做一个行为,有类似静态的方法来检索这些(所以常量将成为函数),你需要创建正确的魔术方法__call来覆盖这些不存在的函数来调用你的数据库,你可以附加这个你正在使用的任何 yii 类的行为。

组件行为的使用

一个组件支持mixin模式并且可以附加一个或多个行为。行为是一个对象,其方法可以通过收集功能而不是专门化(即普通类继承)的方式由其附加组件“继承”。一个组件可以附加多个行为,从而实现“多重继承”。

行为类必须实现IBehavior接口。大多数行为都可以从CBehavior基类扩展。如果需要将行为附加到模型,它也可以从CModelBehaviorCActiveRecordBehavior扩展,后者实现了特定于模型的附加功能。

要使用行为,必须首先通过调用行为的attach()方法将其附加到组件。然后我们可以通过组件调用行为方法:

// $name uniquely identifies the behavior in the component
$component->attachBehavior($name,$behavior);
// test() is a method of $behavior
$component->test();

可以像组件的普通属性一样访问附加的行为。例如,如果一个名为 tree 的行为附加到一个组件,我们可以使用以下方法获取对该行为对象的引用:

$behavior=$component->tree;
// equivalent to the following:
// $behavior=$component->asa('tree');

可以暂时禁用行为,使其方法无法通过组件使用。例如,

$component->disableBehavior($name);
// the following statement will throw an exception
$component->test();
$component->enableBehavior($name);
// it works now
$component->test();

附加到同一组件的两个行为可能具有相同名称的方法。在这种情况下,第一个附加行为的方法将优先。

当与事件一起使用时,行为更加强大。一个行为,当被附加到一个组件时,可以将它的一些方法附加到组件的一些事件上。通过这样做,行为有机会观察或改变组件的正常执行流程。

行为的属性也可以通过它所附加的组件来访问。属性包括公共成员变量和通过行为的 getter 和/或 setter 定义的属性。例如,如果行为具有名为 xyz 的属性,并且该行为附加到组件 $a。然后我们可以使用表达式$a->xyz来访问行为的属性。

更多阅读:
http ://www.yiiframework.com/wiki/44/behaviors-events
http://www.ramirezcobos.com/2010/11/19/how-to-create-a-yii-behavior/

于 2012-07-05T13:34:16.380 回答