如果我了解你在做什么,你就是在寻求在开发区和房地产之间建立一对多的关系。
下面是一个类,如果加载并实例化,它将:
为名为development\\u area的帖子类型创建一个元框,其中包含所有可用属性的复选框列表,以便可以轻松更新两者之间的关系为名为property的帖子类型创建一个元框,其中包含一个选择框,用于选择属性的相关开发区域确保开发区和房地产之间存在一对多的关系,这意味着开发区可以与许多不同的房地产相关,但任何房地产都只能有一个相关的开发区
Note:
此代码说明了如何执行此操作。虽然我已经检查了它是否存在愚蠢的错误,并再次检查了逻辑,但您可能不得不进行调整,因为我没有在运行时测试它。我一直这样做,效果很好。
下面是一个如何使其工作的示例,然后是几个如何使用该类设置的关系的示例。
class One_To_Many_Linker {
protected $_already_saved = false; # Used to avoid saving twice
public function __construct() {
$this->do_initialize();
}
Setting up the meta boxes and save functionality
protected function do_initialize() {
add_action(
\'save_post\',
array( $this, \'save_meta_box_data\' ),
10,
2
);
add_action(
"add_meta_boxes_for_development_area",
array( $this, \'setup_development_area_boxes\' )
);
add_action(
"add_meta_boxes_for_property",
array( $this, \'setup_property_boxes\' )
);
}
Creating our needed meta box其他代谢箱也可以在这里轻松设置。
# Development area post type
# - this post type can have multiple related properties posts
public function setup_development_area_boxes( \\WP_Post $post ) {
add_meta_box(
\'area_related_properties_box\',
__(\'Related Properties\', \'language\'),
array( $this, \'draw_area_related_properties_box\' ),
$post->post_type,
\'advanced\',
\'default\'
);
}
Drawing the Related Properties此代码将相关属性绘制为一系列复选框。
public function draw_area_related_properties_box( \\WP_Post $post ) {
$all_properties = $this->get_all_of_post_type( \'property\' );
$linked_property_ids = $this->get_linked_property_ids( $post->ID );
if ( 0 == count($all_properties) ) {
$choice_block = \'<p>No properties currently in system.</p>\';
} else {
$choices = array();
foreach ( $all_properties as $property ) {
$checked = ( in_array( $property->ID, $linked_property_ids ) ) ? \' checked="checked"\' : \'\';
$display_name = esc_attr( $property->post_title );
$choices[] = <<<HTML
<label><input type="checkbox" name="property_ids[]" value="{$property->ID}" {$checked}/> {$display_name}</label>
HTML;
}
$choice_block = implode("\\r\\n", $choices);
}
# Make sure the user intended to do this.
wp_nonce_field(
"updating_{$post->post_type}_meta_fields",
$post->post_type . \'_meta_nonce\'
);
echo $choice_block;
}
Getting lists of posts这将获取特定类型的所有帖子。如果使用粘滞贴子,则需要在args参数中取消设置粘滞标志。
# Grab all posts of the specified type
# Returns an array of post objects
protected function get_all_of_post_type( $type_name = \'\') {
$items = array();
if ( !empty( $type_name ) ) {
$args = array(
\'post_type\' => "{$type_name}",
\'posts_per_page\' => -1,
\'order\' => \'ASC\',
\'orderby\' => \'title\'
);
$results = new \\WP_Query( $args );
if ( $results->have_posts() ) {
while ( $results->have_posts() ) {
$items[] = $results->next_post();
}
}
}
return $items;
}
Getting linked property ids for a development area
给定开发区域id,这将返回所有链接属性帖子id的数组。
protected function get_linked_property_ids( $area_id = 0 ) {
$ids = array();
if ( 0 < $area_id ) {
$args = array(
\'post_type\' => \'property\',
\'posts_per_page\' => -1,
\'order\' => \'ASC\',
\'orderby\' => \'title\',
\'meta_query\' => array(
array(
\'key\' => \'_development_area_id\',
\'value\' => (int)$area_id,
\'type\' => \'NUMERIC\',
\'compare\' => \'=\'
)
)
);
$results = new \\WP_Query( $args );
if ( $results->have_posts() ) {
while ( $results->have_posts() ) {
$item = $results->next_post();
$ids[] = $item->ID;
}
}
}
return $ids;
}
Setting up the Property meta boxes
如果需要,您可以在此处轻松添加更多元框。
# Post type metabox setup
public function setup_property_boxes( \\WP_Post $post ) {
add_meta_box(
\'property_linked_area_box\',
__(\'Related Development Area\', \'language\'),
array( $this, \'draw_property_linked_area_box\' ),
$post->post_type,
\'advanced\',
\'default\'
);
}
Drawing the Property editor meta box
这将绘制一个选择元素(下拉框),允许用户选择与属性相关的开发区域。我们使用选择框来确保只能指定一个属性,但单选框列表也可以。
public function draw_property_linked_area_box( \\WP_Post $post ) {
$all_areas = $this->get_all_of_post_type(\'development_area\');
$related_area_id = $this->get_property_linked_area_id( $post->ID );
if ( 0 == $all_areas ) {
$choice_block = \'<p>No development areas to choose from yet.</p>\';
} else {
$choices = array();
$selected = ( 0 == $related_area_id )? \' selected="selected"\':\'\';
$choices[] = \'<option value=""\' . $selected . \'> -- None -- </option>\';
foreach ( $all_areas as $area ) {
$selected = ( $area->ID == (int)$related_area_id ) ? \' selected="selected"\' : \'\';
$display_name = esc_attr( $area->post_title );
$choices[] = <<<HTML
<option value="{$area->ID}" {$selected}>{$display_name}</option>
HTML;
}
$choice_list = implode("\\r\\n" . $choices);
$choice_block = <<<HTML
<select name="development_area_id">
{$choice_list}
</select>
HTML;
}
wp_nonce_field(
"updating_{$post->post_type}_meta_fields",
$post->post_type . \'_meta_nonce\'
);
echo $choice_block;
}
The method of linking
请注意,我们通过设置
_development_area_id
每个属性上的键。
开发区可以使用此键查询属性以显示它们,属性可以提取自己的元或过滤查询以提取开发数据
protected function get_property_linked_area_id( $property_id = 0 ) {
$area_id = 0;
if ( 0 < $property_id ) {
$area_id = (int) get_post_meta( $property_id, \'_development_area_id\', true );
}
return $area_id;
}
Saving the meta data
我们只在必要和适当的时候才努力储蓄。请参见代码注释。
public function save_meta_box_data( $post_id = 0, \\WP_Post $post = null ) {
$do_save = true;
$allowed_post_types = array(
\'development_area\',
\'property\'
);
# Do not save if we have already saved our updates
if ( $this->_already_saved ) {
$do_save = false;
}
# Do not save if there is no post id or post
if ( empty($post_id) || empty( $post ) ) {
$do_save = false;
} else if ( ! in_array( $post->post_type, $allowed_post_types ) ) {
$do_save = false;
}
# Do not save for revisions or autosaves
if (
defined(\'DOING_AUTOSAVE\')
&& (
is_int( wp_is_post_revision( $post ) )
|| is_int( wp_is_post_autosave( $post ) )
)
) {
$do_save = false;
}
# Make sure proper post is being worked on
if ( !array_key_exists(\'post_ID\', $_POST) || $post_id != $_POST[\'post_ID\'] ) {
$do_save = false;
}
# Make sure we have the needed permissions to save [ assumes both types use edit_post ]
if ( ! current_user_can( \'edit_post\', $post_id ) ) {
$do_save = false;
}
# Make sure the nonce and referrer check out.
$nonce_field_name = $post->post_type . \'_meta_nonce\';
if ( ! array_key_exists( $nonce_field_name, $_POST) ) {
$do_save = false;
} else if ( ! wp_verify_nonce( $_POST["{$nonce_field_name}"], "updating_{$post->post_type}_meta_fields" ) ) {
$do_save = false;
} else if ( ! check_admin_referer( "updating_{$post->post_type}_meta_fields", $nonce_field_name ) ) {
$do_save = false;
}
if ( $do_save ) {
switch ( $post->post_type ) {
case "development_area":
$this->handle_development_area_meta_changes( $post_id, $_POST );
break;
case "property":
$this->handle_property_meta_changes( $post_id, $_POST );
break;
default:
# We do nothing about other post types
break;
}
# Note that we saved our data
$this->_already_saved = true;
}
return;
}
Updating Development Properties
我们读取已检查属性帖子类型的列表,获取当前相关属性帖子类型的列表,然后使用这些列表确定要更新的内容。
Notice: 我们正在更新这里的物业帖子类型元数据,而不是在我们编辑的开发区。
# Development areas can link to multiple properties
# but each property can only link to a single development area
protected function handle_development_area_meta_changes( $post_id = 0, $data = array() ) {
# Get the currently linked property ids for this development area
$linked_property_ids = $this->get_linked_property_ids( $post_id );
# Get the list of property ids checked when the user saved changes
if ( array_key_exists(\'property_ids\', $data) && is_array( $data[\'property_ids\'] ) ) {
$chosen_property_ids = $data[\'property_ids\'];
} else {
$chosen_property_ids = array();
}
# Build a list of properties to be linked or unlinked from this area
$to_remove = array();
$to_add = array();
if ( 0 < count( $chosen_property_ids ) ) {
# The user chose at least one property to link to
if ( 0 < count( $linked_property_ids ) ) {
# We already had at least one property linked
# Cycle through existing and note any that the user did not have checked
foreach ( $linked_property_ids as $property_id ) {
if ( ! in_array( $property_id, $chosen_property_ids ) ) {
# Currently linked, but not chosen. Remove it.
$to_remove[] = $property_id;
}
}
# Cycle through checked and note any that are not currently linked
foreach ( $chosen_property_ids as $property_id ) {
if ( ! in_array( $property_id, $linked_property_ids ) ) {
# Chosen but not in currently linked. Add it.
$to_add[] = $property_id;
}
}
} else {
# No previously chosen ids, simply add them all
$to_add = $chosen_property_ids;
}
} else if ( 0 < count( $linked_property_ids ) ) {
# No properties chosen to be linked. Remove all currently linked.
$to_remove = $linked_property_ids;
}
if ( 0 < count($to_add) ) {
foreach ( $to_add as $property_id ) {
# This will overwrite any existing value for the linking key
# to ensure we maintain only one dev area linked by each property.
update_post_meta( $property_id, \'_development_area_id\', $post_id );
}
}
if ( 0 < count( $to_remove ) ) {
foreach ( $to_remove as $property_id ) {
# This will delete all existing linked areas for the property
# to ensure we only ever have one linked area per property
delete_post_meta( $property_id, \'_development_area_id\' );
}
}
}
Saving our Property Changes
由于我们的元键位于每个属性上,因此我们只需在必要时更新元数据。由于mysql中的读取速度几乎总是快于写入速度,因此我们只在绝对必要时进行更新。
# Properties only relate to a single development area
protected function handle_property_meta_changes( $post_id = 0, $data = array() ) {
# Get any currently linked development area
$linked_area_id = $this->get_property_linked_area_id( $post_id );
if ( empty($linked_area_id) ) {
$linked_area_id = 0;
}
if ( array_key_exists( \'development_area_id\', $data ) && !empty($data[\'development_area_id\'] ) ) {
$received_area_id = (int)$data[\'development_area_id\'];
} else {
$received_area_id = 0;
}
if ( $received_area_id != $linked_area_id ) {
# This will overwrite any and all existing copies of our meta key
# so we can ensure we only have one linked area per property
update_post_meta( $post_id, \'_development_area_id\', $received_area_id );
}
}
}
How To Use The Class
如果在主题函数文件或插件中加载该类,则可以使用以下方法进行操作:
if ( is_admin() ) {
new One_To_Many_Linker();
}
Some Use Cases Follow下面,我提供了几个前端用例。
显示当前开发区的所有属性显示存档或单个属性中某个属性的开发区
Showing all properties related to the currently displayed development area
global $wp_query;
$area_id = $wp_query->get_queried_object_id();
$args = array(
\'post_type\' => \'property\',
\'posts_per_page\' => -1,
\'meta_query\' => array(
array(
\'key\' => \'_development_area_id\',
\'value\' => $area_id,
\'compare\' => \'=\',
\'type\' => \'NUMERIC\'
)
)
);
$properties = new \\WP_Query( $args );
if ( $properties->have_posts() ) {
while( $properties->have_posts() ) {
$property = $properties->next_post();
# do something with the property
$property_link = get_permalink( $property->ID );
$property_name = esc_attr( $property->post_title );
echo \'<a href="\' . $property_link . \'">\' . $property_name . \'</a>\';
}
}
Showing Linked Development Areas
Method 1: 抓取post元,加载区域,并使用数据
适用于单数(\'property\')为true的页面
global $post;
while ( have_posts() ) {
the_post();
$post_id = get_the_ID(); # could use $post->ID
$dev_area_id = get_post_meta( $post_id, \'_development_area_id\', true);
if ( !empty( $dev_area_id ) ) {
$development_area = get_post( $dev_area_id );
# do something...
$dev_area_link = get_permalink ( $development_area->ID );
$dev_area_title = $development_area->post_title;
$dev_area_content = $development_area->post_content;
echo \'<a href="\' . $dev_area_link . \'">\' . $dev_area_title . \'</a><br />\' . $dev_area_content;
}
}
Method 2: 使用查询筛选器
适用于is单数(\'property\')为true的页面
适用于is post\\u type\\u archive(\'property\')为true的页面add_filter(\'posts_clauses\', \'do_my_maybe_modify_queries\', 10, 2);
function do_my_maybe_modify_queries( $pieces, \\WP_Query $wp_query ) {
if ( !is_admin() && $wp_query->is_main_query() ) {
# We are not in the admin panels and we are the primary query for the template
if ( array_key_exists(\'post_type\', $wp_query->query_vars) ) {
# A post type of some kind was queried.
# Grab it as an array of the types specified
$value = $wp_query->query_vars[\'post_type\'];
if ( !is_array( $value ) ) {
if ( empty( $value ) ) {
$post_types = array();
} else {
$post_types = array( $value );
}
} else {
$post_types = $value;
}
if ( in_array(\'property\', $post_types) ) {
# We were asked for a property
if ( $wp_query->is_post_type_archive || $wp_query->is_singular ) {
# Showing the property post type archive or a singular property.
# We want to add our development area id, title, and content to the returned fields
global $wpdb;
# Link the development post to each property through its postmeta key
# Since there is only 1 development area per property, this works fine
$pieces[\'join\'] .= <<<SQL
LEFT JOIN {$wpdb->prefix}postmeta AS dev_pm ON {$wpdb->prefix}posts.ID = dev_pm.post_id AND dev_pm.meta_key = \'_development_area_id\'
LEFT JOIN {$wpdb->prefix}posts AS dev_post ON dev_post.ID = dev_pm.meta_value
SQL;
# Add our wanted development post fields to those returned by the query
$pieces[\'fields\'] .= ", IFNULL( dev_pm.meta_value, 0 ) as development_area_id";
$pieces[\'fields\'] .= ", IFNULL( dev_post.post_title, \'\') as development_area_title";
$pieces[\'fields\'] .= ", IFNULL( dev_post.post_content, \'\') as development_area_content";
}
}
}
}
return $pieces;
}
有了上述内容,您现在可以在单个属性页或属性存档中访问以下数据。我把与属性相关的分类法作为练习留给读者。
if ( have_posts() ) {
global $post;
while ( have_posts() ) {
the_post();
if ( property_exists( $post, \'development_area_id\' ) ) {
$dev_area_id = $post->development_area_id;
$dev_area_title = $post->development_area_title;
$dev_area_content = $post->development_area_content;
$dev_area_link = get_permalink( $dev_area_id );
echo \'<a href="\' . $dev_area_link . \'">\' . $dev_area_title . \'</a><br />\' . $dev_area_content;
}
}
}
Method 3: 筛选WP_查询
与上面的filter方法类似,但可用于使用WP\\u Query的自定义查询。如果您想编写一个显示一组属性的短代码或小部件,那就太好了。
首先,我们创建过滤器(非常类似于方法2所示的过滤器)
function add_dev_data_to_wp_query( $pieces, \\WP_Query $wp_query ) {
global $wpdb;
if ( !is_admin() && !$wp_query->is_main_query() ) {
# Link the development post to each property through its postmeta key
# Since there is only 1 development area per property, this works fine
$pieces[\'join\'] .= <<<SQL
LEFT JOIN {$wpdb->prefix}postmeta AS dev_pm ON {$wpdb->prefix}posts.ID = dev_pm.post_id AND dev_pm.meta_key = \'_development_area_id\'
LEFT JOIN {$wpdb->prefix}posts AS dev_post ON dev_post.ID = dev_pm.meta_value
SQL;
# Add our wanted development post fields to those returned by the query
$pieces[\'fields\'] .= ", IFNULL( dev_pm.meta_value, 0 ) as development_area_id";
$pieces[\'fields\'] .= ", IFNULL( dev_post.post_title, \'\') as development_area_title";
$pieces[\'fields\'] .= ", IFNULL( dev_post.post_content, \'\') as development_area_content";
}
return $pieces;
}
接下来,我们在创建查询之前应用过滤器,然后立即删除它,以确保不会更改其他查询。
$args = array(
\'post_type\' => \'property\',
\'posts_per_page\' => -1
);
# Apply our filter
add_filter(\'posts_clauses\', \'add_dev_data_to_wp_query\', 10, 2);
# Run the query
$properties = new \\WP_Query( $args );
# Remove our filter
remove_filter(\'posts_clauses\', \'add_dev_data_to_wp_query\', 10);
if ( $properties->have_posts() ) {
while( $properties->have_posts() ) {
$property = $properties->next_post();
# Do stuff with your properties
echo \'<p>The property name is \' . $property->post_title . \'</p>\';
# Do stuff with related development areas if that data is available
if ( property_exists( $property, \'development_area_id\' ) ) {
echo \'<p>Part of the development area named \' . $property->development_area_title . \'</p>\';
}
}
}
虽然这样做需要更多的前期工作,但它提供了一些很好的好处
您只需执行1个查询,而不是2到3个查询来显示相关信息。这加起来很快
一旦你对它感到满意,你可以使用与多个对象的关系,这将为你节省多个额外的查询。每个使用一个变体,你可以向编辑器列表列添加标题(以及查看和编辑链接)。例如,在上述场景中,您可以在管理中的属性列表中列出每个属性的开发当然,这已经足够长了。