假设我们有3个这样的表:
--------- --------------- -------------------
| USER | | USER_STATUS | | STATUS_DICT |
|-------| |-------------- |------------------
| id | | id | | id (int) |
| name | | user_id | | status (string) |
| email | | status_id | -------------------
--------- | timestamp |
---------------
每个user
都有许多与user_status
相关的状态(user.id <-> user_status.user_id
)。每个user_status
在status_dict
中都有一条相关记录,它是int status的字符串标签(例如0 =有效)。
我想要的是让用户模型使用关系检索最后一个用户的状态。设置关系:
/* User model */
public function relations() {
return array(
'status' => array(self::HAS_ONE, 'UserStatus', 'user_id', 'order'=>'status.id DESC'),
);
}
和
/* UserStatus model */
public function relations() {
return array(
'status' => array(self::BELONGS_TO, 'StatusDictionary', 'status_id'),
'user' => array(self::BELONGS_TO, 'User', 'user_id'),
);
}
然后当我致电User::model()->findByPk(1)->status
时,我获得了#1用户的最新状态。
BUT。
当我想查找具有指定状态的所有用户时,它无法正常工作。我没有获得具有指定状态的用户(user_status
中的最新记录),而是获得至少一次具有此状态的所有用户。
我必须做些什么来建立一对一的关系(我为此案例的目的称之为这样)?我看到this article它完成了这项工作,但我想知道是否有办法用Yii的惯例实现它。
我想像这样声明命名范围:
/* in User model */
public function withStatus($status) {
if(!is_numeric($status) && !is_null($status)) {
$status = StatusDictionary::model()->find('lower(name)=lower(:name)', array(':name' => $status))->getAttribute('id');
}
$this->getDbCriteria()->mergeWith(array(
'condition' => (is_null($status) ? 'status.status_id IS NULL' : 'status.status_id=:statusId'),
'params' => array(':statusId' => $status),
'with' => array('status')
));
return $this;
}
但它也不起作用,User::model()->withStatus(0)->findAll()
会返回user_status
中user_status.status_id = 0
与{{1}}相关记录的所有用户,即使某些用户具有较新的不同状态。
提前感谢您的帮助。
答案 0 :(得分:1)
我找到了其他类似的解决方案:
范围功能:
public function withStatus($status) {
if(!is_numeric($status) && !is_null($status)) {
$status = StatusDictionary::model()->find('lower(name)=lower(:name)', array(':name' => $status))->getAttribute('id');
}
$this->with('status');
$this->getDbCriteria()->compare('i.statusId', $status);
return $this;
}
关系记录(此联接是保护与过滤条件的关系):
'status' => array(self::HAS_ONE, 'Status', 'clientId',
'join' => 'INNER JOIN (select max(id) id from status group by clientId) j ON i.id=j.id',
'alias' => 'i'
),
它对我有用。
答案 1 :(得分:1)
好的,我从他的帖子中找到了TheRifler帮助的解决方案。事实证明,我的解决方案就在我的鼻子底下(文章链接在一个问题中)。
关系应定义如下:
/* User model */
public function relations() {
return array(
'status' => array(self::HAS_ONE, 'UserStatus', 'user_id',
'order' => 's.id DESC',
'alias' => 's',
// http://murrayhopkins.wordpress.com/2008/10/28/mysql-left-join-on-last-or-first-record-in-the-right-table/
'join' => 'LEFT JOIN ( SELECT s1.*
FROM `user_status` as s1
LEFT JOIN `user_status` AS s2
ON s1.user_id = s2.user_id AND s1.id < s2.id
WHERE s2.user_id IS NULL ) as `status`
ON (s.user_id = `status`.user_id)'
),
);
}
和命名范围:
public function withStatus($status) {
if(!is_numeric($status) && !is_null($status)) {
$status = StatusDictionary::model()->find('lower(name)=lower(:name)', array(':name' => $status))->getAttribute('id');
}
$this->with('status');
if(!is_null($status)) {
$this->getDbCriteria()->compare('status.status_id', $status);
}
else {
$this->getDbCriteria()->addCondition('s.status_id IS NULL');
}
return $this;
}
它包含一些肮脏的工作,因为withStatus()
使用status
加入子查询的数据(如果指定了$status
或基础s
别名,如果$status
为空(它是因为我们在status
表中寻找没有记录的用户。也许这不是最优雅的解决方案,但可以正常工作。
现在我可以使用:
User::model()->findByPk(1)->status
访问指定用户的状态User::model()->withStatus('active')->findAll()
列出具有指定状态的用户(状态可以作为整数传递[user_status.id
]或字符串[status_dict.name
])User::model()->withStatus(null)->count()
获取没有任何状态的用户数