Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented product models #4

Merged
merged 5 commits into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 55 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ This package is built to easily create products in Akeneo from your ERP or other

Aside from attributes, you can also set a family, categories and all other data you would normally be able to via the [API](https://api.akeneo.com/api-reference.html#Products).

> **Note:** This package does not have support for product models yet.

For more advanced use cases, it is also possible to add your own data to the payload.

Products are retrieved and updated in small chunks to spread the load if your project has access to a lot of products. Updates of these products are only sent to Akeneo if something has been modified to prevent unnecessary requests.
Expand All @@ -39,19 +37,20 @@ Publish the configuration of the package.
php artisan vendor:publish --provider="JustBetter\AkeneoProducts\ServiceProvider" --tag=config
```

Add the following command to your scheduler.
Add the following commands to your scheduler.

```php
<?php

use JustBetter\AkeneoProducts\Commands\ProcessProductsCommand;

$schedule->command(ProcessProductsCommand::class)->everyMinute();
$schedule->command(\JustBetter\AkeneoProducts\Commands\Product\ProcessProductsCommand::class)->everyMinute();
$schedule->command(\JustBetter\AkeneoProducts\Commands\ProductModel\ProcessProductModelsCommand::class)->everyMinute();
```

## How it works

When a product is retrieved, it will fetch the product from the (external) source. If the retrieved data is not null, it will be saved in the database. If the payload of the model has been changed, the `update`-property of the model will be set to `true`. This way, the package is aware of updates and can do them in small batches. The numbers can be tweaked in your configuration file.
This section will describe the process of products but product models will be similar in usage.

When a product is retrieved, it will fetch the product from the (external) source. If the retrieved data is not null, it will be saved in the database. If the payload of the database model has been changed, the `update`-property of the model will be set to `true`. This way, the package is aware of updates and can do them in small batches. The numbers can be tweaked in your retriever class.

When a product has already been retrieved in the past, the `retrieve`-property of the model can be set to `true` in order to automatically fetch the data again. This is done by the `ProcessProductsCommand`, if added to your scheduler.

Expand All @@ -65,9 +64,12 @@ This package does **not** contain a way to retrieve "all" products. If you wish
```php
<?php

use JustBetter\AkeneoProducts\Jobs\RetrieveProductJob;
use JustBetter\AkeneoProducts\Jobs\Product\RetrieveProductJob;
use JustBetter\AkeneoProducts\Jobs\ProductModel\RetrieveProductModelJob;

RetrieveProductJob::dispatch('::identifier::');

RetrieveProductModelJob::dispatch('::code::');
```

## Retrieving products
Expand All @@ -91,9 +93,9 @@ use JustBetter\AkeneoProducts\Contracts\Akeneo\FormatsAttributeValues;
use JustBetter\AkeneoProducts\Data\ProductData;
use JustBetter\AkeneoProducts\Enums\MappingType;
use JustBetter\AkeneoProducts\Models\Mapping;
use JustBetter\AkeneoProducts\Retrievers\BaseRetriever;
use JustBetter\AkeneoProducts\Retrievers\Product\BaseProductRetriever;

class ExampleRetriever extends BaseRetriever
class ExampleProductRetriever extends BaseProductRetriever
{
public function __construct(
protected FormatsAttributeValues $formatValue
Expand Down Expand Up @@ -150,6 +152,27 @@ class ExampleRetriever extends BaseRetriever
}
```

Product models can be implemented similarly use the following example:

```php
<?php

use JustBetter\AkeneoProducts\Data\ProductModelData;
use JustBetter\AkeneoProducts\Retrievers\ProductModel\BaseProductModelRetriever;

class ExampleProductModelRetriever extends BaseProductModelRetriever
{
public function retrieve(string $code): ?ProductModelData
{
//

return ProductModelData::of([
//
]);
}
}
```

## Supported attribute types

Currently, the following attribute types are supported by this package:
Expand All @@ -166,6 +189,8 @@ Currently, the following attribute types are supported by this package:

This package ships with a few commands.

### Products

Retrieve a product by it's identifier, it will automatically be saved to the database.

```bash
Expand All @@ -184,6 +209,26 @@ Update a product by it's identifier. This will manually trigger an update toward
php artisan akeneo-products:update {identifier}
```

### Product models

Retrieve a product model by it's code, it will automatically be saved to the database.

```bash
php artisan akeneo-products:model:retrieve {code}
```

Process all product models, this includes retrieving and updating product models.

```bash
php artisan akeneo-products:model:process
```

Update a product model by it's code. This will manually trigger an update towards Akeneo, regardless if the product model is up-to-date.

```bash
php artisan akeneo-products:model:update {code}
```

## Quality

To ensure the quality of this package, run the following command:
Expand Down
35 changes: 35 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Upgrading

Documentation about upgrading to the next major version.

## From 1.x to 2.x

Since version 2.x, support for product models has been added. By doing so, most classes have been moved to a new namespace. The configuration is also mostly moved to the retriever class itself.

### Namespaces

The retriever class for products has also been changed. Please, update all references to the new namespace.

```php
use JustBetter\AkeneoProducts\Retrievers\BaseRetriever;

// Is now

use JustBetter\AkeneoProducts\Retrievers\Product\BaseProductRetriever;
```

All classes related to products have also been moved within the `Product` namespace. This applies to `Actions`, `Jobs`, `Commands`, etc. Make sure to update all references as well.

Example:

```php
use JustBetter\AkeneoProducts\Jobs\RetrieveProductJob;

// Is now

use JustBetter\AkeneoProducts\Jobs\Product\RetrieveProductJob;
```

### Configuration

In order to have a more dynamic configuration, the options have been moved to the retriever class itself. If you wish to configure the batch sizes or the amount of tries, add these properties to your retriever class.
17 changes: 7 additions & 10 deletions config/akeneo-products.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,13 @@
/* Determines which queue jobs should be dispatched on. */
'queue' => 'default',

/* Max tries before a product sync will automatically be turned off. */
'tries' => 3,
/* Configuration for the retrievers. */
'retrievers' => [

/* Amount of products to be retrieved at once when processing. */
'retrieve_batch_size' => 100,

/* Amount of products to be updated at once when processing. */
'update_batch_size' => 25,

/* Class responsible to retrieve the products from. */
'retriever' => \JustBetter\AkeneoProducts\Retrievers\ProductRetriever::class,
/* Class responsible to retrieve the products from. */
'product' => \JustBetter\AkeneoProducts\Retrievers\Product\ProductRetriever::class,

/* Class responsible to retrieve the product models from. */
'product_model' => \JustBetter\AkeneoProducts\Retrievers\ProductModel\ProductModelRetriever::class,
],
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
public function up(): void
{
Schema::create('akeneo_product_models', function (Blueprint $table): void {
$table->id();
$table->string('code');
$table->boolean('synchronize')->default(true);
$table->boolean('retrieve')->default(false);
$table->boolean('update')->default(false);
$table->integer('fail_count')->default(0);
$table->json('data');
$table->string('checksum')->nullable();
$table->timestamp('retrieved_at')->nullable();
$table->timestamp('modified_at')->nullable();
$table->timestamp('failed_at')->nullable();
$table->timestamps();
$table->softDeletes();
});
}

public function down(): void
{
Schema::dropIfExists('akeneo_product_models');
}
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<?php

namespace JustBetter\AkeneoProducts\Actions;
namespace JustBetter\AkeneoProducts\Actions\Product;

use JustBetter\AkeneoProducts\Contracts\ProcessesProducts;
use JustBetter\AkeneoProducts\Jobs\RetrieveProductJob;
use JustBetter\AkeneoProducts\Jobs\UpdateProductJob;
use JustBetter\AkeneoProducts\Contracts\Product\ProcessesProducts;
use JustBetter\AkeneoProducts\Jobs\Product\RetrieveProductJob;
use JustBetter\AkeneoProducts\Jobs\Product\UpdateProductJob;
use JustBetter\AkeneoProducts\Models\Product;
use JustBetter\AkeneoProducts\Retrievers\Product\BaseProductRetriever;

class ProcessProducts implements ProcessesProducts
{
Expand All @@ -17,8 +18,7 @@ public function process(): void
->where('update', '=', true)
->update(['update' => false]);

/** @var int $retrieveBatchSize */
$retrieveBatchSize = config('akeneo-products.retrieve_batch_size');
$retrieveBatchSize = BaseProductRetriever::current()->retrieveBatchSize();

// Dispatch jobs for all products that should be retrieved.
Product::query()
Expand All @@ -28,8 +28,7 @@ public function process(): void
->pluck('identifier')
->each(fn (string $identifier) => RetrieveProductJob::dispatch($identifier));

/** @var int $updateBatchSize */
$updateBatchSize = config('akeneo-products.update_batch_size');
$updateBatchSize = BaseProductRetriever::current()->updateBatchSize();

// Dispatch jobs for all products that are should be updated.
Product::query()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<?php

namespace JustBetter\AkeneoProducts\Actions;
namespace JustBetter\AkeneoProducts\Actions\Product;

use JustBetter\AkeneoProducts\Contracts\RetrievesProduct;
use JustBetter\AkeneoProducts\Jobs\SaveProductJob;
use JustBetter\AkeneoProducts\Retrievers\BaseRetriever;
use JustBetter\AkeneoProducts\Contracts\Product\RetrievesProduct;
use JustBetter\AkeneoProducts\Jobs\Product\SaveProductJob;
use JustBetter\AkeneoProducts\Retrievers\Product\BaseProductRetriever;

class RetrieveProduct implements RetrievesProduct
{
public function retrieve(string $identifier): void
{
$product = BaseRetriever::current()->retrieve($identifier);
$product = BaseProductRetriever::current()->retrieve($identifier);

if ($product !== null) {
SaveProductJob::dispatch($product);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?php

namespace JustBetter\AkeneoProducts\Actions;
namespace JustBetter\AkeneoProducts\Actions\Product;

use JustBetter\AkeneoProducts\Contracts\SavesProduct;
use JustBetter\AkeneoProducts\Contracts\Product\SavesProduct;
use JustBetter\AkeneoProducts\Data\ProductData;
use JustBetter\AkeneoProducts\Models\Product;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?php

namespace JustBetter\AkeneoProducts\Actions;
namespace JustBetter\AkeneoProducts\Actions\Product;

use JustBetter\AkeneoClient\Client\Akeneo;
use JustBetter\AkeneoProducts\Contracts\UpdatesProduct;
use JustBetter\AkeneoProducts\Contracts\Product\UpdatesProduct;
use JustBetter\AkeneoProducts\Models\Product;

class UpdateProduct implements UpdatesProduct
Expand Down
45 changes: 45 additions & 0 deletions src/Actions/ProductModel/ProcessProductModels.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace JustBetter\AkeneoProducts\Actions\ProductModel;

use JustBetter\AkeneoProducts\Contracts\ProductModel\ProcessesProductModels;
use JustBetter\AkeneoProducts\Jobs\ProductModel\RetrieveProductModelJob;
use JustBetter\AkeneoProducts\Jobs\ProductModel\UpdateProductModelJob;
use JustBetter\AkeneoProducts\Models\ProductModel;
use JustBetter\AkeneoProducts\Retrievers\ProductModel\BaseProductModelRetriever;

class ProcessProductModels implements ProcessesProductModels
{
public function process(): void
{
// If the product model has to be retrieved again, reset the update state.
ProductModel::query()
->where('retrieve', '=', true)
->where('update', '=', true)
->update(['update' => false]);

$retrieveBatchSize = BaseProductModelRetriever::current()->retrieveBatchSize();

// Dispatch jobs for all product models that should be retrieved.
ProductModel::query()
->scopes('shouldRetrieve')
->limit($retrieveBatchSize)
->get()
->pluck('code')
->each(fn (string $code) => RetrieveProductModelJob::dispatch($code));

$updateBatchSize = BaseProductModelRetriever::current()->updateBatchSize();

// Dispatch jobs for all product models that are should be updated.
ProductModel::query()
->scopes('shouldUpdate')
->limit($updateBatchSize)
->get()
->each(fn (ProductModel $productModel) => UpdateProductModelJob::dispatch($productModel));
}

public static function bind(): void
{
app()->singleton(ProcessesProductModels::class, static::class);
}
}
24 changes: 24 additions & 0 deletions src/Actions/ProductModel/RetrieveProductModel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace JustBetter\AkeneoProducts\Actions\ProductModel;

use JustBetter\AkeneoProducts\Contracts\ProductModel\RetrievesProductModel;
use JustBetter\AkeneoProducts\Jobs\ProductModel\SaveProductModelJob;
use JustBetter\AkeneoProducts\Retrievers\ProductModel\BaseProductModelRetriever;

class RetrieveProductModel implements RetrievesProductModel
{
public function retrieve(string $code): void
{
$productModel = BaseProductModelRetriever::current()->retrieve($code);

if ($productModel !== null) {
SaveProductModelJob::dispatch($productModel);
}
}

public static function bind(): void
{
app()->singleton(RetrievesProductModel::class, static::class);
}
}
Loading
Loading