Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c8dcadd
feat(product-types): add migration and prototype model
SlimDeluxe Aug 8, 2025
a717256
Merge remote-tracking branch 'origin/main' into feat/types
KilianTrunk Aug 12, 2025
9b7c71b
feat(product-types): implement product types
KilianTrunk Aug 14, 2025
c662b9a
Merge remote-tracking branch 'origin/main' into chore/types
KilianTrunk Aug 16, 2025
ea64105
chore(types): fix product type policy
KilianTrunk Aug 16, 2025
da7413b
feat: add product data table and more attributes
SlimDeluxe Aug 16, 2025
c273a65
chore(types): product types improvements
KilianTrunk Aug 17, 2025
2ace684
Merge branch 'chore/types' into chore/product_data
KilianTrunk Aug 17, 2025
1453812
feat(product-data): implement product data & product improvements
KilianTrunk Aug 18, 2025
d332f93
refactor: improve tenancy oriented classes
KilianTrunk Aug 18, 2025
cae9182
chore(product-data): testing fixes
KilianTrunk Aug 18, 2025
5153051
fix: fix tenancy issues & refactor
KilianTrunk Aug 18, 2025
d23cf63
feat: add product price model and migration
KilianTrunk Aug 18, 2025
1af2ec6
feat(product-prices): implement prices
KilianTrunk Aug 18, 2025
d47fcfe
Merge branch 'main' into feat/product_prices
KilianTrunk Aug 28, 2025
4489d62
fix: resolve product resource after merging
KilianTrunk Aug 28, 2025
0c53dba
Merge branch 'main' into feat/product_prices
KilianTrunk Aug 29, 2025
2b89d15
fix: format
KilianTrunk Aug 29, 2025
83afeb4
Merge branch 'main' into feat/product_prices
KilianTrunk Sep 10, 2025
29f2bb1
style: fix code style
KilianTrunk Sep 10, 2025
b02706f
fix: resolve tenant fields error
KilianTrunk Sep 10, 2025
6c991b1
chore: ui improvements for setting prices
KilianTrunk Sep 10, 2025
69b1f5c
Merge branch 'main' into fork/KilianTrunk/feat/product_prices
SlimDeluxe Sep 15, 2025
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
49 changes: 49 additions & 0 deletions database/factories/ProductDataFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace Eclipse\Catalogue\Factories;

use Eclipse\Catalogue\Models\Product;
use Eclipse\Catalogue\Models\ProductData;
use Illuminate\Database\Eloquent\Factories\Factory;

/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\Eclipse\Catalogue\Models\ProductData>
*/
class ProductDataFactory extends Factory
{
protected $model = ProductData::class;

public function definition(): array
{
$definition = [
'product_id' => Product::factory(),
'sorting_label' => $this->faker->optional()->word(),
'is_active' => $this->faker->boolean(90),
'available_from_date' => $this->faker->optional()->dateTimeBetween('now', '+3 months'),
'has_free_delivery' => $this->faker->boolean(20),
];

if (config('eclipse-catalogue.tenancy.foreign_key')) {
$tenantModel = config('eclipse-catalogue.tenancy.model');
if (class_exists($tenantModel)) {
$definition[config('eclipse-catalogue.tenancy.foreign_key')] = $tenantModel::factory();
}
}

return $definition;
}

public function active(): static
{
return $this->state(fn (array $attributes) => [
'is_active' => true,
]);
}

public function inactive(): static
{
return $this->state(fn (array $attributes) => [
'is_active' => false,
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?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('pim_product_types', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('code')->nullable();
$table->timestamps();
$table->softDeletes();
});

Schema::create('pim_product_type_data', function (Blueprint $table) {
$table->id();
$table->foreignId('product_type_id')
->constrained('pim_product_types')
->cascadeOnUpdate()
->cascadeOnDelete();

// Add foreign key for tenant if it's configured in the catalogue config
if (config('eclipse-catalogue.tenancy.model')) {
$tenantClass = config('eclipse-catalogue.tenancy.model');
/** @var \Illuminate\Database\Eloquent\Model $tenant */
$tenant = new $tenantClass;
$table->foreignId(config('eclipse-catalogue.tenancy.foreign_key'))
->constrained($tenant->getTable(), $tenant->getKeyName())
->cascadeOnUpdate()
->cascadeOnDelete();
}

$table->boolean('is_active')->default(true);
$table->boolean('is_default')->default(false);
});
}

public function down(): void
{
Schema::dropIfExists('pim_product_type_data');
Schema::dropIfExists('pim_product_types');
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?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::table('catalogue_products', function (Blueprint $table) {
$table->foreignId('product_type_id')
->nullable()
->after('category_id')
->constrained('pim_product_types')
->cascadeOnUpdate()
->restrictOnDelete();
});
}

public function down(): void
{
Schema::table('catalogue_products', function (Blueprint $table) {
$table->dropForeign(['product_type_id']);
$table->dropColumn('product_type_id');
});
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?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('catalogue_product_data', function (Blueprint $table) {
$table->id();
$table->foreignId('product_id');

// Add foreign key for tenant if it's configured in the catalogue config
if (config('eclipse-catalogue.tenancy.model')) {
$tenantClass = config('eclipse-catalogue.tenancy.model');
/** @var \Illuminate\Database\Eloquent\Model $tenant */
$tenant = new $tenantClass;
$table->foreignId(config('eclipse-catalogue.tenancy.foreign_key'))
->constrained($tenant->getTable(), $tenant->getKeyName())
->cascadeOnUpdate()
->cascadeOnDelete();
}

$table->string('sorting_label')->nullable();
$table->boolean('is_active')->default(false);
$table->dateTime('available_from_date')->nullable();
$table->boolean('has_free_delivery')->default(false);
});
}

public function down(): void
{
Schema::dropIfExists('catalogue_product_data');
}
};
36 changes: 36 additions & 0 deletions database/migrations/2025_08_16_121654_add_product_attributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?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::table('catalogue_products', function (Blueprint $table) {
$table->string('origin_country_id', 2)->nullable();
$table->foreign('origin_country_id')
->references('id')
->on('world_countries')
->cascadeOnUpdate()
->restrictOnDelete();
$table->text('meta_description')
->nullable();
$table->string('meta_title')
->nullable();
});
}

public function down(): void
{
Schema::table('catalogue_products', function (Blueprint $table) {
$table->dropForeign(['origin_country_id']);
$table->dropColumn([
'origin_country_id',
'meta_description',
'meta_title',
]);
});
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

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

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('catalogue_product_prices', function (Blueprint $table) {
$table->id();
$table->foreignId('product_id')
->constrained('catalogue_products')
->cascadeOnUpdate()
->cascadeOnDelete();
$table->foreignId('price_list_id')
->constrained('pim_price_lists')
->cascadeOnUpdate()
->cascadeOnDelete();
$table->date('valid_from');
$table->date('valid_to')->nullable();
$table->decimal('price', 20, 5);
$table->boolean('tax_included');
$table->timestamps();
$table->unique(['product_id', 'price_list_id', 'valid_from'], 'uq_cpp_pid_plid_vf');
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('catalogue_product_prices');
}
};
2 changes: 1 addition & 1 deletion database/seeders/CatalogueSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class CatalogueSeeder extends Seeder
public function run(): void
{
$this->call(CategorySeeder::class);

$this->call(ProductTypeSeeder::class);
$this->call(ProductSeeder::class);
}
}
19 changes: 18 additions & 1 deletion database/seeders/ProductSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Eclipse\Catalogue\Seeders;

use Eclipse\Catalogue\Models\Product;
use Eclipse\Catalogue\Models\ProductType;
use Exception;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Http;
Expand All @@ -13,10 +14,17 @@ class ProductSeeder extends Seeder
public function run(): void
{
$this->ensureSampleImagesExist();
$this->ensureProductTypesExist();

$productTypes = ProductType::all();

Product::factory()
->count(100)
->create();
->create([
'product_type_id' => function () use ($productTypes) {
return $productTypes->random()->id;
},
]);
}

private function ensureSampleImagesExist(): void
Expand Down Expand Up @@ -55,4 +63,13 @@ private function ensureSampleImagesExist(): void

$this->command->info('Sample images ready!');
}

private function ensureProductTypesExist(): void
{
$productTypes = ProductType::all();

if ($productTypes->isEmpty()) {
$this->call(ProductTypeSeeder::class);
}
}
}
87 changes: 87 additions & 0 deletions database/seeders/ProductTypeSeeder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

namespace Eclipse\Catalogue\Seeders;

use Eclipse\Catalogue\Models\ProductType;
use Eclipse\Catalogue\Models\ProductTypeData;
use Illuminate\Database\Seeder;

class ProductTypeSeeder extends Seeder
{
public function run(): void
{
$tenantModel = config('eclipse-catalogue.tenancy.model');
$tenantFK = config('eclipse-catalogue.tenancy.foreign_key');
$tenants = $tenantModel::all();

$productTypes = [
[
'name' => $this->getTranslatedName('Common product'),
'code' => 'COMMON',
],
[
'name' => $this->getTranslatedName('T-shirt'),
'code' => 'TSHIRT',
],
[
'name' => $this->getTranslatedName('Shoes'),
'code' => 'SHOES',
],
];

foreach ($productTypes as $productTypeData) {
$productType = ProductType::firstOrCreate(
['code' => $productTypeData['code']],
$productTypeData
);

foreach ($tenants as $tenant) {
ProductTypeData::firstOrCreate(
[
'product_type_id' => $productType->id,
$tenantFK => $tenant->id,
],
[
'product_type_id' => $productType->id,
$tenantFK => $tenant->id,
'is_active' => true,
'is_default' => false,
]
);
}
}
}

/**
* Get translated name for all available locales.
*/
protected function getTranslatedName(string $baseName): array
{
$locales = $this->getAvailableLocales();
$translatedNames = [];

foreach ($locales as $locale) {
if ($locale === 'en') {
$translatedNames[$locale] = $baseName;
} else {
$translatedNames[$locale] = strtoupper($locale).': '.$baseName;
}
}

return $translatedNames;
}

/**
* Get available locales for the application.
*/
protected function getAvailableLocales(): array
{
if (class_exists(\Eclipse\Core\Models\Locale::class)) {
return \Eclipse\Core\Models\Locale::getAvailableLocales()
->pluck('id')
->toArray();
}

return ['en'];
}
}
Loading