Ako filtrovať príspevky podľa poľa hodnôt uložených v meta

Meta dáta vo WordPresse slúžia na ukladanie dát, ktoré súvisia práve s konkrétnym článkom, napr. k článkom typu “hráč” priradíme meta dáta “pozicia”. Aj keď meta dáta sa do databázy ukladajú vždy ako string, existujú situácie, kedy tento string môže predstavovať serializované pole, dátum, číslo a pod.  Pomerne jednoducho možno pridať vlastné meta dáta pomocou tzv. “custom fields” alebo s využitím pluginov ako napr ACF alebo CMB2.

WordPress ponúka celý rad funkcií, ktoré môžete volať odkiaľkoľvek a ktoré vám umožnia získať potrebné dáta. Medzi takéto funkcie patria aj funkcie pre získanie článkov či používateľov, konkrétne funkcia get_posts a funkcia get_users. Obe tieto funkcie maju jediný parameter, ktorý predstavuje pole s argumentami. Jedným z týchto argumentov je meta_query. Ten slúži na filtrovanie výsledkov na základe meta dát.

  1. ziskat data bez meta query a nasledne vo foreach getnut meta a porovnat – neefektivne, zataz na DB,
  2. vygenerovat samostatne meta queries pre každu položku pola a použiť operator LIKE – nebzepecne
  3. pouzit regex

Pred tým, než si ukážeme riešenia na praktíckom príklade, uvedieme alternatívne spôsoby ukladania meta udajov predstavujúce pole.

  • Uloženie každej hodnoty z pola hodnôt do samostatného meta pola s hodnotami true/false, napríklad: position_obranca = 1, position_utocnik = 1, position_obranca = 1 a pod. 
  • Pouzitie vlastnej taxonomi a argumentu tax_query s operátorom IN vo funkcii get_posts()

Príklad z praxe

Predstavme si situáciu, že máme vo WordPresse vytvorený vlastný typ príspevku “player” a k nemu priradené meta pole “positions”. Toto pole môže nadobúdať hodnoty “brankar”, “utocnik” a “obranca”, pričom hráč musí mať priradenú aspoň jednu z týchto hodnôt.

Môžeme si to predstaviť ako multiple checkbox:

Teraz si predstavme, že potrebujeme vybrať vštkých útočníkov a obrancov, teda tých, ktorí majú meta pole “positions” s hodnotou “utocnik”, “obranca” alebo kombináciu týchto dvoch hodnôt. Budeme teda porovnávať pole hodnôt (utocnik, obranca) s polom hodnôt v meta poli “positions”. Keďže ide o hodnotu pola, takato hodnota sa ulozi do databázy ako serializovaný string. Napríklad hráč so všetkými pozíciami (brankar, utocnik, obranca) bude mať uloženú túto hodnotu takto:

				
					a:3:{i:0;s:7:"brankar";i:1;s:7:"utocnik";i:2;s:7:"obranca";}

				
			

Ukážeme si spôsoby, ako dokážeme filtrovať príspevky na základe takto uloženej meta hodnoty v databáze.

1. spôsob - filtrovanie pomocou vlastnej PHP funkcie

Prvým riešenim takéhoto problému je, že si získame všetkých hráčov bez filtrovania (meta_query) a nasledne si každého hráča pomocou foreach prejdeme, získame jeho hodnotu meta “positions” a porovname ju pomocou funkcie array_intersect s polom hľadaných hodnôt (utocnik, obranca). Pre univerzálnosť si vytvoríme teda funkciu s troma argumentami:

  • meta_key (text) – predstavuje kľúč meta hodnoty, ktorú budeme porovnávať
  • posts (pole) – zoznam všetkych hráčov
  • values (pole) – zoznam hľadaných hodnôt meta
				
					function filter_posts_by_meta_array($meta_key,$posts, $values) {
    $filtered_posts = [];

    if (!empty($posts)) {
        foreach ($posts as $post) {
            $meta_values = get_post_meta($post->id, $meta_key, 1);
            $intersect = array_intersect($values, $meta_values);

            if (!empty($intersect))
                $filtered_posts[] = $post;
        }
    }

    return $filtered_posts;
}

				
			

Čiže v našom prípade bude použitie funkcie nasledovné:

				
					$searched_values = [
    'obranca',
	'utocnik'
];

$posts = get_posts([
	'post_type' => 'player',
	'posts_per_page' => -1,
]);

$filtered_posts = filter_posts_by_meta_array('positions', $posts, $searched_values);

				
			

Nevýhodou tohto riešenia by bola zvýšená záťaž na databázu ako aj na server, kedže by sme museli dopytovat meta hodnotu pre každého hráča samostatne a pri veľkom počte hráčov by tento proces mohol spomalovať webstránku.

2. spôsob - viacnásobné meta_query

Ďalším zo spôsobov ako vyriešiť tento problém je, že použijeme argument meta_query pre každú hodnotu z hľadaných hodnôt s použitím operátora LIKE a s relačným vzťahom OR.

Čiže v našom prípade bude meta_query vyzerať nasledovne

				
					$args = [
    'post_type' => 'player',
    'posts_per_page' => -1,
    'meta_query' => [
        'relation' => 'OR',
        [
            'key' => 'positions',
            'value' => 'obranca',
            'compare' => 'LIKE', 
        ],
                [
            'key' => 'positions',
            'value' => 'utocnik',
            'compare' => 'LIKE', 
        ]
    ]
];

$posts = get_posts($args);
				
			

Ak by sme si chceli toto riešenie ešte uľahčiť, môžeme si vytvoriť jednoduchú PHP funkciu na generovanie takýchto viacnásobných meta_query. 

				
					function px_create_meta_query_for_array($meta_key, $values, $relation = "OR"): array
{
    $meta_query = [];

    if (!empty($values)) {
        $meta_query[$meta_key]['relation'] = $relation;

        foreach ($values as $value) {
            $meta_query[$meta_key][] = array(
                'key' => $meta_key,
                'value' => $value,
                'compare' => 'LIKE'
            );
        }

    }

    return $meta_query;
}
				
			

Ako môžeme vidieť, funkcia obsahuje tri parametre:

  • meta_key (text) – povinný parameter predstavujúci kľúč meta hodnoty, ktorú budeme porovnávať
  • values (pole) – povinný parameter predstavujúci zoznam hľadaných hodnôt meta
  • relation – nepovinný parameter s možnosťou výberu použitia relačného vzťahu OR (default) alebo AND

Posledný nepovinný parmeter slúži aj pre prípad, ak by sme potrebovali získať tie záznamy, ktorých meta hodnota “positions” je “obranca” a zároveň (AND) aj “utocnik”. Pri použití OR získame záznamy, ktorých meta hodnota “positions” je “utocnik” alebo “obracna”, resp. kombinácia oboch hodnôt.

Použitie tejto funkcie bude v našom prípade nasledovné:

				
					$searched_values = [
    'obranca',
	'utocnik'
];

$meta_query = px_create_meta_query_for_array('positions', $searched_values);

$args = [
    'post_type' => 'player',
    'posts_per_page' => -1,
    'meta_query' => $meta_query
];

$posts = get_posts($args);
				
			

Nevýhodou tohto riešenia je použitie operátora LIKE, ktorý hľadá zhodu aj medzi substringami, to znamená, že ak by sme napriklad hľadali hodnotu “auto” a existovala by aj meta hodnota s hodnotou “autobus”, aj táto hodnota by bola zahrnutá vo výsledku, čo však nie je správne. Avšak pokial máme vopred známe meta hodnoty a vieme túto chybovosť vylúčiť, čo je aj naš prípad, je toto riešenie správne.

3. spôsob - použitie operátora REGEXP

Na záver si popíšeme najlepšie riešenie tohto problému, a to použitie operátora REGEXP v argumente meta_query. Pre použitie tohto operátora v meta_query, potrebujeme prekonvertovať naše pole hľadných hodnôt do stringu, predstavujúci hľadané hodnoty oddelené znakom “|”. To docielíme použitím funkcie implode(). Okrem toho musí byť tento string ešte v obyčajných zátvorkách. Čiže v našom prípade bude string vyzerať takto “(obranca|utocnik)”

				
					$searched_values = [
    'obranca',
	'utocnik'
];

$args = [
    'post_type' => 'player',
    'posts_per_page' => -1,
    'meta_query' => [
        [
            'key' => 'positions',
            'value' => '"('. implode('|', $searched_values) .')"', //'"(obranca|utocnik)"'
            'compare' => 'REGEXP'
        ],
    ],
];

$posts = get_posts($args);
				
			
Picture of Roman Kraiger

Roman Kraiger

Zakladateľ agentúry Pixeler, vášnivý cyklista, programátor a občasný bloger.

Páčil sa Vám tento článok?

Odmeňte ho páčikom.

alebo ho zdieľajte medzi priateľmi na:

Pridaj komentár

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *