WordPress 插件

Advanced Custom Fields代码示例

Advanced Custom Fields插件可以帮助你快速创建和管理custom fields,当你成功存储了数据后,该如何在网站前台显示它们呢?本文列举了来自ACF官方文档的代码实例,帮助你了解如何使用ACF创建内容丰富的网页。使用模版函数需要修改模版,ACF也提供了shortcode方式,不需修改模版依然能使用其数据。

动态填充字段选项

对于select和checkbox等有选项的字段,可以通过程序动态生成选项,比如创建一个select字段,列出custom post type下的所有posts,比relationship字段的界面更适合单选。

以下示例中my_select_field是字段的名称。

add_filter ('acf/load_field/name=my_select_field', function( $field ){
    $posts = get_posts(array(
        'post_type'   => 'my-post-type',
        'post_status' => 'publish',
        'orderby'     => 'post_title',
        'order'       => 'ASC',
        'numberposts' => -1
    ));
    $field['choices'] = [];
    
    if( is_array($posts) && sizeof($posts)){
        foreach( $posts as $post ){
            $field['choices'][$post->ID] = $post->post_title;
        }
    }
    return $field;
});

获取/显示字段的值

// Get one field
$field_value = get_field( 'field_name' );

// Get  or display field of from post which has ID 10
$field_value = get_field( 'field_name', 10 );

// Display
the_field( 'field_name' );
the_field( 'field_name', 10 );

ACF里所有的字段都可以用get_field( 'field_name' )获取,不同字段获取的值差异巨大。例如,text字段返回简单的字符串,而image字段会返回一个包含该图片所有信息的数组。

ACF字段不仅可以在post / custom post type编辑页面显示,还能出现在很多其它地方,例如menu、menu item、user profile、category和tag等各种taxonomy的编辑界面。

不管出现在哪里,我们都用get_field(),但要给它传入第二个参数,告诉它我们要从哪个对象里获取字段的值。

ACF Group

$group_fields_values = get_field( 'group_name' );
/**
 * Return a array
 *  array (
	  'text_1' => 'value of group sub field',
	  'text_2' => 'value of group sub field',
	)
 */

由于group下的字段的meta_key是{group_key}_{field_key}这种格式,想直接获取group下的某个字段的值,也可以这样做:

$sub_field = get_field('group_name_text_1');

Repeater

$repeater = get_field( 'repeater' );
/**
 *  array (
	  0 => 
	  array (
	    'my_field' => 'value of a repeater sub field',
	  ),
	  1 => 
	  array (
	    'my_field' => 'value of a repeater sub field',
	  ),
	)
 */

获取User字段的值

// Format: get_field( 'field_name', user_{$user_id} );
$uservalue = get_field('text_field', 'user_1')

获取Menu的值

add_filter('wp_nav_menu_items', 'my_wp_nav_menu_items', 10, 2);

function my_wp_nav_menu_items( $items, $args ) {
	
	// get menu
	$menu = wp_get_nav_menu_object($args->menu);
	
	// modify primary only
	if( $args->theme_location == 'top' ) {
		
		// vars
		$logo = get_field('logo', $menu);
		$color = get_field('color', $menu);
		
		// prepend logo
		$html_logo = '<li class="menu-item-logo"><a href="'.home_url().'"><img src="'.$logo['url'].'" alt="'.$logo['alt'].'" /></a></li>';
		
		// append style
		$html_color = '<style type="text/css">.navigation-top{ background: '.$color.';}</style>';
		
		// append html
		$items = $html_logo . $items . $html_color;
	}
	
	// return
	return $items;
}

获取Menu Items的值

add_filter('wp_nav_menu_objects', 'my_wp_nav_menu_objects', 10, 2);

function my_wp_nav_menu_objects( $items, $args ) {
	// loop
	foreach( $items as &$item ) {
		// vars
		$icon = get_field('icon', $item);

		// append icon
		if( $icon ) {
			$item->title .= ' <i class="fa fa-'.$icon.'"></i>';
		}
	}

	return $items;
}

创建Options页面

if( function_exists('acf_add_options_page') ) {
	
	acf_add_options_page(array(
		'page_title' 	=> 'Theme General Settings',
		'menu_title'	=> 'Theme Settings',
		'menu_slug' 	=> 'theme-general-settings',
		'capability'	=> 'edit_posts',
		'redirect'	=> false
	));
        acf_add_options_page(array(
		'page_title' 	=> '网站设置',
		'menu_title'	=> '网站设置',
		'menu_slug'    => 'acf_options_custom_settings',
		'parent_slug'	=> 'options-general.php',
		'capability'	=> 'manage_options',
	));
	
	acf_add_options_sub_page(array(
		'page_title' 	=> 'Theme Header Settings',
		'menu_title'	=> 'Header',
		'parent_slug'	=> 'theme-general-settings',
	));
	
	acf_add_options_sub_page(array(
		'page_title' 	=> 'Theme Footer Settings',
		'menu_title'	=> 'Footer',
		'parent_slug'	=> 'theme-general-settings',
	));
	
}

如果要将options页面放到woocommerce菜单下,parent_slug要写woocommerce

获取ACF字段的详细设置

这里说的设置是指字段设置,需要传入字段组的ID,ID指下图蓝色方框里的字符串。

$field_group = acf_get_fields( 'group_6188fea1357d9' )

返回一个数组,如下所示:

array (
  0 => array (
    'ID'                => 964,
    'key'               => 'field_6188fea972f48',
    'label'             => '一般字段',
    'name'              => 'text_field',
    'prefix'            => 'acf',
    'type'              => 'text',
    'value'             => NULL,
    'menu_order'        => 0,
    'instructions'      => '',
    'required'          => 0,
    'id'                => '',
    'class'             => '',
    'conditional_logic' => 0,
    'parent'            => 963,
    'wrapper'       => array (
      'width' => '',
      'class' => '',
      'id'    => '',
    ),
    'default_value'   => '',
    'placeholder'     => '',
    'prepend'         => '',
    'append'          => '',
    'maxlength'       => '',
    '_name'           => 'text_field',
    '_valid'          => 1,
  ),
  
  1 => array (
    'ID'                => 968,
    'key'               => 'field_6188ff3472f4c',
    'label'             => '字段组',
    'name'              => 'groupname',
    'prefix'            => 'acf',
    'type'              => 'group',
    'value'             => NULL,
    'menu_order'        => 1,
    'instructions'      => '',
    'required'          => 0,
    'id'                => '',
    'class'             => '',
    'conditional_logic' => 0,
    'parent'            => 963,
    'wrapper'       => array (
      'width' => '',
      'class' => '',
      'id'    => '',
    ),
    'layout'      => 'block',
    'sub_fields'    => array (
      0 => array (
        'ID'                => 969,
        'key'               => 'field_6188ff4a72f4d',
        'label'             => '字段1',
        'name'              => 'text_1',
        'prefix'            => 'acf',
        'type'              => 'text',
        'value'             => NULL,
        'menu_order'        => 0,
        'instructions'      => '',
        'required'          => 0,
        'id'                => '',
        'class'             => '',
        'conditional_logic' => 0,
        'parent'            => 968,
        'wrapper'       => array (
          'width' => '40',
          'class' => '',
          'id'    => '',
        ),
        'default_value'   => '',
        'placeholder'     => '',
        'prepend'         => '',
        'append'          => '',
        'maxlength'       => '',
        '_name'           => 'text_1',
        '_valid'          => 1,
      ),
    ),
    '_name'     => 'groupname',
    '_valid'    => 1,
  ),
)

保存选项时进行操作

根据get_current_screen()返回的值可以判断当前处于什么页面。

add_action( 'acf/save_post', 'update_on_save_post' );
function update_on_save_post( $post_id ){
	$screen = get_current_screen();
	if( $screen && $screen->base == 'woocommerce_page_acf-options-whatever'){
		// do your stuff...
	}
}

参考链接: ACF文档

验证字段

要想增加自定义字段验证逻辑,可以使用acf/validate_value这个filter。需要注意的是,它用ajax的方式判断,所以不能用正常方法获取页面信息,比如global $post,或者get_the_ID()等等,打印$_POST变量看一下有哪些可用信息即可。

function my_acf_validate_value($valid, $value, $field, $input_name) {

    // Bail early if value is already invalid.
    if ($valid !== true) {
        return $valid;
    }

    // 要访问可用数据,请使用$_POST变量
    $post_id   = absint($_POST['post_id']);
    $post_type = $_POST['post_type'];
    $old_value = get_post_meta($post_id, 'my_custom_meta', true);

    if ($old_value != $value) {
        //进行逻辑判断

        // 如果验证不通过,可以返回一个字符串格式的错误信息
        return __('A custom error message');
    }

    return $valid;
}

// 针对所有字段
add_filter('acf/validate_value', 'my_acf_validate_value', 10, 4);

// 针对textarea类型的字段
// add_filter('acf/validate_value/type=textarea', 'my_acf_validate_value', 10, 4);

// 针对字段名称(name,不是label)为hero_text的字段
// add_filter('acf/validate_value/name=hero_text', 'my_acf_validate_value', 10, 4);

// 针对key是field_123abcf的字段
// add_filter('acf/validate_value/key=field_123abcf', 'my_acf_validate_value', 10, 4);

自动生成标题

使用acf/save_post这个action,可以访问被保存的字段值,这样我们就能根据填写的数据来生成标题了。

add_action('acf/save_post', 'acf_auto_post_title');
function acf_auto_post_title( $post_id ){
    if ('mycpt' == get_post_type($post_id)) {
        $name = get_post_meta($post_id, 'cpt_name', true);
        $update_post = array(
            'ID'           => $post_id,
            'post_title'   => sanitize_post($name . ' #' . $post_id)
        );
        wp_update_post($update_post);
    }
}

改变文件上传目录

可以上传文件的字段,比如file、image等,默认使用WordPress的上传设置,将文件放到uploads文件夹下以年月日分类的目录里。如果想使用自定义的目录,可以使用如下代码。

add_filter('acf/upload_prefilter/name=custom_file_upload', 'sola_acf_upload_dir_prefilter', 10, 3);
function sola_acf_upload_dir_prefilter($errors, $file, $field) {

    if (!current_user_can('manage_options')) {
        $errors[] = 'Only Administrators may upload attachments';
    }

    // This filter changes directory just for item being uploaded
    add_filter('upload_dir', 'sola_acf_upload_dir');
    return $errors;
}

function sola_acf_upload_dir($param) {
    $custom_dir    = 'my_custom_folder';
    $param['path'] = $param['basedir'] . '/' . $custom_dir;
    $param['url']  = $param['baseurl'] . '/' . $custom_dir;
    return $param;
}

常用文件扩展名

  • 图片:jpg,jpeg,png,gif,webp,svg,apng,avif,jfif
  • Office文档:doc,docx,rtf,xls,xlsx,ppt,pptx

16条评论

  1. solagirl,预祝愉快。是这样,我使用pro版的。repeater的名称为:homenew ,sub_field我使用的是post_object。我想输出post_object的标题,固定连接等;我写的是:

    <a href="”>

    字段规则是应对首页的,我想在首页输出几个指定的文章。我知道还有别的方法,关键是我还需要列出文章字段的信息,所以,麻烦您就告诉如何通过文章对象输出title就可以了。其他的我可以自己琢磨。谢谢了。

    1. 代码:
      <?php if(have_rows(‘homenew’)):while(have_rows(‘homenew’)):the_row();//外层循环
      $postinfo=get_sub_field(‘homenewpost’);setup_postdata(‘$postinfo’);//setup_postdata我是看官方文档,大概理解成格式化处理吧。?>
      <h3><a href=”<?php the_permalink();?>”><?php the_title();?></a></h3>
      <?php wp_reset_postdata();//官方文档说这个必须要endwhile;endif;?>

      补充,出现的循环,并没有列出文章的标题,而是把首页的标题列出来了。求问原因在哪里?

      1. 我没有使用官方文档的,我是自己输出对象的值可以,求问solagirl我按照官方文档的做没有成功。错在哪了?

      2. repeater field是针对一个post,而setup_postdata是用在WordPress WP_Query循环中的,理应WP_Query循环为外层,repeater field循环在内层,你的顺序正好反着。

        如果你不理解这俩循环的区别,建议使用更直观的方式,官方文档http://www.advancedcustomfields.com/resources/repeater/中Basic Loop (PHP foreach loop)这一段的代码可以参考,你直接用get_field获取repeater字段的内容,打印一下就明白这个repeater field到底存了哪些数据,是怎么存的。获取文章其它信息都用带着post id的函数,就不会出错了。比如获取文章标题,用get_the_title( $post_id )这种

  2. sola好,我用advanced custom field pro (上次你给我的pro版,再次感谢) ,在页面增加了字段然后保存页面后,内容莫名其妙的就没了,又要重新保存,请问是什么情况下会这样呢

  3. Pingback: wordpress自定义字段自定义域插件Advanced Custom Fields | whybu
  4. 博主你好,希望你能及时看到,我现在使用repeat与gallery一起,gallery是repeat的sub field,可是我前台调用只能调用到repeat,gallery不显示

  5. Hi Sola,
    我使用<i-mg src="” alt=”” />这样的方式输出自定义的图像,后台(这个插件)自定义字段(image_test)配置中”预览图大小”我选择中或者我自定义的其它几个裁剪图片尺寸,前台输出的图像始终是图片原始图,后台文章中预览图大小正常,希望得到你的帮助
    谢谢!

    1. image字段可以选返回值类型,你选择返回image object,这个包含所有尺寸图片的地址,用var_dump看一下返回值结构即明了。
      也可以参考官方文档http://www.advancedcustomfields.com/resources/field-types/image/
      看Customized display (Object)一节里的数组结构

  6. 博主好,希望请教一个问题,我设置了一个文本类型的字段用来处理链接。例如field name是post_url,内容是http://url,在php中调用<a href="”>TEXT</a>,但尝试后不成功。网页会显示到标签之前,不知道该如何修改。

    1. 评论中的代码被过滤掉了,所以你说的问题我不太明白,你是说acf调用出错吧。我用这个插件没出过问题,建议你用全新wordpress+默认主题测试。

      1. 简单的说,我定义了一个名为url的文本类型字段,内容填写的是一个完整网址,然后在PHP里调用出来作为a标签的href属性,想以此产生一个超链接(比如转载文章时,用来加原文链接),就是这个不成功,其他都成功的,实际页面的HTML会显示到a标签之前的内容。我用的就是3.6+2012主题。

        1. 文本类型下面应该有个设置控制输出html还是纯文本,选择纯文本。
          然后用var_dump()函数输出一下你的字段,看看输出的字符串到底是不是你想要的。
          看不到代码我也不知道到底什么问题,能想到的可能就是是本该传递给变量的值被直接输出了。

评论已关闭。