【问题标题】:Selecting the newest record of a hasMany relationship选择 hasMany 关系的最新记录
【发布时间】:2013-09-14 15:49:18
【问题描述】:

我的 CakePHP 应用程序中有 2 个基本模型:用户和登录。用户与 Logins 具有 hasMany 关系(即,每次用户登录时都会创建一个新的登录记录)。

现在,我想建立从 User 模型到 Login 模型的 2 个关系: 用户有很多登录 和 用户有一个 lastLogin

最后一个关系应该只包括所选用户的登录模型的最后一条记录。

我尝试如下:

var $belongsTo = array
(
    'LastLogin' => array
    (
        'className' => 'Login',
        'order' => 'LastLogin.created DESC',
        'limit' =>  1

    )
); 

但是,这不起作用。关于如何让它发挥作用的任何想法?

【问题讨论】:

  • 什么不起作用?意外数据?
  • 也许它应该是'order' => 'Login.created DESC' - 正如您所指的Login 表?
  • 我认为您过度使用模型关系。当您可以对登录模型进行简单查询以获取一条记录时,为什么还需要 lastLogin?
  • @Ross:正确,每次登录我都会得到一条记录,而每个用户需要一条记录。另外,我应该参考 LastLogin 而不是 Login,因为这是表别名。
  • @Anh Pham:我可以这样做,但我宁愿做一个查询来从那个人那里获得我需要的所有信息。在显示用户列表时,我只对该用户的最新登录感兴趣。

标签: cakephp


【解决方案1】:

针对评论的更新答复

对于belongsTo 关系,外键应该在current 模型中。

这意味着,如果您想建立 User belongsTo LastLogin 的关系,users 表应该有一个 last_login_id 字段。

在您的情况下,您可能希望使用 hasOne 关系,并且您将不得不在 fields 键中使用 MAX() SQL 函数。请注意,获取 last_login 完全独立于您的 User hasMany Login 关系。因此,如果您想要的只是最后一次登录,您可以删除 hasMany 关系并保留 hasOne。

使用下面的示例代码,您将得到:

/users/index 的输出:

Array
(
    [User] => Array
        (
            [id] => 1
            [name] => user1
            [last_login] => 2011-05-01 14:00:00
        )
    [Login] => Array
        (
            [0] => Array
                (
                    [id] => 1
                    [user_id] => 1
                    [created] => 2011-05-01 12:00:00
                )
            [1] => Array
                (
                    [id] => 2
                    [user_id] => 1
                    [created] => 2011-05-01 13:00:00
                )
            [2] => Array
                (
                    [id] => 3
                    [user_id] => 1
                    [created] => 2011-05-01 14:00:00
                )
        )
)

如果您不使用 Model::afterFind() 回调,您的结果将看起来更像这样(登录数组被剪断以节省空间):

Array
(
    [User] => Array
        (
            [id] => 1
            [name] => user1
        )

    [0] => Array
        (
            [last_login] => 2011-05-01 14:00:00
        )
)


示例代码:

用户表:

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
)

登录表:

CREATE TABLE `logins` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `created` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
)

用户模型:

class User extends AppModel {
    var $name = 'User';
    var $hasMany = array('Login');
    var $hasOne = array(
        'LastLogin' => array(
            'className' => 'Login',
            'fields' => array('MAX(LastLogin.created) as last_login')
        )
    );

    // This takes the last_login field from the [0] keyed array and puts it into 
    // [User]. You could also put this into your AppModel and it would work for 
    // all find operations where you use an SQL function in the 'fields' key.
    function afterFind($results, $primary=false) {
        if (!empty($results)) {
            foreach ($results as $i => $result) {
                if (!empty($result[0])) { // If the [0] key exists in a result...
                    foreach ($result[0] as $key => $value) { // ...cycle through all its fields...
                        $results[$i][$this->alias][$key] = $value; // ...move them to the main result...
                    }
                    unset($results[$i][0]); // ...and finally remove the [0] array
                }
            }
        }
        return parent::afterFind($results, $primary=false); // Don't forget to call the parent::afterFind()
    }
}

用户控制器:

class UsersController extends AppController {
    var $name = 'Users';

    function index() {
        $this->autoRender = false;
        pr($this->User->find('all'));
    }
}

【讨论】:

  • 感谢您的回答。我试过这个,但是当我说一个用户有 4 次登录时,我得到 4 条用户记录,该用户每次登录都有一条。添加限制并不能解决问题。
  • 奇怪,我刚刚对此进行了快速测试,它对我有用。我将使用我正在使用的代码更新我的答案,以便您查看。问题可能来自您应用中的其他代码。
  • 我已经用一些工作代码更新了我的答案。希望它可以帮助您找出问题所在。
  • 再次感谢您提供帮助并发布您的代码!在视图模式下,它就像一个魅力。但是,当我调用 index 函数(使用 find('all') 或在 Scaffold 模式下)时,我仍然会在每次登录时获得一条记录。
  • 我明白你的意思。我认为没有任何方法可以使用 hasOne 关系绕过重复的结果。在这种情况下,您可能希望将 LastLogin 添加到您的 hasMany 数组中,并将其“className”键设置为“Login”,将“order”键设置为“created desc”,将“limit”键设置为“1”。您应该能够通过这种方式消除重复项。
【解决方案2】:

我也遇到过类似的情况

Product hasMany Price

我想要一个Product hasOne CurrentPrice,其中CurrentPrice 定义为如果按created 按降序排序找到的最高记录。

我这样解决了我的问题。但我将改用您的 User 和 LastLogin。

class User extends AppModel {
    var $name = 'User';
    var $hasMany = array('Login');
    var $hasOne = array(
        'LastLogin' => array(
            'className' => 'Login',
            'order' => 'LastLogin.created DESC'
       )
   );

仔细想想,created 和 id 的意思是一样的。所以你也可以按降序使用 id。

我假设您的表架构类似于mtnorthrop 建议的one

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
)
logins table:

CREATE TABLE `logins` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `created` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-27
    • 2023-03-22
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 2015-10-20
    • 1970-01-01
    • 2020-08-04
    相关资源
    最近更新 更多