You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 05:14:54