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

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

  1. $dates属性配置错误:属性里写的是updatedAt,但$attributes和$datamap中定义的对应字段是modifiedAt,会导致时间字段自动格式化失效
  2. setUserCommented方法查询条件缺失:->where('commenter')没有传入匹配值,查询结果永远不正确
  3. 实体层重复初始化数据库连接、切换数据表,不符合分层设计原则,数据库查询逻辑应该全部放在Model层处理,Entity只负责数据存取和转换,不要直接操作数据库连接

内容的提问来源于stack exchange,提问作者Programming_Squirrel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 20:42:17