Cakephp 选择hasMany关系的最新记录
我的CakePHP应用程序中有两个基本模型:用户和登录。用户与登录有很多关系(即,每次用户登录时都会创建一个新的登录记录) 现在,我想建立两个从用户模型到登录模型的关系: 用户有多个登录名 和 用户有一个最后登录名 最后一个关系应仅包括所选用户登录模型的最后一条记录 我试着这样做:Cakephp 选择hasMany关系的最新记录,cakephp,Cakephp,我的CakePHP应用程序中有两个基本模型:用户和登录。用户与登录有很多关系(即,每次用户登录时都会创建一个新的登录记录) 现在,我想建立两个从用户模型到登录模型的关系: 用户有多个登录名 和 用户有一个最后登录名 最后一个关系应仅包括所选用户登录模型的最后一条记录 我试着这样做: var $belongsTo = array ( 'LastLogin' => array ( 'className' => 'Login', 'order
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”。您应该能够通过这种方式消除重复项。