הטמעת ייבוא מוצרים מקבצי ספקים (CSV/Excel/XML/JSON)
לעתים קרובות אנו נתקלים בספקים ששולחים מחירונים בפורמט שנוח להם: חלק ב-Excel, חלק ב-XML, וחלק ב-CSV עם תווים מפרידים לא תקניים. אנו פותרים את האתגר של בניית ייבוא מוצרים אוניברסלי שעובד עם כל פורמט דרך ממשק יחיד, ללא כתיבת קוד נפרד לכל ספק. בפרויקט אחד, שילבנו מחירונים מ-15 ספקים שונים, שלכל אחד מבנה עמודות משלו. עד 500,000 שורות עובדו במשמרת אחת — טעינה ידנית לא הייתה אפשרית. הפתרון היה מפענח אוניברסלי עם מפעל (factory) ומיפוי שדות, שקיצץ את זמן הפיתוח ב-60% ואיפשר קליטת ספק חדש תוך שעתיים. ארכיטקטורת ייבוא נכונה משתלמת כבר מהספק השלישי.
מדוע ממשק מפענח אוניברסלי הוא המפתח לסקלביליות
FileParserInterface יחיד מאפשר הוספת פורמטים חדשים ללא שינוי בלוגיקת הייבוא. מפעל (factory) בוחר את המפענח לפי סיומת או סוג MIME. לדוגמה, עבור CSV אנו משתמשים בתו מפריד ובקידוד הניתנים להגדרה; עבור XML אנו משתמשים ב-XMLReader בסטרימינג, שחוסך פי 10 בזיכרון לעומת SimpleXML. הוספת פורמט חדש מסתכמת בכתיבת מחלקה אחת ורישומה במפעל.
interface FileParserInterface {
/** @return iterable<array<string, mixed>> */
public function parse(string $filePath): iterable;
public function supports(string $mimeType, string $extension): bool;
} class FileParserFactory {
private array $parsers;
public function make(string $filePath): FileParserInterface {
$ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
$mime = mime_content_type($filePath);
foreach ($this->parsers as $parser) {
if ($parser->supports($mime, $ext)) {
return $parser;
}
}
throw new \RuntimeException("No parser for: {$ext} / {$mime}");
}
} כיצד לטפל ב-CSV עם תווים מפרידים וקידודים לא תקניים
CSV הוא הפורמט הכי לא צפוי. תווים מפרידים: פסיק, נקודה-פסיק, טאב. קידודים: UTF-8 עם או בלי BOM, Windows-1251. המפענח שלנו ניתן להגדרה לפי מקור: אנו מזהים BOM אוטומטית, ממירים Windows-1251 ל-UTF-8, ומטפלים בתווים מפרידים לא תקניים דרך הגדרות. דוגמה:
class CsvParser implements FileParserInterface {
public function __construct(
private string $delimiter = ',',
private string $enclosure = '"',
private bool $hasHeader = true,
) {}
public function parse(string $filePath): iterable {
$handle = fopen($filePath, 'r');
$bom = fread($handle, 3);
fclose($handle);
if ($bom === "\xEF\xBB\xBF") {
$filePath = $this->removeBom($filePath);
}
$handle = fopen($filePath, 'r');
$headers = $this->hasHeader ? fgetcsv($handle, 0, $this->delimiter, $this->enclosure) : null;
while ($row = fgetcsv($handle, 0, $this->delimiter, $this->enclosure)) {
if (!array_filter($row)) continue;
yield $headers ? array_combine($headers, $row) : $row;
}
fclose($handle);
}
public function supports(string $mimeType, string $extension): bool {
return in_array($extension, ['csv', 'txt']) || str_contains($mimeType, 'csv');
}
}עבור קבצים מעל 10 MB אנו משתמשים בסטרימינג, מה שמפחית את צריכת הזיכרון פי 3–5. כפי שצוין בתיעוד של PhpSpreadsheet, סטרימינג מפחית את השימוש בזיכרון עד 70%. קובץ Excel של 200 MB עם interface FileParserInterface { /** @return iterable<array<string, mixed>> */ public function parse(string $filePath): iterable; public function supports(string $mimeType, string $extension): bool; } מעובד תוך 40 שניות על VPS טיפוסי.
עיבוד קבצי Excel ו-XML גדולים
עבור Excel אנו משתמשים ב-PhpSpreadsheet עם אפשרויות חיסכון בזיכרון. עבור XML אנו משתמשים ב-class FileParserFactory { private array $parsers; public function make(string $filePath): FileParserInterface { $ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION)); $mime = mime_content_type($filePath); foreach ($this->parsers as $parser) { if ($parser->supports($mime, $ext)) return $parser; } throw new \RuntimeException("No parser for: {$ext} / {$mime}"); } } בסטרימינג. השוואה:
| מפענח | סטרימינג? | שימוש בזיכרון | מהירות | מתאים לקבצים >100 MB |
|---|---|---|---|---|
| XMLReader | כן | נמוך | גבוה | כן |
| SimpleXML | לא | גבוה | בינוני | לא |
| JsonMachine | כן | נמוך | גבוה | כן |
| PhpSpreadsheet (ברירת מחדל) | לא | גבוה | בינוני | לא |
| PhpSpreadsheet (setReadDataOnly) | חלקי | בינוני | בינוני | כן (עד 500 MB) |
מפענח XML בסטרימינג מעבד קבצים פי 10 מהר יותר מאשר טעינת המסמך כולו עם class CsvParser implements FileParserInterface { public function __construct( private string $delimiter = ',', private string $enclosure = '"', private bool $hasHeader = true, ) {} public function parse(string $filePath): iterable { $handle = fopen($filePath, 'r'); $bom = fread($handle, 3); fclose($handle); if ($bom === "\xEF\xBB\xBF") { $filePath = $this->removeBom($filePath); } $handle = fopen($filePath, 'r'); $headers = $this->hasHeader ? fgetcsv($handle, 0, $this->delimiter, $this->enclosure) : null; while ($row = fgetcsv($handle, 0, $this->delimiter, $this->enclosure)) { if (!array_filter($row)) continue; yield $headers ? array_combine($headers, $row) : $row; } fclose($handle); } public function supports(string $mimeType, string $extension): bool { return in_array($extension, ['csv', 'txt']) || str_contains($mimeType, 'csv'); } } . זה קריטי כשספק שולח מחירון עם מיליון פריטים. בבדיקה עם קובץ של 500 MB, המפענח בסטרימינג עיבד מיליון רשומות תוך 90 שניות.
הבטחת שלמות הנתונים
כל שורה מאומתת לפני כתיבתה למסד הנתונים. שורות לא תקינות (שדות חובה חסרים, SKU שגוי) מתועדות ביומן ואינן מפריעות לייבוא. ייבוא חוזר אינו יוצר כפילויות — אנו משתמשים ב-upsert לפי מפתח SKU. לבקרת שלמות, אנו שומרים hash של השורה ותאריך עדכון אחרון.
מיפוי עמודות מבטל עבודה ידנית
כל ספק משתמש בשמות עמודות משלו. תצורת המיפוי נשמרת במסד הנתונים וניתנת לעריכה דרך פאנל הניהול:
{ "sku": "Артикул",
"name": "Наименование",
"price": "Цена руб.",
"qty": "Кол-во",
"description": "Описание",
"category": "Раздел" }הממיר (transformer) מחיל את המיפוי לפני העברת הנתונים למייבא. הודות לתצורה הנשמרת במסד הנתונים, הגדרת ספק חדש אורכת 30 דקות במקום 4 שעות. מיפויים שונים נתמכים עבור ספקים שונים.
מה כלול בעבודה
- פיתוח מפענחים לכל הפורמטים (CSV, Excel, XML, JSON).
- הגדרת מיפוי עמודות לכל ספק.
- יצירת ממשק משתמש לניהול מיפוי וצפייה ביומנים.
- בדיקות עם קבצים אמיתיים (עד מיליון שורות).
- תיעוד הארכיטקטורה והוספת פורמטים חדשים.
- הדרכת מפעילים על פאנל הניהול.
- תמיכה טכנית במהלך האינטגרציה.
תהליך ההטמעה
- ניתוח פורמטי קבצי הספקים ודרישות המיפוי.
- פיתוח מפענחי בסיס וצינור ייבוא (import pipeline).
- הגדרת מיפוי ועיבוד בסטרימינג.
- בדיקות אינטגרציה עם ייצואי הספקים.
- פריסה לשרת הייצור ועלייה לאוויר.
לוח זמנים להטמעה
| שלב | משך |
|---|---|
| מפענחי CSV + Excel, מיפוי, צינור בסיסי | מיומיים |
| XML (סטרימינג) + JSON + זיהוי פורמט אוטומטי | +יום אחד |
| הגדרת ממשק משתמש, קידודים, טיפול בשגיאות | +יום אחד |
סה"כ: בין 3 ל-4 ימים לאינטגרציה בסיסית. לוח הזמנים משתנה בהתאם למספר הפורמטים ומורכבות המיפוי.
צרו קשר להערכת פרויקט — נכין הצעה מסחרית תוך יום אחד. הזמינו הטמעת ייבוא אוניברסלי ושכחו מטעינה ידנית של מחירונים.







