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

如何在CodeIgniter中实现国家-州-城市联动下拉菜单?自动加载国家列表

嘿,这个需求我之前在几个CodeIgniter项目里都落地过,分享一套成熟的方案给你,完美适配自动加载国家列表、动态联动州和城市的要求~

核心思路

整体采用「后端API提供数据 + 前端AJAX动态渲染」的模式:

  1. 预先在数据库中存入完整的国家、州、城市数据集(不用手动录入,网上有很多公开的免费数据集可以导入)
  2. CodeIgniter后端写三个接口,分别返回所有国家、指定国家下的州、指定州下的城市
  3. 前端监听下拉框的change事件,通过AJAX拉取对应数据并填充下一级下拉框
第一步:数据库表设计

先建三张关联表,用外键保证数据关联性:

-- 国家表
CREATE TABLE `countries` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `iso_code` varchar(2) DEFAULT NULL, -- 可选,比如US、CN
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 州/省表
CREATE TABLE `states` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `country_id` int(11) NOT NULL,
  `name` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  FOREIGN KEY (`country_id`) REFERENCES `countries`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 城市表
CREATE TABLE `cities` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `state_id` int(11) NOT NULL,
  `name` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  FOREIGN KEY (`state_id`) REFERENCES `states`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

提示:可以找免费的国家/州/城市数据集(比如GeoNames)导入到这三张表,省得自己手动加数据

第二步:CodeIgniter后端实现

1. 模型(Models)

分别创建三个模型来处理数据查询:

application/models/Country_model.php

<?php
class Country_model extends CI_Model {
    public function get_all_countries() {
        return $this->db->order_by('name', 'ASC')->get('countries')->result();
    }
}
?>

application/models/State_model.php

<?php
class State_model extends CI_Model {
    public function get_states_by_country($country_id) {
        return $this->db->where('country_id', $country_id)
                        ->order_by('name', 'ASC')
                        ->get('states')
                        ->result();
    }
}
?>

application/models/City_model.php

<?php
class City_model extends CI_Model {
    public function get_cities_by_state($state_id) {
        return $this->db->where('state_id', $state_id)
                        ->order_by('name', 'ASC')
                        ->get('cities')
                        ->result();
    }
}
?>

2. 控制器(Controller)

创建一个控制器来提供API接口:

application/controllers/Location_controller.php

<?php
class Location_controller extends CI_Controller {
    public function __construct() {
        parent::__construct();
        $this->load->model('Country_model');
        $this->load->model('State_model');
        $this->load->model('City_model');
        // 允许跨域(如果前端和后端不在同一域名下,可选)
        header("Access-Control-Allow-Origin: *");
    }

    // 获取所有国家
    public function get_countries() {
        $countries = $this->Country_model->get_all_countries();
        $this->output
             ->set_content_type('application/json')
             ->set_output(json_encode($countries));
    }

    // 根据国家ID获取州
    public function get_states() {
        $country_id = $this->input->get('country_id');
        if (!$country_id) {
            $this->output->set_output(json_encode([]));
            return;
        }
        $states = $this->State_model->get_states_by_country($country_id);
        $this->output
             ->set_content_type('application/json')
             ->set_output(json_encode($states));
    }

    // 根据州ID获取城市
    public function get_cities() {
        $state_id = $this->input->get('state_id');
        if (!$state_id) {
            $this->output->set_output(json_encode([]));
            return;
        }
        $cities = $this->City_model->get_cities_by_state($state_id);
        $this->output
             ->set_content_type('application/json')
             ->set_output(json_encode($cities));
    }
}
?>
第三步:前端联动实现

在视图文件里(比如application/views/location_form.php)写HTML和JS逻辑:

<div class="form-group">
    <label>国家</label>
    <select id="country" class="form-control">
        <option value="">请选择国家</option>
    </select>
</div>

<div class="form-group">
    <label>州/省</label>
    <select id="state" class="form-control" disabled>
        <option value="">请先选择国家</option>
    </select>
</div>

<div class="form-group">
    <label>城市</label>
    <select id="city" class="form-control" disabled>
        <option value="">请先选择州/省</option>
    </select>
</div>

<!-- 引入jQuery,如果项目里已经有了就不用重复引 -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function() {
    // 页面加载时获取所有国家
    $.ajax({
        url: '<?= base_url("location_controller/get_countries") ?>',
        type: 'GET',
        dataType: 'json',
        success: function(response) {
            let options = '<option value="">请选择国家</option>';
            response.forEach(country => {
                options += `<option value="${country.id}">${country.name}</option>`;
            });
            $('#country').html(options);
        }
    });

    // 国家选择改变时加载对应州
    $('#country').change(function() {
        let countryId = $(this).val();
        $('#state').html('<option value="">加载中...</option>').prop('disabled', false);
        $('#city').html('<option value="">请先选择州/省</option>').prop('disabled', true);

        if (!countryId) {
            $('#state').html('<option value="">请先选择国家</option>').prop('disabled', true);
            return;
        }

        $.ajax({
            url: '<?= base_url("location_controller/get_states") ?>',
            type: 'GET',
            data: { country_id: countryId },
            dataType: 'json',
            success: function(response) {
                let options = '<option value="">请选择州/省</option>';
                if (response.length > 0) {
                    response.forEach(state => {
                        options += `<option value="${state.id}">${state.name}</option>`;
                    });
                } else {
                    options += '<option value="">暂无数据</option>';
                    $('#city').prop('disabled', true);
                }
                $('#state').html(options);
            }
        });
    });

    // 州选择改变时加载对应城市
    $('#state').change(function() {
        let stateId = $(this).val();
        $('#city').html('<option value="">加载中...</option>').prop('disabled', false);

        if (!stateId) {
            $('#city').html('<option value="">请先选择州/省</option>').prop('disabled', true);
            return;
        }

        $.ajax({
            url: '<?= base_url("location_controller/get_cities") ?>',
            type: 'GET',
            data: { state_id: stateId },
            dataType: 'json',
            success: function(response) {
                let options = '<option value="">请选择城市</option>';
                if (response.length > 0) {
                    response.forEach(city => {
                        options += `<option value="${city.id}">${city.name}</option>`;
                    });
                } else {
                    options += '<option value="">暂无数据</option>';
                }
                $('#city').html(options);
            }
        });
    });
});
</script>
几个优化小技巧
  • 缓存国家列表:因为国家数据几乎不会变动,可以用CodeIgniter的缓存机制缓存get_countries的结果,减少数据库查询:
    // 在Country_model的get_all_countries方法里加缓存
    public function get_all_countries() {
        $cache_key = 'all_countries';
        if ($this->cache->get($cache_key)) {
            return $this->cache->get($cache_key);
        }
        $data = $this->db->order_by('name', 'ASC')->get('countries')->result();
        $this->cache->save($cache_key, $data, 3600*24*7); // 缓存7天
        return $data;
    }
    
  • 前端loading状态:上面的代码已经加了加载中提示,避免用户误以为没反应
  • 异常处理:后端接口可以加try-catch,前端AJAX加error回调,处理请求失败的情况
  • 默认选中项:如果需要默认选中某个国家,可以在前端渲染时判断并设置selected属性

内容的提问来源于stack exchange,提问作者Muhammad Umar Bilal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:44:48