Add properties filtering and relationships in Product model

This commit is contained in:
2026-02-05 01:08:07 +05:00
parent 45bc0b61a1
commit 38fa0e2e45
11 changed files with 227 additions and 2 deletions

View 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!');
}
}

View File

@@ -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));
}

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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',
];
}

View File

@@ -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');
}
}

View 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();
}
}

View 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();
}
}

View File

@@ -28,7 +28,7 @@ class AppServiceProvider extends ServiceProvider
Relation::morphMap(config('ecommerce.models'));
$this->loadMigrationsFrom($this->findModuleMigrations());
// $this->listenDB();
$this->listenDB();
}
/**

View File

@@ -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);
}
/**