javascript - wordpress收藏系统无插件学习方法
问题描述
下午好开发人员,我使用 Wordpress 和 php 开发了一个最喜欢的系统,它的工作原理如下,单击添加收藏夹图标我向 php 发送一个 ajax 请求,其中包含最喜欢的 id 和登录的用户 id。php 通过保存在 user_meta 中来处理这个包含收藏用户数量的信息以及登录用户中包含收藏夹 ID 的数组。无论如何,问题是,系统正在工作,但是每当我开发一些东西时,我一直想知道是否有办法改进代码,如果有办法以更好的方式来做,我会把我的代码留在这里。
这一切都是为了学习好吗?
HTML
<div class="stats -favorites">
<a class="icon -click <?php echo is_favorite(get_current_user_id(), $user_ID) ? 'fa' : 'far' ?> fa-heart" data-js="favorite" data-favorite="<?php echo $user_ID; ?>" data-user="<?php echo get_current_user_id(); ?>"></a>
<span class="label" data-js="favorite_label">
<?php echo get_favorites_num( $user_ID ); ?>
</span>
<span class="value">favoritos</span>
</div>
JS
function setFavorite(userid, favoriteid) {
var favorite_field = $('[data-js="favorite"]');
var favorite_label = $('[data-js="favorite_label"]');
$.ajax({
url : appmeninas_ajax_params.ajaxurl,
data : {
'action' : 'setfavorite',
'userid' : userid,
'favoriteid' : favoriteid,
},
dataType : 'json',
type : 'POST',
cache : false,
beforeSend : function( xhr ) {
favorite_field.removeClass('far fa fa-heart').addClass('fas fa-circle-notch fa-spin');
},
success : function( data ) {
var icon = (data.is_favorite) ? 'fa fa-heart' : 'far fa-heart';
favorite_field.removeClass('fas fa-circle-notch fa-spin');
favorite_field.addClass(icon);
favorite_label.html('');
favorite_label.append(data.favorites_num);
}
});
};
$('[data-js=favorite]').click(function() {
var favoriteid = $(this).data('favorite');
var userid = $(this).data('user');
setFavorite(userid, favoriteid);
});
PHP
function setfavorite() {
$userid = $_POST['userid'];
$favoriteid = $_POST['favoriteid'];
// user require favorite
$favorites_list = get_field( 'favorites_list', 'user_' .$userid );
$favorites_num = get_field( 'favorites', 'user_' .$favoriteid );
if ( !$favorites_list ) {
$favorites_list = [];
}
// profile favorite
if ( in_array( $favoriteid, $favorites_list ) ) {
$favorites_num--;
$tmp = array_search( $userid, $favorites_list );
array_splice( $favorites_list, $tmp, 1 );
$is_favorite = false;
} else {
$favorites_num++;
$favorites_list[] = $favoriteid;
$is_favorite = true;
}
// set favorite counter
update_user_meta( $favoriteid, 'favorites', $favorites_num );
// set favorite list
update_user_meta( $userid, 'favorites_list', $favorites_list );
echo json_encode( array(
'favorites_num' => $favorites_num,
'favorites_list' => $favorites_list,
'is_favorite' => $is_favorite,
) );
die();
}
function is_favorite($userid, $favoriteid) {
$favorites_list = get_field( 'favorites_list', 'user_' .$userid );
return in_array( $favoriteid, $favorites_list );
}
function get_favorites_num( $userid ) {
if ( get_field( 'favorites', 'user_' .$userid ) ) {
return get_field( 'favorites', 'user_' .$userid );
} else {
return '0';
}
}
add_action('wp_ajax_setfavorite', 'setfavorite');
add_action('wp_ajax_nopriv_setfavorite', 'setfavorite');
解决方案
我可能会在接下来的 2 个月内实现相同的功能。我已经开始研究插件,但正如你的问题所述,它似乎并不难。
我试着回来,但首先我需要一个前端个人资料页面,我可以在其中列出收藏夹。
到目前为止,第一印象看起来不错,但首先我会注意$_POST
数组并清理并可能验证值,因为有时不仅您的 ajax 会调用。
if ( isset($_POST['userid']) && isset($_POST['favoriteid']) ) { // Both $_POST values exist
$userid = filter_var($_POST['userid'], FILTER_SANITIZE_NUMBER_INT);
$favoriteid = filter_var($_POST['favoriteid'], FILTER_SANITIZE_NUMBER_INT);
if ( filter_var($userid, FILTER_VALIDATE_INT) && filter_var($favoriteid, FILTER_VALIDATE_INT) ) {
// $_POST was save, both values are Integers
}
}
第二个与get_field
哪个是ACF Plugin提供的功能有关。因此,在停用它或将其替换为 JCF 时,可能会导致错误。
您可以通过使用来避免这种情况if ( function_exists('get_field') ) {
。然后,您的代码仅在 ACF 被停用时才停止工作。
否则似乎没有必要使用 ACF 函数,您可以使用 WP 原生函数get_user_meta代替:
function is_favorite($userid, $favoriteid){
$favorites_list = get_user_meta($userid, 'favorites_list', true);
// check the new output instead of get_field
return in_array( $favoriteid, $favorites_list );
}
然后所有的呼吁get_field('favorites', 'user_' .$favoriteid)
似乎都是错误的。ACF Docs 说get_field
ist 的第二个参数是一个帖子 ID,所以我不知道“user_”是什么意思。我会打电话给:
function get_favorites_num($favoriteid){
return get_user_meta($favoriteid, 'favorites', true) || 0;
}
我现在准备好了我自己最喜欢的系统,用户可以在其中收藏来自特定 post_type 的帖子
HTML
<?php while ( have_posts() ) : the_post() ?>
<?php
$customPostsMeta = get_post_custom();
$favorite_class = 'favorite-me disabled';
$fav_count = isset($customPostsMeta['_favorites']) ? intval($customPostsMeta['_favorites'][0]) : 0;
$fav_count_text = $fav_count > 0 ? '(' . $fav_count . ')' : '';
$fav_count = ' <span class="fav-count clearfix">' . $fav_count_text . '</span>';
$favorite_title = '';
if ( is_user_logged_in() ) {
$user = wp_get_current_user();
$favorites = get_user_meta($user->ID, '_favorite_posts', true);
$fav_key = array_search($post_id, $favorites);
$is_favorite = ( $fav_key !== false );
if ( $is_favorite ) {
$favorite_class .= ' is-favorite';
$favorite_title = ' title="' . get_the_title() . ' ' . __('favorisieren', 'myTheme') . '"';
} else {
$favorite_title = ' title="' . get_the_title() . ' ' . __('nicht mehr favorisieren', 'myTheme') . '"';
}
}
?>
<a class="<?php echo $favorite_class; ?>" href="#post-<?php the_ID() ?>"<?php echo $favorite_title; ?>><?php echo __('Favorit', 'myTheme')?><?php echo $fav_count; ?></a>
<?php endwhile; ?>
JS
// i use a small self written JS module frame where this is included as module
// favorite.setup() is fired imediatly, favorite.ready() fires on document ready
// you can see a full version here: https://dev.alphabetisierung.at/wp-content/themes/sandbox_2017/js/actions.js
// line 732
/**
* Favorites ajax system
* =====================
* https://stackoverflow.com/questions/60468237
*/
favorite: {
options: {
selectors: {
link: '.favorite-me',
fav_count: '.fav-count'
},
classNames: {
disabled: 'disabled',
is_favorite: 'is-favorite',
}
},
events: function(){
var options = this.options,
selectors = options.selectors,
classNames = options.classNames,
info = this.info;
this.$favorites.on('click', function(event){
var post_id = this.hash.replace('#post-', ''),
$favorite_link = $(this).addClass(classNames.disabled),
$fav_count = $favorite_link.children(selectors.fav_count),
$favorite = $.ajax({
url: myTheme.page.urls.ajax,
type: 'post',
data: {
action: info.name, // derived from the module name "favorite"
verify: myTheme.page.verify, // https://developer.wordpress.org/reference/functions/check_ajax_referer/
post_id: post_id
// user_id of user who takes the action is not necessary
}
});
$favorite.done(function(data){
var fav_count = data.hasOwnProperty('fav_count') ? parseInt(data.fav_count) : 0,
fav_count_text = '',
is_favorite = data.hasOwnProperty('is_favorite') ? data.is_favorite : false;
if ( fav_count > 0 ) {
fav_count_text = '(' + fav_count + ')';
}
$fav_count.html(fav_count_text);
if ( is_favorite && !$favorite_link.is('.' + classNames.is_favorite) ) {
$favorite_link.addClass(classNames.is_favorite);
} else {
$favorite_link.removeClass(classNames.is_favorite);
}
$favorite_link.removeClass(classNames.disabled);
});
event.preventDefault();
});
},
ready: function ready(){
var selectors = this.options.selectors,
classNames = this.options.classNames;
this.$favorites = $(selectors.link).removeClass(classNames.disabled);
this.events();
},
setup: function setup(){
var setup = myTheme.info.is_user_logged_in && myTheme.info.post_type === 'my_custom_post_type';
return setup; // only for my post_type
}
PHP
add_action('wp_enqueue_scripts', 'myTheme_enqueue_scripts');
add_action('wp_ajax_favorite', 'myTheme_set_favorite');
// wp_ajax_nopriv_{action} is not necessary when feature is only for logged in users
function myTheme_enqueue_scripts(){
$data = array(
'id' => get_the_ID(),
'urls' => array(
'ajax' => admin_url('admin-ajax.php'),
'template' => get_stylesheet_directory_uri(),
),
'verify' => wp_create_nonce('myThemeOrAction_ajax_call'), // used for check_ajax_referer()
// ...
'info' => array(
// ...
'is_user_logged_in' => is_user_logged_in(),
'post_type' => get_post_type(),
),
);
// ...
wp_localize_script('actions', 'myTheme_data', $data );
}
function myTheme_set_favorite(){
check_ajax_referer('myThemeOrAction_ajax_call', 'verify');
if ( isset($_POST['post_id']) ) {
$user = wp_get_current_user(); // here we get the user ID of the current user
$post_id = filter_var($_POST['post_id'], FILTER_SANITIZE_NUMBER_INT);
$post = get_post($post_id);
// $fav_id = filter_var($_POST['fav_id'], FILTER_SANITIZE_NUMBER_INT);
// $fav_user = get_userdata($fav_id); // WP_User
$is_favorite = false;
if ( $post instanceof WP_Post ) { // post ID is valid
// for user favorites it would be
// if ( $fav_user instanceof WP_User ) {
$fav_count = intval(get_post_meta($post->ID, '_favorites', true));
$favorites = get_user_meta($user->ID, '_favorite_posts', true);
if ( !filter_var($fav_count, FILTER_VALIDATE_INT) ) {
$fav_count = 0;
}
if ( !is_array($favorites) || empty($favorites) ) {
$favorites = array();
}
$fav_key = array_search($post->ID, $favorites);
if ( $fav_key !== false ) { // is favorite, remove it
$fav_count--;
unset($favorites[$fav_key]);
} else { // is no favorite, add it
$fav_count++;
$favorites[] = $post->ID;
$is_favorite = true;
}
// set favorite counter
update_post_meta($post->ID, '_favorites', $fav_count);
// set favorite list
update_user_meta($user->ID, '_favorite_posts', $favorites);
// Output
$json = array(
'fav_count' => $fav_count,
'favorites' => $favorites,
'error' => false,
'is_favorite' => $is_favorite,
);
} else {
$json = array('is_favorite' => $is_favorite, 'error' => true, 'message' => 'Invalid Post ID');
}
} else {
$json = array('is_favorite' => $is_favorite, 'error' => true, 'message' => 'No post_id Post ID sent');
}
// wp_send_json sets the http header and ends the request
wp_send_json($json);
}
这是我在路上注意到的事情:
wp_create_nonce()
使用和验证推荐人check_ajax_referer()
- 检查 JSON 结果数据键是否存在
data.hasOwnProperty('key')
以避免可能的 JS 错误 - 检查有效
WP_User
或WP_Post
反对 - 当前用户 ID 不需要通过 POST 发送
wp_ajax_nopriv_{action}
当功能仅适用于登录用户时,不需要- 用于
wp_send_json()
结束响应
亲切的问候汤姆
推荐阅读
- python - 无法在 Python 中使用 __repr__ 打印对象 repr
- typescript - 使用 Typescript 在运行时动态增长对象类型
- google-apps-script - 如何在保持文本样式的同时替换 Google doc 中的单词?
- python-3.x - 如何将同一破折号(情节)应用程序布局的部分调用到网站的各个部分
- python - 当管道 linux 命令时,如何避免管道 python 打印
- .net - 澄清 - 我可以将带有 odp.net 12c 的 .net 应用程序连接到 Oracle 19c 数据库吗?
- javascript - 如何将 mp3 文件从 Java 后端发送到前端并在 Vue.js 前端播放文件
- php - 在结帐重定向到错误的 URL 之前检查并重定向以登录
- python - 如何使用 Tkinter 使用下拉菜单打开文件夹中的文件
- ios - 安装新的 iOS 应用后接收相同的 FCM 令牌