Skip to content

Commit f31a89d

Browse files
authored
Add re-import action to view page (#334)
1 parent da72c0a commit f31a89d

5 files changed

Lines changed: 229 additions & 0 deletions

File tree

resources/lang/en/messages.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@
88
'option_title_price_nostock' => ':title - :price (Out of Stock)',
99
'option_title_nostock' => ':title (Out of Stock)',
1010
'out_of_stock' => 'Out of Stock',
11+
'reimport_product' => 'Reimport from Shopify',
1112
];

src/Actions/ReimportProduct.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace StatamicRadPack\Shopify\Actions;
4+
5+
use Statamic\Actions\Action;
6+
use Statamic\Contracts\Entries\Entry;
7+
use StatamicRadPack\Shopify\Jobs\ImportSingleProductJob;
8+
use StatamicRadPack\Shopify\Support\StoreConfig;
9+
10+
class ReimportProduct extends Action
11+
{
12+
public string $icon = 'arrow-down';
13+
14+
public static function title()
15+
{
16+
return __('shopify::messages.reimport_product');
17+
}
18+
19+
public function visibleTo($item)
20+
{
21+
return $this->context['view'] === 'form'
22+
&& $item instanceof Entry
23+
&& $item->collectionHandle() === config('shopify.collection_handle', 'products');
24+
}
25+
26+
public function visibleToBulk($items)
27+
{
28+
return false;
29+
}
30+
31+
public function authorize($user, $item)
32+
{
33+
return $user->can('access shopify');
34+
}
35+
36+
public function run($entries, $values)
37+
{
38+
$entries->each(function (Entry $entry) {
39+
$storeHandle = null;
40+
41+
if (StoreConfig::isMultiStore() && StoreConfig::getMode() === 'localized') {
42+
$storeHandle = StoreConfig::findBySite($entry->locale())['handle'] ?? null;
43+
}
44+
45+
ImportSingleProductJob::dispatch((int) $entry->get('product_id'), [], $storeHandle);
46+
});
47+
}
48+
}

src/ServiceProvider.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ class ServiceProvider extends AddonServiceProvider
2626
Commands\ShopifyWebhooksRegister::class,
2727
];
2828

29+
protected $actions = [
30+
Actions\ReimportProduct::class,
31+
];
32+
2933
protected $fieldtypes = [
3034
Fieldtypes\Variants::class,
3135
Fieldtypes\DisabledText::class,

src/Support/StoreConfig.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@ public static function findByHandle(string $handle): ?array
5050
return array_merge(['handle' => $handle], $stores[$handle]);
5151
}
5252

53+
public static function findBySite(string $siteHandle): ?array
54+
{
55+
$stores = config('shopify.multi_store.stores', []);
56+
57+
foreach ($stores as $handle => $store) {
58+
if (($store['site'] ?? null) === $siteHandle) {
59+
return array_merge(['handle' => $handle], $store);
60+
}
61+
}
62+
63+
return null;
64+
}
65+
5366
public static function findByDomain(string $domain): ?array
5467
{
5568
$stores = config('shopify.multi_store.stores', []);
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<?php
2+
3+
namespace StatamicRadPack\Shopify\Tests\Unit;
4+
5+
use Illuminate\Support\Facades\Queue;
6+
use PHPUnit\Framework\Attributes\Test;
7+
use Statamic\Facades;
8+
use StatamicRadPack\Shopify\Actions\ReimportProduct;
9+
use StatamicRadPack\Shopify\Jobs\ImportSingleProductJob;
10+
use StatamicRadPack\Shopify\Tests\TestCase;
11+
12+
class ReimportProductActionTest extends TestCase
13+
{
14+
private function makeProductEntry(int $productId = 123, string $locale = 'default')
15+
{
16+
$entry = Facades\Entry::make()
17+
->collection(config('shopify.collection_handle', 'products'))
18+
->locale($locale)
19+
->slug('test-product')
20+
->data(['product_id' => $productId]);
21+
22+
$entry->save();
23+
24+
return $entry;
25+
}
26+
27+
private function makeVariantEntry()
28+
{
29+
$entry = Facades\Entry::make()
30+
->collection('variants')
31+
->slug('test-variant')
32+
->data(['product_id' => 999]);
33+
34+
$entry->save();
35+
36+
return $entry;
37+
}
38+
39+
private function actionWithContext(string $view = 'form'): ReimportProduct
40+
{
41+
return (new ReimportProduct)->context(['view' => $view]);
42+
}
43+
44+
#[Test]
45+
public function is_visible_on_product_entry_form_view()
46+
{
47+
$entry = $this->makeProductEntry();
48+
49+
$this->assertTrue($this->actionWithContext('form')->visibleTo($entry));
50+
}
51+
52+
#[Test]
53+
public function is_not_visible_on_list_view()
54+
{
55+
$entry = $this->makeProductEntry();
56+
57+
$this->assertFalse($this->actionWithContext('list')->visibleTo($entry));
58+
}
59+
60+
#[Test]
61+
public function is_not_visible_on_non_product_entry()
62+
{
63+
$entry = $this->makeVariantEntry();
64+
65+
$this->assertFalse($this->actionWithContext('form')->visibleTo($entry));
66+
}
67+
68+
#[Test]
69+
public function is_not_visible_in_bulk()
70+
{
71+
$this->assertFalse($this->actionWithContext()->visibleToBulk(collect()));
72+
}
73+
74+
#[Test]
75+
public function dispatches_import_job_with_product_id()
76+
{
77+
Queue::fake();
78+
79+
$entry = $this->makeProductEntry(productId: 456);
80+
81+
$this->actionWithContext()->run(collect([$entry]), []);
82+
83+
Queue::assertPushed(ImportSingleProductJob::class, function ($job) {
84+
return $job->productId === 456 && $job->storeHandle === null;
85+
});
86+
}
87+
88+
#[Test]
89+
public function dispatches_job_without_store_handle_in_single_store_mode()
90+
{
91+
Queue::fake();
92+
93+
config(['shopify.multi_store.enabled' => false]);
94+
95+
$entry = $this->makeProductEntry(productId: 789);
96+
97+
$this->actionWithContext()->run(collect([$entry]), []);
98+
99+
Queue::assertPushed(ImportSingleProductJob::class, function ($job) {
100+
return $job->productId === 789 && $job->storeHandle === null;
101+
});
102+
}
103+
104+
#[Test]
105+
public function dispatches_job_with_store_handle_in_localized_multi_store_mode()
106+
{
107+
Queue::fake();
108+
109+
Facades\Site::setSites([
110+
'en' => ['url' => '/', 'locale' => 'en_US'],
111+
'fr' => ['url' => '/fr/', 'locale' => 'fr_FR'],
112+
]);
113+
114+
Facades\Collection::find(config('shopify.collection_handle', 'products'))->sites(['en', 'fr'])->save();
115+
116+
config(['shopify.multi_store' => [
117+
'enabled' => true,
118+
'mode' => 'localized',
119+
'primary_store' => 'uk',
120+
'stores' => [
121+
'uk' => ['url' => 'uk.myshopify.com', 'admin_token' => 'tok', 'site' => 'en'],
122+
'fr' => ['url' => 'fr.myshopify.com', 'admin_token' => 'tok', 'site' => 'fr'],
123+
],
124+
]]);
125+
126+
$entry = Facades\Entry::make()
127+
->collection(config('shopify.collection_handle', 'products'))
128+
->locale('fr')
129+
->slug('test-product-fr')
130+
->data(['product_id' => 101]);
131+
132+
$entry->save();
133+
134+
$this->actionWithContext()->run(collect([$entry]), []);
135+
136+
Queue::assertPushed(ImportSingleProductJob::class, function ($job) {
137+
return $job->productId === 101 && $job->storeHandle === 'fr';
138+
});
139+
}
140+
141+
#[Test]
142+
public function dispatches_job_without_store_handle_in_unified_multi_store_mode()
143+
{
144+
Queue::fake();
145+
146+
config(['shopify.multi_store' => [
147+
'enabled' => true,
148+
'mode' => 'unified',
149+
'primary_store' => 'uk',
150+
'stores' => [
151+
'uk' => ['url' => 'uk.myshopify.com', 'admin_token' => 'tok', 'site' => 'en'],
152+
],
153+
]]);
154+
155+
$entry = $this->makeProductEntry(productId: 202);
156+
157+
$this->actionWithContext()->run(collect([$entry]), []);
158+
159+
Queue::assertPushed(ImportSingleProductJob::class, function ($job) {
160+
return $job->productId === 202 && $job->storeHandle === null;
161+
});
162+
}
163+
}

0 commit comments

Comments
 (0)