Files
clinicpro/data/seed_full.php
T
hamed 28dbfa67e2 Add seed script for populating database with initial data
- Implemented a comprehensive PHP script to seed the database with test data for users, doctors, clinics, appointments, payments, ratings, comments, and blogs.
- Included functions for generating random data such as UUIDs, mobile numbers, and images.
- Downloaded and stored images for doctors and clinics using parallel curl requests.
- Inserted static data for insurances, patients, and doctors with various attributes including specialties and services.
- Ensured data integrity by disabling foreign key checks during the seeding process and re-enabling them afterward.
- Provided a summary of the total records inserted into each table at the end of the script.
2026-06-10 15:06:37 +03:30

709 lines
41 KiB
PHP

<?php
declare(strict_types=1);
set_time_limit(0);
ini_set('memory_limit', '512M');
$pdo = new PDO('mysql:host=db;dbname=db;charset=utf8mb4', 'db', 'db');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec('SET NAMES utf8mb4');
$pdo->exec('SET FOREIGN_KEY_CHECKS = 0');
$pdo->exec("SET SESSION sql_mode = ''");
$now = time();
$BASE_DIR = '/var/www/html/public';
$BASE_URL = '';
// ── Helpers ───────────────────────────────────────────────────────────────────
function uuid(): string {
return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand(0,0xffff),mt_rand(0,0xffff),mt_rand(0,0xffff),
mt_rand(0,0x0fff)|0x4000,mt_rand(0,0x3fff)|0x8000,
mt_rand(0,0xffff),mt_rand(0,0xffff),mt_rand(0,0xffff));
}
function ri(array $a) { return $a[array_rand($a)]; }
function ris(array $a, int $min, int $max): array {
$n = rand($min, min($max, count($a)));
shuffle($a);
return array_slice($a, 0, $n);
}
function mobile(array &$used): string {
$prefixes = ['0910','0911','0912','0913','0914','0915','0916','0917','0918','0919',
'0930','0933','0935','0936','0937','0938','0901','0902','0920','0921',
'0991','0992','0993','0994'];
do {
$m = ri($prefixes) . str_pad((string)rand(1000000,9999999),7,'0',STR_PAD_LEFT);
} while (isset($used[$m]));
$used[$m] = true;
return $m;
}
function pw(): string { return '$2y$13$fakehash' . substr(str_replace(['+','/','='],'',base64_encode(random_bytes(24))),0,44); }
// ── Download images with parallel curl ───────────────────────────────────────
function downloadParallel(array $urls, array $paths, int $batchSize = 20): void {
$total = count($urls);
for ($offset = 0; $offset < $total; $offset += $batchSize) {
$batch = array_slice($urls, $offset, $batchSize, true);
$batchPath = array_slice($paths, $offset, $batchSize, true);
$mh = curl_multi_init();
$handles = [];
foreach ($batch as $i => $url) {
if (file_exists($batchPath[$i])) continue;
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_USERAGENT => 'Mozilla/5.0',
]);
curl_multi_add_handle($mh, $ch);
$handles[$i] = $ch;
}
$running = null;
do { curl_multi_exec($mh, $running); curl_multi_select($mh); } while ($running > 0);
foreach ($handles as $i => $ch) {
$data = curl_multi_getcontent($ch);
if ($data && strlen($data) > 1000) {
@mkdir(dirname($batchPath[$i]), 0755, true);
file_put_contents($batchPath[$i], $data);
}
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
curl_multi_close($mh);
echo '.';
}
echo "\n";
}
function imgJson(string $url, string $filename, int $filesize = 0): string {
return json_encode([[
'fid' => rand(10000, 999999),
'uuid' => uuid(),
'url' => $url,
'filename' => $filename,
'filemime' => 'image/jpeg',
'filesize' => $filesize > 0 ? $filesize : rand(25000, 90000),
]], JSON_UNESCAPED_UNICODE);
}
// ── STEP 1 — Download doctor photos ──────────────────────────────────────────
echo "=== Downloading doctor photos ===\n";
$doctorPhotoDir = $BASE_DIR . '/uploads/doctors/2026-06';
@mkdir($doctorPhotoDir, 0755, true);
$photoUrls = [];
$photoPaths = [];
$doctorPhotos = ['male' => [], 'female' => []];
// 99 male portraits
for ($n = 1; $n <= 99; $n++) {
$url = "https://randomuser.me/api/portraits/men/$n.jpg";
$path = "$doctorPhotoDir/m-$n.jpg";
$photoUrls[] = $url;
$photoPaths[] = $path;
$doctorPhotos['male'][] = [
'url' => "/uploads/doctors/2026-06/m-$n.jpg",
'file' => "m-$n.jpg",
'path' => $path,
];
}
// 99 female portraits
for ($n = 1; $n <= 99; $n++) {
$url = "https://randomuser.me/api/portraits/women/$n.jpg";
$path = "$doctorPhotoDir/f-$n.jpg";
$photoUrls[] = $url;
$photoPaths[] = $path;
$doctorPhotos['female'][] = [
'url' => "/uploads/doctors/2026-06/f-$n.jpg",
'file' => "f-$n.jpg",
'path' => $path,
];
}
echo "Downloading 198 doctor portraits ";
downloadParallel($photoUrls, $photoPaths, 25);
// ── STEP 2 — Download clinic gallery & logos ──────────────────────────────────
echo "=== Downloading clinic images ===\n";
$galleryDir = $BASE_DIR . '/uploads/clinics/gallery/2026-06';
$logoDir = $BASE_DIR . '/uploads/clinics/logo/2026-06';
@mkdir($galleryDir, 0755, true);
@mkdir($logoDir, 0755, true);
$clinicPhotoUrls = [];
$clinicPhotoPaths = [];
$clinicGalleryPhotos = [];
$clinicLogoPhotos = [];
// 60 clinic gallery photos (medical/building themed via picsum seeds)
$clinicSeeds = [100,200,300,400,500,600,700,800,900,1000,1100,1200,1300,1400,1500,
1600,1700,1800,1900,2000,2100,2200,2300,2400,2500,2600,2700,2800,
2900,3000,3100,3200,3300,3400,3500,3600,3700,3800,3900,4000,
4100,4200,4300,4400,4500,4600,4700,4800,4900,5000,5100,5200,5300,5400,5500,5600,5700,5800,5900,6000];
foreach ($clinicSeeds as $seed) {
$url = "https://picsum.photos/seed/$seed/640/480";
$path = "$galleryDir/c-$seed.jpg";
$clinicPhotoUrls[] = $url;
$clinicPhotoPaths[] = $path;
$clinicGalleryPhotos[] = ['url' => "/uploads/clinics/gallery/2026-06/c-$seed.jpg", 'file' => "c-$seed.jpg"];
}
// 40 clinic logos (smaller)
$logoSeeds = [10,20,30,40,50,60,70,80,90,110,120,130,140,150,160,170,180,190,210,220,
230,240,250,260,270,280,290,310,320,330,340,350,360,370,380,390,410,420,430,440];
foreach ($logoSeeds as $seed) {
$url = "https://picsum.photos/seed/logo$seed/200/200";
$path = "$logoDir/logo-$seed.jpg";
$clinicPhotoUrls[] = $url;
$clinicPhotoPaths[] = $path;
$clinicLogoPhotos[] = ['url' => "/uploads/clinics/logo/2026-06/logo-$seed.jpg", 'file' => "logo-$seed.jpg"];
}
echo "Downloading 100 clinic images ";
downloadParallel($clinicPhotoUrls, $clinicPhotoPaths, 25);
// ── STEP 3 — Download insurance logos ─────────────────────────────────────────
echo "=== Downloading insurance logos ===\n";
$insLogoDir = $BASE_DIR . '/uploads/insurances/logo/2026-06';
@mkdir($insLogoDir, 0755, true);
$insLogoUrls = [];
$insLogoPaths = [];
$insLogoFiles = [];
for ($n = 1; $n <= 20; $n++) {
$seed = $n * 50;
$url = "https://picsum.photos/seed/ins$seed/200/200";
$path = "$insLogoDir/ins-$n.jpg";
$insLogoUrls[] = $url;
$insLogoPaths[] = $path;
$insLogoFiles[] = "/uploads/insurances/logo/2026-06/ins-$n.jpg";
}
echo "Downloading 20 insurance logos ";
downloadParallel($insLogoUrls, $insLogoPaths, 20);
// ── Load DB data ──────────────────────────────────────────────────────────────
$provinces = $pdo->query('SELECT id FROM provinces')->fetchAll(PDO::FETCH_COLUMN);
$cities = $pdo->query('SELECT id, province_id FROM cities WHERE id != 600')->fetchAll(PDO::FETCH_ASSOC);
$cityIds = array_column($cities, 'id');
$cityProvMap = array_column($cities, 'province_id', 'id');
$specParents = $pdo->query('SELECT id FROM specialties WHERE parent_id IS NULL')->fetchAll(PDO::FETCH_COLUMN);
$specChildren = $pdo->query('SELECT id, parent_id FROM specialties WHERE parent_id IS NOT NULL')->fetchAll(PDO::FETCH_ASSOC);
$allServiceRows = $pdo->query('SELECT id, specialty_id FROM doctor_services')->fetchAll(PDO::FETCH_ASSOC);
$serviceIds = array_column($allServiceRows, 'id');
$specSvcMap = [];
foreach ($allServiceRows as $r) {
if ($r['specialty_id']) $specSvcMap[$r['specialty_id']][] = $r['id'];
}
$specChildMap = [];
foreach ($specChildren as $r) {
$specChildMap[$r['parent_id']][] = $r['id'];
}
// ── Static data ───────────────────────────────────────────────────────────────
$maleNames = ['علی','محمد','حسین','احمد','رضا','مهدی','حسن','اکبر','ابراهیم','کریم','جواد','امیر',
'سعید','مجید','فرید','کامران','سیاوش','داریوش','پرویز','شهرام','بهرام','فرهاد','نادر',
'مسعود','وحید','یاسر','حامد','سجاد','ایمان','میثم','آرش','آرمان','پدرام','پویا','کیان',
'سیروس','بابک','فریبرز','هوشنگ','نیما','شاهین','کاوه','آریا','پارسا','دانیال','سینا',
'رامین','کورش','منوچهر','فرامرز','بهزاد','پیمان','امین','اشکان','شایان','آیدین','ارسلان'];
$femaleNames = ['فاطمه','زهرا','مریم','سارا','نرگس','لیلا','نازنین','مهناز','شیرین','پروین','ملیحه',
'الناز','آتنا','باران','ترانه','دلارام','رویا','شادی','گلناز','ماهرخ','ندا','نیلوفر',
'هانیه','یاسمن','زینب','حدیثه','سمیه','طاهره','عاطفه','منیژه','سوده','ریحانه','فرزانه',
'نسرین','ناهید','مینا','گیتی','آذر','پرستو','ژیلا','شکوفه','آفرین','بهناز','مهسا','رها'];
$lastNames = ['احمدی','محمدی','رضایی','حسینی','موسوی','جعفری','کریمی','نوری','صادقی','قاسمی',
'عباسی','میرزایی','رحیمی','اکبری','یوسفی','حیدری','طاهری','نصیری','سلیمانی','زارعی',
'مرادی','شریفی','منصوری','ذبیحی','تهرانی','اصفهانی','مشهدی','شیرازی','تبریزی','همدانی',
'ملکی','درویشی','امیری','نجفی','شاهی','بهرامی','خسروی','گلمحمدی','فدایی','ولیپور',
'نظری','فروزان','پورعلی','دهقانی','صالحی','ربیعی','مجیدی','سبحانی','زمانی','بهشتی',
'عزیزی','خلیلی','رستمی','پورحسین','نظام‌الدینی','میرمحمدی','سیدی','سلطانی','نادری','ابراهیمی'];
$degrees = ['متخصص','فوق تخصص','دکترای عمومی','فلوشیپ','استادیار','دانشیار','پروفسور','رزیدنت'];
$bios = [
'دارای بیش از ۱۵ سال سابقه در حوزه تخصصی، عضو انجمن پزشکی ایران و سازمان نظام پزشکی.',
'فارغ‌التحصیل دانشگاه علوم پزشکی تهران با تجربه کار در بیمارستان‌های معتبر کشور.',
'متخصص با سابقه درخشان، ارائه خدمات با بالاترین کیفیت با استفاده از جدیدترین تجهیزات.',
'عضو هیئت علمی دانشگاه با بیش از ۱۰ سال تجربه بالینی و پژوهشی.',
'دارای گواهینامه‌های تخصصی از مراکز بین‌المللی، آشنا با جدیدترین روش‌های درمانی.',
'پزشک باتجربه با رویکرد جامع در درمان و اعتقاد راسخ به طب پیشگیری.',
'متعهد به ارائه بهترین مراقبت‌های پزشکی با رعایت کامل اصول اخلاق پزشکی.',
'دارای مدرک تخصصی از اروپا و تجربه کار در مراکز درمانی پیشرفته اروپا و آمریکا.',
'فارغ‌التحصیل ممتاز دانشگاه علوم پزشکی مشهد، برنده جایزه پزشک نمونه سال.',
'متخصص با سابقه بیش از ۲۰ سال در درمان بیماری‌های مزمن و پیچیده.',
'عضو چندین انجمن علمی تخصصی، دارای مقالات متعدد در مجلات معتبر.',
'پزشک متخصص با رویکرد بیمار‌محور و استفاده از روش‌های نوین تشخیص و درمان.',
];
$clinicNames = ['کلینیک تخصصی','مرکز درمانی','بیمارستان','کلینیک جامع','مرکز مشاوره پزشکی',
'مطب','کلینیک ویژه','مرکز پزشکی','درمانگاه','کلینیک سلامت','مجتمع پزشکی'];
$clinicSuffixes = ['امید','نوین','رازی','ابن سینا','سینا','شفا','سلامت','آریا','پارس','ایران',
'مهر','صبا','فجر','بهار','شهید بهشتی','حضرت علی','امام رضا','گلستان','نور','طلوع'];
$streets = ['خیابان ولیعصر','خیابان انقلاب','خیابان آزادی','بلوار کشاورز','خیابان شریعتی',
'خیابان مطهری','خیابان فردوسی','خیابان پاسداران','خیابان جمهوری','خیابان طالقانی',
'بلوار سعادت‌آباد','خیابان وزرا','خیابان بهشتی','خیابان میرداماد','خیابان حقانی',
'خیابان دکتر چمران','بزرگراه شهید همت','خیابان ملاصدرا','خیابان زرتشت','بلوار اشرفی'];
$buildings = ['ساختمان پزشکان','برج طبی','مجتمع پزشکی','پلاک','مطب طبقه','واحد'];
$workingDaysList = [
'شنبه تا چهارشنبه ۸ تا ۲۰', 'شنبه تا پنجشنبه ۸ تا ۱۸',
'یک‌شنبه تا پنجشنبه ۹ تا ۱۷', 'شنبه تا سه‌شنبه ۷ تا ۱۹',
'همه روزه ۸ تا ۲۲', 'شنبه تا چهارشنبه ۹ تا ۲۰',
'دوشنبه تا جمعه ۱۰ تا ۱۸', 'شنبه تا پنجشنبه ۷ تا ۱۳ و ۱۶ تا ۲۰',
];
$apptStatuses = ['reserved','visited','completed','cancelled_by_user','cancelled_by_doctor','no_show','waiting','checked_in'];
$apptWeights = [15, 25, 30, 10, 5, 5, 5, 5];
$commentTexts = [
'دکتر بسیار دلسوز و با حوصله هستند. توضیحات کاملی دادند و از معاینه راضی بودم.',
'معاینه بسیار دقیق بود. تشخیص درست و درمان موثر. ممنونم از دکتر عزیز.',
'وقت انتظار کمی داشتم و دکتر سر وقت حاضر بودند. کیفیت ویزیت بالا بود.',
'بسیار متبحر و با تجربه. مشکلم کاملاً حل شد و از نتیجه درمان راضی هستم.',
'توصیه می‌کنم به همه. رفتار حرفه‌ای و انسانی. مطب تمیز و منظم.',
'دکتر خوبی هستند ولی وقت ویزیت کمی کوتاه بود. در کل تجربه خوبی داشتم.',
'از ویزیت کاملاً راضی بودم. درمان موثر و پیگیری خوب.',
'یکی از بهترین پزشکانی که تاکنون به آنها مراجعه کرده‌ام. بسیار ماهر و دلسوز.',
'کمی منتظر ماندم اما ارزشش را داشت. دکتر وقت کافی برای بیمار می‌گذارند.',
'متخصص واقعی در حوزه کار خود. با صبر و حوصله همه سوالات را جواب دادند.',
'رزرو آنلاین خیلی راحت بود. دکتر به موقع ویزیت کردند و مشکلم برطرف شد.',
'برخورد محترمانه و صادقانه. داروها اثر کردند. حتماً دوباره مراجعه خواهم کرد.',
];
// ── STEP 4 — Insurances ───────────────────────────────────────────────────────
echo "\n=== Inserting insurances ===\n";
$insData = [
['تأمین اجتماعی','basic'],['خدمات درمانی','basic'],['نیروهای مسلح','basic'],
['کمیته امداد امام خمینی','basic'],['سازمان بهزیستی','basic'],['بیمه روستایی','basic'],
['ایران','supplementary'],['آسیا','supplementary'],['البرز','supplementary'],
['پارسیان','supplementary'],['سامان','supplementary'],['میهن','supplementary'],
['دانا','supplementary'],['رازی','supplementary'],['کوثر','supplementary'],
['معلم','supplementary'],['ملت','supplementary'],['حافظ','supplementary'],
['اتکا','supplementary'],['توسعه','supplementary'],
];
$insStmt = $pdo->prepare('INSERT INTO insurances (uuid,name,type,logo_url,status) VALUES (?,?,?,?,1)');
$insIds = [];
foreach ($insData as $i => $ins) {
$logo = isset($insLogoFiles[$i]) ? $insLogoFiles[$i] : null;
$insStmt->execute([uuid(), $ins[0], $ins[1], $logo]);
$insIds[] = (int)$pdo->lastInsertId();
}
echo 'Insurances: ' . count($insIds) . "\n";
// ── STEP 5 — Patients ─────────────────────────────────────────────────────────
echo "=== Inserting patients (3000) ===\n";
$usedMobiles = [];
// Reserve admin mobile
$usedMobiles['09120671713'] = true;
$patientIds = [];
$uStmt = $pdo->prepare(
'INSERT INTO users (uuid,mobile_number,password_hash,email,real_name,roles,status,created_at,updated_at)
VALUES (?,?,?,?,?,?,1,?,?)'
);
$pStmt = $pdo->prepare(
'INSERT INTO profiles (uuid,label,family,gender,blood_type,marital_status,sharing_with_user,
basic_insurance_id,supplementary_insurance_id,created_at,updated_at,user_id)
VALUES (?,?,?,?,?,?,1,?,?,?,?,?)'
);
$bloodTypes = ['A+','A-','B+','B-','O+','O-','AB+','AB-'];
$marital = ['single','married','divorced','widowed'];
$pdo->beginTransaction();
$batchCount = 0;
for ($i = 0; $i < 3000; $i++) {
$gender = rand(0,2) === 0 ? 'female' : 'male';
$firstName = $gender === 'female' ? ri($femaleNames) : ri($maleNames);
$lastName = ri($lastNames);
$name = $firstName . ' ' . $lastName;
$mob = mobile($usedMobiles);
$email = 'patient' . $i . '@mail.ir';
$ts = $now - rand(86400, 86400 * 730);
$uStmt->execute([uuid(), $mob, pw(), $email, $name, '["ROLE_USER"]', $ts, $ts]);
$userId = (int)$pdo->lastInsertId();
$patientIds[] = $userId;
// Profile for some patients
if ($i % 3 === 0) {
$basicIns = ri(array_slice($insIds, 0, 6));
$suppIns = rand(0,1) ? ri(array_slice($insIds, 6)) : null;
$pStmt->execute([
uuid(), $firstName, $lastName, $gender, ri($bloodTypes), ri($marital),
$basicIns, $suppIns, $ts, $ts, $userId
]);
}
$batchCount++;
if ($batchCount % 500 === 0) {
$pdo->commit();
$pdo->beginTransaction();
echo " Patients: $batchCount\n";
}
}
$pdo->commit();
echo 'Total patients: ' . count($patientIds) . "\n";
// ── STEP 6 — Doctors (1000) ───────────────────────────────────────────────────
echo "=== Inserting doctors (1000) ===\n";
$doctorIds = [];
$doctorUserIds = [];
$doctorGenderMap = [];
$duStmt = $pdo->prepare(
'INSERT INTO users (uuid,mobile_number,password_hash,email,real_name,roles,status,created_at,updated_at)
VALUES (?,?,?,?,?,?,1,?,?)'
);
$dStmt = $pdo->prepare(
'INSERT INTO doctors (uuid,name,gender,medical_system_code,mobile_number,activity_time,degree,info,
images,doctor_rate,doctor_rate_percentage,active_doctor_appointment,created_at,updated_at,user_id)
VALUES (?,?,?,?,?,?,?,?,?,?,?,1,?,?,?)'
);
$dsStmt = $pdo->prepare('INSERT IGNORE INTO doctor_specialties (doctor_id,specialty_id) VALUES (?,?)');
$deStmt = $pdo->prepare('INSERT IGNORE INTO doctor_expertise (doctor_id,service_id) VALUES (?,?)');
$dpStmt = $pdo->prepare('INSERT IGNORE INTO doctor_provinces (doctor_id,province_id) VALUES (?,?)');
$dcStmt = $pdo->prepare('INSERT IGNORE INTO doctor_cities (doctor_id,city_id) VALUES (?,?)');
$daStmt = $pdo->prepare(
'INSERT INTO doctor_addresses (uuid,name,address,telephone,latitude,longitude,created_at,updated_at,doctor_id,city_id,province_id)
VALUES (?,?,?,?,?,?,?,?,?,?,?)'
);
$malePhotos = $doctorPhotos['male'];
$femalePhotos = $doctorPhotos['female'];
$pdo->beginTransaction();
for ($i = 0; $i < 1000; $i++) {
$gender = $i % 4 === 0 ? 'female' : 'male';
$firstName = $gender === 'female' ? ri($femaleNames) : ri($maleNames);
$lastName = ri($lastNames);
$name = 'دکتر ' . $firstName . ' ' . $lastName;
$mob = mobile($usedMobiles);
$email = 'doctor' . $i . '@clinic-pro.ir';
$ts = $now - rand(86400 * 60, 86400 * 1000);
// User
$duStmt->execute([uuid(), $mob, pw(), $email, $name, '["ROLE_USER","ROLE_DOCTOR"]', $ts, $ts]);
$userId = (int)$pdo->lastInsertId();
$doctorUserIds[] = $userId;
// Pick photo
$photoPool = $gender === 'female' ? $femalePhotos : $malePhotos;
$photo = $photoPool[$i % count($photoPool)];
$imgJson = json_encode([[
'fid' => rand(10000,999999),
'uuid' => uuid(),
'url' => $photo['url'],
'filename' => $photo['file'],
'filemime' => 'image/jpeg',
'filesize' => rand(30000, 80000),
]], JSON_UNESCAPED_UNICODE);
$rate = round(rand(32, 50) / 10, 1);
$ratePct = rand(68, 98);
$dStmt->execute([
uuid(), $name, $gender,
(string)rand(10000000, 99999999), $mob,
rand(3, 30), ri($degrees), ri($bios),
$imgJson, $rate, $ratePct, $ts, $ts, $userId
]);
$doctorId = (int)$pdo->lastInsertId();
$doctorIds[] = $doctorId;
$doctorGenderMap[$doctorId] = $gender;
// 1-3 parent specialties
$chosenParents = ris($specParents, 1, 2);
foreach ($chosenParents as $spId) {
$dsStmt->execute([$doctorId, $spId]);
// 1-3 child specialties under same parent
$kids = $specChildMap[$spId] ?? [];
foreach (ris($kids, 1, 3) as $kid) {
$dsStmt->execute([$doctorId, $kid]);
}
// 3-8 services
$svcPool = $specSvcMap[$spId] ?? array_slice($serviceIds, 0, 15);
foreach (ris($svcPool, 3, 8) as $svcId) {
$deStmt->execute([$doctorId, $svcId]);
}
}
// 1-3 cities
$chosenCities = ris($cityIds, 1, 3);
foreach ($chosenCities as $cid) {
$provId = $cityProvMap[$cid] ?? null;
$dcStmt->execute([$doctorId, $cid]);
if ($provId) $dpStmt->execute([$doctorId, $provId]);
// Address
$street = ri($streets);
$bld = ri($buildings);
$no = rand(1, 300);
$addr = $street . '، ' . $bld . ' ' . $no;
$lat = 33.0 + rand(0, 700) / 100;
$lon = 48.0 + rand(0, 1500) / 100;
$tel = '0' . ri(['21','511','411','31','71','81','351','441','131','3411']) . rand(10000000, 99999999);
$ts2 = $now - rand(86400, 86400 * 200);
$daStmt->execute([uuid(), 'مطب ' . $firstName . ' ' . $lastName, $addr, $tel, $lat, $lon, $ts2, $ts2, $doctorId, $cid, $provId]);
}
if (($i + 1) % 100 === 0) {
$pdo->commit();
$pdo->beginTransaction();
echo " Doctors: " . ($i + 1) . "\n";
}
}
$pdo->commit();
echo 'Total doctors: ' . count($doctorIds) . "\n";
// ── STEP 7 — Clinics (200) ────────────────────────────────────────────────────
echo "=== Inserting clinics (200) ===\n";
$clinicIds = [];
$clStmt = $pdo->prepare(
'INSERT INTO clinics (uuid,name,info,address,telephone,is_24_7,working_days,latitude,longitude,
city_id,province_id,images_clinic,clinic_logo,created_at,updated_at,user_id)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)'
);
$clDStmt = $pdo->prepare('INSERT IGNORE INTO clinic_doctors (clinic_id,doctor_id) VALUES (?,?)');
$clSpStmt = $pdo->prepare('INSERT IGNORE INTO clinic_specialties (clinic_id,specialty_id) VALUES (?,?)');
$clSvStmt = $pdo->prepare('INSERT IGNORE INTO clinic_services (clinic_id,service_id) VALUES (?,?)');
$clInStmt = $pdo->prepare('INSERT IGNORE INTO clinic_insurances (clinic_id,insurance_id) VALUES (?,?)');
// 60% of doctors go into clinics
$clinicDoctorPool = array_slice($doctorIds, 0, 600);
shuffle($clinicDoctorPool);
$poolIdx = 0;
$pdo->beginTransaction();
for ($i = 0; $i < 200; $i++) {
$cityRow = ri($cities);
$cityId = (int)$cityRow['id'];
$provId = (int)($cityRow['province_id'] ?? 0);
$name = ri($clinicNames) . ' ' . ri($clinicSuffixes);
$is247 = rand(0, 7) === 0 ? 1 : 0;
$ts = $now - rand(86400 * 60, 86400 * 730);
$street = ri($streets);
$no = rand(1, 300);
$addr = $street . '، پلاک ' . $no;
$tel = '0' . ri(['21','511','411','31','71','81','351','441']) . rand(10000000, 99999999);
$lat = 33.0 + rand(0, 700) / 100;
$lon = 48.0 + rand(0, 1500) / 100;
$ownerId = $doctorUserIds[array_rand($doctorUserIds)];
// Clinic photos (1-3 gallery + 1 logo)
$galleryCount = rand(1, 3);
$galleryArr = [];
for ($g = 0; $g < $galleryCount; $g++) {
$photo = $clinicGalleryPhotos[($i * 3 + $g) % count($clinicGalleryPhotos)];
$galleryArr[] = ['fid'=>rand(10000,999999),'uuid'=>uuid(),'url'=>$photo['url'],
'filename'=>$photo['file'],'filemime'=>'image/jpeg','filesize'=>rand(60000,200000)];
}
$logoPhoto = $clinicLogoPhotos[$i % count($clinicLogoPhotos)];
$logoArr = [['fid'=>rand(10000,999999),'uuid'=>uuid(),'url'=>$logoPhoto['url'],
'filename'=>$logoPhoto['file'],'filemime'=>'image/jpeg','filesize'=>rand(20000,50000)]];
$clStmt->execute([
uuid(), $name,
'ارائه خدمات تخصصی پزشکی با استفاده از جدیدترین تجهیزات و کادر درمانی مجرب.',
$addr, $tel, $is247, ri($workingDaysList), $lat, $lon,
$cityId, $provId > 0 ? $provId : null,
json_encode($galleryArr, JSON_UNESCAPED_UNICODE),
json_encode($logoArr, JSON_UNESCAPED_UNICODE),
$ts, $ts, $ownerId
]);
$clinicId = (int)$pdo->lastInsertId();
$clinicIds[] = $clinicId;
// 3-6 doctors per clinic
$numDocs = rand(3, 6);
$addedDocs = 0;
while ($addedDocs < $numDocs && $poolIdx < count($clinicDoctorPool)) {
$clDStmt->execute([$clinicId, $clinicDoctorPool[$poolIdx++]]);
$addedDocs++;
}
// 2-4 specialties
foreach (ris($specParents, 2, 4) as $spId) {
$clSpStmt->execute([$clinicId, $spId]);
}
// 3-6 services
foreach (ris($serviceIds, 3, 6) as $svId) {
$clSvStmt->execute([$clinicId, $svId]);
}
// 3-6 insurances
foreach (ris($insIds, 3, 6) as $inId) {
$clInStmt->execute([$clinicId, $inId]);
}
if (($i + 1) % 50 === 0) {
$pdo->commit();
$pdo->beginTransaction();
echo " Clinics: " . ($i + 1) . "\n";
}
}
$pdo->commit();
echo 'Total clinics: ' . count($clinicIds) . "\n";
// ── STEP 8 — Appointments (8000) ──────────────────────────────────────────────
echo "=== Inserting appointments (8000) ===\n";
$apptStmt = $pdo->prepare(
'INSERT INTO appointments (uuid,slot_start,slot_end,status,note,created_at,updated_at,doctor_id,user_id)
VALUES (?,?,?,?,?,?,?,?,?)'
);
$apptIds = [];
$apptStatusArr = [];
$notes = ['','مراجعه اول','پیگیری','نوبت اضطراری','مشاوره','کنترل دوره‌ای','آزمایش تکمیلی','چکاپ سالانه'];
function wRand(array $items, array $weights): mixed {
$total = array_sum($weights); $r = rand(1, $total); $c = 0;
foreach ($items as $k => $item) { $c += $weights[$k]; if ($r <= $c) return $item; }
return $items[0];
}
$pdo->beginTransaction();
for ($i = 0; $i < 8000; $i++) {
$doctorId = ri($doctorIds);
$userId = ri($patientIds);
$status = wRand($apptStatuses, $apptWeights);
if (in_array($status, ['visited','completed','no_show','cancelled_by_user','cancelled_by_doctor','checked_in'])) {
$slotStart = $now - rand(3600, 86400 * 365);
} else {
$slotStart = $now + rand(3600, 86400 * 45);
}
$slotEnd = $slotStart + 1800;
$ts = $slotStart - rand(3600, 86400 * 14);
$apptStmt->execute([uuid(), $slotStart, $slotEnd, $status, ri($notes), $ts, $ts, $doctorId, $userId]);
$apptId = (int)$pdo->lastInsertId();
$apptIds[] = $apptId;
$apptStatusArr[] = $status;
if (($i + 1) % 1000 === 0) {
$pdo->commit();
$pdo->beginTransaction();
echo " Appointments: " . ($i + 1) . "\n";
}
}
$pdo->commit();
// ── STEP 9 — Payments ─────────────────────────────────────────────────────────
echo "=== Inserting payments ===\n";
$payStmt = $pdo->prepare(
'INSERT INTO payments (uuid,order_id,amount_rials,status,gateway,type,reference_id,created_at,updated_at,user_id,appointment_id)
VALUES (?,?,?,?,?,?,?,?,?,?,?)'
);
$gateways = ['mellat','sep','zarinpal','parsian','sadad'];
$amounts = [1500000,2000000,2500000,3000000,3500000,4000000,4500000,5000000,5500000,6000000,7000000,8000000];
$pdo->beginTransaction();
$payCount = 0;
foreach ($apptIds as $k => $apptId) {
$st = $apptStatusArr[$k];
if (!in_array($st, ['visited','completed','reserved','waiting','checked_in'])) continue;
$payStatus = in_array($st, ['visited','completed','checked_in']) ? 'paid' : ri(['paid','pending']);
$userId = ri($patientIds);
$ts = $now - rand(3600, 86400 * 365);
$refId = $payStatus === 'paid' ? (string)rand(100000000, 999999999) : null;
$payStmt->execute([
uuid(), 'ORD-' . strtoupper(substr(uuid(), 0, 8)), ri($amounts),
$payStatus, ri($gateways), 'appointment', $refId, $ts, $ts, $userId, $apptId
]);
$payCount++;
if ($payCount % 1000 === 0) {
$pdo->commit();
$pdo->beginTransaction();
echo " Payments: $payCount\n";
}
}
$pdo->commit();
echo "Total payments: $payCount\n";
// ── STEP 10 — Ratings ─────────────────────────────────────────────────────────
echo "=== Inserting ratings (3000) ===\n";
$rateStmt = $pdo->prepare('INSERT INTO rates (uuid,score,created_at,updated_at,user_id,doctor_id) VALUES (?,?,?,?,?,?)');
$usedPairs = [];
$rateCount = 0;
$pdo->beginTransaction();
$tries = 0;
while ($rateCount < 3000 && $tries < 20000) {
$tries++;
$doctorId = ri($doctorIds);
$userId = ri($patientIds);
$key = $userId . '-' . $doctorId;
if (isset($usedPairs[$key])) continue;
$usedPairs[$key] = true;
$score = wRand([1,2,3,4,5], [3,5,15,35,42]);
$ts = $now - rand(3600, 86400 * 400);
$rateStmt->execute([uuid(), $score, $ts, $ts, $userId, $doctorId]);
$rateCount++;
if ($rateCount % 1000 === 0) {
$pdo->commit();
$pdo->beginTransaction();
echo " Ratings: $rateCount\n";
}
}
$pdo->commit();
echo "Total ratings: $rateCount\n";
// ── STEP 11 — Comments ────────────────────────────────────────────────────────
echo "=== Inserting comments (2000) ===\n";
$comStmt = $pdo->prepare('INSERT INTO comments (uuid,body,status,created_at,updated_at,user_id,doctor_id) VALUES (?,?,?,?,?,?,?)');
$comStatuses = ['approved','approved','approved','approved','pending','rejected'];
$pdo->beginTransaction();
for ($i = 0; $i < 2000; $i++) {
$ts = $now - rand(3600, 86400 * 400);
$comStmt->execute([uuid(), ri($commentTexts), ri($comStatuses), $ts, $ts, ri($patientIds), ri($doctorIds)]);
if (($i + 1) % 500 === 0) {
$pdo->commit();
$pdo->beginTransaction();
echo " Comments: " . ($i + 1) . "\n";
}
}
$pdo->commit();
// ── STEP 12 — Blogs ───────────────────────────────────────────────────────────
echo "=== Inserting blogs ===\n";
$blogStmt = $pdo->prepare(
'INSERT INTO blogs (uuid,title,slug,body,summary,tags,status,created_at,updated_at,author_id)
VALUES (?,?,?,?,?,?,?,?,?,?)'
);
$blogData = [
['راهنمای جامع پیشگیری از بیماری‌های قلبی','heart-disease-prevention','بیماری‌های قلبی از مهم‌ترین علل مرگ هستند. با تغذیه سالم، ورزش منظم، ترک سیگار و کنترل فشار خون می‌توان خطر ابتلا را به شدت کاهش داد. چکاپ سالیانه و مشاوره با متخصص قلب توصیه می‌شود.',['قلب','پیشگیری','سلامت']],
['تغذیه سالم برای کودکان در سنین رشد','healthy-nutrition-children','مصرف متعادل پروتئین، لبنیات، میوه و سبزیجات برای رشد کودکان ضروری است. از مصرف قند و چربی اشباع بپرهیزید و آب کافی بنوشید.',['کودکان','تغذیه','رشد']],
['اهمیت چکاپ منظم سالانه برای همه سنین','annual-checkup-importance','آزمایش خون، فشار خون، قند خون و تست‌های غربالگری سرطان باید سالانه انجام شوند. تشخیص زودهنگام درمان را ساده‌تر می‌کند.',['چکاپ','پیشگیری','غربالگری']],
['مدیریت استرس و اضطراب در زندگی مدرن','stress-management','تکنیک‌های تنفس عمیق، مدیتیشن، ورزش منظم و ارتباط اجتماعی سالم از راهکارهای موثر مدیریت استرس هستند.',['استرس','سلامت روان','مدیتیشن']],
['دیابت نوع ۲: علائم، پیشگیری و کنترل','diabetes-type2','تشنگی زیاد، ادرار مکرر، خستگی و تاری دید از علائم دیابت هستند. رژیم غذایی مناسب، ورزش و کنترل وزن در پیشگیری و مدیریت آن حیاتی است.',['دیابت','قند خون','پیشگیری']],
['نقش ورزش در سلامت قلب و عروق','exercise-cardiovascular-health','۱۵۰ دقیقه فعالیت هوازی متوسط در هفته برای سلامت قلب توصیه می‌شود. پیاده‌روی، شنا و دوچرخه‌سواری گزینه‌های عالی هستند.',['ورزش','قلب','سلامت']],
['خواب کافی و تاثیر آن بر سلامت جسم و روان','sleep-health','بزرگسالان به ۷-۹ ساعت خواب شبانه نیاز دارند. کمبود خواب با دیابت، چاقی، افسردگی و بیماری قلبی ارتباط دارد.',['خواب','سلامت','استراحت']],
['مراقبت از پوست در برابر آفتاب و آلودگی','skin-care-sun-pollution','استفاده از کرم ضدآفتاب با SPF 30+، پوشش مناسب و آنتی‌اکسیدان‌ها به محافظت از پوست کمک می‌کنند.',['پوست','ضدآفتاب','آلودگی']],
['پیشگیری از پوکی استخوان با تغذیه مناسب','osteoporosis-prevention','مصرف کافی کلسیم، ویتامین D، ورزش با وزن بدن و پرهیز از سیگار و الکل از مهم‌ترین راهکارهای پیشگیری از پوکی استخوان هستند.',['استخوان','کلسیم','پیشگیری']],
['راهنمای کامل مراقبت از بینایی','eye-care-guide','معاینه سالانه چشم، استفاده از عینک آفتابی مناسب، رعایت قانون ۲۰-۲۰-۲۰ هنگام کار با صفحه نمایش و تغذیه سرشار از لوتئین برای سلامت چشم ضروری است.',['چشم','بینایی','سلامت']],
['سرطان پستان: تشخیص زودهنگام نجات‌بخش است','breast-cancer-early-detection','معاینه خودآزمایی ماهانه، ماموگرافی سالانه برای زنان بالای ۴۰ سال و مراجعه به پزشک در صورت تغییرات مشکوک توصیه می‌شود.',['سرطان','پستان','غربالگری']],
['بهداشت دهان و دندان در همه سنین','dental-hygiene','مسواک زدن دو بار در روز، استفاده از نخ دندان و مراجعه به دندانپزشک هر ۶ ماه یک‌بار از اساسی‌ترین اصول بهداشت دهان است.',['دندان','بهداشت','پیشگیری']],
['کنترل فشار خون بالا با تغییر سبک زندگی','high-blood-pressure-control','کاهش مصرف نمک، کنترل وزن، ورزش منظم، پرهیز از استرس و مصرف منظم داروها در کنترل فشار خون ضروری هستند.',['فشار خون','قلب','سبک زندگی']],
['واکسیناسیون بزرگسالان؛ آنچه باید بدانید','adult-vaccination-guide','آنفلوانزا، کزاز، هپاتیت B، آبله مرغان و واکسن HPV از واکسن‌های مهم بزرگسالی هستند. برنامه واکسیناسیون را با پزشک خود مشورت کنید.',['واکسن','پیشگیری','بیماری']],
['آرتروز زانو: درمان و بازتوانی','knee-osteoarthritis-treatment','فیزیوتراپی، ورزش‌های تقویتی، کاهش وزن و در موارد پیشرفته جراحی از روش‌های درمان آرتروز زانو هستند.',['آرتروز','زانو','ارتوپدی']],
['تغذیه درمانی در بیماری‌های کلیوی','renal-diet','رژیم کم‌پتاسیم، کم‌فسفر و کم‌سدیم برای بیماران کلیوی ضروری است. مقدار پروتئین مصرفی باید با پزشک هماهنگ شود.',['کلیه','تغذیه','رژیم']],
['سلامت روان در محیط کار','workplace-mental-health','تعادل کار و زندگی، ارتباط موثر با همکاران، مدیریت زمان و استراحت کافی از عوامل مهم سلامت روان در محیط کار هستند.',['سلامت روان','کار','استرس']],
['آلرژی فصلی و راهکارهای مقابله','seasonal-allergy-management','اجتناب از محرک‌ها، استفاده از داروهای آنتی‌هیستامین، شستن منظم دست و چشم و مشاوره با متخصص آلرژی از راهکارهای اصلی هستند.',['آلرژی','تنفس','بهار']],
['اهمیت ورزش در دوران بارداری','pregnancy-exercise','پیاده‌روی، شنا و یوگای بارداری با نظر پزشک برای سلامت مادر و جنین مفید هستند. از ورزش‌های پرضربه پرهیز کنید.',['بارداری','ورزش','سلامت']],
['پیشگیری از سرطان روده بزرگ','colorectal-cancer-prevention','غربالگری با کولونوسکوپی از سن ۴۵ سالگی، رژیم پرفیبر، کاهش مصرف گوشت قرمز و ورزش منظم خطر را کاهش می‌دهند.',['سرطان','روده','غربالگری']],
];
$adminRow = $pdo->query("SELECT id FROM users WHERE roles LIKE '%ROLE_ADMIN%' LIMIT 1")->fetch(PDO::FETCH_ASSOC);
$adminId = $adminRow ? $adminRow['id'] : $doctorUserIds[0];
$statuses = ['published','published','published','published','published','draft'];
foreach ($blogData as $idx => $b) {
$ts = $now - rand(86400, 86400 * 300);
$blogStmt->execute([
uuid(), $b[0], $b[1] . '-' . rand(100,999), $b[2],
mb_substr($b[2], 0, 200), json_encode($b[3], JSON_UNESCAPED_UNICODE),
ri($statuses), $ts, $ts, $adminId
]);
}
echo 'Blogs: ' . count($blogData) . "\n";
$pdo->exec('SET FOREIGN_KEY_CHECKS = 1');
// ── Summary ───────────────────────────────────────────────────────────────────
echo "\n=== COMPLETE — Record counts ===\n";
$tables = ['users','doctors','clinics','clinic_doctors','clinic_specialties','clinic_services','clinic_insurances',
'doctor_specialties','doctor_expertise','doctor_provinces','doctor_cities','doctor_addresses',
'appointments','payments','rates','comments','blogs','insurances','profiles'];
$total = 0;
foreach ($tables as $t) {
$c = (int)$pdo->query("SELECT COUNT(*) FROM `$t`")->fetchColumn();
printf(" %-30s %d\n", $t . ':', $c);
$total += $c;
}
echo "\nTotal records: $total\n";