如何在CodeIgniter中实现国家-州-城市联动下拉菜单?自动加载国家列表
嘿,这个需求我之前在几个CodeIgniter项目里都落地过,分享一套成熟的方案给你,完美适配自动加载国家列表、动态联动州和城市的要求~
核心思路
整体采用「后端API提供数据 + 前端AJAX动态渲染」的模式:
- 预先在数据库中存入完整的国家、州、城市数据集(不用手动录入,网上有很多公开的免费数据集可以导入)
- CodeIgniter后端写三个接口,分别返回所有国家、指定国家下的州、指定州下的城市
- 前端监听下拉框的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
相关产品推荐
相关产品推荐

