Skip to content
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
16 changes: 5 additions & 11 deletions database/factories/ProductFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
namespace Eclipse\Catalogue\Factories;

use Eclipse\Catalogue\Models\Product;
use Exception;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;

class ProductFactory extends Factory
{
Expand Down Expand Up @@ -47,21 +45,17 @@ public function definition(): array
];
}

public function configure(): static
public function withImages(): static
{
return $this->afterCreating(function (Product $product) {
$imageNumber = rand(1, 15);
$imagePath = storage_path("app/public/sample-products/{$imageNumber}.jpg");

if (file_exists($imagePath)) {
try {
$product->addMedia($imagePath)
->preservingOriginal()
->withCustomProperties(['is_cover' => true])
->toMediaCollection('images');
} catch (Exception $e) {
Log::warning("Failed to attach image to product {$product->id}: ".$e->getMessage());
}
$product->addMedia($imagePath)
->preservingOriginal()
->withCustomProperties(['is_cover' => true])
->toMediaCollection('images');
}
});
}
Expand Down
248 changes: 174 additions & 74 deletions database/seeders/ProductSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use Eclipse\Catalogue\Models\ProductType;
use Exception;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;

class ProductSeeder extends Seeder
Expand All @@ -25,7 +25,7 @@ public function run(): void
$productTypes = ProductType::all();

Product::factory()
->count(100)
->count(20)
->create([
'product_type_id' => function () use ($productTypes) {
return $productTypes->random()->id;
Expand All @@ -35,112 +35,200 @@ public function run(): void
$tenantFK = config('eclipse-catalogue.tenancy.foreign_key');
$tenantModel = config('eclipse-catalogue.tenancy.model');

$products = Product::query()->latest('id')->take(100)->get();
$products = Product::query()->latest('id')->take(20)->get();
$groupProductInserts = [];
$productDataInserts = [];

foreach ($products as $index => $product) {
if ($tenantFK && $tenantModel && class_exists($tenantModel)) {
$tenants = $tenantModel::all();
if ($tenantFK && $tenantModel && class_exists($tenantModel)) {
$tenants = $tenantModel::all();

$categoriesByTenant = Category::query()
->withoutGlobalScopes()
->get()
->groupBy($tenantFK);

$groupsByTenant = Group::all()->groupBy($tenantFK);

foreach ($products as $index => $product) {
foreach ($tenants as $tenant) {
$categoryId = Category::query()
->withoutGlobalScopes()
->where($tenantFK, $tenant->id)
->inRandomOrder()
->value('id');
$categories = $categoriesByTenant->get($tenant->id);
$categoryId = $categories?->random()?->id;

$productData = ProductData::factory()->create([
$productDataInserts[] = [
'product_id' => $product->id,
$tenantFK => $tenant->id,
'is_active' => true,
'has_free_delivery' => false,
'category_id' => $categoryId,
]);
];

// Assign random product status for this tenant
$this->assignRandomProductStatus($productData, $tenant->id);

// Get groups for this specific tenant
$tenantGroups = Group::where($tenantFK, $tenant->id)->get();
$tenantGroups = $groupsByTenant->get($tenant->id, collect());
$groupsToAdd = $this->determineGroupsForProduct($index, $tenantGroups);

foreach ($groupsToAdd as $group) {
$group->addProduct($product);
foreach ($groupsToAdd as $groupIndex => $group) {
$groupProductInserts[] = [
'group_id' => $group->id,
'product_id' => $product->id,
'sort' => $groupIndex + 1,
];
}
}
} else {
$categoryId = Category::query()->inRandomOrder()->value('id');
}
} else {
$categories = Category::all();
$groups = Group::all();

$productData = ProductData::factory()->create([
foreach ($products as $index => $product) {
$productDataInserts[] = [
'product_id' => $product->id,
'is_active' => true,
'has_free_delivery' => false,
'category_id' => $categoryId,
]);

// Assign random product status for non-tenant scenario
$this->assignRandomProductStatus($productData, null);
'category_id' => $categories->random()->id,
];

// For non-tenant scenarios, use all groups
$groups = Group::all();
$groupsToAdd = $this->determineGroupsForProduct($index, $groups);
foreach ($groupsToAdd as $group) {
$group->addProduct($product);
foreach ($groupsToAdd as $groupIndex => $group) {
$groupProductInserts[] = [
'group_id' => $group->id,
'product_id' => $product->id,
'sort' => $groupIndex + 1,
];
}
}
}

if (! empty($productDataInserts)) {
ProductData::insert($productDataInserts);

$createdProductData = ProductData::whereIn('product_id', $products->pluck('id'))->get();

if ($tenantFK && $tenantModel && class_exists($tenantModel)) {
foreach ($createdProductData as $productData) {
$this->assignRandomProductStatus($productData, $productData->{$tenantFK});
}
} else {
foreach ($createdProductData as $productData) {
$this->assignRandomProductStatus($productData, null);
}
}
}

if (! empty($groupProductInserts)) {
DB::table('pim_group_has_product')->insert($groupProductInserts);
}

$this->attachImagesToProducts($products);
}

private function determineGroupsForProduct(int $productIndex, $groups): array
{
$groupsToAdd = [];
if ($groups->isEmpty()) {
return [];
}

// Randomly assign 1-3 groups per product
$numGroupsToAdd = rand(1, min(3, $groups->count()));

// Get random groups for this product
$randomGroups = $groups->random($numGroupsToAdd);

foreach ($randomGroups as $group) {
$groupsToAdd[] = $group;
}

return $groupsToAdd;
return $groups->random($numGroupsToAdd)->all();
}

private function ensureSampleImagesExist(): void
{
Storage::disk('public')->makeDirectory('sample-products');

$existingImages = Storage::disk('public')->files('sample-products');

if (count($existingImages) >= 15) {
$this->command->info('Sample images already exist.');

if (count(Storage::disk('public')->files('sample-products')) >= 15) {
return;
}

$this->command->info('Downloading sample product images...');

for ($i = 1; $i <= 15; $i++) {
$imagePath = "sample-products/{$i}.jpg";

if (Storage::disk('public')->exists($imagePath)) {
if (Storage::disk('public')->exists("sample-products/{$i}.jpg")) {
continue;
}

try {
$imageUrl = "https://picsum.photos/400/300?random={$i}";
$response = Http::timeout(10)->get($imageUrl);
$image = $this->generatePlaceholderImage($i);
Storage::disk('public')->put("sample-products/{$i}.jpg", $image);
}
}

private function generatePlaceholderImage(int $index): string
{
$image = imagecreatetruecolor(400, 300);
$hue = ($index * 24) % 360;

if ($response->successful()) {
Storage::disk('public')->put($imagePath, $response->body());
$this->command->info("Downloaded image {$i}/15");
}
} catch (Exception $e) {
$this->command->warn("Failed to download image {$i}: ".$e->getMessage());
}
$this->drawBackground($image, $hue);
$this->drawShapes($image, $hue);
$this->drawOverlay($image, $hue);

ob_start();
imagejpeg($image, null, 85);
$imageData = ob_get_clean();
imagedestroy($image);

return $imageData;
}

private function drawBackground($image, int $hue): void
{
[$r, $g, $b] = $this->hslToRgb($hue / 360, 0.6, 0.5);
$bgColor = imagecolorallocate($image, $r, $g, $b);
imagefill($image, 0, 0, $bgColor);
}

private function drawShapes($image, int $baseHue): void
{
for ($i = 0; $i < 50; $i++) {
$hue = ($baseHue + rand(-30, 30)) / 360;
[$r, $g, $b] = $this->hslToRgb($hue, rand(40, 80) / 100, rand(30, 70) / 100);
$color = imagecolorallocate($image, $r, $g, $b);

match (rand(0, 2)) {
0 => imagefilledellipse($image, rand(0, 400), rand(0, 300), rand(20, 100), rand(20, 100), $color),
1 => imageline($image, rand(0, 400), rand(0, 300), rand(0, 400), rand(0, 300), $color),
2 => imagefilledrectangle($image, rand(0, 400), rand(0, 300), rand(0, 400), rand(0, 300), $color),
};
}
}

$this->command->info('Sample images ready!');
private function drawOverlay($image, int $baseHue): void
{
for ($i = 0; $i < 3; $i++) {
[$r, $g, $b] = $this->hslToRgb($baseHue / 360, 0.1, rand(80, 95) / 100);
$overlayColor = imagecolorallocatealpha($image, $r, $g, $b, rand(90, 110));
imagefilledellipse($image, rand(-50, 350), rand(-50, 250), rand(200, 400), rand(200, 400), $overlayColor);
}
}

private function hslToRgb(float $h, float $s, float $l): array
{
if ($s == 0) {
$rgb = $l;

return [round($rgb * 255), round($rgb * 255), round($rgb * 255)];
}

$q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s;
$p = 2 * $l - $q;

return [
round($this->hueToRgb($p, $q, $h + 1 / 3) * 255),
round($this->hueToRgb($p, $q, $h) * 255),
round($this->hueToRgb($p, $q, $h - 1 / 3) * 255),
];
}

private function hueToRgb(float $p, float $q, float $t): float
{
$t = match (true) {
$t < 0 => $t + 1,
$t > 1 => $t - 1,
default => $t,
};

return match (true) {
$t < 1 / 6 => $p + ($q - $p) * 6 * $t,
$t < 1 / 2 => $q,
$t < 2 / 3 => $p + ($q - $p) * (2 / 3 - $t) * 6,
default => $p,
};
}

private function ensureProductTypesExist(): void
Expand Down Expand Up @@ -170,31 +258,43 @@ private function ensureProductStatusesExist(): void
}
}

/**
* Assign a random product status to a product data record, respecting tenancy.
*/
private function assignRandomProductStatus(ProductData $productData, ?int $tenantId): void
{
$tenantFK = config('eclipse-catalogue.tenancy.foreign_key');

// Get available product statuses for the tenant
$query = ProductStatus::query();

if ($tenantFK && $tenantId !== null) {
// Multi-tenant scenario: get statuses for this specific tenant
$query->where($tenantFK, $tenantId);
} elseif (! $tenantFK) {
// Single-tenant scenario: get all statuses (site_id will be null)
$query->whereNull('site_id');
}

$availableStatuses = $query->get();

if ($availableStatuses->isNotEmpty()) {
// Randomly select a status (with 70% chance of getting a status, 30% chance of no status)
if (rand(1, 100) <= 70) {
$randomStatus = $availableStatuses->random();
$productData->update(['product_status_id' => $randomStatus->id]);
if ($availableStatuses->isNotEmpty() && rand(1, 100) <= 70) {
$randomStatus = $availableStatuses->random();
$productData->update(['product_status_id' => $randomStatus->id]);
}
}

private function attachImagesToProducts($products): void
{
$productsWithImages = $products->random(10);

foreach ($productsWithImages as $index => $product) {
$imageNumber = ($index % 15) + 1;
$sourceFileName = "{$imageNumber}.jpg";
$sourcePath = storage_path("app/public/sample-products/{$sourceFileName}");

if (file_exists($sourcePath)) {
try {
$product->addMedia($sourcePath)
->preservingOriginal()
->withCustomProperties(['is_cover' => true])
->toMediaCollection('images', 'public');
} catch (Exception $e) {
}
}
}
}
Expand Down