Attribuut-taxonomieën (WooCommerce)

Uit De Vliegende Brigade
Naar navigatie springen Naar zoeken springen
Database-model van vermoedelijk WordPress 4.4. Ietwat achterhaald, maar prima om een indruk te krijgen

WooCommerce kent drie taxonomische systemen: Categorieën (zwaar en veelzijdig), Tags (ken ik niet) en Attributes (licht en eenvoudig, Kenmerken in het Nederlands). Dit artikel gaat over dit laatste systeem.

Attributes komen in twee smaken:

  • Predefined Attributes
  • Custom Attributes.

Dit hoofdstuk gaat voornamelijk over deze eerste variant.

Database-tabellen

wp_woocommerce_attribute_taxonomies

Tabel wp_woocommerce_attribute_taxonomies Lijkt niets te doen. Zie hoodstuk over verwijdering van attribuut-taxonomieën, elders in dit artikel:

SELECT * FROM kbo3.wp_woocommerce_attribute_taxonomies;


attribute_id   attribute_name      attribute_label     attribute_type   attribute_orderby   attribute_public
------------   --------------      -----------------   --------------   -----------------   ----------------
5	       breedte             Breedte	       text	        name_num	    1
6	       diepte	           Diepte	       text	        name_num	    1
7	       lengte	           Lengte	       text	        name_num	    1
8	       automatische_stop   Automatische stop   text	        name	            1

wp_terms

Subtaxons verschijnen gewoon in wp_terms. Dat maakt het gemakkelijk om met subtaxons te werken, want het werkt hetzelfde als voor gewone categorieën.

wp_term_taxonomy

Tabel wp_term_taxonomy verbindt taxons met taxonomieën. Hier kun je zien dat WooCommerce 'onder water' losse taxonomieën defineert voor alle attributen. Je herkent ze aan de prefix pa_. Merk op dat parent=0: Er wordt niet gebruik gemaakt van hierarchie! Ook niet zo vreemd, omdat je bij attributen max. twee lagen hebt. Voorbeeld:

SELECT * FROM kbo3.wp_term_taxonomy where taxonomy like "pa_%";

term_taxonomy_id   term_id   taxonomy     description     parent   count
----------------   -------   ----------   -------------   ------   -----
110                110       pa_breedte   Breedte-taxon   0        0

wp_term_relationships

Tabel wp_term_relationships verbindt taxons met WordPress-objecten.

wp_termmeta

Tabel wp_termmeta lijkt metadata te bevatten: Als je nog iets extra's kwijt wilt over een taxon, kun je dat hier doen. WooCommerce maakt hier bv. voor product_cat-taxonomieën aan, om de count per hoofdtaxon in op te slaan.

Het lijkt vooralsnog niet gebruikt te worden voor attribuut-taxonomieën.

Database-velden

In het algemeen zijn veldnamen binnen een WordPress-instantie niet consistent. Dat kun je zien aan het ER-diagram elders in dit artikel:

  • In tabel wp_users heet de pk ID
  • In tabel wp_usermeta heet dit veld user_id
  • In tabel wp_posts heet dit veld post_author.

Desalniettemin: Rondom taxonomieën lijken veldnamen consistent te zijn. Anders had dit hoofdstuk weinig zin.

term_id

term_id is de pk voor een taxon of term.

term_taxonomy_id

  • term_taxonomy_id is de pk van de tussentabel wp_term_taxonomy, die ervoor zorgt dat je een many-to-many-relatie hebt tussen terms en term_relationships
  • Zo'n veel-veel-relatie ben ik tot op heden nog niet tegengekomen. Vandaar dat deze twee velden steeds dezelfde waardes lijken te hebben: Er zijn dan evenveel records in beide tabellen
  • Overigens: Ik heb geen idee waarom je zo'n veel-veel-relatie wilt hebben, maar daar gaat het nu niet om.

UI-Velden

Dit zijn de velden die ik zoal in de user interface tegenkom. Namen zijn inconsistent en niet-intuïtief

Name - attribute_name

Shown on the front-end

Slug - attribute_label

Max. 28 karakters.

attribute_type

Het soort control dat gebruikt wordt als interface??? Bv.:

  • select - Beperkt aantal keuzes, zoals een lookup-tabel of voor Boolese waarden
  • text - Tekstveld, lijkt me.

Enable Archives

Dus dat je een 'taxon-pagina' hebt. Altijd doen. Voor Attributes-taxonomieën kun je helaas geen afbeelding of tekst toevoegen (itt. gewone Categorieën) [1]

Custom ordering - attribute_orderby

Niet zo spannend:

  • menu_order
  • name
  • etc.

attribute_public

Taxonomieën worden binnen een WordPress-site veelvuldig gebruikt voor interne aangelegenheden. Bv. om vertalingen bij te houden. Vandaar dat je er voor kunt kiezen om attribuut-taxonomieën verborgen te houden voor gebruikers.

Taxonomie toevoegen

Met taxonomie bedoel ik hier hetzelfde als hoofdtaxon .

Toevoegen van een hoofdtaxon/taxonomie, wil niet lukken met bv. wp_insert_term. Aangezien Attributes een speciaal geval is van een categorie, zou je verwachten dat dit werkt. Maar dat doet het niet. Gelukkig kun je dit op een meer low-level fixen mbv. $wpdb. Daar gebruik ik deze functies voor. Deze staat in één van m'n libraries (niet in functions.php). Geïnspireerd op [2]:

function dvb_add_attribute_taxonomy($attribute)
{
    # Add a WooCommerce Attribute-taxonomy
    #######################################################################################################
    #
    # * Somehow, the usual function register_taxonomy doesn't work for WooCommerce Attributes, hence the
    #   lower-level approach in this function
    # * Based on the example https://wordpress.stackexchange.com/questions/244335/creating-custom-woocommerce-attribute-taxonomies-from-a-plugin
    # * Jeroen Strompf, De Vliegende Brigade - April 2019
    # * To be included in functions.php where appropriate


    #######################################################################################################
    # Get acces to $wpdb class
    #######################################################################################################
    #
    global $wpdb;


    #######################################################################################################
    # Verify input
    #######################################################################################################
    #
    if (empty($attribute['attribute_type'])) { $attribute['attribute_type'] = 'text';}
    if (empty($attribute['attribute_orderby'])) { $attribute['attribute_orderby'] = 'menu_order';}
    if (empty($attribute['attribute_public'])) { $attribute['attribute_public'] = 0;}
    if (empty( $attribute['attribute_name'] ) || empty( $attribute['attribute_label'] ) ) 
    {
            return new WP_Error( 'error', __( 'Attribute name and/and slug is missing.', 'woocommerce' ) );
    } 
    elseif ( ( $valid_attribute_name = dvb_check_attribute_name( $attribute['attribute_name'] ) ) && is_wp_error( $valid_attribute_name ) ) 
    {
            return $valid_attribute_name;
    } 
    elseif ( taxonomy_exists( wc_attribute_taxonomy_name( $attribute['attribute_name'] ) ) ) 
    {
            return new WP_Error( 'error', sprintf( __( 'Slug "%s" already in use', 'woocommerce' ), sanitize_title( $attribute['attribute_name'] ) ) );
    }


    #######################################################################################################
    # Insert into WordPress
    #######################################################################################################
    #
    $wpdb->insert( $wpdb->prefix . 'woocommerce_attribute_taxonomies', $attribute );

    do_action( 'woocommerce_attribute_added', $wpdb->insert_id, $attribute );

    flush_rewrite_rules();
    delete_transient( 'wc_attribute_taxonomies' );

    return true;
};

function dvb_check_attribute_name( $attribute_name )
{
    # Check if a given attribute name is valid
    #######################################################################################################
    #
    # * Based on the example https://wordpress.stackexchange.com/questions/244335/creating-custom-woocommerce-attribute-taxonomies-from-a-plugin
    # * Jeroen Strompf, De Vliegende Brigade - April 2019
    # * To be included in functions.php where appropriate

    if ( strlen( $attribute_name ) >= 28 ) 
    {
		return new WP_Error( 'error', sprintf( __( 'Slug "%s" is longer than 28 characters)', 'woocommerce' ), sanitize_title( $attribute_name ) ) );
    } 
    elseif ( wc_check_if_attribute_name_is_reserved( $attribute_name ) ) 
    {
		return new WP_Error( 'error', sprintf( __( 'Slug "%s" is not allowed because it is a reserved term.', 'woocommerce' ), sanitize_title( $attribute_name ) ) );
    }
    return true;
};

Voorbeeld van een aanroep:

<?php

###############################################################
# Call require_once
###############################################################
#
# Functions "dvb_add_attribute_taxonomy" and 
# "dvb_check_attribute_name" have been included in functions.php. 
# They become available through this "require_once" command
#
require_once("/home/strompf/www/example.com/wp-load.php");


###############################################################
# Define new attribute-taxonomy
###############################################################
#
$attribute_definition = array
(
	'attribute_name'	=>	'attribute-naam (2)',
	'attribute_label'	=>	'attribute-plakker (2)',
	'attribute_type'	=>	'text',
	'attribute_orderby'	=>	'menu_order',
	'attribute_public'	=>	false
);


###############################################################
# Create this new attribute-taxonomy
###############################################################
#
dvb_add_attribute_taxonomy($attribute_definition);

Subtaxons toevoegen

  • Attribuut-taxonomieën zijn niet-hiërarchisch, en zitten iets anders in elkaar dan product_cat-taxonomieën:
  • Subtaxons toevoegen, gaat op dezelfde manier als voor product_cat-taxonomieën, met één verschil: De naam van de taxonomie, is de naam, voorafgegaan door pa_
  • De gebruikelijke API-functie hiervoor: wp_insert_term

Voorbeeld uit een SQL-script, waarbij pa_breedte de betreffende taxonomie is:

# Create table
##############
#
create table dim1_taxons_tmp
select distinct 
	dim1,
    concat
    (
	"wp_insert_term('",
        replace(dim1, ".", ","),"'",
        ", 'pa_breedte',",
        " array('description' => 'Alle koolborstels met een breedte van ",replace(dim1, ".", ",")," mm',",
        " 'parent' => 0, 'slug' => 'breedte-",replace(dim1, "," ,""),"' ));"
     ) as dim1_php
from root_tmp;

Je weet de 'pa-naam' pas met zekerheid, nadat er subtaxons zijn aangemaakt. Dan vind je 'm terug met zoiets als dit (in SQL):

SELECT distinct taxonomy FROM example_com.wp_term_taxonomy;

Mochten er nog geen taxons zijn toegevoegd:

  • Voeg er eentje handmatig toe, om het te testen
  • Gebruik je gezond verstand om uit te vogelen wat de naam zou zijn.

Nog één voorbeeld:

<?php
require_once("/var/www/kbo3.dvb/wp-load.php");

wp_insert_term
(
	'Met automatische stop' ,
	'pa_automatische_stop', 
	array
	(
		'description' => 'Alle koolborstels met automatische stop', 
		'parent' => 0, 
		'slug' => 'met-automatische-stop'
	)
);

wp_insert_term
(
	'Zonder automatische stop' ,
	'pa_automatische_stop', 
	array
	(
		'description' => 'Alle koolborstels zonder automatische stop', 
		'parent' => 0, 
		'slug' => 'zonder-automatische-stop'
	)
);

Verwijderen

Hoe kun je alle attribuut-taxonomieën compleet verwijderen? Dus inclusief hoofdtaxons, subtaxons, en koppelen met objecten? Voor een beetje serieuse site, is het geen optie om dit handmatig te doen, vandaar. Ik kwam geen kant-en-klare code tegen, dus lekker zelf doen!

De uitdaging is, om de onderdelen in de juiste volgorde te verwijderen, zodat je weet wat-wat is. Als ik bv. terms als laatste verwijder, is er een kans dat ik niet meer weet bij welke taxonomie ze horen. Een impressie:

select * from wp_term_relationships;		     # Taxons <-> Objecten - Vroeg verwijderen
select * from wp_termmeta;			     # Metadata <-> Taxons - Vroeg verwijderen
select * from wp_term_taxonomy;			     # Taxons <-> Taxonomy - Laat verwijderen
select * from wp_terms;				     # Taxons              - Laat verwijderen
select * from wp_woocommerce_attribute_taxonomies;   # Hoofdtaxons         - Als laatste verwijderen

Taxons verwijderen

Dit script verwijdert alle WooCommerce-gerelateerde taxons, inclusief product_cat-taxons:

function dvb_delete_all_woocommerce_taxonomical_data()
{
	global $wpdb;

	###################################################################################
	# Delete all category & attribute taxons
	###################################################################################
	#
	# * This SQL script deletes taxons, not the taxonomies themselves
	# * It works at the db-level. It doesn't use any API calls (except for db-access)
	# * Is uses a temporary table "taxon_tmp" to hold intermediate results.
	#   As this is a temporary table, it doesn't need to be explicitly deleted
	#
	$sql_01 = "drop table if exists taxon_tmp;";
	$sql_02 = "create temporary table taxon_tmp select term_id from ".$wpdb->prefix."term_taxonomy where taxonomy like 'product_cat' or taxonomy like 'pa%';";
	$sql_03 = "delete ".$wpdb->prefix."termmeta from ".$wpdb->prefix."termmeta where meta_key like 'product_count_product_cat';";
	$sql_04 = "delete ".$wpdb->prefix."termmeta from ".$wpdb->prefix."termmeta join taxon_tmp on wp_termmeta.term_id = taxon_tmp.term_id;";
	$sql_05 = "delete ".$wpdb->prefix."term_relationships from ".$wpdb->prefix."term_relationships join taxon_tmp on ".$wpdb->prefix."term_relationships.term_taxonomy_id = taxon_tmp.term_id;";
	$sql_06 = "delete ".$wpdb->prefix."term_taxonomy from ".$wpdb->prefix."term_taxonomy where taxonomy like 'product_cat';";
	$sql_07 = "delete ".$wpdb->prefix."term_taxonomy from ".$wpdb->prefix."term_taxonomy where taxonomy like 'pa_%';";
	$sql_08 = "delete ".$wpdb->prefix."terms from ".$wpdb->prefix."terms join taxon_tmp on ".$wpdb->prefix."terms.term_id = taxon_tmp.term_id;";

	# Ready to go?
	######################################
	#
	// echo "\nsql_01: ".$sql_01;
	// echo "\nsql_02: ".$sql_02;
	// echo "\nsql_03: ".$sql_03;
	// echo "\nsql_04: ".$sql_04."\n";

	 
	try
	{
	    $wpdb->query($sql_01);
	    $wpdb->query($sql_02);
	    $wpdb->query($sql_03);
	    $wpdb->query($sql_04);
	    $wpdb->query($sql_05);
	    $wpdb->query($sql_06);
	    $wpdb->query($sql_07);
	    $wpdb->query($sql_08);	    
	}
	catch(Exception $e){}
}

Attribuut-taxonomieën verwijderen

Om gelijk met iets opmerkelijks te beginnen: Ik kan deze taxonomieën niet handmatig verwijderen binnen de webinterface.

Niet wp_woocommerce_attribute_taxonomies

De tabel wp_woocommerce_attribute_taxonomies lijkt niet te maken te hebben met deze attributen: Als ik de inhoud verwijder, verandert er niets:

Beschikbare attribuut-taxonomieëm - Voor en na verwijderen van wp_woocommerce_attribute_taxonomies
Tabel wp_woocommerce_attribute_taxonomies - Voor ik 'm verwijderde

wp_options - _transient_wc_attribute_taxonomies

Tjakka: wp_options bevat de definities. Waarde van option_value in bovenstaand voorbeeld:

a:11:{i:0;O:8:"stdClass":6:{s:12:"attribute_id";s:2:"10";s:14:"attribute_name";s:8:"apparaat";s:15:"attribute_label";s:8:"Apparaat";s:14:"attribute_type";s:6:"select";s:17:"attribute_orderby";s:10:"menu_order";s:16:"attribute_public";s:1:"1";}i:1;O:8:"stdClass":6:{s:12:"attribute_id";s:1:"5";s:14:"attribute_name";s:17:"automatische_stop";s:15:"attribute_label";s:17:"Automatische stop";s:14:"attribute_type";s:6:"select";s:17:"attribute_orderby";s:10:"menu_order";s:16:"attribute_public";s:1:"1";}i:2;O:8:"stdClass":6:{s:12:"attribute_id";s:1:"1";s:14:"attribute_name";s:7:"breedte";s:15:"attribute_label";s:7:"Breedte";s:14:"attribute_type";s:4:"text";s:17:"attribute_orderby";s:10:"menu_order";s:16:"attribute_public";s:1:"1";}i:3;O:8:"stdClass":6:{s:12:"attribute_id";s:1:"7";s:14:"attribute_name";s:9:"connector";s:15:"attribute_label";s:9:"Connector";s:14:"attribute_type";s:6:"select";s:17:"attribute_orderby";s:10:"menu_order";s:16:"attribute_public";s:1:"1";}i:4;O:8:"stdClass":6:{s:12:"attribute_id";s:1:"4";s:14:"attribute_name";s:7:"diagram";s:15:"attribute_label";s:7:"Diagram";s:14:"attribute_type";s:4:"text";s:17:"attribute_orderby";s:10:"menu_order";s:16:"attribute_public";s:1:"1";}i:5;O:8:"stdClass":6:{s:12:"attribute_id";s:1:"2";s:14:"attribute_name";s:6:"diepte";s:15:"attribute_label";s:6:"Diepte";s:14:"attribute_type";s:4:"text";s:17:"attribute_orderby";s:10:"menu_order";s:16:"attribute_public";s:1:"1";}i:6;O:8:"stdClass":6:{s:12:"attribute_id";s:1:"3";s:14:"attribute_name";s:6:"lengte";s:15:"attribute_label";s:6:"Lengte";s:14:"attribute_type";s:4:"text";s:17:"attribute_orderby";s:10:"menu_order";s:16:"attribute_public";s:1:"1";}i:7;O:8:"stdClass":6:{s:12:"attribute_id";s:1:"9";s:14:"attribute_name";s:4:"merk";s:15:"attribute_label";s:4:"Merk";s:14:"attribute_type";s:6:"select";s:17:"attribute_orderby";s:10:"menu_order";s:16:"attribute_public";s:1:"1";}i:8;O:8:"stdClass":6:{s:12:"attribute_id";s:2:"11";s:14:"attribute_name";s:21:"originele koolborstel";s:15:"attribute_label";s:21:"Originele Koolborstel";s:14:"attribute_type";s:6:"select";s:17:"attribute_orderby";s:10:"menu_order";s:16:"attribute_public";s:1:"1";}i:9;O:8:"stdClass":6:{s:12:"attribute_id";s:1:"6";s:14:"attribute_name";s:11:"stroomdraad";s:15:"attribute_label";s:11:"Stroomdraad";s:14:"attribute_type";s:6:"select";s:17:"attribute_orderby";s:10:"menu_order";s:16:"attribute_public";s:1:"1";}i:10;O:8:"stdClass":6:{s:12:"attribute_id";s:1:"8";s:14:"attribute_name";s:4:"veer";s:15:"attribute_label";s:4:"Veer";s:14:"attribute_type";s:6:"select";s:17:"attribute_orderby";s:10:"menu_order";s:16:"attribute_public";s:1:"1";}}
De waarde van het veld option_value verwijderd - De definities zijn verdwenen, maar WooCommerce is het er niet mee eens
Het hele record verwijderd - Da's beter. Dus zoiets als delete from wp_options where option_name like "_transient_wc_attribute_taxonomies";

In code, toegevoegd aan de code eerder in dit hoofdstuk:

	###################################################################################
	# Delete attribute-taxonomie-definities
	###################################################################################
	#
	$sql_01 = "delete from ".$wpdb->prefix."wp_options where option_name like '_transient_wc_attribute_taxonomies';";

	try
	{
	    $wpdb->query($sql_01);
	}
	catch(Exception $e){}

Zie ook

Bronnen