['Corolla', 'Yaris', 'RAV4', 'Auris', 'C-HR', 'Camry'], 'Volkswagen' => ['Golf', 'Passat', 'Polo', 'Tiguan', 'T-Roc', 'Arteon'], 'Skoda' => ['Octavia', 'Fabia', 'Superb', 'Kodiaq', 'Karoq', 'Scala'], 'Ford' => ['Focus', 'Fiesta', 'Mondeo', 'Kuga', 'Puma'], 'Opel' => ['Astra', 'Corsa', 'Insignia', 'Mokka', 'Grandland'], 'BMW' => ['Seria 3', 'Seria 5', 'X1', 'X3', 'X5'], 'Audi' => ['A3', 'A4', 'A6', 'Q3', 'Q5'], 'Mercedes-Benz' => ['Klasa A', 'Klasa C', 'Klasa E', 'GLC', 'GLA'], 'Renault' => ['Clio', 'Megane', 'Captur', 'Kadjar'], 'Kia' => ['Ceed', 'Sportage', 'Rio', 'Niro'], 'Hyundai' => ['i20', 'i30', 'Tucson', 'Kona'], 'Tesla' => ['Model 3', 'Model Y', 'Model S'], 'Fiat' => ['Panda', '500', 'Tipo'], 'Dacia' => ['Duster', 'Sandero', 'Logan'], ]; private const COLORS = ['czarny', 'biały', 'srebrny', 'szary', 'niebieski', 'czerwony', 'zielony', 'grafitowy', 'beżowy']; private const PLATE_PREFIXES = ['WA', 'WE', 'WI', 'KR', 'KK', 'PO', 'PZ', 'GD', 'GA', 'WR', 'DW', 'LU', 'SK', 'SC', 'EL', 'BI', 'OP', 'RZ', 'TK', 'NO', 'ZS', 'CB', 'FZ']; private const CUSTOMER_NOTES = [ 'Preferuje kontakt telefoniczny po godzinie 16.', 'Stały klient, przysługuje rabat 10% na usługi serwisowe.', 'Prosi o przypomnienia SMS przed terminem przeglądu.', 'Faktury wysyłać wyłącznie na adres e-mail.', 'Klient flotowy - rozliczenie zbiorcze na koniec miesiąca.', 'Zainteresowany wymianą auta na nowsze w przyszłym roku.', 'Kontakt tylko w dni robocze.', ]; private const CAR_NOTES = [ 'Wymiana oleju przy 15 000 km.', 'Do sprawdzenia klocki hamulcowe przy następnej wizycie.', 'Opony zimowe przechowywane w serwisie.', 'Auto po szkodzie parkingowej - naprawiony tylny zderzak.', 'Gwarancja producenta do końca roku.', 'Klient zgłaszał stuki w zawieszeniu.', 'Zalecana wymiana rozrządu.', ]; private const VIN_CHARS = 'ABCDEFGHJKLMNPRSTUVWXYZ0123456789'; /** * The seed truncates its tables first, so it can safely be re-run. * * @return bool */ public function isIdempotent(): bool { return true; } /** * @return void */ public function run(): void { $faker = Factory::create('pl_PL'); $faker->seed(20261019); $now = date('Y-m-d H:i:s'); $hasher = new DefaultPasswordHasher(); $this->execute('SET FOREIGN_KEY_CHECKS=0'); foreach (['cars', 'customers', 'users'] as $table) { $this->execute("TRUNCATE TABLE `{$table}`"); } $this->execute('SET FOREIGN_KEY_CHECKS=1'); $this->table('users')->insert([ [ 'email' => 'admin@example.com', 'password' => $hasher->hash('Admin123!'), 'first_name' => 'Adam', 'last_name' => 'Administrator', 'role' => 'admin', 'active' => true, 'created' => $now, 'modified' => $now, ], [ 'email' => 'user@example.com', 'password' => $hasher->hash('User1234!'), 'first_name' => 'Jan', 'last_name' => 'Użytkownik', 'role' => 'user', 'active' => true, 'created' => $now, 'modified' => $now, ], ])->saveData(); $customers = []; $emails = []; for ($i = 1; $i <= 100; $i++) { $isCompany = $faker->boolean(25); $firstName = $faker->firstName(); $lastName = $faker->lastName(); do { $email = $this->slug($firstName) . '.' . $this->slug($lastName) . $faker->numberBetween(1, 999) . '@' . $faker->freeEmailDomain(); } while (isset($emails[$email])); $emails[$email] = true; $created = $faker->dateTimeBetween('-3 years', '-1 day')->format('Y-m-d H:i:s'); $customers[] = [ 'type' => $isCompany ? 'company' : 'individual', 'first_name' => $firstName, 'last_name' => $lastName, 'email' => $email, 'phone' => '+48 ' . $faker->numerify('### ### ###'), 'company_name' => $isCompany ? $faker->company() : null, 'tax_id' => $isCompany ? $faker->numerify('##########') : null, 'street' => $faker->boolean(90) ? $faker->streetAddress() : null, 'city' => $faker->city(), 'postal_code' => $faker->numerify('##-###'), 'country' => 'PL', 'birth_date' => $isCompany ? null : $faker->dateTimeBetween('-80 years', '-18 years')->format('Y-m-d'), 'notes' => $faker->boolean(20) ? $faker->randomElement(self::CUSTOMER_NOTES) : null, 'created_by' => $faker->randomElement([1, 2]), 'created' => $created, 'modified' => $created, ]; } $this->table('customers')->insert($customers)->saveData(); $cars = []; $vins = []; $plates = []; for ($customerId = 1; $customerId <= 100; $customerId++) { $count = $faker->numberBetween(1, 5); for ($j = 0; $j < $count; $j++) { $make = $faker->randomElement(array_keys(self::CARS)); $isTesla = $make === 'Tesla'; $fuel = $isTesla ? 'electric' : $faker->randomElement(['petrol', 'petrol', 'diesel', 'diesel', 'lpg', 'hybrid']); do { $vin = $this->randomVin($faker); } while (isset($vins[$vin])); $vins[$vin] = true; do { $plate = $faker->randomElement(self::PLATE_PREFIXES) . $faker->numberBetween(10000, 99999); $plate = substr($plate, 0, 2) . $faker->bothify('?') . substr($plate, 3); $plate = strtoupper($plate); } while (isset($plates[$plate])); $plates[$plate] = true; $year = $faker->numberBetween(2005, 2026); $created = $faker->dateTimeBetween('-3 years', '-1 day')->format('Y-m-d H:i:s'); $cars[] = [ 'customer_id' => $customerId, 'make' => $make, 'model' => $faker->randomElement(self::CARS[$make]), 'production_year' => $year, 'vin' => $vin, 'registration_number' => $plate, 'fuel_type' => $fuel, 'color' => $faker->randomElement(self::COLORS), 'engine_capacity' => $fuel === 'electric' ? null : $faker->randomElement([999, 1197, 1395, 1498, 1598, 1968, 1995, 2494, 2993]), 'power_hp' => $faker->numberBetween(70, $isTesla ? 500 : 300), 'transmission' => $isTesla ? 'automatic' : $faker->randomElement(['manual', 'manual', 'automatic']), 'mileage' => max(0, (2026 - $year) * $faker->numberBetween(5000, 25000) + $faker->numberBetween(0, 5000)), 'notes' => $faker->boolean(15) ? $faker->randomElement(self::CAR_NOTES) : null, 'created' => $created, 'modified' => $created, ]; } } $this->table('cars')->insert($cars)->saveData(); $this->getIo()?->out(sprintf('Seeded 2 users, %d customers and %d cars.', count($customers), count($cars))); } /** * @param \Faker\Generator $faker Faker * @return string */ private function randomVin(Generator $faker): string { $vin = ''; for ($i = 0; $i < 17; $i++) { $vin .= self::VIN_CHARS[$faker->numberBetween(0, strlen(self::VIN_CHARS) - 1)]; } return $vin; } /** * ASCII slug for e-mail addresses (strips Polish diacritics). * * @param string $text Text * @return string */ private function slug(string $text): string { $map = ['ą' => 'a', 'ć' => 'c', 'ę' => 'e', 'ł' => 'l', 'ń' => 'n', 'ó' => 'o', 'ś' => 's', 'ź' => 'z', 'ż' => 'z']; return preg_replace('/[^a-z]/', '', strtr(mb_strtolower($text), $map)) ?? ''; } }