From 57bd016fdceb2e6cb79c5ff66d541be208c15a74 Mon Sep 17 00:00:00 2001 From: Finn <71390226+FinnPaes@users.noreply.github.com> Date: Tue, 12 Dec 2023 11:59:16 +0100 Subject: [PATCH] Product import command & add utility to start imports (#46) --- README.md | 30 ++++++++- config/rapidez-statamic.php | 12 ++++ .../import_utility/imports.blade.php | 35 ++++++++++ src/Actions/StatamicEntryAction.php | 41 ++++++++++++ src/Commands/ImportCategories.php | 58 ++++------------ src/Commands/ImportProducts.php | 66 +++++++++++++++++++ src/Http/Controllers/ImportsController.php | 37 +++++++++++ src/RapidezStatamicServiceProvider.php | 56 ++++++++++++---- 8 files changed, 277 insertions(+), 58 deletions(-) create mode 100644 resources/views/utilities/import_utility/imports.blade.php create mode 100644 src/Actions/StatamicEntryAction.php create mode 100644 src/Commands/ImportProducts.php create mode 100644 src/Http/Controllers/ImportsController.php diff --git a/README.md b/README.md index 564bcb7..5bab231 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,9 @@ By default you'll get the configured content on categories and products availabl - Product: `resources/views/vendor/rapidez/product/overview.blade.php` - Category: `resources/views/vendor/rapidez/category/overview.blade.php` -### Importing categories from Magento +### Importing categories or products from Magento + +#### Categories To make it easier to change category content in bulk you can create category entries with content copied over in bulk. @@ -123,6 +125,32 @@ Event::listen('rapidez-statamic:category-entry-data', fn($category) => [ ] ); ``` + +#### Products + +To make it easier to change product content in bulk you can create product entries with content copied over in bulk. + +To do this run one of the following: + +```bash +# Most basic, import all products in all sites +php artisan rapidez:statamic:import:products + +# Import all products in the site with handle "default" only +php artisan rapidez:statamic:import:products --site=default +``` + +By default the slug and title of the product are copied. + +If you have a custom blueprint and would like to add more data from the product you can do so by hooking into the `rapidez-statamic:product-entry-data` event: + +```php +Event::listen('rapidez-statamic:product-entry-data', fn($product) => [ + 'description' => $product->description, + ] +); +``` + ### Forms When you create a form you could use `rapidez-statamic::emails.form` as HTML template which uses the [Laravel mail template](https://laravel.com/docs/master/mail#customizing-the-components) with all fields in a table, make sure you enable markdown! diff --git a/config/rapidez-statamic.php b/config/rapidez-statamic.php index ab51539..d1de45a 100644 --- a/config/rapidez-statamic.php +++ b/config/rapidez-statamic.php @@ -17,6 +17,18 @@ 'category' => true, ], + // Which collection and blueprint should be used for importing products? + 'import' => [ + 'categories' => [ + 'collection' => 'categories', + 'blueprint' => 'category', + ], + 'products' => [ + 'collection' => 'products', + 'blueprint' => 'product', + ], + ], + 'runway' => [ // Should we configure Runway? You'll get a products, // categories and brands / manufacturers resource. diff --git a/resources/views/utilities/import_utility/imports.blade.php b/resources/views/utilities/import_utility/imports.blade.php new file mode 100644 index 0000000..364060e --- /dev/null +++ b/resources/views/utilities/import_utility/imports.blade.php @@ -0,0 +1,35 @@ +@extends('statamic::layout') +@section('title', __('Import')) + +@section('content') +
+

{{ __('Import categories') }}

+
+ +
+
+ @csrf + +

@lang('The import of non-existing categories may be started through the button below.')

+ +
+ +
+
+
+ +
+

{{ __('Import products') }}

+
+ +
+
+ @csrf + +

@lang('The import of non-existing products may be started through the button below.')

+
+ +
+
+
+@stop \ No newline at end of file diff --git a/src/Actions/StatamicEntryAction.php b/src/Actions/StatamicEntryAction.php new file mode 100644 index 0000000..e4ac214 --- /dev/null +++ b/src/Actions/StatamicEntryAction.php @@ -0,0 +1,41 @@ +where($attributes)->count()) { + // Entry was already created. + return; + } + + /** @var \Statamic\Entries\Entry $entry */ + $entry = Entry::make(); + $values = array_merge($attributes, $values); + + static::setEntryData($entry, $values)->save(); + } + + public static function setEntryData(\Statamic\Entries\Entry $entry, array $values = []) : \Statamic\Entries\Entry + { + $reflectedEntry = new ReflectionClass($entry); + foreach ($values as $key => $value) { + // Check if the key is a statamic setter + if (!$reflectedEntry->hasMethod($key) || $reflectedEntry->getMethod($key)->getNumberOfParameters() < 1) { + continue; + } + + $entry->$key($value); + unset($values[$key]); + } + + $entry->merge($values); + + return $entry; + } +} diff --git a/src/Commands/ImportCategories.php b/src/Commands/ImportCategories.php index 1d13c20..c43dd6c 100644 --- a/src/Commands/ImportCategories.php +++ b/src/Commands/ImportCategories.php @@ -2,16 +2,11 @@ namespace Rapidez\Statamic\Commands; +use Statamic\Facades\Site; use Illuminate\Console\Command; -use Illuminate\Support\Arr; -use Illuminate\Support\Facades\Event; -use Illuminate\Support\Str; use Rapidez\Core\Facades\Rapidez; -use Rapidez\Statamic\Jobs\ImportCategoriesJob; -use ReflectionClass; -use Statamic\Facades\Entry; -use Statamic\Facades\Site; -use Symfony\Component\Console\Helper\ProgressBar; +use Illuminate\Support\Facades\Event; +use Rapidez\Statamic\Actions\StatamicEntryAction; class ImportCategories extends Command { @@ -24,6 +19,9 @@ public function handle(): int $categoryModel = config('rapidez.models.category'); $categoryModelInstance = new $categoryModel; + /** @var StatamicEntryAction $statamicEntryAction */ + $statamicEntryAction = app(StatamicEntryAction::class); + $categoryIdentifiers = $this->argument('categories'); if (!$categoryIdentifiers && !$this->option('all')) { $this->error(__('You must enter categories or pass the --all flag.')); @@ -60,14 +58,17 @@ public function handle(): int ->lazy(); foreach ($categories as $category) { - static::createEntry( + $statamicEntryAction::createEntry( [ - 'collection' => 'categories', - 'blueprint' => 'category', + 'collection' => config('rapidez-statamic.import.categories.collection', 'categories'), + 'blueprint' => config('rapidez-statamic.import.categories.blueprint', 'category'), 'site' => $site->handle(), 'linked_category' => $category->entity_id, ], - array_merge(...Event::dispatch('rapidez-statamic:category-entry-data', ['category' => $category])) + array_merge([ + 'locale' => $site->handle(), + 'site' => $site->handle(), + ], ...Event::dispatch('rapidez-statamic:category-entry-data', ['category' => $category])) ); } @@ -75,39 +76,6 @@ public function handle(): int } $bar->finish(); - return static::SUCCESS; } - - protected static function createEntry(array $attributes, array $values = []) - { - if (Entry::query()->where($attributes)->count()) { - // Entry was already created. - return; - } - - /** @var \Statamic\Entries\Entry $entry */ - $entry = Entry::make(); - $values = array_merge($attributes, $values); - - static::setEntryData($entry, $values)->save(); - } - - protected static function setEntryData(\Statamic\Entries\Entry $entry, array $values = []) : \Statamic\Entries\Entry - { - $reflectedEntry = new ReflectionClass($entry); - foreach ($values as $key => $value) { - // Check if the key is a statamic setter - if (!$reflectedEntry->hasMethod($key) || $reflectedEntry->getMethod($key)->getNumberOfParameters() < 1) { - continue; - } - - $entry->$key($value); - unset($values[$key]); - } - - $entry->merge($values); - - return $entry; - } } diff --git a/src/Commands/ImportProducts.php b/src/Commands/ImportProducts.php new file mode 100644 index 0000000..bdb4302 --- /dev/null +++ b/src/Commands/ImportProducts.php @@ -0,0 +1,66 @@ +option('site'); + $sites = $sites + ? array_filter(array_map(fn($handle) => (Site::get($handle) ?: Site::findByUrl($handle)) ?: $this->output->warning(__('No site found with handle or url: :handle', ['handle' => $handle])), $sites)) + : Site::all(); + + $bar = $this->output->createProgressBar(count($sites)); + $bar->start(); + foreach($sites as $site) { + $bar->display(); + + $siteAttributes = $site->attributes(); + if (!isset($siteAttributes['magento_store_id'])) { + continue; + } + + Rapidez::setStore($siteAttributes['magento_store_id']); + + $products = $productModel::query() + ->selectAttributes(['entity_id', 'sku', 'name', 'url_key']) + ->lazy(); + + foreach ($products as $product) { + $statamicEntryAction::createEntry( + [ + 'collection' => config('rapidez-statamic.import.products.collection', 'products'), + 'blueprint' => config('rapidez-statamic.import.products.blueprint', 'product'), + 'site' => $site->handle(), + 'linked_product' => $product->sku, + ], + array_merge([ + 'locale' => $site->handle(), + 'site' => $site->handle(), + ], ...Event::dispatch('rapidez-statamic:product-entry-data', ['product' => $product])) + ); + } + + $bar->advance(); + } + $bar->finish(); + + return static::SUCCESS; + } +} diff --git a/src/Http/Controllers/ImportsController.php b/src/Http/Controllers/ImportsController.php new file mode 100644 index 0000000..320c916 --- /dev/null +++ b/src/Http/Controllers/ImportsController.php @@ -0,0 +1,37 @@ +onQueue('imports'); + + Toast::success(__('The import of categories has started!'))->duration(5000); + + return redirect(cp_route('utilities.imports')); + } + + public function importProducts() : RedirectResponse + { + Artisan::queue('rapidez:statamic:import:products') + ->onQueue('imports'); + + Toast::success(__('The import of products has started!'))->duration(5000); + + return redirect(cp_route('utilities.imports')); + } +} diff --git a/src/RapidezStatamicServiceProvider.php b/src/RapidezStatamicServiceProvider.php index 22e7518..24fe71d 100644 --- a/src/RapidezStatamicServiceProvider.php +++ b/src/RapidezStatamicServiceProvider.php @@ -2,25 +2,29 @@ namespace Rapidez\Statamic; +use Statamic\Statamic; +use Statamic\Sites\Sites; +use Statamic\Facades\Site; +use Statamic\Facades\Entry; +use Statamic\Facades\Utility; +use Illuminate\Routing\Router; +use Rapidez\Core\Facades\Rapidez; +use Statamic\Events\GlobalSetSaved; +use Illuminate\Support\Facades\View; use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Event; -use Illuminate\Support\Facades\View; +use Rapidez\Statamic\Tags\Alternates; +use Statamic\Events\GlobalSetDeleted; use Illuminate\Support\ServiceProvider; use Illuminate\View\View as RenderedView; -use Rapidez\Core\Facades\Rapidez; +use Rapidez\Statamic\Forms\JsDrivers\Vue; +use Rapidez\Statamic\Commands\ImportProducts; use Rapidez\Statamic\Commands\ImportCategories; use Rapidez\Statamic\Extend\SitesLinkedToMagentoStores; -use Rapidez\Statamic\Forms\JsDrivers\Vue; +use Rapidez\Statamic\Http\Controllers\ImportsController; use Rapidez\Statamic\Http\Controllers\StatamicRewriteController; use Rapidez\Statamic\Http\ViewComposers\StatamicGlobalDataComposer; -use Rapidez\Statamic\Tags\Alternates; -use Statamic\Events\GlobalSetDeleted; -use Statamic\Events\GlobalSetSaved; -use Statamic\Facades\Entry; -use Statamic\Facades\Site; -use Statamic\Sites\Sites; -use Statamic\Statamic; class RapidezStatamicServiceProvider extends ServiceProvider { @@ -41,7 +45,8 @@ public function boot() ->bootListeners() ->bootRunway() ->bootComposers() - ->bootPublishables(); + ->bootPublishables() + ->bootUtilities(); Vue::register(); Alternates::register(); @@ -50,7 +55,8 @@ public function boot() public function bootCommands() : self { $this->commands([ - ImportCategories::class + ImportCategories::class, + ImportProducts::class, ]); return $this; @@ -90,6 +96,11 @@ public function bootListeners() : self 'title' => $category->name, 'slug' => trim($category->url_key), ]); + + Event::listen('rapidez-statamic:product-entry-data', fn($product) => [ + 'title' => $product->name, + 'slug' => trim($product->url_key), + ]); } return $this; @@ -161,6 +172,27 @@ public function bootPublishables() : self return $this; } + public function bootUtilities() : static + { + Utility::extend(function () : void { + Utility::register('imports') + ->icon('synchronize') + ->action(ImportsController::class) + ->title(__('Import')) + ->navTitle(__('Import')) + ->description(__('Import products or categories from Magento')) + ->routes(function (Router $router) : void { + $router->post('/import-categories', [ImportsController::class, 'importCategories']) + ->name('import-categories'); + + $router->post('/import-products', [ImportsController::class, 'importProducts']) + ->name('import-products'); + }); + }); + + return $this; + } + public function currentSiteIsEnabled(): bool { return !config('statamic.sites.sites.' . Site::current()->handle() . '.attributes.disabled', false);