Cakephp 选择hasMany关系的最新记录

Cakephp 选择hasMany关系的最新记录,cakephp,Cakephp,我的CakePHP应用程序中有两个基本模型:用户和登录。用户与登录有很多关系(即,每次用户登录时都会创建一个新的登录记录) 现在,我想建立两个从用户模型到登录模型的关系: 用户有多个登录名 和 用户有一个最后登录名 最后一个关系应仅包括所选用户登录模型的最后一条记录 我试着这样做: var $belongsTo = array ( 'LastLogin' => array ( 'className' => 'Login', 'order

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

现在,我想建立两个从用户模型到登录模型的关系: 用户有多个登录名 和 用户有一个最后登录名

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

我试着这样做:

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

    )
); 

然而,这不起作用。关于如何使这项工作正常进行,有什么想法吗?

更新了对评论的回复

使用
belongsTo
关系时,外键应位于当前模型中

这意味着,如果您想建立用户属于上次登录的关系,
users
表应该有一个
last\u login\u id
字段

在您的情况下,您可能希望使用hasOne关系,并且必须在
字段中使用
MAX()
SQL函数。请注意,获取最后一次登录完全独立于您的用户有许多登录关系。因此,如果您只需要最后一次登录,您可以删除hasMany关系,只需离开hasOne即可

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

输出/用户/索引:

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
                )
        )
)
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'));
    }
}
如果您不使用Model::afterFind()回调,您的结果将更像这样(为了节省空间而截断登录数组):


示例代码:

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
                )
        )
)
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'));
    }
}
用户表:

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
                )
        )
)
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'));
    }
}
登录表:

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
                )
        )
)
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'));
    }
}
用户型号:

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
                )
        )
)
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'));
    }
}
用户控制器:

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
                )
        )
)
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'));
    }
}

我也有过类似的情况,但我的情况很糟糕

Product hasMany Price
我想要一个
产品有一个CurrentPrice
,其中
CurrentPrice
被定义为按描述顺序创建的
排序的最顶层记录

我这样解决了我的问题。但我将使用您的用户和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

我假设您的表模式与建议的类似

i、 e


什么不起作用?意外数据?可能应该是
'order'=>'Login.created DESC'
-当您指的是
Login
表时?我认为您做得太多了。当你可以对登录模型进行简单的查询以获得一条记录时,为什么你需要最后一次登录?@Ross:正确,我每个登录都获得一条记录,而我每个用户都需要一条记录。另外,我应该参考LastLogin而不是Login,因为这是表别名。@Anh Pham:我可以这样做,但我宁愿只做一次查询,从那个人那里获得我需要的所有信息。当显示用户列表时,我只对该用户的最新登录名感兴趣。谢谢您的回答。我尝试过这个,但是当我说,一个用户登录4次时,我得到4个用户记录,每个用户登录一次。增加一个限制并不能解决问题。奇怪的是,我刚刚用这个做了一个快速测试,它对我有效。我将用我正在使用的代码更新我的答案,以便您可以看到。问题可能来自你应用程序中的其他代码。我已使用一些工作代码更新了我的答案。希望它能帮助您找出问题所在。再次感谢您的帮助,并发布您的代码!在视图模式下,它就像一个符咒。但是,当我调用index函数(使用find('all')或Scaffold模式)时,每次登录都会得到一条记录。我明白你的意思了。我不认为有任何方法可以通过hasOne关系绕过重复的结果。在这种情况下,您可能希望将LastLogin添加到hasMany数组中,并将其“className”键设置为“Login”,将其“order”键设置为“created desc”,将其“limit”键设置为“1”。您应该能够通过这种方式消除重复项。