CodeIgniter 4 如何遍历Image实体对象实现图片画廊展示
问题背景
我搭建的站点支持会员执行以下操作:
- 上传图片
- 订阅其他会员
- 对图片进行点赞、点踩、收藏操作
- 查看其他会员的个人主页
画廊视图需要展示所有图片,同时附带每张图片的上传者、点赞评分(点赞数占点赞与点踩总数的百分比)、上传时间信息。我已创建Image实体类存储上述相关信息,希望能以Image对象的形式遍历画廊所有图片,确认该需求是否可实现。
现有代码
Image实体类
class Image extends Entity { protected $attributes = [ 'viewkey' => NULL, 'id' => NULL, 'uploader' => NULL, 'filename' => NULL, 'title' => NULL, 'tags' => NULL, 'createdAt' => NULL, 'modifiedAt' => NULL, 'likeCount' => NULL, 'dislikeCount' => NULL, 'viewCount' => NULL, 'favoriteCount' => NULL, 'commentCount' => NULL, 'rating' => NULL, 'userLiked' => NULL, 'userDisliked' => NULL, 'userViewed' => NULL, 'userFavorited' => NULL, 'userCommented' => NULL, 'userSubscribed' => NULL, 'action' => NULL, ]; protected $datamap = [ 'createdAt' => 'created_at', 'modifiedAt' => 'modified_at', ]; protected $dates = ['createdAt', 'updatedAt',]; protected $casts = [ 'likeCount' => 'int', 'dislikeCount' => 'int', 'viewCount' => 'int', 'favoriteCount' => 'int', 'commentCount' => 'int', 'rating' => 'float', 'userDisliked' => 'bool', 'userLiked' => 'bool', 'userViewed' => 'bool', 'userFavorited' => 'bool', ]; protected $builder; public function __construct (array $data = NULL) { parent::__construct($data); $db = \Config\Database::connect(); $this->builder = $db->table('actions'); } /** * Custom __set Methods */ public function setDislikeCount(string $viewkey) { $where = []; $where = [ 'viewkey' => $viewkey, 'action' => 0, ]; $this->attributes['dislikeCount'] = $this->builder ->where($where) ->countAllResults(); } public function setLikeCount(string $viewkey) { $where = []; $where = [ 'viewkey' => $viewkey, 'action' => 1, ]; $this->attributes['likeCount'] = $this->builder ->where($where) ->countAllResults(); } public function setViewCount(string $viewkey) { $where = []; $where = [ 'viewkey' => $viewkey, 'action' => 2, ]; $this->attributes['viewCount'] = $this->builder ->where($where) ->countAllResults(); } public function setFavoriteCount(string $viewkey) { $where = []; $where = [ 'viewkey' => $viewkey, 'action' => 3, ]; $this->attributes['favoriteCount'] = $this->builder ->where($where) ->countAllResults(); } public function setCommentCount(string $viewkey) { $this->attributes['commentCount'] = $this->builder ->where('viewkey', $viewkey) ->countAllResults(); } public function setRating(string $viewkey) { helper('arithmetic'); $whereDislike = $whereLike = []; $whereDislike = [ 'viewkey' => $viewkey, 'action' => 0, ]; $dislikes = $this->builder ->where($whereDislike) ->countAllResults(); $whereLike = [ 'viewkey' => $viewkey, 'action' => 1, ]; $likes = $this->builder ->where($whereLike) ->countAllResults(); $this->attributes['rating'] = get_percentage($likes + $dislikes, $likes, 0); } public function setUserDisliked(string $viewkey) { $where = []; $where = [ 'viewkey' => $viewkey, 'username' => session()->get('username'), 'action' => 0, ]; $userDisliked = $this->builder ->where($where) ->countAllResults(); if ($userDisliked === 1) { $this->attributes['userDisliked'] = TRUE; } else { $this->attributes['userDisliked'] = FALSE; } } public function setUserLiked(string $viewkey) { $where = []; $where = [ 'viewkey' => $viewkey, 'username' => session()->get('username'), 'action' => 1, ]; $userLiked = $this->builder ->where($where) ->countAllResults(); if ($userLiked === 1) { $this->attributes['userLiked'] = TRUE; } else { $this->attributes['userLiked'] = FALSE; } } public function setUserViewed(string $viewkey) { $where = []; $where = [ 'viewkey' => $viewkey, 'username' => session()->get('username'), 'action' => 2, ]; $userViewed = $this->builder ->where($where) ->countAllResults(); if ($userViewed === 1) { $this->attributes['userViewed'] = TRUE; } else { $this->attributes['userViewed'] = FALSE; } } public function setUserFavorited(string $viewkey) { $where = []; $where = [ 'viewkey' => $viewkey, 'username' => session()->get('username'), 'action' => 3, ]; $userFavorited = $this->builder ->where($where) ->countAllResults(); if ($userFavorited === 1) { $this->attributes['userFavorited'] = TRUE; } else { $this->attributes['userFavorited'] = FALSE; } } public function setUserCommented(string $subscriber) { $db = \Config\Database::connect(); $this->builder = $db->table('comments'); $userCommented = $this->builder ->where('commenter') ->countAllResults(); if ($userCommented === 1) { $this->attributes['userCommented'] = TRUE; } else { $this->attributes['userCommented'] = FALSE; } } public function setUserSubscribed(string $uploader) { $db = \Config\Database::connect(); $this->builder = $db->table('subscribers'); $where = []; $where = [ 'profile' => $uploader, 'subscriber' => session()->get('username'), ]; $userSubscribed = $this->builder ->where($where) ->countAllResults(); if ($userSubscribed === 1) { $this->attributes['userSubscribed'] = TRUE; } else { $this->attributes['userSubscribed'] = FALSE; } } /** * Custom __get Methods */ }
ImageModel中的fillImageEntity方法
public function fillImageEntity(string $viewkey) { $imageData = $this->builder() ->where('viewkey', $viewkey) ->get() ->getRowArray(); $image = new \App\Entities\Image(); $image->fill($imageData); $image->setDislikeCount($viewkey); $image->setLikeCount($viewkey); $image->setViewCount($viewkey); $image->setFavoriteCount($viewkey); $image->setCommentCount($viewkey); $image->setRating($viewkey); $image->setUserDisliked($viewkey); $image->setUserLiked($viewkey); $image->setUserViewed($viewkey); $image->setUserFavorited($viewkey); $image->setUserCommented($viewkey); $image->setUserSubscribed($imageData['uploader']); return $image; }
遇到的问题
尝试创建Gallery对象类存储图片,向其中填充Image对象时,遇到*“无法存储Entity对象数组”*的报错,需要确认该问题是逻辑错误还是整体实现思路有误。
解答
需求可实现,报错和整体思路无关
以Image对象形式遍历画廊图片的需求完全可以实现,你遇到的存储Entity数组报错,本质是自定义Gallery类没有正确声明接收数组类型的属性,CodeIgniter4的Entity本身不限制实例被存入数组或其他对象属性,不需要特殊适配。
不需要额外封装Gallery实体类,直接在Model层写批量查询方法,复用现有fillImageEntity逻辑即可:
// 在ImageModel中新增画廊查询方法 public function getGalleryList(int $perPage = 24, int $page = 1): array { // 分页拉取图片基础数据 $baseImageList = $this->builder() ->select('viewkey, uploader') ->orderBy('created_at', 'DESC') ->paginate($perPage, 'gallery', $page); $gallery = []; foreach ($baseImageList as $item) { $gallery[] = $this->fillImageEntity($item['viewkey']); } return $gallery; }
控制器调用该方法拿到返回的数组后,直接foreach循环就能拿到每一个Image实体实例,在视图层遍历渲染即可。
现有代码必须优化的问题
你当前的实现存在严重的性能缺陷,直接上线会导致画廊页加载极慢:
- 存在典型的N+1查询问题:每加载1张图片会执行12次独立SQL查询,加载24张图就会发起288次数据库请求,数据量稍大就会触发请求超时
- 优化方向:把单图循环查询的统计逻辑(点赞数、点踩数、浏览数等)改为批量聚合查询,一次SQL查出当前页所有图片的统计数据,再循环赋值给对应实体;登录用户的交互状态、订阅状态同样做批量查询,不要单图重复查
现有代码的显性bug
$dates属性配置错误:属性里写的是updatedAt,但$attributes和$datamap中定义的对应字段是modifiedAt,会导致时间字段自动格式化失效setUserCommented方法查询条件缺失:->where('commenter')没有传入匹配值,查询结果永远不正确- 实体层重复初始化数据库连接、切换数据表,不符合分层设计原则,数据库查询逻辑应该全部放在Model层处理,Entity只负责数据存取和转换,不要直接操作数据库连接
内容的提问来源于stack exchange,提问作者Programming_Squirrel
相关产品推荐
相关产品推荐

