Eén van de leuke dingen van (ook) white label diensten aan andere WordPress professionals aan te bieden, is dat je letterlijk de ‘krenten uit de pap’ krijgt. Ik houd van ‘dingen uitzoeken’ en leuke uitdagingen en nog niet zo lang geleden kreeg ik wel een hele leuke uitdaging.
Zoals je wellicht weet, is mijn ‘Support Strippenkaart‘ ook beschikbaar voor collega WordPress Professionals wanneer ze een keer iets gedaan willen hebben, wat niet zo makkelijk te realiseren is. Het leuke hiervan voor mij is, dat ik de meest uitzonderlijke opdrachten krijg. Een kolfje naar mijn hand.
Een tijdje terug heb ik een leuke eerdere uitdaging hier gepubliceerd. Een webshop die letterlijk op zondag moest sluiten. Etalage kijken was ok, maar ‘op de Dag des HEEREN’ mag niet worden gehandeld volgens de eindklant.
Altijd leuk om een soort James T. Kirk of Mister Spock te zijn en ’to boldly (of in mijn geval bijna ‘baldly’) go where no-one has gone before’. De grenzen van WordPress en WooCommerce (mijn mini-universum) verkennen is altijd leuk.
Nog niet zo lang geleden kreeg ik een nieuwe vraag. Een (kersverse) klant van mij had een klant met een lokaal bezorgingsrestaurant. En dit restaurant biedt op specifieke dagen specifieke gerechten tegen een gereduceerde prijs aan.
Omdat het ‘not done’ is om teveel te vertellen over ‘klanten van klanten’ en vooral niet, wanneer het identificerende gegevens zijn, heb ik een compleet ander concreet voorbeeld genomen.
Ik houd van pizza. Niet echt goed voor mijn gewicht, dus mijn inname is heel beperkt, maar het blijft toch wel heel lekker. Ik houd ook van pasta. Zelfde verhaal. Dus in mijn voorbeeld gebruik ik de situatie dat het restaurant op woensdag alle pizza’s voor 7 euro aanbiedt, met uitzondering van de calzones. Daar betaal je de volle prijs voor.
Op donderdag krijg je korting van 10% op alle pasta schotels, behalve op hun lasagna met huisgemaakte lasagna vellen.
Kun je mij nog volgen? Indien niet, geen probleem. Ik moest het ook uittekenen voor ik het precies kon begrijpen.
Maar het komt erop neer, dat op sommige dagen producten in een categorie goedkoper zijn, behalve producten in een specifieke sub-categorie.
Beperkingen
Ik heb deze code ontwikkeld voor een specifieke klant. En gelukkig voor mij zijn de kortingen op pizza’s en pasta voor die dagen alleen geldig op standaard pizza’s. In zijn aanbod heeft hij ook ‘composite products‘ (maak je eigen pizza), maar daar biedt hij geen kortingen op aan.
Alle producten waar de korting op geldt zijn dus ‘Eenvoudige / Enkelvoudige / Simpele’ producten.
Een tweede beperking waar ik best blij om was, is dat de klant van mijn klant vond dat in periodes met korting, de ‘kortingsdagen’ niet meetelden. Ook dit zou de code onnodige hebben bemoeilijks.
De code
Om dit te realiseren zou mijn favorite oplossing zijn om de variabele factoren niet in de code op te nemen. Wanneer de klant zou besluiten bijvoorbeeld om op een woensdag of donderdag die op een feestdag valt geen korting aan te bieden, zou het leuk zijn om ook dit in de code op te nemen. Hoe je dat doet zie je overigens in het eerdere artikel over de ‘zondag sluiting’.
De eerste vraag is natuurlijk, hoe bepalen we welke dag van de week het is. Dat is eigenlijk super eenvoudig. Tenminste, wanneer je tot 7 kan tellen.
<?php
$current_day_of_week = date('N');
De PHP ‘date’ functie is één van de meest magische functies om wel gegoochel met data dan ook te doen.
Mocht je overigens nog steeds PHP 7 of lager gebruiken, zal bovenstaande functie niet werken. Maar aangezien PHP 7 een uitgefaseerde versie is, houd ik hier geen rekening mee. Je hebt minimaal PHP 8.0, of je gaat daar zo snel mogelijk naar over.
Punt uit.
date(‘N’) geeft de numerieke waarde van de dag terug, met maandag als de eerste dag van de week. Omdat we geïnteresseerd zijn in woensdag en donderdag, kijken wij dus naar de waarden 3 en 4.
En na al die andere snippets weet je natuurlijk ook dat we -om ‘in te breken’ op de standaard processen van WordPress- we ‘filterhooks’ moeten gebruiken, Want we willen geen acties uitvoeren (action hooks), maar gegevens aanpassen (‘filter hooks’).
Laten we inhaken…
De hooks die we hier nodig hebben zijn ‘woocommerce_product_get_price’ en ‘woocommerce_product_get_regular_price’. De eerste geeft de berekende prijs na alle kortingen, de tweede geeft de ‘reguliere prijs’. De wens van de klant was om de ‘normale prijs’ op de kortingsdagen niet te tonen. Zijn gedachten daarachter was heel eenvoudig. Als mensen zien hoeveel korting ze hebben, kiezen ze automatisch het product met de meeste korting. Als ze het niet zien, kiezen ze wat ze liever hebben.
Mocht jij of jouw klant willen, dat de korting wel zichtbaar is, dan maak je geen hook for ‘woocommerce_product_get_regular_price’.
Het geraamte voor de prijsverandering wordt in mijn geval:
<?php
add_filter('woocommerce_product_get_price', 'wxp_price_on_specific_day_of_week', 10, 2);
add_filter('woocommerce_product_get_regular_price', 'wxp_price_on_specific_day_of_week', 10, 2); //laat deze regel weg als je de prijzen wilt vergelijken.
function wxp_price_on_specific_day_of_week($price, $product) {
//doe je ding!
}
Ik wil eigenlijk de code zo generiek mogelijk maken. Wanneer de klant-van-de-klant morgen beslist dat maandag alle soepen voor de halve prijs zijn behalve de minestrone soep, dan moet dat ook makkelijk toe te voegen zijn.
Wanneer je al enige -of veel- PHP ervaring hebt, dan komt misschien de volgende constructie in gedachten.
<?php
add_filter('woocommerce_product_get_price', 'wxp_price_on_specific_day_of_week', 10, 2);
add_filter('woocommerce_product_get_regular_price', 'wxp_price_on_specific_day_of_week', 10, 2); //laat deze regel weg als je de prijzen wilt vergelijken.
function wxp_price_on_specific_day_of_week($price, $product) {
$current_day_of_week = date('N');
switch($current_day_of_week) {
case 1 : //maandag
//code
break;
case 2 : //dinsdag
//code
break;
//etc
}
}
En dat is de meest effectieve manier, als het altijd zo zou zijn dat er maar één aanbieding per dag kan zijn.
Maar wat wanneer op woensdagen alle pizza’s maar 7 euro kosten, en er 20% op de chianti wijnen is?
Wat we hier dus nodig hebben is niet de super effective ‘switch’, die maar één keuze toelaat, maar een structuur die meerdere keuzes per dag toelaat.
Had ik je al verteld, dat ik van uitdagingen houd?
Laten we wat meer vlees aan dit geraamte toevoegen.
<?php
add_filter('woocommerce_product_get_price', 'wxp_price_on_specific_day_of_week', 10, 2);
add_filter('woocommerce_product_get_regular_price', 'wxp_price_on_specific_day_of_week', 10, 2); //laat deze regel weg als je de prijzen wilt vergelijken.
function wxp_price_on_specific_day_of_week($price, $product) {
$actions = Array(
Array(
'weekday' => 3,
'include' => Array('pizza'),
'exclude' => Array('calzone'),
'fixed', => 7,
),
Array(
'weekday' => 4,
'include' => Array('pasta'),
'exclude' => Array('home-lasagna'),
'fixed' => false,
'perc' => 10,
),
);
$current_day_of_week = date('N');
//rest van de code volgt
}
}
Wat ik hier dus gedaan heb, is een array gedefinieerd met de ‘actiedagen’ en welke categorieën bij de actiedag behoren, en welke subcategorieën uitgezonderd moeten worden. Wat de werkelijke waarden zijn, kan je uit de ‘slugs’ van de productcategorieën halen in WordPress/WooCommerce.
Ik gebruik hier nog steeds de ‘oude’ array structuur van PHP. In PHP 8 is de geprefereerde array structuur iets anders, maar zowel de ‘oude’ als de ‘nieuwe’ structuur zijn nog steeds geldig. Als alternatief had je ook kunnen schrijven
<?php
add_filter('woocommerce_product_get_price', 'wxp_price_on_specific_day_of_week', 10, 2);
add_filter('woocommerce_product_get_regular_price', 'wxp_price_on_specific_day_of_week', 10, 2); //laat deze regel weg als je de prijzen wilt vergelijken.
function wxp_price_on_specific_day_of_week($price, $product) {
$actions = [
[
'weekday' => 3,
'include' => ['pizza'],
'exclude' => ['calzone'],
'fixed' => 7,
],
[
'weekday' => 4,
'include' => ['pasta'],
'exclude' => ['home-lasagna'],
'fixed' => false,
'perc' => 10,
],
];
$current_day_of_week = date('N');
//rest van de code volgt
}
}
Heel wat minder tikwerk, maar wanneer ik code uit moet leggen vind ik de oude structuur duidelijker. Ik ga dus ook met de oude structuur door in de voorbeelden.
Ik heb nu dus een ‘array’, een ‘lijst’ met de verschillende kortingsstructuren aangeleverd. Het enige wat we nu nog hoeven te doen is in een ‘loop’ te kijken, welke kortingen vandaag van toepassing zijn. En met de paar velden die we hebben, kunnen we dit perfect doen.
Betekenis van de array elementen
Het array is een gestructureerd array. Dat wil zeggen, dat de elementen in het array een naam hebben. Welke waarden verwachten we hier?
weekday
De waarde van de dag van de week. Later bedacht ik mij, dat ik dit nog flexibeler had kunnen maken door ook hier een array met dagen te gebruiken, voor het geval de eindklant ook eenzelfde korting op meerdere dagen aan zou willen bieden. Maar dat kan hij ook doen, door een element met een nieuw dagnummer te kopiëren.
include
Welke categorieën krijgen de korting? Dit is een array, dus meerdere productgroepen zijn mogelijk
exclude
En welke categorieën binnen de categorieën krijgen toch geen korting?
fixed
Indien het veld een waarde heeft, geeft het aan wat het vaste bedrag voor die dag voor de producten is.
Heeft het veld de waarde ‘false’, dan wordt naar het volgende veld gekeken
perc
Indien het geen fixed bedrag is, het percentage korting wat de klant krijgt.
Aangezien we nu alles op een rijtje hebben, laten we de code afmaken.
Uiteindelijke code
<?php
add_filter('woocommerce_product_get_price', 'wxp_price_on_specific_day_of_week', 10, 2);
add_filter('woocommerce_product_get_regular_price', 'wxp_price_on_specific_day_of_week', 10, 2); //laat deze regel weg als je de prijzen wilt vergelijken.
function wxp_price_on_specific_day_of_week($price, $product) {
$actions = Array(
Array(
'weekday' => 3,
'include' => Array('pizza'),
'exclude' => Array('calzone'),
'fixed', => 7,
),
Array(
'weekday' => 4,
'include' => Array('pasta'),
'exclude' => Array('home-lasagna'),
'fixed' => false,
'perc' => 10,
),
);
$current_day_of_week = date('N');
if ($product->is_type('simple') {
foreach($actions as $action) {
if (has_term($action['include']) && !has_term($action['exclude'])) {
if ($action['fixed']) {
return $action['fixed'];
} else {
return $price * ($action['perc'] /100);
}
} else {
return $price;
}
}
}
}