1010use Eclipse \Catalogue \Models \ProductType ;
1111use Exception ;
1212use Illuminate \Database \Seeder ;
13- use Illuminate \Support \Facades \Http ;
13+ use Illuminate \Support \Facades \DB ;
1414use Illuminate \Support \Facades \Storage ;
1515
1616class ProductSeeder extends Seeder
@@ -25,7 +25,7 @@ public function run(): void
2525 $ productTypes = ProductType::all ();
2626
2727 Product::factory ()
28- ->count (100 )
28+ ->count (20 )
2929 ->create ([
3030 'product_type_id ' => function () use ($ productTypes ) {
3131 return $ productTypes ->random ()->id ;
@@ -35,112 +35,200 @@ public function run(): void
3535 $ tenantFK = config ('eclipse-catalogue.tenancy.foreign_key ' );
3636 $ tenantModel = config ('eclipse-catalogue.tenancy.model ' );
3737
38- $ products = Product::query ()->latest ('id ' )->take (100 )->get ();
38+ $ products = Product::query ()->latest ('id ' )->take (20 )->get ();
39+ $ groupProductInserts = [];
40+ $ productDataInserts = [];
3941
40- foreach ($ products as $ index => $ product ) {
41- if ($ tenantFK && $ tenantModel && class_exists ($ tenantModel )) {
42- $ tenants = $ tenantModel ::all ();
42+ if ($ tenantFK && $ tenantModel && class_exists ($ tenantModel )) {
43+ $ tenants = $ tenantModel ::all ();
44+
45+ $ categoriesByTenant = Category::query ()
46+ ->withoutGlobalScopes ()
47+ ->get ()
48+ ->groupBy ($ tenantFK );
49+
50+ $ groupsByTenant = Group::all ()->groupBy ($ tenantFK );
51+
52+ foreach ($ products as $ index => $ product ) {
4353 foreach ($ tenants as $ tenant ) {
44- $ categoryId = Category::query ()
45- ->withoutGlobalScopes ()
46- ->where ($ tenantFK , $ tenant ->id )
47- ->inRandomOrder ()
48- ->value ('id ' );
54+ $ categories = $ categoriesByTenant ->get ($ tenant ->id );
55+ $ categoryId = $ categories ?->random()?->id;
4956
50- $ productData = ProductData:: factory ()-> create ( [
57+ $ productDataInserts [] = [
5158 'product_id ' => $ product ->id ,
5259 $ tenantFK => $ tenant ->id ,
5360 'is_active ' => true ,
5461 'has_free_delivery ' => false ,
5562 'category_id ' => $ categoryId ,
56- ]) ;
63+ ];
5764
58- // Assign random product status for this tenant
59- $ this ->assignRandomProductStatus ($ productData , $ tenant ->id );
60-
61- // Get groups for this specific tenant
62- $ tenantGroups = Group::where ($ tenantFK , $ tenant ->id )->get ();
65+ $ tenantGroups = $ groupsByTenant ->get ($ tenant ->id , collect ());
6366 $ groupsToAdd = $ this ->determineGroupsForProduct ($ index , $ tenantGroups );
6467
65- foreach ($ groupsToAdd as $ group ) {
66- $ group ->addProduct ($ product );
68+ foreach ($ groupsToAdd as $ groupIndex => $ group ) {
69+ $ groupProductInserts [] = [
70+ 'group_id ' => $ group ->id ,
71+ 'product_id ' => $ product ->id ,
72+ 'sort ' => $ groupIndex + 1 ,
73+ ];
6774 }
6875 }
69- } else {
70- $ categoryId = Category::query ()->inRandomOrder ()->value ('id ' );
76+ }
77+ } else {
78+ $ categories = Category::all ();
79+ $ groups = Group::all ();
7180
72- $ productData = ProductData::factory ()->create ([
81+ foreach ($ products as $ index => $ product ) {
82+ $ productDataInserts [] = [
7383 'product_id ' => $ product ->id ,
7484 'is_active ' => true ,
7585 'has_free_delivery ' => false ,
76- 'category_id ' => $ categoryId ,
77- ]);
78-
79- // Assign random product status for non-tenant scenario
80- $ this ->assignRandomProductStatus ($ productData , null );
86+ 'category_id ' => $ categories ->random ()->id ,
87+ ];
8188
82- // For non-tenant scenarios, use all groups
83- $ groups = Group::all ();
8489 $ groupsToAdd = $ this ->determineGroupsForProduct ($ index , $ groups );
85- foreach ($ groupsToAdd as $ group ) {
86- $ group ->addProduct ($ product );
90+ foreach ($ groupsToAdd as $ groupIndex => $ group ) {
91+ $ groupProductInserts [] = [
92+ 'group_id ' => $ group ->id ,
93+ 'product_id ' => $ product ->id ,
94+ 'sort ' => $ groupIndex + 1 ,
95+ ];
8796 }
8897 }
8998 }
99+
100+ if (! empty ($ productDataInserts )) {
101+ ProductData::insert ($ productDataInserts );
102+
103+ $ createdProductData = ProductData::whereIn ('product_id ' , $ products ->pluck ('id ' ))->get ();
104+
105+ if ($ tenantFK && $ tenantModel && class_exists ($ tenantModel )) {
106+ foreach ($ createdProductData as $ productData ) {
107+ $ this ->assignRandomProductStatus ($ productData , $ productData ->{$ tenantFK });
108+ }
109+ } else {
110+ foreach ($ createdProductData as $ productData ) {
111+ $ this ->assignRandomProductStatus ($ productData , null );
112+ }
113+ }
114+ }
115+
116+ if (! empty ($ groupProductInserts )) {
117+ DB ::table ('pim_group_has_product ' )->insert ($ groupProductInserts );
118+ }
119+
120+ $ this ->attachImagesToProducts ($ products );
90121 }
91122
92123 private function determineGroupsForProduct (int $ productIndex , $ groups ): array
93124 {
94- $ groupsToAdd = [];
125+ if ($ groups ->isEmpty ()) {
126+ return [];
127+ }
95128
96- // Randomly assign 1-3 groups per product
97129 $ numGroupsToAdd = rand (1 , min (3 , $ groups ->count ()));
98130
99- // Get random groups for this product
100- $ randomGroups = $ groups ->random ($ numGroupsToAdd );
101-
102- foreach ($ randomGroups as $ group ) {
103- $ groupsToAdd [] = $ group ;
104- }
105-
106- return $ groupsToAdd ;
131+ return $ groups ->random ($ numGroupsToAdd )->all ();
107132 }
108133
109134 private function ensureSampleImagesExist (): void
110135 {
111136 Storage::disk ('public ' )->makeDirectory ('sample-products ' );
112137
113- $ existingImages = Storage::disk ('public ' )->files ('sample-products ' );
114-
115- if (count ($ existingImages ) >= 15 ) {
116- $ this ->command ->info ('Sample images already exist. ' );
117-
138+ if (count (Storage::disk ('public ' )->files ('sample-products ' )) >= 15 ) {
118139 return ;
119140 }
120141
121- $ this ->command ->info ('Downloading sample product images... ' );
122-
123142 for ($ i = 1 ; $ i <= 15 ; $ i ++) {
124- $ imagePath = "sample-products/ {$ i }.jpg " ;
125-
126- if (Storage::disk ('public ' )->exists ($ imagePath )) {
143+ if (Storage::disk ('public ' )->exists ("sample-products/ {$ i }.jpg " )) {
127144 continue ;
128145 }
129146
130- try {
131- $ imageUrl = "https://picsum.photos/400/300?random= {$ i }" ;
132- $ response = Http::timeout (10 )->get ($ imageUrl );
147+ $ image = $ this ->generatePlaceholderImage ($ i );
148+ Storage::disk ('public ' )->put ("sample-products/ {$ i }.jpg " , $ image );
149+ }
150+ }
151+
152+ private function generatePlaceholderImage (int $ index ): string
153+ {
154+ $ image = imagecreatetruecolor (400 , 300 );
155+ $ hue = ($ index * 24 ) % 360 ;
133156
134- if ($ response ->successful ()) {
135- Storage::disk ('public ' )->put ($ imagePath , $ response ->body ());
136- $ this ->command ->info ("Downloaded image {$ i }/15 " );
137- }
138- } catch (Exception $ e ) {
139- $ this ->command ->warn ("Failed to download image {$ i }: " .$ e ->getMessage ());
140- }
157+ $ this ->drawBackground ($ image , $ hue );
158+ $ this ->drawShapes ($ image , $ hue );
159+ $ this ->drawOverlay ($ image , $ hue );
160+
161+ ob_start ();
162+ imagejpeg ($ image , null , 85 );
163+ $ imageData = ob_get_clean ();
164+ imagedestroy ($ image );
165+
166+ return $ imageData ;
167+ }
168+
169+ private function drawBackground ($ image , int $ hue ): void
170+ {
171+ [$ r , $ g , $ b ] = $ this ->hslToRgb ($ hue / 360 , 0.6 , 0.5 );
172+ $ bgColor = imagecolorallocate ($ image , $ r , $ g , $ b );
173+ imagefill ($ image , 0 , 0 , $ bgColor );
174+ }
175+
176+ private function drawShapes ($ image , int $ baseHue ): void
177+ {
178+ for ($ i = 0 ; $ i < 50 ; $ i ++) {
179+ $ hue = ($ baseHue + rand (-30 , 30 )) / 360 ;
180+ [$ r , $ g , $ b ] = $ this ->hslToRgb ($ hue , rand (40 , 80 ) / 100 , rand (30 , 70 ) / 100 );
181+ $ color = imagecolorallocate ($ image , $ r , $ g , $ b );
182+
183+ match (rand (0 , 2 )) {
184+ 0 => imagefilledellipse ($ image , rand (0 , 400 ), rand (0 , 300 ), rand (20 , 100 ), rand (20 , 100 ), $ color ),
185+ 1 => imageline ($ image , rand (0 , 400 ), rand (0 , 300 ), rand (0 , 400 ), rand (0 , 300 ), $ color ),
186+ 2 => imagefilledrectangle ($ image , rand (0 , 400 ), rand (0 , 300 ), rand (0 , 400 ), rand (0 , 300 ), $ color ),
187+ };
141188 }
189+ }
142190
143- $ this ->command ->info ('Sample images ready! ' );
191+ private function drawOverlay ($ image , int $ baseHue ): void
192+ {
193+ for ($ i = 0 ; $ i < 3 ; $ i ++) {
194+ [$ r , $ g , $ b ] = $ this ->hslToRgb ($ baseHue / 360 , 0.1 , rand (80 , 95 ) / 100 );
195+ $ overlayColor = imagecolorallocatealpha ($ image , $ r , $ g , $ b , rand (90 , 110 ));
196+ imagefilledellipse ($ image , rand (-50 , 350 ), rand (-50 , 250 ), rand (200 , 400 ), rand (200 , 400 ), $ overlayColor );
197+ }
198+ }
199+
200+ private function hslToRgb (float $ h , float $ s , float $ l ): array
201+ {
202+ if ($ s == 0 ) {
203+ $ rgb = $ l ;
204+
205+ return [round ($ rgb * 255 ), round ($ rgb * 255 ), round ($ rgb * 255 )];
206+ }
207+
208+ $ q = $ l < 0.5 ? $ l * (1 + $ s ) : $ l + $ s - $ l * $ s ;
209+ $ p = 2 * $ l - $ q ;
210+
211+ return [
212+ round ($ this ->hueToRgb ($ p , $ q , $ h + 1 / 3 ) * 255 ),
213+ round ($ this ->hueToRgb ($ p , $ q , $ h ) * 255 ),
214+ round ($ this ->hueToRgb ($ p , $ q , $ h - 1 / 3 ) * 255 ),
215+ ];
216+ }
217+
218+ private function hueToRgb (float $ p , float $ q , float $ t ): float
219+ {
220+ $ t = match (true ) {
221+ $ t < 0 => $ t + 1 ,
222+ $ t > 1 => $ t - 1 ,
223+ default => $ t ,
224+ };
225+
226+ return match (true ) {
227+ $ t < 1 / 6 => $ p + ($ q - $ p ) * 6 * $ t ,
228+ $ t < 1 / 2 => $ q ,
229+ $ t < 2 / 3 => $ p + ($ q - $ p ) * (2 / 3 - $ t ) * 6 ,
230+ default => $ p ,
231+ };
144232 }
145233
146234 private function ensureProductTypesExist (): void
@@ -170,31 +258,43 @@ private function ensureProductStatusesExist(): void
170258 }
171259 }
172260
173- /**
174- * Assign a random product status to a product data record, respecting tenancy.
175- */
176261 private function assignRandomProductStatus (ProductData $ productData , ?int $ tenantId ): void
177262 {
178263 $ tenantFK = config ('eclipse-catalogue.tenancy.foreign_key ' );
179264
180- // Get available product statuses for the tenant
181265 $ query = ProductStatus::query ();
182266
183267 if ($ tenantFK && $ tenantId !== null ) {
184- // Multi-tenant scenario: get statuses for this specific tenant
185268 $ query ->where ($ tenantFK , $ tenantId );
186269 } elseif (! $ tenantFK ) {
187- // Single-tenant scenario: get all statuses (site_id will be null)
188270 $ query ->whereNull ('site_id ' );
189271 }
190272
191273 $ availableStatuses = $ query ->get ();
192274
193- if ($ availableStatuses ->isNotEmpty ()) {
194- // Randomly select a status (with 70% chance of getting a status, 30% chance of no status)
195- if (rand (1 , 100 ) <= 70 ) {
196- $ randomStatus = $ availableStatuses ->random ();
197- $ productData ->update (['product_status_id ' => $ randomStatus ->id ]);
275+ if ($ availableStatuses ->isNotEmpty () && rand (1 , 100 ) <= 70 ) {
276+ $ randomStatus = $ availableStatuses ->random ();
277+ $ productData ->update (['product_status_id ' => $ randomStatus ->id ]);
278+ }
279+ }
280+
281+ private function attachImagesToProducts ($ products ): void
282+ {
283+ $ productsWithImages = $ products ->random (10 );
284+
285+ foreach ($ productsWithImages as $ index => $ product ) {
286+ $ imageNumber = ($ index % 15 ) + 1 ;
287+ $ sourceFileName = "{$ imageNumber }.jpg " ;
288+ $ sourcePath = storage_path ("app/public/sample-products/ {$ sourceFileName }" );
289+
290+ if (file_exists ($ sourcePath )) {
291+ try {
292+ $ product ->addMedia ($ sourcePath )
293+ ->preservingOriginal ()
294+ ->withCustomProperties (['is_cover ' => true ])
295+ ->toMediaCollection ('images ' , 'public ' );
296+ } catch (Exception $ e ) {
297+ }
198298 }
199299 }
200300 }
0 commit comments