메타 값을 직렬화 배열로하는 meta_query


37

사용자 정의 게시물 유형과 사용자 정의 게시물 유형과 관련된 메타 박스를 통해 입력 된 사용자 정의 데이터를 만드는 프로젝트를 진행하고 있습니다. 어떤 이유로 든 나는 각 메타 박스의 입력이 배열의 일부가되도록 메타 박스를 코딩하기로 결정했습니다. 예를 들어 경도와 위도를 저장하고 있습니다.

<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>

어떤 이유로 든, 나는 각 메타 박스마다 단일 postmeta 항목을 갖는 아이디어를 좋아했습니다. 온 save_post후크, 나는 데이터가 너무 좋아하는 저장 :

update_post_meta($post_id, '_coordinates', $_POST['coordinates']);

세 개의 메타 박스가 있고 각 게시물에 대해 3 개의 postmeta 값을 갖는 것을 좋아하기 때문에이 작업을 수행했습니다. 그러나 이제는 이것으로 잠재적 인 문제를 깨달았습니다. 이러한 메타 값을 기반으로 특정 게시물을 꺼내기 위해 WP_Query를 사용할 수 있습니다. 예를 들어 위도 값이 50보다 큰 모든 게시물을 가져오고 싶을 수 있습니다.이 데이터를 데이터베이스에 개별적으로 (아마도 key 사용) 사용하는 latitude경우 다음과 같은 작업을 수행합니다.

$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => '50',
            'compare' => '>'
        )
    )
 );
$query = new WP_Query( $args );

_coordinatespostmeta의 일부로 위도가 있으므로 작동하지 않습니다.

그래서 내 질문은, meta_query이 시나리오에서와 같이 직렬 배열을 쿼리하는 데 활용할 수 있는 방법 이 있습니까?

답변:


37

불가능합니다. 심지어 위험 할 수도 있습니다.

데이터를 직렬화 해제하고 저장 루틴을 수정하는 것이 좋습니다. 이와 비슷한 것이 데이터를 새로운 형식으로 변환해야합니다.

$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함수 를 통해 직렬화 된 PHP 객체를 저장 하는 것은 위험 할 수 있습니다. 불행히도 객체를 WordPress에 전달하면 직렬화됩니다. 이는 객체가 직렬화 해제 될 때 객체가 생성되고 모든 웨이크 업 메소드와 생성자가 실행되기 때문입니다. 사용자가 신중하게 조작 된 입력을 몰래 관리하여 데이터베이스에서 데이터를 읽고 WordPress에서 직렬화 해제 할 때 원격 코드 실행으로 이어질 때까지 큰 문제가되지 않을 수 있습니다.

대신 JSON을 사용하면 피할 수 있습니다. 이로 인해 쿼리가 더 쉬워 지지만 데이터를 올바르게 저장하고 구조화 된 직렬화 된 데이터를 피하는 것이 훨씬 쉽고 빠릅니다.


5
지나가는 사람들을 위해, 독서를 멈추지 마십시오 : 더 유용한 (그리고 최근의) 답변은 아래에 있습니다
Erenor Paz

저장할 ID 배열이 있고 각각 '위도'등과 같이 저장할 수있는 다른 키를 나타내지 않으면 관계를 저장할 때와 같이 모두 하나의 키 일뿐입니다. 그러면 어떻게해야합니까? @rabni의 솔루션?
trainoasis

1
키를 두 번 이상 저장할 수 있으며 키 값 쌍은 고유하지 않습니다. 관계에 관해서는, 그것은 분류법에 대한 것입니다. 메타를 사용하여 여러 것을 무언가에 매핑하는 경우 분류법으로 대신하십시오
Tom J Nowell

24

나는 또한이 상황에 부딪친 다. 여기 내가 한 일 :

$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => sprintf(':"%s";', $value),
            'compare' => 'LIKE'
        )
    )
);

이 도움을 바랍니다


1
나는이 솔루션을 정말 좋아했다. 불행히도이 $value또한 ID 인 경우에는 적용 할 수 없습니다 . 이 경우 데이터를 저장하기 전에 각 배열 요소에 문자를 추가하는 함수를 만들고 데이터를 사용하기 전에 문자를 제거하는 다른 함수를 만드는 것이 좋습니다. 이와 같이 직렬화 된 i:2인덱스는 i:D2"실제"데이터 와 혼동되지 않습니다 . 그러면 메타 쿼리 매개 변수가 'value' => sprintf(':"D%s";', $value),되고이 훌륭한 답변의 올바른 기능을 유지하게됩니다!
Erenor Paz

이 솔루션은 저에게
Vishal

이것은 또한 나를 위해 완벽하게 작동했습니다. 나는 허용 솔루션하지만봤을 때 미니 공황이 있었나요
셰인 존스

@Erenor Paz, 방금 ID와 문자열 모두에서 잘 작동하는 솔루션을 게시했습니다. wordpress.stackexchange.com/a/299325/25264
Pablo SG Pacheco

사용하는 LIKE것은 서버를 다운시킬 수있는 훌륭하고 빠른 방법입니다 (거짓 긍정은 말할 것도없고) 캐싱이 더 좋습니다.
Mark Kaplun

10

WP 데이터베이스에 항목을 직렬화 할 때 효율적인 방식으로 데이터를 쿼리 할 수 ​​없게됩니다.

직렬화를 통해 달성 할 수 있다고 생각하는 전반적인 성능 절약 및 이득은 크게 눈에 띄지 않을 것입니다. 약간 작은 데이터베이스 크기를 얻을 수 있지만 해당 필드를 쿼리하고 유용하고 의미있는 방식으로 비교하려고하면 SQL 트랜잭션 비용이 많이들 것입니다.

대신, 그 성격으로 쿼리하지 않으려는 데이터에 대한 직렬화를 저장하지 말고 직접 WP API 호출을 통해 수동 방식으로 만 액세스하십시오. get_post_meta()이 함수에서 직렬화 된 항목의 압축을 풀어 배열 특성에도 액세스 할 수 있습니다.

실제로 true 값을 다음 과 같이 할당합니다 .

$meta = get_post_meta( $post->ID, 'key', true );

데이터를 배열로 반환하여 평소대로 반복 할 수 있습니다.

캐싱, CSS 및 JS 축소와 같은 다른 데이터베이스 / 사이트 최적화 및 필요한 경우 CDN과 같은 서비스 사용에 집중할 수 있습니다. 이름하지만 몇 가지려면 .... 워드 프레스 코덱스은 해당 항목에 폭로 이상으로 포인트를 시작하는 좋은이다 : 여기


3

방금 직렬화 된 필드를 다루었 고 쿼리 할 수있었습니다. 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 레코드의 양이 필터링하기에 더 적습니다. 그런 다음 필터링하여 행을 더 줄이기 위해 where 문을 추가했습니다.meta_key

ID는 get_posts에 필요한 배열로 멋지게 끝납니다.

추신. 좋은 하위 쿼리 성능을 위해서는 MySQL v5.6 이상이 필요합니다


1

이 예는 정말 도움이되었습니다. 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. */
            }
    }
?>

1

String과 Integers로 저장되는 결과의 문제를 해결할 수있는 두 가지 솔루션이 있다고 생각합니다. 그러나 다른 사람들이 지적했듯이 Integer로 저장된 결과의 무결성을 보장 할 수는 없다고 말하는 것이 중요합니다. 이러한 값은 직렬화 된 배열로 저장된 값과 인덱스 및 값이 동일한 패턴으로 정확하게 저장되기 때문입니다. 예:

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'
            )
        )
    );

REGEXP특정 상황에서는 좋지만 사용할 수 있다면 LIKE선호하는 방법이라고 생각합니다. 내 견해로는 오래된 링크이지만 여전히 유용합니다 : thingsilearn.wordpress.com/2008/02/28/… :-)
Erenor Paz

@ErenorPaz 당신이 맞아요. LIKE가 더 빠르다. 그러나 이것은 문자열과 숫자 모두에서 작동하는 솔루션입니다.
Pablo SG Pacheco

그렇습니다. (항상 그렇듯이) 대답은 상황에 따라 "LIKE"를 사용할 수있는 경우입니다. REGEXP는 다음과 같이 할 것입니다 :-)
Erenor Paz

@ ErenorPaz, 나는 LIKE숫자와 문자열 모두 를 사용 하지만 작동 하는 새로운 솔루션을 추가하여 답변을 편집했습니다 . 다음을 사용하여 결과를 비교해야하기 때문에 성능에 대해 확신이 없습니다.OR
Pablo SG Pacheco

바로 그거죠 !!! 이 같은 결과를 얻을 필요가 .... 고마워!
kuldip Makadiya

0

WP_Query직렬화 된 배열 로 필터링 을 실행하기위한 많은 팁을 읽은 후 마지막으로 수행 한 방법은 다음과 같습니다 . 요청한 값에 대한 쉼표로 구분 된 목록을 검색하는 데 $wpdb사용 되는 사용자 지정 SQL 쿼리 와 함께 implode를 사용하여 쉼표로 구분 된 값의 배열을 만듭니다 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);포함 된 것을 모르는 경우 a 를 수행하십시오. 원하는 경우 루프를 설정하기 위해 쉽게 수정할 수 있습니다 ( setup_postdata($post);코덱스를 살펴보면 SELECT *게시물 ID 만 선택 $wpdb->get_results하고 올바른 $wpdb유형 으로 변경해야 할 수도 있습니다 - - 해당 주제 $wpdb에 대한 정보 는 코덱을 참조하십시오 ).

Whelp, 약간의 노력이 들었지만 직렬 또는 쉼표로 구분 된 값을 wp_query지원하지 'compare' => 'IN'않으므로이 shim이 가장 좋습니다!

이것이 누군가를 돕기를 바랍니다.


0

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%' )

0

내 메타 데이터가 배열 유형 인 경우 메타로 쿼리하기 위해이 방법을 사용합니다.

$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);

이로 인해 게시물 ID가 직렬화 된 문자열의 ID와 동일한 값인 경우 원하지 않는 결과가 발생할 수 있습니다.
Erenor Paz

0

위의 답변에서 궁금한 점이 있었으므로 대신 meta_querylatitude를 타겟팅했습니다 _coordinates. 메타 쿼리에서 직렬화 된 배열 내부의 특정 키를 대상으로 할 수 있는지 실제로 테스트해야했습니다. :)

그것은 사실이 아니 었습니다.

따라서 올바른 타겟팅 키는 _coordinates대신입니다 latitude.

$args = array(
     'post_type' => 'my-post-type',
     'meta_query' => array(
         array(
             'key' => '_coordinates',
             'value' => sprintf(':"%s";', $value),
             'compare' => 'LIKE'
         )
     )
 );

노트:

  1. 이 방법을 사용하면 정확히 일치하는 항목 만 타겟팅 할 수 있습니다. 따라서 50보다 큰 모든 위도 같은 것은 불가능합니다.

  2. 부분 문자열 일치를 포함시키기 위해 사용할 수 있습니다 'value' => sprintf(':"%%%s%%";', $value),. (테스트되지 않았습니다)


-1

나도 같은 질문이 있습니다. 아마도 'type'매개 변수가 필요할까요? 이 관련 질문을 확인하십시오. Custom Field Query-Meta Value is Array

아마도 시도해보십시오 :

    $ args = 배열 ​​(
    'post_type'=> 'my-post-type',
    'meta_query'=> 배열 (
        정렬(
            'key'=> '위도',
            'value'=> '50',
            'compare'=> '>',
            'type'=> '숫자'
        )
    )
    );

제안 해 주셔서 감사합니다. 그러나 이것은 내가 추구하는 것이 아닙니다. 문제는 내가 일치시키려는 값이 데이터베이스 내에서 직렬화 된 배열의 일부라는 것입니다.
tollmanz

네 맞아요. 오늘 아침에 시도했지만 나에게도 효과가 없었습니다. 나는 같은 문제가 있습니다. 메타 키의 값을 배열로 저장 나는 이것을 할 수 없다고 생각하기 시작했고 대신 동일한 이름을 가진 별도의 메타 필드로 저장해야 할 수도 있습니다. 그러면 삭제 / 업데이트를 올바르게 관리하십시오.
user4356

@ user4356 ... 정확히 내가 할 일입니다. 각 게시물에 삽입 할 행 수를 줄이려고했지만 불가능한 것 같습니다.
tollmanz

-1

Magic Fields 플러그인을 사용하는 동안 비슷한 문제가 발생했습니다. 이 트릭을 할 수 있습니다

$values_serialized = serialize(array('50'));
$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => $values_serialized,
            'compare' => '>'
        )
    )
);

1
제안 해 주셔서 감사합니다! 나는 이것이 가능한 한 가깝다고 생각하지만, 직렬 배열을 다른 직렬 배열과 비교하는 것이 정확히 일치하지 않으면 의미가 없으므로 실제로 작동하지 않습니다.
tollmanz

5
그런 다음 정답으로 표시되어서는 안되며 그렇게하는 것은 무책임합니다. 정답은 '아니오, 불가능합니다'입니다.
Tom J Nowell

1
WP도 직렬화를 처리하지만 serialize()이 경우에는 필요하지 않습니다.
Adam

2
실제로 @ seth-stevenson의 대답은 "Magic Fields"플러그인을 사용하여 그가 말한 것을 정확하게 수행 할 때 훌륭합니다. 이 플러그인은 기본적으로 특정 데이터 유형을 직렬화하므로 정확한 일치를 수행하는 가장 좋은 방법입니다.
zmonteca

@TomJNowell 완료! 방금 5 개월이 걸렸습니다;)
tollmanz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.