نسخة كاملة من مستودع سيرو عند ecfe7568 لتكون أساس تطبيق «انطلق». نُسخ المتعقَّب في git فقط (12,509 ملفاً / 302 م.ب) بـ git archive، لا `cp -r` — فاستُثنيت تلقائياً مخلفات البناء (build · node_modules · .dart_tool · .gradle · Pods ≈ 10.7 غ.ب) وكل ما يستثنيه .gitignore. هذا الكوميت **بلا أي تعديل عمداً** حتى يكون كل ما يليه فرقاً مقروءاً مقابل سيرو الأصلي. سيرو نفسه لم يُمسّ. ⚠️ لا يبني بعد: `.env` و`lib/env/env.g.dart` غير متعقَّبين في سيرو (وهذا صحيح — أسرار لكل مستأجر). كل تطبيق فلاتر هنا يحتاج .env خاصاً بانطلق ثم توليد env.g.dart عبر build_runner. لا تُنسخ أسرار سيرو. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
100 lines
3.5 KiB
PHP
100 lines
3.5 KiB
PHP
<?php
|
|
/**
|
|
* Get Places - Optimized with FULLTEXT and SPATIAL indexes.
|
|
* يستخدم الفهارس الذكية للبحث السريع جداً.
|
|
* * يتطلب:
|
|
* - connect.php: $con (PDO object) + filterRequest() + jsonSuccess() / jsonError()
|
|
* - جدول 'places11' مهيأ بالفهارس (FULLTEXT & SPATIAL)
|
|
*/
|
|
|
|
require_once __DIR__ . '/../../connect.php';
|
|
|
|
// اسم الجدول المستهدف
|
|
$tableName = 'palces11';
|
|
|
|
// تأكد من أن PDO يرمي استثناءات عند حدوث أخطاء
|
|
try {
|
|
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
} catch (PDOException $e) {
|
|
// تجاهل الخطأ إذا كان قد تم تعيينه بالفعل
|
|
}
|
|
|
|
// 1. استقبال المدخلات باستخدام دالة filterRequest الخاصة بك
|
|
$query = trim((string) filterRequest("query"));
|
|
$latMin = filterRequest("lat_min");
|
|
$latMax = filterRequest("lat_max");
|
|
$lngMin = filterRequest("lng_min");
|
|
$lngMax = filterRequest("lng_max");
|
|
|
|
// 2. التحقق من المدخلات
|
|
if ($query === "" || $latMin === null || $latMax === null || $lngMin === null || $lngMax === null) {
|
|
jsonError("Missing required parameters: query, lat_min, lat_max, lng_min, lng_max");
|
|
exit;
|
|
}
|
|
|
|
// تحويل الإحداثيات إلى أرقام عشرية
|
|
$latMin = (float) $latMin;
|
|
$latMax = (float) $latMax;
|
|
$lngMin = (float) $lngMin;
|
|
$lngMax = (float) $lngMax;
|
|
|
|
|
|
// 3. بناء الاستعلام الذكي (الجزء المحدّث)
|
|
|
|
// تحضير كلمة البحث لوضعها في MATCH() AGAINST()
|
|
// نضيف '*' لكل كلمة للبحث عن الكلمات التي تبدأ بهذا الجزء
|
|
$search_terms = preg_split('/\s+/', $query, -1, PREG_SPLIT_NO_EMPTY);
|
|
$search_boolean = '';
|
|
foreach ($search_terms as $term) {
|
|
$search_boolean .= '+' . $term . '* '; // '+' تعني أن الكلمة يجب أن تكون موجودة
|
|
}
|
|
$search_boolean = trim($search_boolean);
|
|
|
|
|
|
// بناء المضلع الجغرافي (Bounding Box Polygon) للفهرس المكاني
|
|
$bbox_wkt = sprintf(
|
|
'POLYGON((%f %f, %f %f, %f %f, %f %f, %f %f))',
|
|
$lngMin, $latMin,
|
|
$lngMax, $latMin,
|
|
$lngMax, $latMax,
|
|
$lngMin, $latMax,
|
|
$lngMin, $latMin
|
|
);
|
|
|
|
|
|
// الاستعلام النهائي الذي يجمع بين البحث النصي والجغرافي
|
|
$sql = "
|
|
SELECT
|
|
id, latitude, longitude, name, name_ar, name_en, address, category, created_at
|
|
FROM
|
|
`{$tableName}`
|
|
WHERE
|
|
-- الشرط الأول: البحث بالنص الكامل (سريع جداً)
|
|
MATCH(name, name_ar, name_en, address, category) AGAINST(? IN BOOLEAN MODE)
|
|
|
|
-- الشرط الثاني: البحث الجغرافي (سريع جداً)
|
|
AND ST_CONTAINS(ST_GEOMFROMTEXT(?), location)
|
|
LIMIT 50; -- حد أعلى للنتائج الأولية
|
|
";
|
|
|
|
// 4. تنفيذ الاستعلام وإرجاع النتيجة
|
|
try {
|
|
$stmt = $con->prepare($sql);
|
|
|
|
// ربط المتغيرات بالاستعلام بالترتيب
|
|
$stmt->execute([$search_boolean, $bbox_wkt]);
|
|
|
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
jsonSuccess($rows);
|
|
|
|
} catch (PDOException $e) {
|
|
// يمكنك استخدام هذا السطر أثناء التطوير لعرض الخطأ الفعلي
|
|
// jsonError('DB Error: ' . $e->getMessage()); exit;
|
|
|
|
// تسجيل الخطأ في سجلات الخادم للرجوع إليه لاحقاً
|
|
error_log("search_places_optimized.php error: " . $e->getMessage());
|
|
jsonError("Database query error occurred");
|
|
}
|
|
?>
|