Add properties filtering and relationships in Product model
This commit is contained in:
47
app/Console/Commands/SyncProductPropertiesJson.php
Normal file
47
app/Console/Commands/SyncProductPropertiesJson.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Ecommerce\Product\Product\Product;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class SyncProductPropertiesJson extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'products:sync-properties-json';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Syncs all product properties to the new JSON column';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Starting sync of product properties JSON...');
|
||||
|
||||
// Using cursor to be memory efficient
|
||||
$products = Product::cursor();
|
||||
$count = Product::count();
|
||||
|
||||
$bar = $this->output->createProgressBar($count);
|
||||
$bar->start();
|
||||
|
||||
foreach ($products as $product) {
|
||||
$product->syncPropertiesJson();
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
$this->info('Sync completed successfully!');
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,7 @@ class ProductFilterer
|
||||
'min_price' => ['nullable', 'numeric'],
|
||||
'max_price' => ['nullable', 'numeric'],
|
||||
'backorder' => ['nullable', 'in:0,1'],
|
||||
'properties' => ['nullable', 'array'],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -60,6 +61,18 @@ class ProductFilterer
|
||||
return $this->queryBuilder;
|
||||
}
|
||||
|
||||
if ($this->request->filled('properties')) {
|
||||
foreach ($this->request->input('properties') as $attributeSlug => $values) {
|
||||
$valuesArray = explode(',', $values);
|
||||
|
||||
$this->queryBuilder->where(function ($query) use ($attributeSlug, $valuesArray) {
|
||||
foreach ($valuesArray as $value) {
|
||||
$query->orWhereJsonContains("properties_json->{$attributeSlug}", $value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->request->filled('brands')) {
|
||||
$this->queryBuilder->whereIntegerInRaw('products.brand_id', explode(',', $this->request->brands));
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Product\Resources;
|
||||
|
||||
use App\Http\Resources\MediaResource;
|
||||
use App\Repositories\Ecommerce\Product\Property\PropertyRepository;
|
||||
use App\Http\Controllers\Api\V1\Product\Resources\Variant\ProductVariantResource;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ProductIndexResource extends JsonResource
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Ecommerce\Product\Product\Concerns;
|
||||
|
||||
trait HasPropertiesJson
|
||||
{
|
||||
/**
|
||||
* Initialize the trait
|
||||
*/
|
||||
public function initializeHasPropertiesJson()
|
||||
{
|
||||
$this->casts['properties_json'] = 'array';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync properties to JSON column
|
||||
*/
|
||||
public function syncPropertiesJson(): void
|
||||
{
|
||||
$this->load(['properties.attribute', 'properties.values.value']);
|
||||
|
||||
$propertiesJson = [];
|
||||
|
||||
foreach ($this->properties as $property) {
|
||||
$attributeSlug = $property->attribute->slug;
|
||||
|
||||
if (! isset($propertiesJson[$attributeSlug])) {
|
||||
$propertiesJson[$attributeSlug] = [];
|
||||
}
|
||||
|
||||
foreach ($property->values as $value) {
|
||||
// We want to store both the key (for standard values) and the custom value
|
||||
// so we can filter by either.
|
||||
if ($value->value) {
|
||||
$propertiesJson[$attributeSlug][] = $value->value->key;
|
||||
}
|
||||
|
||||
if ($value->product_custom_value) {
|
||||
$propertiesJson[$attributeSlug][] = $value->product_custom_value;
|
||||
}
|
||||
}
|
||||
|
||||
// Unique values just in case
|
||||
$propertiesJson[$attributeSlug] = array_values(array_unique($propertiesJson[$attributeSlug]));
|
||||
}
|
||||
|
||||
$this->properties_json = $propertiesJson;
|
||||
$this->saveQuietly();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Models\Ecommerce\Product\Product;
|
||||
|
||||
use App\Models\Concerns\HasSchemalessAttributes;
|
||||
use App\Models\Ecommerce\Product\Product\Concerns\HasPropertiesJson;
|
||||
use App\Models\Ecommerce\Product\Product\Concerns\ProductFrontEndHelpers;
|
||||
use App\Models\Ecommerce\Product\Product\Concerns\ProductMedia;
|
||||
use App\Models\Ecommerce\Product\Product\Concerns\ProductProperties;
|
||||
@@ -31,6 +32,11 @@ class Product extends Model implements HasMedia, Viewable
|
||||
*/
|
||||
use HasSchemalessAttributes;
|
||||
|
||||
/**
|
||||
* Has Properties Json
|
||||
*/
|
||||
use HasPropertiesJson;
|
||||
|
||||
/**
|
||||
* Has Slug (spatie/laravel-sluggable)
|
||||
*/
|
||||
@@ -117,5 +123,6 @@ class Product extends Model implements HasMedia, Viewable
|
||||
'is_visible',
|
||||
'colour',
|
||||
'size',
|
||||
'properties_json',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -69,4 +69,12 @@ class ProductAttributeValue extends Model
|
||||
{
|
||||
return $this->belongsTo(AttributeValue::class, 'attribute_value_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Product Property
|
||||
*/
|
||||
public function productProperty(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ProductProperty::class, 'product_attribute_id');
|
||||
}
|
||||
}
|
||||
|
||||
32
app/Observers/ProductAttributeValueObserver.php
Normal file
32
app/Observers/ProductAttributeValueObserver.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Models\Ecommerce\Product\Property\ProductAttributeValue;
|
||||
|
||||
class ProductAttributeValueObserver
|
||||
{
|
||||
/**
|
||||
* Handle the ProductAttributeValue "created" event.
|
||||
*/
|
||||
public function created(ProductAttributeValue $productAttributeValue): void
|
||||
{
|
||||
$productAttributeValue->productProperty->product->syncPropertiesJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProductAttributeValue "updated" event.
|
||||
*/
|
||||
public function updated(ProductAttributeValue $productAttributeValue): void
|
||||
{
|
||||
$productAttributeValue->productProperty->product->syncPropertiesJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProductAttributeValue "deleted" event.
|
||||
*/
|
||||
public function deleted(ProductAttributeValue $productAttributeValue): void
|
||||
{
|
||||
$productAttributeValue->productProperty->product->syncPropertiesJson();
|
||||
}
|
||||
}
|
||||
32
app/Observers/ProductPropertyObserver.php
Normal file
32
app/Observers/ProductPropertyObserver.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Models\Ecommerce\Product\Property\ProductProperty;
|
||||
|
||||
class ProductPropertyObserver
|
||||
{
|
||||
/**
|
||||
* Handle the ProductProperty "created" event.
|
||||
*/
|
||||
public function created(ProductProperty $productProperty): void
|
||||
{
|
||||
$productProperty->product->syncPropertiesJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProductProperty "updated" event.
|
||||
*/
|
||||
public function updated(ProductProperty $productProperty): void
|
||||
{
|
||||
$productProperty->product->syncPropertiesJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProductProperty "deleted" event.
|
||||
*/
|
||||
public function deleted(ProductProperty $productProperty): void
|
||||
{
|
||||
$productProperty->product->syncPropertiesJson();
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
Relation::morphMap(config('ecommerce.models'));
|
||||
|
||||
$this->loadMigrationsFrom($this->findModuleMigrations());
|
||||
// $this->listenDB();
|
||||
$this->listenDB();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,10 @@ use App\Events\Ecommerce\Product\Order\OrderCreated;
|
||||
use App\Listeners\Ecommerce\Category\UpdateCategoryProductsPriceByTax;
|
||||
use App\Listeners\Ecommerce\Channel\HideChannelProducts;
|
||||
use App\Listeners\Ecommerce\Order\SendOrderCreatedNotification;
|
||||
use App\Models\Ecommerce\Product\Property\ProductAttributeValue;
|
||||
use App\Models\Ecommerce\Product\Property\ProductProperty;
|
||||
use App\Observers\ProductAttributeValueObserver;
|
||||
use App\Observers\ProductPropertyObserver;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
@@ -39,7 +43,8 @@ class EventServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
ProductProperty::observe(ProductPropertyObserver::class);
|
||||
ProductAttributeValue::observe(ProductAttributeValueObserver::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user