序列化数组的meta_query具有元值
-
22-10-2019 - |
题
我正在研究一个项目,在该项目中,我正在创建通过与我的自定义帖子类型相关的元框输入的自定义帖子类型和自定义数据。无论出于何种原因,我决定以每种Metabox中的输入是一个数组的一部分来编码元框。例如,我正在存储经度和纬度:
<p>
<label for="latitude">Latitude:</label><br />
<input type="text" id="latitude" name="coordinates[latitude]" class="full-width" value="" />
</p>
<p>
<label for="longitude">Longitude:</label><br />
<input type="text" id="longitude" name="coordinates[longitude]" class="full-width" value="" />
</p>
无论出于何种原因,我都喜欢为每个Metabox拥有一个单一的后表条目的想法。在 save_post
钩子,我这样保存数据:
update_post_meta($post_id, '_coordinates', $_POST['coordinates']);
之所以这样做是因为我有三个metaboxes,我喜欢每个帖子只有3个后量值。但是,我现在意识到这是一个潜在的问题。我可能想使用WP_QUERY仅提取基于这些元值的某些帖子。例如,我可能想获取所有具有纬度值高于50的帖子。如果我单独使用此数据,也许使用键 latitude
, ,我会做类似的事情:
$args = array(
'post_type' => 'my-post-type',
'meta_query' => array(
array(
'key' => 'latitude',
'value' => '50',
'compare' => '>'
)
)
);
$query = new WP_Query( $args );
由于我有纬度作为一部分 _coordinates
Postmeta,这是行不通的。
所以,我的问题是,有没有办法利用 meta_query
在这种情况下像我一样查询一个序列化数组?
解决方案
不,这是不可能的,甚至可能是危险的。
我强烈建议您解散数据并修改保存例程。与此类似的东西应该将您的数据转换为新格式:
$args = array(
'post_type' => 'my-post-type',
'meta_key' => '_coordinates',
'posts_per_page' => -1
);
$query = new WP_Query( $args );
if($query->have_posts()){
while($query->have_posts()){
$query->the_post();
$c = get_post_meta($post->id,'_coordinates',true);
add_post_meta($post->ID,'_longitude',$c['longitude']);
add_post_meta($post->ID,'_latitude',$c['latitude']);
delete_post_meta($post->ID,'_coordinates',$c);
}
}
然后,您将能够按照单个钥匙进行查询
如果您需要存储多个纵向和多个纬度,则可以存储具有相同名称的多个帖子元。只需使用第三个参数 get_post_meta
, ,它将将它们全部归还为一个数组
您为什么不能在序列化数据中查询?
MySQL将其视为一个字符串,无法将其分解为结构化数据。将其分解为结构化数据正是上述代码所做的
您可能可以查询部分日期,但这将是超级不可靠,昂贵,缓慢且非常脆弱的,并且有很多边缘案例。序列化数据不打算用于SQL查询,也不是以常规且恒定的方式进行格式化的。
除了部分字符串搜索的成本外,元查询还很慢,并且序列化数据可能会根据内容的长度而改变,从而使搜索变得非常昂贵,即使不是不可能,根据您要搜索的价值
将记录/实体/对象作为序列化对象存储/实体/对象的注释
您可能需要将事务记录存储在元元后或用户元中的其他一些数据结构中,然后遇到上面的问题。
这里的解决方案不是将其分解为单个元后的元后,而是要意识到它绝不应该是元的,而是一种自定义的帖子类型。例如,日志或记录可以是自定义帖子类型,原始帖子作为父母,也可以通过分类学期加入
安全性和序列化对象
通过 serialize
功能可能很危险, ,这是不幸的,因为将对象传递给WordPress将意味着它被序列化。这是因为当对象被去序列化时,创建对象,并且其所有唤醒方法和构造函数将被执行。直到用户设法偷偷进行精心制作的输入之前,这似乎没什么大不了的,当从数据库中读取数据并被WordPress删除时,导致远程代码执行。
可以通过使用JSON来避免这种情况,这也可以使查询更容易,但是只要正确存储数据并避免结构化的序列化数据就更容易/更快。
其他提示
我也遇到了这种情况。我在这里做了什么:
$args = array(
'post_type' => 'my-post-type',
'meta_query' => array(
array(
'key' => 'latitude',
'value' => sprintf(':"%s";', $value),
'compare' => 'LIKE'
)
)
);
希望这有所帮助
当将条目序列化到WP数据库时,您确实会失去以任何有效方式查询数据的能力。
您认为您正在通过序列化实现的总体绩效节省和收获在很大程度上不会引人注目。您可能会获得一个稍小的数据库大小,但是如果您查询这些字段并尝试以任何有用的有意义的方式进行比较,SQL交易的成本将很重。
相反,将序列化保存在您不打算以这种性质查询的数据中,而是只能通过直接WP API调用以被动方式访问 get_post_meta()
- 从该函数中,您也可以解开序列化条目以访问其数组属性。
实际上分配了 真的 如
$meta = get_post_meta( $post->ID, 'key', true );
将返回数据作为数组,可访问您按照正常情况迭代。
您可以专注于其他数据库/站点优化,例如缓存,CSS和JS缩小,并在需要的情况下使用cdn等服务。仅举几个.... WordPress codex是发现该主题更多的好起点: 这里
我刚刚处理了序列化字段,可以查询它们。不使用meta_query,而是使用SQL查询。
global $wpdb;
$search = serialize('latitude').serialize(50);
$query = $wpdb->prepare("SELECT `post_id`
FROM `wp_postmeta`
WHERE `post_id` IN (SELECT `ID` FROM `wp_posts` WHERE `post_type` = 'my-post-type')
AND `meta_key` = '_coordinates'
AND `meta_value` LIKE '%s'",'%'.$search.'%');
$ids = $wpdb->get_col($query);
$args = array(
'post__in' => $ids
'post_type' => 'team' //add the type because the default will be 'post'
);
$posts = get_posts($args);
查询首先搜索具有匹配的post_type的帖子,因此WP_PostMeta记录的量将减少过滤。然后,我添加了一个通过过滤来进一步减少行的说明以进一步减少行 meta_key
IDS最终会根据需要的got_posts很好地进入数组。
PS。 MySQL v5.6或更高的子查询性能需要
这个示例真的帮助了我。它专门用于S2Members插件(序列化用户元数据)。但是它允许您查询meta_key中序列化数组的一部分。
它通过使用MySQL RegexP函数来工作。
这里 是来源
这是查询所有居住在美国的用户的代码。我很容易修改它以查询我的自定义注册字段之一,并立即工作。
<?php
global $wpdb;
$users = $wpdb->get_results ("SELECT `user_id` as `ID` FROM `" . $wpdb->usermeta .
"` WHERE `meta_key` = '" . $wpdb->prefix . "s2member_custom_fields' AND
`meta_value` REGEXP '.*\"country_code\";s:[0-9]+:\"US\".*'");
if (is_array ($users) && count ($users) > 0)
{
foreach ($users as $user)
{
$user = /* Get full User object now. */ new WP_User ($user->ID);
print_r($user); /* Get a full list of properties when/if debugging. */
}
}
?>
我认为有两种解决方案可以尝试解决结果的结果问题和整数。但是,必须说,正如其他人指出的那样,不可能保证存储为整数的结果的完整性,因为作为将这些值存储为序列化阵列,索引和值以相同的模式将其精确存储。例子:
array(37,87);
被存储为一个序列化数组,
a:2:{i:0;i:37;i:1;i:87;}
注意 i:0
作为数组的第一个位置, i:37
作为第一个值。图案是相同的。但是让我们去解决方案
1)REGEXP解决方案
无论将其保存为字符串或数字 / ID的元值如何,该解决方案对我有用。但是它使用 REGEXP
, ,它不像使用那样快 LIKE
$args = array(
'post_type' => 'my-post-type',
'meta_query' => array(
array(
'key' => 'latitude',
'value' => '\;i\:' . $value . '\;|\"' . $value . '\";',
'compare' => 'REGEXP'
)
)
);
2)喜欢解决方案
我不确定性能差异,但这是一种使用的解决方案 LIKE
并且还适用于数字和字符串
$args = array(
'post_type' => 'my-post-type',
'meta_query' => array(
'relation' => 'OR',
array(
'key' => 'latitude',
'value' => sprintf(':"%s";', $value),
'compare' => 'LIKE'
),
array(
'key' => 'latitude',
'value' => sprintf(';i:%d;', $value),
'compare' => 'LIKE'
)
)
);
在阅读了一堆用于运行的技巧之后 WP_Query
通过序列化阵列过滤,这是我最终完成的:通过使用爆式与一个结合结合的逗号分隔值的数组 $wpdb
自定义SQL查询利用 FIND_IN_SET
搜索逗号分隔列表中的请求值。
(这类似于Tomas的答案,但对于SQL查询,其性能较少)
1.在functions.php中:
在您的functions.php文件(或在任何设置元框的地方)中 yourname_save_post()
功能使用
update_post_meta($post->ID, 'checkboxArray', implode(",", $checkboxArray)); //adding the implode
创建包含逗号分离值的数组。
您还需要在 yourname_post_meta()
管理元盒构造功能
$checkboxArray = explode(",", get_post_custom($post->ID)["checkboxArray"][0]); //adding the explode
2.在模板PHP文件中:
测试:如果您运行 get_post_meta( $id );
你应该看 checkboxArray
作为包含逗号分离值的数组,而不是序列化数组。
现在,我们使用使用自定义SQL查询 $wpdb
.
global $wpdb;
$search = $post->ID;
$query = "SELECT * FROM wp_posts
WHERE FIND_IN_SET( $search, (
SELECT wp_postmeta.meta_value FROM wp_postmeta
WHERE wp_postmeta.meta_key = 'blogLocations'
AND wp_postmeta.post_id = wp_posts.ID )
)
AND ( wp_posts.post_type = 'post' )
AND ( wp_posts.post_status = 'publish' );";
$posts = $wpdb->get_results($query);
foreach ($posts as $post) {
//your post content here
}
注意 FIND_IN_SET
, ,这就是魔术发生的地方。
现在...因为我正在使用 SELECT *
这返回 所有帖子数据 并在 foreach
您可以回应您想要的东西(做一个 print_r($posts);
如果您不知道其中包括什么。它不会为您设置“循环”(我更喜欢这种方式),但是如果您愿意,可以轻松修改以设置循环(看看 setup_postdata($post);
在法典中,您可能需要更改 SELECT *
仅选择发布ID和 $wpdb->get_results
正确 $wpdb
类型 - 请参阅Codex for $wpdb
也有关信息 那 主题)。
whelp,这花了一点努力,但是 wp_query
不支持这样做 'compare' => 'IN'
序列化或逗号分隔值此垫片是您的最佳选择!
希望这对某人有帮助。
如果您使用 like
比较元查询中的操作员,在串行阵列中查看内部应该很好。
$wp_user_search = new WP_User_Query(array(
'meta_query' => array(
array(
'key' => 'wp_capabilities',
'value' => 'subscriber',
'compare' => 'not like'
)
)
)
);
结果是:
[query_where] => WHERE 1=1 AND (
( wp_usermeta.meta_key = 'wp_capabilities'
AND CAST(wp_usermeta.meta_value AS CHAR) NOT LIKE '%subscriber%' )
如果我的元数据是数组类型,我将使用此方法进行元查询:
$args = array(
'post_type' => 'fotobank',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => 'collections',
'value' => ':"'.$post->ID.'";',
'compare' => 'LIKE'
)
)
);
$fotos = new WP_Query($args);
我对上面的答案感到好奇, meta_query
针对钥匙 latitude
代替 _coordinates
. 。必须去测试元查询中是否真的有可能针对序列化数组中的特定键。 :)
显然事实并非如此。
因此,请注意,目标的正确关键是 _coordinates
代替 latitude
.
$args = array( 'post_type' => 'my-post-type', 'meta_query' => array( array( 'key' => '_coordinates', 'value' => sprintf(':"%s";', $value), 'compare' => 'LIKE' ) ) );
笔记:
这种方法只能针对精确匹配。所以像这样 所有纬度大于50 不可能。
要包括子字符串匹配,可以使用
'value' => sprintf(':"%%%s%%";', $value),
. 。 (尚未测试)
我也有同样的问题。也许您需要“类型”参数?查看此相关问题:自定义字段查询 - 元值是数组
也许尝试:
$args = array( 'post_type' => 'my-post-type', 'meta_query' => array( array( 'key' => 'latitude', 'value' => '50', 'compare' => '>', 'type' => 'numeric' ) ) );
使用魔术字段插件时,我遇到了类似的东西。这可能会解决问题
$values_serialized = serialize(array('50'));
$args = array(
'post_type' => 'my-post-type',
'meta_query' => array(
array(
'key' => 'latitude',
'value' => $values_serialized,
'compare' => '>'
)
)
);