使用元框关联两个自定义帖子类型

时间:2019-08-21 作者:Praveen

我有两种自定义帖子类型:

1.产品2。卖者

现在,我想使用元框来关联这两种帖子类型。我想在卖家帖子中显示产品,以便卖家可以选择所需的多个产品,并为每个所选产品指定价格。最后,在卖家的帖子中,这些产品需要显示在元框中输入的价格。

请帮我做这个,我想在没有插件的情况下做这个。我正在尝试编码。如果我有任何想法,将张贴在这里。提前谢谢。

4 个回复
最合适的回答,由SO网友:rozklad 整理而成

我想你最终会得到这样的结果:enter image description here

编码方式

如果你想把这个逻辑放在你的模板/插件中,正如你在问题中所说的那样,有相当多的编码。

您需要:

创建自定义帖子类型为卖家创建metabox-在metabox文档中称为“回调”,创建保存metabox中数据的逻辑,为产品创建metabox检索前端数据,我假设您已经完成了这项操作,但让我们注册自定义帖子类型productseller 此处手动:

产品(“产品”)

/**
 * Post Type: Products.
 */
function se345571_register_cpt_product() {

  $labels = array(
    "name" => __( "Products", "mytheme_textdomain" ),
    "singular_name" => __( "Product", "mytheme_textdomain" ),
  );

  $args = array(
    "label" => __( "Products", "mytheme_textdomain" ),
    "labels" => $labels,
    "description" => "",
    "public" => true,
    "publicly_queryable" => true,
    "show_ui" => true,
    "delete_with_user" => false,
    "show_in_rest" => true,
    "rest_base" => "",
    "rest_controller_class" => "WP_REST_Posts_Controller",
    "has_archive" => false,
    "show_in_menu" => true,
    "show_in_nav_menus" => true,
    "exclude_from_search" => false,
    "capability_type" => "post",
    "map_meta_cap" => true,
    "hierarchical" => false,
    "rewrite" => array( "slug" => "product", "with_front" => true ),
    "query_var" => true,
    "supports" => array( "title", "editor", "thumbnail" ),
  );

  register_post_type( "product", $args );
}

add_action( \'init\', \'se345571_register_cpt_product\' );
卖方(“卖方”)
/**
 * Post Type: Sellers.
 */
function se345571_register_cpt_seller() {

  $labels = array(
    "name" => __( "Sellers", "mytheme_textdomain" ),
    "singular_name" => __( "Seller", "mytheme_textdomain" ),
  );

  $args = array(
    "label" => __( "Sellers", "mytheme_textdomain" ),
    "labels" => $labels,
    "description" => "",
    "public" => true,
    "publicly_queryable" => true,
    "show_ui" => true,
    "delete_with_user" => false,
    "show_in_rest" => true,
    "rest_base" => "",
    "rest_controller_class" => "WP_REST_Posts_Controller",
    "has_archive" => false,
    "show_in_menu" => true,
    "show_in_nav_menus" => true,
    "exclude_from_search" => false,
    "capability_type" => "post",
    "map_meta_cap" => true,
    "hierarchical" => false,
    "rewrite" => array( "slug" => "seller", "with_front" => true ),
    "query_var" => true,
    "supports" => array( "title", "editor", "thumbnail" ),
  );

  register_post_type( "seller", $args );
}

add_action( \'init\', \'se345571_register_cpt_seller\' );

为产品创建元盒
/**
 * Adds a box to "advanced" part on the Seller edit screen.
 * - See the different screens defined in $screens array.
 */
function se345571_add_seller_meta_box() {

  $screens = array( \'seller\' );

  foreach ( $screens as $screen ) {

    // https://codex.wordpress.org/Function_Reference/add_meta_box - add_meta_box(), see for further params
    add_meta_box(
      \'product_settings_box\',                           // HTML \'id\' attribute of the edit screen section
      __( \'Product settings\', \'mytheme_textdomain\' ),   // Title of the edit screen section, visible to user
      \'se345571_product_settings_meta_box_callback\',    // Function that prints out the HTML for the edit screen section.
      $screen                                           // Which writing screen (\'post\',\'page\',\'dashboard\',\'link\',\'attachment\',\'custom_post_type\',\'comment\')
    );

  }
}
add_action( \'add_meta_boxes\', \'se345571_add_seller_meta_box\' );
在这里您可以看到,添加了一个动作,该动作在add_meta_boxes - 这意味着,当Wordpress构建用于书写屏幕的元框时,它将调用我们的自定义se345571_add_seller_meta_box() 功能也一样。

为metabox创建内容

/**
 * Prints the box content.
 * 
 * @param WP_Post $post The object for the current post/page.
 */
function se345571_product_settings_meta_box_callback( $post, $box ) {

  // Add a nonce field so we can check for it later.
  wp_nonce_field( \'se345571_product_settings_meta_box_data\', \'se345571_product_settings_meta_box_nonce\' );

  /*
   * Use get_post_meta() to retrieve an existing value
   * from the database and use the value for the form.
   */
  $value = get_post_meta( $post->ID, \'_product_settings\', true );

  if ($value) {
    $product_settings = json_decode($value, true);
  }

  // Get available products so we can show them in select box
  $args = [
    \'post_type\' => \'product\',
    \'numberposts\' => -1,
    \'orderby\' => \'id\',
    \'order\' => \'ASC\'
  ];

  $products = new WP_Query($args);

  // As you can see, i have 5 product fields, this can be just about any number
  $max = 5;

  ?>
  <table>
    <?php for ($index = 0; $index < $max; $index++) : ?>
    <tr>
      <td>
        <label for="product-<?php echo $index + 1 ?>-product"><?php _e( \'Product #\' . ($index + 1), \'mytheme_textdomain\' )?></label>
      </td>
      <td>
        <?php $productindex = 0; ?>
        <select name="product_settings[<?php echo $index ?>][product_id]" id="product-<?php echo ($index + 1) ?>-product">
          <?php while($products->have_posts()) : $products->the_post(); $productindex++; ?>
            <option value="<?php the_ID() ?>" <?php echo (isset($product_settings[$index][\'product_id\']) && (int)$product_settings[$index][\'product_id\'] === get_the_ID()) ? \'selected\' : \'\' ?>><?php the_title() ?></option>
          <?php endwhile; ?>
        </select>
      </td>
      <td>
        <label for="product-<?php echo $index + 1 ?>-price"><?php _e( \'Price\', \'mytheme_textdomain\' )?></label>
      </td>
      <td>
        <input 
          name="product_settings[<?php echo $index ?>][price]" 
          type="text" 
          class="components-text-control__input" 
          id="product-<?php echo ($index + 1) ?>-price"
          value="<?php echo isset($product_settings[$index][\'price\']) ? $product_settings[$index][\'price\'] : \'\' ?>">
      </td>
    </tr>
    <?php endfor; ?>
  </table>
  <?php

  // Don\'t forget about this, otherwise you will mess up with other data on the page
  wp_reset_postdata();

}
好的,这里有很多代码。正如该线程中的其他人所建议的,您可能会发现使用javascript(如select2.js)扩展此逻辑很有用。您可以将其直接写入此函数的HTML输出。

保存数据


/**
 * When the post is saved, saves our custom data.
 *
 * @param int $post_id The ID of the post being saved.
 */
function se345571_product_settings_meta_box_data( $post_id, $post ) {

  // Check if our nonce is set.
  if ( ! isset( $_POST[\'se345571_product_settings_meta_box_nonce\'] ) ) {
    return;
  }

  // Verify that the nonce is valid.
  if ( ! wp_verify_nonce( $_POST[\'se345571_product_settings_meta_box_nonce\'], \'se345571_product_settings_meta_box_data\' ) ) {
    return;
  }

  // If this is an autosave, our form has not been submitted, so we don\'t want to do anything.
  if ( defined( \'DOING_AUTOSAVE\' ) && DOING_AUTOSAVE ) {
    return;
  }

  /* OK, it\'s safe for us to save the data now. */

  // Make sure that it is set.
  if ( ! isset( $_POST[\'product_settings\'] ) ) {
    return;
  }

  // HERE STARTS THE ACTUAL FUNCTIONALITY

  // Sanitize user input.
  $product_settings = json_encode( $_POST[\'product_settings\'] );

  // Update the meta field in the database.
  update_post_meta( $post_id, \'_product_settings\', $product_settings );

}

add_action( \'save_post\', \'se345571_product_settings_meta_box_data\', 10, 2 );
这是另一个钩子,save\\u post和我们的自定义逻辑。基本上是1:1Codex 对于add\\u meta\\u box()函数,只需交换POST字段名称。

为产品创建元数据库

您要求显示卖家为不同产品输入的价格,因为这里没有任何前端,我将在产品管理页面中显示。但同样的逻辑也可以用于在前端显示它。


/**
 * Adds a box to "advanced" part on the Seller edit screen.
 * - See the different screens defined in $screens array.
 */
function se345571_add_product_meta_box() {

  $screens = array( \'product\' );

  foreach ( $screens as $screen ) {

    // https://codex.wordpress.org/Function_Reference/add_meta_box - add_meta_box(), see for further params
    add_meta_box(
      \'product_settings_box\',                           // HTML \'id\' attribute of the edit screen section
      __( \'Seller prices\', \'mytheme_textdomain\' ),      // Title of the edit screen section, visible to user
      \'se345571_seller_prices_meta_box_callback\',       // Function that prints out the HTML for the edit screen section.
      $screen                                           // Which writing screen (\'post\',\'page\',\'dashboard\',\'link\',\'attachment\',\'custom_post_type\',\'comment\')
    );

  }
}
add_action( \'add_meta_boxes\', \'se345571_add_product_meta_box\' );

/**
 * Prints the box content.
 * 
 * @param WP_Post $post The object for the current post/page.
 */
function se345571_seller_prices_meta_box_callback( $post, $box ) {

  $product_id = get_the_ID();

  $args = [
    \'post_type\' => \'seller\',
    \'numberposts\' => -1,
    \'meta_query\' => [
      [
        \'key\' => \'_product_settings\',
        \'value\' => \'"product_id":"\' .$product_id. \'"\',
        \'compare\' => \'LIKE\',
      ]
    ]
  ];

  $sellers = new WP_Query($args);

  while($sellers->have_posts()) : $sellers->the_post();

    $seller_prices = json_decode(get_post_meta( get_the_ID(), \'_product_settings\', true ), true);

    $seller_prices_for_product = array_filter($seller_prices, function($element) use ($product_id) { 
      if (isset($element[\'product_id\'])) {
        return (int)$element[\'product_id\'] === $product_id;
      }
      return false;
    });

    foreach($seller_prices_for_product as $price) :
      ?>
      <p>
        <?php the_title(); ?> <?php _e(\'for\', \'mytheme_textdomain\'); ?> <?php echo $price[\'price\'] ?>
      </p>
      <?php
    endforeach;

  endwhile;

  // Don\'t forget about this, otherwise you will mess up with other data on the page
  wp_reset_postdata();

}

检索数据

最后,当您需要在前端获取数据时,您可以转到模板并使用类似以下内容:

$value = get_post_meta( get_the_ID(), \'_product_settings\', true );
$product_settings = json_decode($value, true);
get_post_meta() 使用您当前的帖子ID、要检索的字段的键(可选)以及只需要一个或所有元的任何内容(可选-未设置返回所有自定义字段)。

也就是说,我实际上会选择插件方式(除非你自己编写插件)。

像其他人指出的那样,对于这种情况,我会选择插件方式Advanced Custom Fields 插件。基本上可以做相同的事情,上面的代码。

通过用户界面设置元框。

检索数据

要检索数据,您可以使用函数get\\u field()或the\\u field()-无论您想立即返回值还是打印值(与WP中的命名约定相同)。

get_field(\'your_field_name\');
来源和进一步阅读http://themefoundation.com/wordpress-meta-boxes-guide/https://codex.wordpress.org/Function_Reference/add_meta_boxhttp://www.smashingmagazine.com/2011/10/04/create-custom-post-meta-boxes-wordpress/

SO网友:pax

您需要一个中继器关系自定义字段。最简单的方法仍然是为这一部分使用插件——我很高兴ACF1–但有很多选择。一个优点是它们提供font end forms 选项,以便您可以在前端为客户移动编辑(无需访问/wp-admin) –这需要一些编码。

然后,如果你真的不想使用插件,你可以看看它是如何工作的,并自己复制功能——请注意,你还需要复制repeater metabox UI——这可能不是那么简单。

或者,您可以include ACF in your code –这意味着您不会将其用作installeg插件,但您仍然需要在yout插件/主题中包含一个必需的ACF脚本。

repeater(中继器)字段需要PRO版本–对于原型,您可以在gpldl.

SO网友:Technoh

这比仅仅使用插件要复杂一点,但肯定是可行的。由于您没有提供代码示例(截至2019-08-28),我无法在回答中使用它,因此我将尝试在伪代码中尽可能具体。

我通常的做法是使用自定义帖子类型(CPT)ID创建一个meta\\u字段(get\\u post\\u meta,update\\u post\\u meta)。然而,在这种情况下,这可能意味着每个产品都有一个卖家ID数组,每个卖家都有一个产品ID数组。这根本不切实际,它打破了关系的概念,如果一条记录更新了,而另一条记录没有更新,那么它就打开了数据损坏的大门。

由于您有多对多关系(卖家可以有不同的产品,产品可以有不同的卖家),因此您希望创建自己的联接表,其中只有两个字段:product\\u idseller\\u id,每个id都是CPT的post id。请参见此related post 有关多对多关系的详细信息。

然后,当您需要显示产品或卖家时,您可以看到需要加载其他产品或卖家中的哪一个。只要加载关系表中的ID与您显示的产品或卖家ID匹配的所有产品或卖家即可。

SO网友:Ahmad Wael

您需要执行以下逻辑:

在sellers post type中:创建一个元框,从products post type中选择卖家产品,您可以使用select2。js,便于选择卖家产品。

使用将卖家帖子中选定的产品保存为产品ID数组update_post_meta.

  • 在卖家单页上,您可以使用get_post_meta().

    创建WP_Query 使用上一步中的给定ID显示当前卖家的产品。

  • 相关推荐

    特色图像Metabox不会移动自定义帖子类型

    我正在进行主题开发,我必须移动我的特色图像元盒,以便它更方便用户使用。我搜索了移动特征图像元盒。感谢真主,我找到了解决办法对于测试,我发现问题在于它适用于“post”post类型,而不是自定义post类型# InformationWP版本:5.0.3 HP版本:7.2.11我正在使用Laragon# This is the codeadd_action(\'do_meta_boxes\', \'ppdc_screenshot_move_metabox\' ); function ppdc