Skip to content

Commit 1f30920

Browse files
committed
fix: add save_post fallbacks for woocommerce product cache invalidation
1 parent 1af9b7c commit 1f30920

2 files changed

Lines changed: 147 additions & 0 deletions

File tree

src/Subscriber/Compatibility/WooCommerceSubscriber.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ public static function getSubscribedEvents(): array
7979
return [
8080
'transient_woocommerce_blocks_asset_api_script_data' => 'fixAssetUrlPathsInCachedScriptData',
8181
'transient_woocommerce_blocks_asset_api_script_data_ssl' => 'fixAssetUrlPathsInCachedScriptData',
82+
'save_post_product' => ['clearCacheOnProductSave', 20, 3],
83+
'save_post_product_variation' => ['clearCacheOnProductVariationSave', 20, 3],
8284
'woocommerce_csv_importer_check_import_file_path' => 'disableCheckImportFilePath',
8385
'woocommerce_log_directory' => 'changeLogDirectory',
8486
'woocommerce_product_csv_importer_check_import_file_path' => 'disableCheckImportFilePath',
@@ -128,6 +130,36 @@ public function clearCacheOnProductVariationUpdate($variationId, $variation)
128130
$this->clearProductUrls($parentId);
129131
}
130132

133+
/**
134+
* Clear product-related URLs when a product post is saved directly.
135+
*/
136+
public function clearCacheOnProductSave(int $postId, \WP_Post $post, bool $update): void
137+
{
138+
if (!$update || $this->isAutosaveOrRevision($postId)) {
139+
return;
140+
}
141+
142+
$this->clearProductUrls($postId);
143+
}
144+
145+
/**
146+
* Clear product-related URLs when a product variation post is saved directly.
147+
*/
148+
public function clearCacheOnProductVariationSave(int $postId, \WP_Post $post, bool $update): void
149+
{
150+
if (!$update || $this->isAutosaveOrRevision($postId)) {
151+
return;
152+
}
153+
154+
$parentId = (int) wp_get_post_parent_id($postId);
155+
156+
if ($parentId <= 0) {
157+
return;
158+
}
159+
160+
$this->clearProductUrls($parentId);
161+
}
162+
131163
/**
132164
* Disable "check import file path" so that imports work with S3 storage.
133165
*/
@@ -215,4 +247,12 @@ private function clearProductUrls($productId)
215247

216248
$this->pageCacheClient->clearUrls($urlsToClear);
217249
}
250+
251+
/**
252+
* Check if a post save is an autosave or revision.
253+
*/
254+
private function isAutosaveOrRevision(int $postId): bool
255+
{
256+
return (bool) wp_is_post_autosave($postId) || (bool) wp_is_post_revision($postId);
257+
}
218258
}

tests/Unit/Subscriber/Compatibility/WooCommerceSubscriberTest.php

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Ymir\Plugin\Tests\Mock\ContentDeliveryNetworkPageCacheClientInterfaceMockTrait;
2020
use Ymir\Plugin\Tests\Mock\EventManagerMockTrait;
2121
use Ymir\Plugin\Tests\Mock\FunctionMockTrait;
22+
use Ymir\Plugin\Tests\Mock\WPPostMockTrait;
2223
use Ymir\Plugin\Tests\Mock\WPTermMockTrait;
2324
use Ymir\Plugin\Tests\Unit\TestCase;
2425

@@ -27,6 +28,7 @@ class WooCommerceSubscriberTest extends TestCase
2728
use ContentDeliveryNetworkPageCacheClientInterfaceMockTrait;
2829
use EventManagerMockTrait;
2930
use FunctionMockTrait;
31+
use WPPostMockTrait;
3032
use WPTermMockTrait;
3133

3234
public function testChangeLogDirectoryWhenLogDirectoryIsAStringThatDoesntStartWithThePublicCloudStorageProtocol()
@@ -204,6 +206,109 @@ public function testClearCacheOnProductVariationUpdateFallsBackToVariationPostPa
204206
$this->createSubscriber('https://foo.com', '', false, ['invalidation_enabled' => true], $pageCacheClient)->clearCacheOnProductVariationUpdate(456, new \stdClass());
205207
}
206208

209+
public function testClearCacheOnProductSave()
210+
{
211+
$wp_is_post_autosave = $this->getFunctionMock($this->getNamespace(WooCommerceSubscriber::class), 'wp_is_post_autosave');
212+
$wp_is_post_autosave->expects($this->once())
213+
->with(123)
214+
->willReturn(false);
215+
216+
$wp_is_post_revision = $this->getFunctionMock($this->getNamespace(WooCommerceSubscriber::class), 'wp_is_post_revision');
217+
$wp_is_post_revision->expects($this->once())
218+
->with(123)
219+
->willReturn(false);
220+
221+
$function_exists = $this->getFunctionMock($this->getNamespace(WooCommerceSubscriber::class), 'function_exists');
222+
$function_exists->expects($this->once())
223+
->with('wc_get_page_permalink')
224+
->willReturn(true);
225+
226+
$get_permalink = $this->getFunctionMock($this->getNamespace(WooCommerceSubscriber::class), 'get_permalink');
227+
$get_permalink->expects($this->once())
228+
->with(123)
229+
->willReturn('https://foo.com/product/bar/');
230+
231+
$wc_get_page_permalink = $this->getFunctionMock($this->getNamespace(WooCommerceSubscriber::class), 'wc_get_page_permalink');
232+
$wc_get_page_permalink->expects($this->once())
233+
->with('shop')
234+
->willReturn('https://foo.com/shop/');
235+
236+
$get_the_terms = $this->getFunctionMock($this->getNamespace(WooCommerceSubscriber::class), 'get_the_terms');
237+
$get_the_terms->expects($this->exactly(2))
238+
->willReturn([]);
239+
240+
$pageCacheClient = $this->getContentDeliveryNetworkPageCacheClientInterfaceMock();
241+
$pageCacheClient->expects($this->once())
242+
->method('clearUrls')
243+
->with($this->callback(function ($urls) {
244+
$this->assertSame([
245+
'https://foo.com/product/bar/',
246+
'https://foo.com/shop/*',
247+
], $urls->all());
248+
249+
return true;
250+
}));
251+
252+
$post = $this->getWPPostMock();
253+
254+
$this->createSubscriber('https://foo.com', '', false, ['invalidation_enabled' => true], $pageCacheClient)
255+
->clearCacheOnProductSave(123, $post, true);
256+
}
257+
258+
public function testClearCacheOnProductVariationSave()
259+
{
260+
$wp_is_post_autosave = $this->getFunctionMock($this->getNamespace(WooCommerceSubscriber::class), 'wp_is_post_autosave');
261+
$wp_is_post_autosave->expects($this->once())
262+
->with(456)
263+
->willReturn(false);
264+
265+
$wp_is_post_revision = $this->getFunctionMock($this->getNamespace(WooCommerceSubscriber::class), 'wp_is_post_revision');
266+
$wp_is_post_revision->expects($this->once())
267+
->with(456)
268+
->willReturn(false);
269+
270+
$wp_get_post_parent_id = $this->getFunctionMock($this->getNamespace(WooCommerceSubscriber::class), 'wp_get_post_parent_id');
271+
$wp_get_post_parent_id->expects($this->once())
272+
->with(456)
273+
->willReturn(123);
274+
275+
$function_exists = $this->getFunctionMock($this->getNamespace(WooCommerceSubscriber::class), 'function_exists');
276+
$function_exists->expects($this->once())
277+
->with('wc_get_page_permalink')
278+
->willReturn(true);
279+
280+
$get_permalink = $this->getFunctionMock($this->getNamespace(WooCommerceSubscriber::class), 'get_permalink');
281+
$get_permalink->expects($this->once())
282+
->with(123)
283+
->willReturn('https://foo.com/product/bar/');
284+
285+
$wc_get_page_permalink = $this->getFunctionMock($this->getNamespace(WooCommerceSubscriber::class), 'wc_get_page_permalink');
286+
$wc_get_page_permalink->expects($this->once())
287+
->with('shop')
288+
->willReturn('https://foo.com/shop/');
289+
290+
$get_the_terms = $this->getFunctionMock($this->getNamespace(WooCommerceSubscriber::class), 'get_the_terms');
291+
$get_the_terms->expects($this->exactly(2))
292+
->willReturn([]);
293+
294+
$pageCacheClient = $this->getContentDeliveryNetworkPageCacheClientInterfaceMock();
295+
$pageCacheClient->expects($this->once())
296+
->method('clearUrls')
297+
->with($this->callback(function ($urls) {
298+
$this->assertSame([
299+
'https://foo.com/product/bar/',
300+
'https://foo.com/shop/*',
301+
], $urls->all());
302+
303+
return true;
304+
}));
305+
306+
$post = $this->getWPPostMock();
307+
308+
$this->createSubscriber('https://foo.com', '', false, ['invalidation_enabled' => true], $pageCacheClient)
309+
->clearCacheOnProductVariationSave(456, $post, true);
310+
}
311+
207312
public function testDisableCheckImportFilePath()
208313
{
209314
$this->assertFalse($this->createSubscriber()->disableCheckImportFilePath());
@@ -312,6 +417,8 @@ public function testGetSubscribedEvents()
312417
$subscribedEvents = [
313418
'transient_woocommerce_blocks_asset_api_script_data' => 'fixAssetUrlPathsInCachedScriptData',
314419
'transient_woocommerce_blocks_asset_api_script_data_ssl' => 'fixAssetUrlPathsInCachedScriptData',
420+
'save_post_product' => ['clearCacheOnProductSave', 20, 3],
421+
'save_post_product_variation' => ['clearCacheOnProductVariationSave', 20, 3],
315422
'woocommerce_csv_importer_check_import_file_path' => 'disableCheckImportFilePath',
316423
'woocommerce_log_directory' => 'changeLogDirectory',
317424
'woocommerce_product_csv_importer_check_import_file_path' => 'disableCheckImportFilePath',

0 commit comments

Comments
 (0)