CodeIgniter 4中如何正确使用Entities避免重复添加度假村收藏
问题:CodeIgniter 4收藏功能重复插入相同记录
我很少用CodeIgniter 4的Entities,不确定当前写法对不对。现在做添加度假村到收藏表的功能,代码能正常新增记录,但重复点收藏按钮会插入除自增ID外完全一样的数据。我知道save()应该支持新增或更新,想问问怎么避免重复,是不是我的实现有问题?
我的代码
function myFavorite() { $resortID = $this->request->getVar('resortID'); $user_id = $this->current_user->id; if ($resortID && $user_id) { // Load the model $SknowedWeatherFavoritesModel = new \App\Models\SknowedWeatherFavoritesModel(); // Create a new entity $SknowedWeatherFavoritesEntity = new \App\Entities\SknowedWeatherFavoritesEntity(); $SknowedWeatherFavoritesEntity->sknowedWeather_id = $resortID; $SknowedWeatherFavoritesEntity->user_id = $user_id; // Save the entity if ($SknowedWeatherFavoritesModel->save($SknowedWeatherFavoritesEntity)) { return "Favorite added successfully."; die(); } else { return "Failed to add favorite."; die(); } } else { return "Missing resortID or user_id."; die(); } }
数据库表结构(简化后)
CREATE TABLE `sknowedWeatherFavorites` ( `sknowedWeatherFavorites_id` int NOT NULL, `sknowedWeather_id` varchar(17) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, `user_id` int UNSIGNED NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; CREATE TABLE `sknowedWeather` ( `id` varchar(17) NOT NULL, `website` varchar(64) DEFAULT NULL, `updated_at` datetime DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; CREATE TABLE `user` ( `id` int UNSIGNED NOT NULL, `name` varchar(128) NOT NULL, `updated_at` datetime DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
表关系:用户表(user)和度假村表(sknowedWeather)通过收藏表(sknowedWeatherFavorites)实现多对多关联。
解决方案
核心原因
CodeIgniter 4的save()方法判断是新增还是更新,只看实体是否携带了主键值。你的收藏表主键是自增的sknowedWeatherFavorites_id,每次新建实体时这个值是空的,所以save()永远执行新增操作,自然会产生重复数据。而收藏场景的唯一约束应该是用户ID+度假村ID的组合,所以需要从数据库和代码两层处理:
1. 给数据库加组合唯一约束(必做)
这是最可靠的底层防护,就算代码逻辑有问题,数据库也会拦截重复数据:
ALTER TABLE `sknowedWeatherFavorites` ADD UNIQUE KEY `unique_user_resort` (`user_id`, `sknowedWeather_id`);
2. 调整代码逻辑(选一种即可)
方案一:先查再存(最直观)
在保存前先检查用户是否已经收藏过该度假村,存在就提示,不存在再新增:
function myFavorite() { $resortID = $this->request->getVar('resortID'); $user_id = $this->current_user->id; if ($resortID && $user_id) { $favoritesModel = new \App\Models\SknowedWeatherFavoritesModel(); // 查询是否已有该收藏记录 $existing = $favoritesModel->where([ 'user_id' => $user_id, 'sknowedWeather_id' => $resortID ])->first(); if ($existing) { return "该度假村已经在你的收藏列表里了"; } $favoriteEntity = new \App\Entities\SknowedWeatherFavoritesEntity(); $favoriteEntity->sknowedWeather_id = $resortID; $favoriteEntity->user_id = $user_id; return $favoritesModel->save($favoriteEntity) ? "收藏成功" : "收藏失败"; } else { return "缺少度假村ID或用户ID"; } }
方案二:用replace()方法(适合有唯一约束的场景)
如果已经加了组合唯一约束,可以用replace()方法——它会自动判断:存在则更新,不存在则插入:
// 把原代码中的save替换成replace if ($favoritesModel->replace($favoriteEntity)) { return "收藏操作完成"; } else { return "操作失败"; }
方案三:通过主键触发save()的更新逻辑(不推荐此场景)
如果一定要用save()实现自动更新,需要先查到已有记录的主键ID并赋值给实体,这样save()才会识别为更新操作。但这种方式在纯收藏场景没必要,适合需要更新其他字段的情况:
$existing = $favoritesModel->where([...])->first(); if ($existing) { $favoriteEntity->sknowedWeatherFavorites_id = $existing->sknowedWeatherFavorites_id; } $favoritesModel->save($favoriteEntity);
3. 检查Model配置(确保正确)
确认你的SknowedWeatherFavoritesModel配置正确,避免因Model设置错误导致的问题:
class SknowedWeatherFavoritesModel extends Model { protected $table = 'sknowedWeatherFavorites'; protected $primaryKey = 'sknowedWeatherFavorites_id'; protected $returnType = \App\Entities\SknowedWeatherFavoritesEntity::class; protected $allowedFields = ['sknowedWeather_id', 'user_id']; }
内容的提问来源于stack exchange,提问作者spreaderman
相关产品推荐
相关产品推荐

