Hiện nay, các doanh nghiệp mở rộng nhiều chi nhánh, nhiều cửa hàng là điều không hề xa lạ. Ví dụ như Thế Giới Di Động, họ có tới hàng nghìn cửa hàng trải khắp từ Bắc tới Nam, ở mọi tỉnh thành, quận huyện đều có. Và khi khách hàng truy cập vào website. Để tìm kiếm vị trí các cửa hàng một cách thuận tiện, thì việc xây dựng “Hệ thống chi nhánh Google Maps” là rất cần thiết, giúp tăng trải nghiệm khách hàng.
Trong phạm vi bài viết này, tôi sẽ hướng dẫn các bạn cách để tự làm trang hệ thống cửa hàng, chi nhánh đơn giản nhất mà không cần bỏ chi phí mua plugin. Mà vẫn đem lại sự hiệu quả.
Bài viết khác
Phần 1: Hướng dẫn tạo hệ thống chi nhánh với Google Maps
Bước 01: Tạo shortcode
Chúng ta sẽ tạo shortcode và code vào trong đó để tiện linh hoạt khi đặt vào bất cứ đâu. Trong code này sẽ viết các mã HTML, CSS, JAVASCRIPT
<?php add_shortcode('vdh_map_local', function(){ ob_start(); ?> // Code tại đây <?php wp_reset_postdata(); $content = ob_get_contents(); ob_end_clean(); return $content; }); ?>
Bước 02: Tạo cấu trúc HTML với 2 cột
Đặt code vào trong shortcode trên, bên trái sẽ là danh sách các địa điểm, bên phải sẽ là bản đồ
<!-- HTML --> <div class="pk_row"> <div class="pk_column pk-column-data"> </div> <div class="pk_column pk-column-map"> </div> </div> <!-- CSS--> <style> .pk_row { height: 600px; } .pk_column { float: left; width: 50%; padding: 10px; height:100%; background:white; border: 5px solid #ccc; } .pk_row:after { content: ""; display: table; clear: both; } </style>
Bước 03: Tạo danh sách dữ liệu trong cột bên trái (pk-column-data)
Tôi sẽ demo bằng dữ liệu danh sách cửa hàng Mediamart Hà Nội. Các bạn lưu ý đặt dữ liệu theo cấu trúc mẫu phía dưới, các thẻ <li> có thuộc tính data-map
chính là src mã google maps embed chúng ta lấy ở trên https://google.com/maps
<!-- HTML --> <ul id="pk_list_map"> <li data-map="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d5264.906561212957!2d105.81119061764379!3d21.078607217417975!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x3135ab633709e735:0xbce4be42931e79d7!2zTWVkaWFtYXJ0IEzhuqFjIExvbmcgUXXDom4!5e0!3m2!1svi!2sus!4v1722133884574!5m2!1svi!2sus"> 672 Lạc Long Quân, Nhật Tân, Tây Hồ, Hà Nội <br> ☏ <i><small>0986209305</small></i> </li> <li data-map="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3130.8015597413832!2d105.80639835563994!3d21.065811820350714!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x3135aae689795b9b:0x39e9258e56203cb9!2zU2nDqnUgdGjhu4sgxJBp4buHbiBtw6F5IE1lZGlhIE1hcnQ!5e0!3m2!1svi!2sus!4v1722133932522!5m2!1svi!2sus"> 583 Lạc Long Quân, Xuân La, Tây Hồ, Hà Nội <br> ☏ <i><small>0986209305</small></i> </li> <li data-map="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2214.094906617745!2d105.80137716234678!3d21.046728308119164!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x3135ab8c9f51eeef:0x7c09768e45ee74cb!2sMedia Mart!5e0!3m2!1svi!2sus!4v1722133970512!5m2!1svi!2sus"> 16 Hoàng Quốc Việt, Nghĩa Đô, Cầu Giấy, Hà Nội <br> ☏ <i><small>0986209305</small></i> </li> <li data-map="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3833.2027027936233!2d105.76428382213098!3d21.029529258588838!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x313454baf43641c3:0x75277794a7c0b824!2zU2nDqnUgdGjhu4sgxJBp4buHbiBNw6F5IE1lZGlhIE1hcnQ!5e0!3m2!1svi!2sus!4v1722134009397!5m2!1svi!2sus"> 16 Lê Đức Thọ, Nam Từ Liêm, Hà Nội <br> ☏ <i><small>0986209305</small></i> </li> <li data-map="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3415.3947807557834!2d105.79690308937232!3d21.012093216795318!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x3135ab9708dddbc1:0xc72f7e9f994503e2!2zTWVkaWFtYXJ0IFRy4bqnbiBEdXkgSMawbmc!5e0!3m2!1svi!2sus!4v1722137271657!5m2!1svi!2sus"> 40 Trần Duy Hưng, Cầu Giấy, Hà Nội <br> ☏ <i><small>0986209305</small></i> </li> <li data-map="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3415.326610172044!2d105.80953837029361!3d21.015070348300892!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x3135abdc18b626a9:0x9d51f4ff9c19e8ad!2zTWVkaWFtYXJ0IEzDoW5nIEjhuqE!5e0!3m2!1svi!2sus!4v1722137304395!5m2!1svi!2sus"> 26 Láng Hạ, Đống Đa, Hà Nội <br> ☏ <i><small>0986209305</small></i> </li> <li data-map="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3131.4436900706596!2d105.81549671021469!3d21.035282000583322!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x3135abce85df50c1:0x5f86be6355b1e2c5!2zU2nDqnUgVGjhu4sgxJBp4buHbiBNw6F5IE1lZGlhTWFydCDEkOG7mWkgQ-G6pW4!5e0!3m2!1svi!2s!4v1722137779617!5m2!1svi!2s"> 218 Đội Cấn, Ba Đình, Hà Nội <br> ☏ <i><small>0986209305</small></i> </li> </ul> <!-- CSS --> <style> .pk-column-data ul li { margin-bottom:5px; margin-left:0; padding: 10px; padding-left:15px; background: #efefefc7; list-style-type:none; } .pk-column-data ul li:hover { cursor: pointer; background:#326e5130; } .pk-column-data ul li.pk-active { background:#326e51; color:white; } </style>
Bước 04: Chèn iframe google maps vào cột bên phải (pk-column-map)
Chúng ta sẽ lấy full cấu trúc embed mà trên google maps đã chia sẻ. Lưu ý thay đổi thuộc tính width, height cho phù hợp với khung đã tạo. Ở đây tôi đặt width=”100%” và height=”570″
<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d5264.906561212957!2d105.81119061764379!3d21.078607217417975!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x3135ab633709e735:0xbce4be42931e79d7!2zTWVkaWFtYXJ0IEzhuqFjIExvbmcgUXXDom4!5e0!3m2!1svi!2sus!4v1722133884574!5m2!1svi!2sus" width="100%" height="570" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
Link iframe sẽ là địa chỉ mặc định đầu tiên ở danh sách trên. Lúc này giao diện hiển thị ra đã hoàn chỉnh, nhưng khi click vào địa chỉ thì chưa hoạt động, không có thay đổi gì. Chúng ta cần code thêm javascript.
Bước 04: Viết code Javascript để thực hiện thao tác
<script> const list_map = document.querySelectorAll('#pk_list_map li') list_map.forEach(item => { item.addEventListener('click', function(){ var map = this.getAttribute('data-map') document.querySelector('.pk-column-map iframe').src = map list_map.forEach(e => e.classList.remove("pk-active")); this.classList.add("pk-active"); }) }); </script>
Bước 05: Hoàn thành cấu trúc và chức năng
Vậy là đến bước này, đã có thể sử dụng tính năng một cách hoàn chỉnh về mặt cấu trúc + chức năng. Để thay đổi danh sách thì bạn quay lại bước 03 để cập nhật. Các bạn hãy xem video hướng dẫn để có thể hiểu hơn về tính năng này.
Bài viết này sẽ còn update thêm tính năng lấy dữ liệu từ trong admin, database để thuận tiện cho việc chỉnh sửa mà không cần phải sửa trong code.
Phần 2: Quản lý dữ liệu chi nhánh trong Admin & phân theo tỉnh (thành), quận (huyện)
Ở phần 1 các bạn đã có thể tạo được 1 trang tĩnh cơ bản đủ chức năng mong muốn, tại phần 2 này, tôi sẽ hướng dẫn tạo phần quản lý chi nhánh trong Admin và nâng cấp bộ lọc địa chỉ. Các bạn cùng theo dõi.
Bước 01: Tạo Custom Post Type & Taxonomy
<?php add_action('init', function(){ register_post_type('location-map', array( 'public' => true, 'has_archive' => true, 'menu_icon' => 'dashicons-store', 'supports' => array('title', 'thumbnail'), 'labels' => array( 'name' => 'Chi nhánh', 'singular_name' => 'Chi nhánh', 'add_new_item' => 'Thêm mới chi nhánh', 'add_new' => 'Thêm mới', 'edit_item' => 'Sửa chi nhánh', 'featured_image' => 'Ảnh đại diện', 'set_featured_image' => 'Chọn ảnh', 'remove_featured_image' => 'Xóa ảnh', 'menu_name' => 'Chi nhánh' ), ) ); register_taxonomy('province', 'location-map', [ 'label' => 'Tỉnh thành', 'hierarchical' => true, 'show_admin_column' => true, 'show_in_rest' => true ]); }); ?>
Bước 02: Tạo thêm trường dữ liệu với add_meta_box
<?php add_action('add_meta_boxes', function(){ add_meta_box('chi-nhanh', 'Thông tin chi nhánh', function($post){ $location_address = get_post_meta( $post->ID, 'location_address', true ); $location_phone = get_post_meta( $post->ID, 'location_phone', true ); $location_link = get_post_meta( $post->ID, 'location_link', true ); ?> <div class="input_custom"> <label for="location_address">Địa chỉ: </label> <input type="text" id="location_address" name="location_address" placeholder="Địa chỉ" style="width:500px" value="<?php echo !empty($location_address) ? esc_attr($location_address) : null ?>" /> </div> <div class="input_custom"> <label for="location_link">Link iframe: </label> <input type="text" id="location_link" name="location_link" placeholder="Link iframe" style="width:500px" value="<?php echo !empty($location_link) ? esc_attr($location_link) : null ?>" /> </div> <div class="input_custom"> <label for="location_phone">Số điện thoại: </label> <input type="text" id="location_phone" name="location_phone" placeholder="Số điện thoại" style="width:500px" value="<?php echo !empty($location_phone) ? esc_attr($location_phone) : null ?>" /> </div> <style> .input_custom { margin-bottom: 10px; } .input_custom label { display:inline-block; width: 100px; } </style> <?php }, 'location-map'); }); ?>
Lưu dữ liệu từ custom meta box ở trên vào database
<?php add_action('save_post', function($post_id){ if(!empty($_POST['location_address'])) { $location_address = sanitize_text_field($_POST['location_address']); update_post_meta($post_id, 'location_address', $location_address); } if(!empty($_POST['location_link'])) { $location_link = sanitize_text_field($_POST['location_link']); update_post_meta($post_id, 'location_link', $location_link); } if(!empty($_POST['location_phone'])) { $location_phone = sanitize_text_field($_POST['location_phone']); update_post_meta($post_id, 'location_phone', $location_phone); } }); ?>
Tạo danh sách chi nhánh mẫu
Bước 03: Get dữ liệu Tỉnh thành, Quận huyện + Chi nhánh
Ở giao diện bên ngoài, sẽ cần hiển thị danh sách các dữ liệu ở trên ra bên ngoài. Ở shortcode trong Bước 1 (Phần 1), đặt code sau để lấy dữ liệu cần thiết
/* Lấy tỉnh thành quận huyện */ $province = get_terms([ 'taxonomy' => 'province', 'hide_empty' => false, ]); /* Lấy danh sách chi nhánh */ $locations = get_posts([ 'post_type' => 'location-map', 'post_status' => 'publish', 'posts_per_page' => -1 ]);
Tạo cấu trúc Tỉnh thành – Quận huyện bằng Select Option và hiển thị dữ liệu đã lấy ở trên
<div class="pk_row"> <div class="pk_column pk-column-data"> <div class="province location"> <label for="">Tỉnh thành</label> <select id="province"> <option value="">Lựa chọn</option> <?php foreach($province as $item): if($item->parent == 0): ?> <option value="<?php echo $item->term_id ?? null ?>"><?php echo $item->name ?? null ?></option> <?php endif; endforeach; ?> </select> </div> <div class="district location"> <label for="">Quận/Huyện</label> <select id="district" disabled=""> <option value="">Lựa chọn</option> <?php foreach($province as $item): if($item->parent > 0): ?> <option value="<?php echo $item->term_id ?? null ?>" data-parent-id="<?php echo $item->parent ?? null ?>"><?php echo $item->name ?? null ?></option> <?php endif; endforeach; ?> </select> </div> <div class="clearfix"></div> <hr> <ul id="pk_list_map"> <?php foreach($locations as $location): $location_address = get_post_meta( $location->ID, 'location_address', true ); $location_phone = get_post_meta( $location->ID, 'location_phone', true ); $location_link = get_post_meta( $location->ID, 'location_link', true ); $terms = wp_get_post_terms($location->ID, 'province'); $ids = array_column($terms, 'term_id'); ?> <li data-term="<?php echo json_encode($ids) ?? null ?>" data-map="<?php echo $location_link ?? null ?>"> <p class="location_title"><?php echo $location->post_title ?? null ?></p> <p class="location_address"><?php echo $location_address ?? null ?></p> ☏ <i><small><?php echo $location_phone ?? null ?></small></i> </li> <?php endforeach; ?> </ul> </div> <div class="pk_column pk-column-map"> <iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d1861.8583177565963!2d105.81213348044113!3d21.0440212023018!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x3135ab105387c82f:0x743e0ea8682ba96c!2zVGjhur8gR2nhu5tpIERpIMSQ4buZbmc!5e0!3m2!1svi!2s!4v1722329069267!5m2!1svi!2s" width="100%" height="570" style="border:0;" allowfullscreen=""></iframe> </div> </div> <style> .pk-column-data ul li.pk-active, .pk-column-data ul li.pk-active .location_title { background:#326e51; color:white; } .location { float: left; width: 48%; } .clearfix { clear:both; } #district option.hidden { display:none; } .province { margin-right: 20px; } .location_title, .location_address { margin-bottom:2px; } .location_title { font-weight: bold; color:#326e51; } </style>
Bước 04: Sử dụng Javascript để tạo trải nghiệm
Những bước ở trên đã có thể tạo được giao diện hoàn chỉnh + dữ liệu hoàn chỉnh, nhưng thao tác sử dụng chưa có đúng ở bộ lọc, cần áp dụng thêm code javascript sau
<script> const locations = document.querySelectorAll("#pk_list_map li"); const options = document.querySelectorAll("#district option"); const province = document.getElementById('province'); const district = document.getElementById('district'); document.getElementById('province').addEventListener('change', function() { var province_id = this.value; if(province_id == '') { district.setAttribute('disabled', '') locations.forEach(e => e.classList.remove('hidden')) } else { district.removeAttribute('disabled') getDistrict(province_id) getLocation(province_id) } district.selectedIndex = 0 }); function getDistrict(province_id) { options.forEach(function(v){ var parent_id = v.getAttribute('data-parent-id') if(v.hasAttribute('data-parent-id')) { if(parent_id != province_id) { v.classList.add("hidden") } else { v.classList.remove("hidden") } } }) } document.getElementById('district').addEventListener('change', function() { var value = this.value; getLocation(value) }); function getLocation(term_id) { var province_id = province.value; locations.forEach(function(v){ var parent_id = v.getAttribute('data-term') parent_id = JSON.parse(parent_id) if(term_id == '') { province_id = parseInt(province_id) if(parent_id.includes(province_id)) { v.classList.remove("hidden") } else { v.classList.add("hidden") } } else { var parent_id = v.getAttribute('data-term') parent_id = JSON.parse(parent_id) term_id = parseInt(term_id) if(parent_id.includes(term_id)) { v.classList.remove("hidden") } else { v.classList.add("hidden") } } }) } const list_map = document.querySelectorAll('#pk_list_map li') list_map.forEach(item => { item.addEventListener('click', function(){ var map = this.getAttribute('data-map') document.querySelector('.pk-column-map iframe').src = map list_map.forEach(e => e.classList.remove('pk-active')) this.classList.add('pk-active') }) }); </script>
Xem video hướng dẫn thực hiện
Bài viết khác