📄 SeedProductImagesCommand.php
📁 Path: /home/skhata/new.chandrapharma.com/app/Console/Commands/SeedProductImagesCommand.php
📊 Size: 10.51 KB
🔒 Perm: 0644
📝 MIME: text/x-php
<?php
namespace App\Console\Commands;
use App\Modules\Catalog\Models\Product;
use App\Modules\Catalog\Models\ProductImage;
use Illuminate\Console\Command;
/**
* Artisan command: php artisan products:seed-images
*
* Assigns reliable stock-photo images to products that have no images yet.
* Uses Picsum Photos (picsum.photos) — free, no hotlink blocks, always works.
* Safe to run multiple times — skips products that already have local images.
*
* Options:
* --force Overwrite ALL existing product images
*/
class SeedProductImagesCommand extends Command
{
protected $signature = 'products:seed-images {--force : Overwrite existing images}';
protected $description = 'Seed product images using reliable stock photo URLs';
/**
* keyword (lowercase) → Picsum photo ID
*
* Picsum IDs are stable and serve real photos at:
* https://picsum.photos/id/{ID}/300/300
*
* Chosen to be visually appropriate for each product category.
*/
private array $imageMap = [
// ── Pain / fever ──────────────────────────────────────────────────────
'paracetamol' => '1005',
'ibuprofen' => '1005',
'aspirin' => '1005',
'diclofenac' => '1005',
'mefenamic' => '1005',
'500mg' => '1005',
'400mg' => '1005',
// ── Cold / cough / flu ────────────────────────────────────────────────
'cold & flu' => '376',
'cold and flu' => '376',
'cough syrup' => '376',
'cough' => '376',
'lozenges' => '376',
'antihistamine' => '376',
'cetirizine' => '376',
// ── Topical / skin ────────────────────────────────────────────────────
'burn cream' => '67',
'burn relief' => '67',
'antiseptic cream' => '67',
'antiseptic liquid'=> '67',
'antiseptic' => '67',
'wound cream' => '67',
'antifungal' => '67',
'anti-fungal' => '67',
'moisturizer' => '67',
'face wash' => '67',
'vapor rub' => '67',
'balm' => '67',
'diaper rash' => '67',
'diaper' => '67',
'cracked heel' => '67',
'foot scrub' => '67',
// ── First aid / bandages ──────────────────────────────────────────────
'bandage' => '305',
'adhesive bandage' => '305',
'gauze' => '305',
'sterile gauze' => '305',
'cotton roll' => '305',
// ── Eye / nasal ───────────────────────────────────────────────────────
'eye drop' => '314',
'lubricating' => '314',
'nasal spray' => '314',
'saline' => '314',
'oxymetazoline' => '314',
// ── Digestive / gut ───────────────────────────────────────────────────
'antacid' => '431',
'ors powder' => '431',
'oral rehydration' => '431',
'probiotic' => '431',
'liver tonic' => '431',
// ── Vitamins / supplements ────────────────────────────────────────────
'vitamin c' => '447',
'vitamin d' => '447',
'vitamin d3' => '447',
'folic acid' => '447',
'iron' => '447',
'multivitamin' => '447',
'omega-3' => '447',
'omega 3' => '447',
'fish oil' => '447',
'zinc' => '447',
'effervescent' => '447',
'chewable' => '447',
'glucosamine' => '447',
'sporidex' => '447',
// ── Ayurveda / herbal ─────────────────────────────────────────────────
'ashwagandha' => '200',
'triphala' => '200',
'chyawanprash' => '200',
'amla' => '200',
'gooseberry' => '200',
'apple cider' => '200',
// ── Protein / sports nutrition ────────────────────────────────────────
'whey protein' => '139',
'mass gainer' => '139',
// ── Women's health ────────────────────────────────────────────────────
'sanitary' => '175',
'tampon' => '175',
'panty liner' => '175',
'overnight' => '175',
'pregnancy test' => '175',
'fertility test' => '175',
'ovulation' => '175',
'condom' => '175',
// ── Baby care ─────────────────────────────────────────────────────────
'baby shampoo' => '256',
'baby soap' => '256',
'mild baby' => '256',
'tear-free' => '256',
'infant formula' => '256',
'infant cereal' => '256',
'lactose-free' => '256',
// ── Personal care / hygiene ───────────────────────────────────────────
'shampoo' => '137',
'conditioner' => '137',
'anti-dandruff' => '137',
'dandruff' => '137',
'toothpaste' => '137',
'mouthwash' => '137',
'soap' => '137',
'body wash' => '137',
'handwash' => '137',
'hand wash' => '137',
'hand sanitizer' => '137',
'sanitizer' => '137',
'antibacterial' => '137',
'shaving cream' => '137',
'razor' => '137',
'compact powder' => '137',
'cetaphil' => '137',
// ── Medical devices ───────────────────────────────────────────────────
'bp monitor' => '366',
'blood pressure' => '366',
'thermometer' => '366',
'flexible tip' => '366',
'oximeter' => '366',
'pulse ox' => '366',
'glucometer' => '366',
'glucose monitor' => '366',
'blood glucose' => '366',
'step counter' => '366',
'pedometer' => '366',
'upper arm' => '366',
'wearable' => '366',
'digital pulse' => '366',
// ── Sleep ─────────────────────────────────────────────────────────────
'sleep mask' => '116',
'contoured sleep' => '116',
// ── Skin care (cosmetic) ──────────────────────────────────────────────
'refress' => '67',
];
public function handle(): int
{
$force = $this->option('force');
$products = Product::all();
$assigned = 0;
$skipped = 0;
$this->info("Processing {$products->count()} products...");
$bar = $this->output->createProgressBar($products->count());
$bar->start();
foreach ($products as $product) {
$bar->advance();
// Skip products that already have non-URL images (picsum or local)
if (! $force) {
$hasGoodImage = ProductImage::where('product_id', $product->id)
->whereIn('disk', ['picsum', 'public'])
->exists();
if ($hasGoodImage) {
$skipped++;
continue;
}
}
$name = strtolower($product->getTranslation('name', 'en') ?: '');
$photoId = $this->findPhotoId($name);
// Generic medicine fallback if no keyword match
if (! $photoId) {
$photoId = '1005';
}
$thumbUrl = "https://picsum.photos/id/{$photoId}/300/300";
$medUrl = "https://picsum.photos/id/{$photoId}/600/600";
// Remove old failed (disk='url') rows for this product
ProductImage::where('product_id', $product->id)
->where('disk', 'url')
->delete();
if ($force) {
ProductImage::where('product_id', $product->id)->delete();
}
ProductImage::create([
'product_id' => $product->id,
'url' => $medUrl,
'url_thumbnail' => $thumbUrl,
'url_medium' => $medUrl,
'url_large' => $medUrl,
'disk' => 'picsum',
'is_primary' => true,
'sort_order' => 0,
'alt_text' => $product->getTranslation('name', 'en'),
]);
$assigned++;
}
$bar->finish();
$this->newLine(2);
$this->info("✅ Done! Assigned: {$assigned} | Skipped (already local): {$skipped}");
return self::SUCCESS;
}
private function findPhotoId(string $name): ?string
{
$bestMatch = null;
$bestLen = 0;
foreach ($this->imageMap as $keyword => $photoId) {
if (str_contains($name, $keyword) && strlen($keyword) > $bestLen) {
$bestMatch = $photoId;
$bestLen = strlen($keyword);
}
}
return $bestMatch;
}
}