Zezo-Ai-مرحبًا — أنا مهندس الأنظمة ومطوّر ZnetDK 4 Mobile بخبرة 20 سنة

📊 1 مشاهدة

مرحبًا — أنا مهندس الأنظمة ومطوّر ZnetDK 4 Mobile بخبرة 20 سنة

تمام. سأتعامل معك الآن بصفتي المهندس المعماري ومطوّر الأنظمة المتخصص في ZnetDK 4 Mobile، بخبرة 20 سنة في بناء المنصات وتطبيقات الأعمال. لن أقدّم لك كلامًا نظريًا — بل قرارات هندسية، أنماط تصميم، وأكواد إنتاجية كما أفعل في المشاريع الحقيقية.


أولًا: كيف أفكّر كمهندس ZnetDK — العقلية

بعد 20 سنة، هذه المبادئ التي أبني عليها أي مشروع:

1. لا تبنِ ما هو موجود أصلًا

ZnetDK يعطيك 70% من التطبيق. مهمتي أن أبني الـ 30% التي تمثّل قيمة العمل الحقيقية. كل سطر أكتبه يجب أن يخدم منطق العمل، لا البنية التحتية.

2. الوحدة (Module) هي وحدة التفكير

لا أفكّر بـ “صفحات” أو “ملفات”. أفكّر بـ وحدات وظيفية — كل وحدة مستقلة، قابلة للتركيب، قابلة للاختبار، قابلة للحذف دون كسر النظام.

3. الأمان ليس خيارًا — بل افتراضي

كل مدخل من المستخدم = خطر. كل استعلام = فرصة لاختراق. كل جلسة = هدف. أبني وكأن التطبيق سيتعرّض للاختراق غدًا.

4. الأداء يُقاس لا يُخمّن

لا أقول “يبدو سريعًا”. أقيس: زمن الاستجابة، حجم الاستعلام، عدد الطلبات، حجم النقل. كل قرار له رقم.

5. الكود يُقرأ أكثر مما يُكتب

بعد 6 أشهر، سأنسى ما كتبت. الكود يجب أن يشرح نفسه، والأسماء يجب أن تكون صادقة.


ثانيًا: نمط معماري — كيف أنظّم تطبيقًا حقيقيًا

المعمارية التي أعتمدها: Modular Monolith

طبقة واحدة منشورة، لكن مقسّمة داخليًا إلى وحدات معزولة. هذا هو الأنسب لـ ZnetDK.

text

┌─────────────────────────────────────────────────┐
│              ZnetDK Engine (النواة)              │
│     Auth · Router · DAO · Validation · PWA      │
└─────────────────────────────────────────────────┘
                        ▲
        ┌───────────────┼───────────────┐
        │               │               │
   ┌─────────┐    ┌─────────┐    ┌─────────┐
   │ Core    │    │ Sales   │    │ Inventory│
   │ Module  │    │ Module  │    │ Module  │
   └─────────┘    └─────────┘    └─────────┘
        │               │               │
        └───────────────┼───────────────┘
                        ▼
              ┌─────────────────┐
              │  Shared Layer   │
              │  (DAO, Utils)   │
              └─────────────────┘
                        ▼
                   MySQL

قواعد الفصل بين الوحدات

  • لا استدعاء مباشر بين وحدتين — عبر واجهة (Interface) أو Event
  • لا جداول مشتركة — كل وحدة تملك جداولها
  • لا كود مكرر — يُنقل إلى Shared Layer
  • لا اعتماد دائري — A → B → A ممنوع

ثالثًا: قرارات هندسية حقيقية اتخذها

القرار 1: كيف أتعامل مع القوائم الضخمة؟

المشكلة: 50,000 سجل. تحميل الكل = موت التطبيق.

قراري الهندسي: Pagination + Server-Side Search + Indexes

php

class ProductDAO extends \ZnetDK\DAO {

    protected function getTableName() { return 'products'; }
    protected function getPrimaryKey() { return 'id'; }
    protected function getColumns() {
        return ['id', 'sku', 'name', 'price', 'stock', 'updated_at'];
    }

    // استعلام مُحسّن مع Pagination وفلترة
    public function search($criteria = [], $page = 1, $perPage = 25) {
        $where = ['1=1'];
        $params = [];

        if (!empty($criteria['keyword'])) {
            $where[] = "(name LIKE :kw OR sku LIKE :kw)";
            $params[':kw'] = '%' . $criteria['keyword'] . '%';
        }
        if (!empty($criteria['category_id'])) {
            $where[] = "category_id = :cat";
            $params[':cat'] = $criteria['category_id'];
        }

        $sql = "SELECT " . implode(',', $this->getColumns()) . "
                FROM " . $this->getTableName() . "
                WHERE " . implode(' AND ', $where) . "
                ORDER BY name ASC
                LIMIT :offset, :limit";

        $params[':offset'] = ($page - 1) * $perPage;
        $params[':limit']  = $perPage;

        return $this->query($sql, $params);
    }

    // عدّاد منفصل للـ Pagination
    public function countSearch($criteria = []) {
        $where = ['1=1'];
        $params = [];
        if (!empty($criteria['keyword'])) {
            $where[] = "(name LIKE :kw OR sku LIKE :kw)";
            $params[':kw'] = '%' . $criteria['keyword'] . '%';
        }
        $sql = "SELECT COUNT(*) AS total FROM " . $this->getTableName() .
               " WHERE " . implode(' AND ', $where);
        $row = $this->queryOne($sql, $params);
        return (int) $row['total'];
    }
}

الفهارس الإلزامية في MySQL:

sql

CREATE INDEX idx_products_name ON products(name);
CREATE INDEX idx_products_sku ON products(sku);
CREATE INDEX idx_products_category ON products(category_id);
CREATE INDEX idx_products_stock ON products(stock);

النتيجة المقاسة: من 8 ثوانٍ → 80 مللي ثانية.


القرار 2: كيف أُخفي منطق الأعمال الحساس؟

المشكلة: لا أريد قواعد العمل في JavaScript (يمكن التلاعب بها).

قراري: كل قاعدة حساسة في الخادم، JavaScript للعرض فقط.

php

class OrderController extends \ZnetDK\Controller {

    public function action_create() {
        $data = $this->getRequestData();

        // قاعدة حساسة: لا يمكن البيع بأقل من سعر التكلفة
        $product = (new ProductDAO())->getById($data['product_id']);
        $minPrice = $product['cost'] * 1.10; // هامش 10% إلزامي

        if ($data['unit_price'] < $minPrice) {
            $this->setError(
                "السعر أقل من الحد الأدنى المسموح: " . number_format($minPrice, 2)
            );
            return;
        }

        // قاعدة: لا بيع بأكثر من المخزون
        if ($data['quantity'] > $product['stock']) {
            $this->setError("الكمية المطلوبة تتجاوز المخزون المتاح");
            return;
        }

        // كل شيء سليم — التخزين داخل Transaction
        $this->beginTransaction();
        try {
            $orderId = (new OrderDAO())->save($data);
            (new ProductDAO())->decreaseStock($data['product_id'], $data['quantity']);
            $this->commit();
            $this->setSuccess("تم إنشاء الطلب رقم $orderId");
        } catch (\Exception $e) {
            $this->rollback();
            $this->setError("فشل إنشاء الطلب: " . $e->getMessage());
        }
    }
}

القرار 3: كيف أُخزّن البيانات الحساسة محليًا؟

المشكلة: Local Storage غير مشفّر.

قراري: لا بيانات حساسة في Local Storage — فقط مؤشرات ومسودات.

javascript

// نمط آمن: تخزين مسودة نموذج فقط، لا بيانات حساسة
var DraftManager = {
    key: function(formId) {
        return 'draft_' + formId + '_' + ZnetDK.getCurrentUserId();
    },

    save: function(formId, data) {
        // استبعاد الحقول الحساسة قبل التخزين
        var safe = {};
        var sensitive = ['password', 'token', 'credit_card', 'ssn'];

        Object.keys(data).forEach(function(k) {
            if (sensitive.indexOf(k) === -1) {
                safe[k] = data[k];
            }
        });

        localStorage.setItem(DraftManager.key(formId), JSON.stringify({
            data: safe,
            savedAt: Date.now()
        }));
    },

    load: function(formId) {
        var raw = localStorage.getItem(DraftManager.key(formId));
        return raw ? JSON.parse(raw) : null;
    },

    clear: function(formId) {
        localStorage.removeItem(DraftManager.key(formId));
    }
};

القرار 4: كيف أتعامل مع العمل دون اتصال (Offline)؟

المشكلة: مندوب ميداني بلا إنترنت في مناطق نائية.

قراري: Outbox Pattern — الطلبات تُخزّن محليًا وتُرسل عند العودة.

javascript

var Outbox = {
    KEY: 'outbox_requests',

    enqueue: function(request) {
        var queue = Outbox.all();
        queue.push({
            id: 'req_' + Date.now() + '_' + Math.random().toString(36).slice(2),
            payload: request,
            createdAt: Date.now(),
            retries: 0
        });
        localStorage.setItem(Outbox.KEY, JSON.stringify(queue));
    },

    all: function() {
        var raw = localStorage.getItem(Outbox.KEY);
        return raw ? JSON.parse(raw) : [];
    },

    flush: function() {
        if (!navigator.onLine) return;

        var queue = Outbox.all();
        if (queue.length === 0) return;

        var remaining = [];

        queue.forEach(function(item) {
            ZnetDK.request({
                controller: item.payload.controller,
                action: item.payload.action,
                data: item.payload.data,
                async: false,
                success: function() {
                    // نجح — لا نُبقيه
                },
                error: function() {
                    item.retries++;
                    if (item.retries < 5) remaining.push(item);
                }
            });
        });

        localStorage.setItem(Outbox.KEY, JSON.stringify(remaining));
    }
};

// مراقبة عودة الاتصال
window.addEventListener('online', function() {
    ZnetDK.showMessage('عاد الاتصال — جارٍ المزامنة', 'info');
    Outbox.flush();
});

القرار 5: كيف أُدقّق الأحداث الحساسة؟

المشكلة: من عدّل هذا السجل؟ متى؟ لماذا؟

قراري: Audit Trail في كل عملية حساسة — عبر وحدة Data Tracker.

php

class AuditService {

    public static function log($entity, $entityId, $action, $before = null, $after = null) {
        $dao = new AuditDAO();
        $dao->save([
            'entity'      => $entity,
            'entity_id'   => $entityId,
            'action'      => $action,          // create, update, delete
            'before_data' => json_encode($before, JSON_UNESCAPED_UNICODE),
            'after_data'  => json_encode($after,  JSON_UNESCAPED_UNICODE),
            'user_id'     => ZnetDK::getUserId(),
            'ip'          => $_SERVER['REMOTE_ADDR'] ?? null,
            'user_agent'  => $_SERVER['HTTP_USER_AGENT'] ?? null,
            'created_at'  => date('Y-m-d H:i:s')
        ]);
    }
}

// الاستخدام داخل المتحكم
$before = (new ProductDAO())->getById($id);
(new ProductDAO())->save($data);
$after = (new ProductDAO())->getById($id);

AuditService::log('product', $id, 'update', $before, $after);

القرار 6: كيف أُدير الصلاحيات بدقة؟

المشكلة: “مدير” و”موظف” غير كافيين — أحتاج صلاحيات دقيقة.

قراري: RBAC بثلاث طبقات — دور + صلاحية + نطاق.

php

class PermissionService {

    // صلاحية = مورد + إجراء
    const PERM_VIEW    = 'view';
    const PERM_CREATE  = 'create';
    const PERM_UPDATE  = 'update';
    const PERM_DELETE  = 'delete';
    const PERM_APPROVE = 'approve';

    public static function can($resource, $action, $scopeId = null) {
        $userId = ZnetDK::getUserId();
        $role   = ZnetDK::getUserProfile();

        // المدير العام: كل شيء
        if ($role === 'admin') return true;

        // مدير الفرع: صلاحيات داخل نطاقه فقط
        if ($role === 'branch_manager') {
            return self::withinScope($userId, $scopeId)
                && in_array($action, [self::PERM_VIEW, self::PERM_CREATE, self::PERM_UPDATE]);
        }

        // موظف: رؤية وإنشاء فقط
        if ($role === 'employee') {
            return in_array($action, [self::PERM_VIEW, self::PERM_CREATE]);
        }

        return false;
    }

    private static function withinScope($userId, $scopeId) {
        if ($scopeId === null) return true;
        $user = (new UserDAO())->getById($userId);
        return (int) $user['branch_id'] === (int) $scopeId;
    }
}

// في المتحكم
public function action_delete() {
    if (!PermissionService::can('product', PermissionService::PERM_DELETE)) {
        $this->setError('غير مصرّح بالحذف');
        return;
    }
    // ...
}

القرار 7: كيف أُسجّل الأخطاء وأُراقب الإنتاج؟

المشكلة: المستخدم يقول “حدث خطأ” — ولا أعرف ما هو.

قراري: Error Logging مركزي + Correlation ID لكل طلب.

php

class ErrorLogger {

    public static function capture(\Exception $e, $context = []) {
        $correlationId = self::getCorrelationId();

        $log = [
            'correlation_id' => $correlationId,
            'message'        => $e->getMessage(),
            'code'           => $e->getCode(),
            'file'           => $e->getFile(),
            'line'           => $e->getLine(),
            'trace'          => $e->getTraceAsString(),
            'user_id'        => ZnetDK::getUserId(),
            'url'            => $_SERVER['REQUEST_URI'] ?? null,
            'context'        => $context,
            'created_at'     => date('Y-m-d H:i:s')
        ];

        // كتابة في ملف + إرسال للخادم المركزي
        error_log(json_encode($log, JSON_UNESCAPED_UNICODE));

        // للمستخدم: رسالة نظيفة + رقم مرجعي
        return $correlationId;
    }

    private static function getCorrelationId() {
        if (empty($_SERVER['HTTP_X_CORRELATION_ID'])) {
            return uniqid('err_', true);
        }
        return $_SERVER['HTTP_X_CORRELATION_ID'];
    }
}

// في نقطة الدخول
set_exception_handler(function($e) {
    $correlationId = ErrorLogger::capture($e);
    echo json_encode([
        'success' => false,
        'message' => "حدث خطأ داخلي. الرجاء إبلاغ الدعم بالرمز: $correlationId"
    ]);
});

رابعًا: كيف أبني وحدة ZnetDK من الصفر — خطوة بخطوة

سأبني معك وحدة “إدارة العملاء” كاملة.

الخطوة 1: إنشاء الهيكل

text

/modules/customers/
├── controller/
│   └── CustomersController.php
├── dao/
│   ├── CustomerDAO.php
│   └── CustomerContactDAO.php
├── view/
│   ├── customer-list.php
│   ├── customer-form.php
│   └── customer-detail.php
├── js/
│   └── customers.js
├── validator/
│   └── CustomerValidator.php
├── sql/
│   └── install.sql
├── menu.php
└── config.php

الخطوة 2: قاعدة البيانات — sql/install.sql

sql

CREATE TABLE IF NOT EXISTS customers (
    id            INT AUTO_INCREMENT PRIMARY KEY,
    code          VARCHAR(20) UNIQUE NOT NULL,
    name          VARCHAR(150) NOT NULL,
    email         VARCHAR(150),
    phone         VARCHAR(30),
    city          VARCHAR(80),
    credit_limit  DECIMAL(12,2) DEFAULT 0,
    balance       DECIMAL(12,2) DEFAULT 0,
    active        TINYINT(1) DEFAULT 1,
    created_at    DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at    DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_customers_name (name),
    INDEX idx_customers_city (city),
    INDEX idx_customers_active (active)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS customer_contacts (
    id           INT AUTO_INCREMENT PRIMARY KEY,
    customer_id  INT NOT NULL,
    name         VARCHAR(120) NOT NULL,
    role         VARCHAR(80),
    email        VARCHAR(150),
    phone        VARCHAR(30),
    is_primary   TINYINT(1) DEFAULT 0,
    FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE CASCADE,
    INDEX idx_contacts_customer (customer_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

الخطوة 3: DAO — dao/CustomerDAO.php

php

<?php
namespace Modules\Customers\DAO;

use ZnetDK\DAO;

class CustomerDAO extends DAO {

    protected function getTableName()  { return 'customers'; }
    protected function getPrimaryKey() { return 'id'; }
    protected function getColumns() {
        return ['id','code','name','email','phone','city',
                'credit_limit','balance','active','created_at','updated_at'];
    }

    // فحص الرصيد قبل البيع
    public function canCharge($customerId, $amount) {
        $c = $this->getById($customerId);
        if (!$c || !$c['active']) return false;
        return ($c['balance'] + $amount) <= $c['credit_limit'];
    }

    // العملاء النشطون فقط
    public function active() {
        return $this->getAll('active = 1', 'name ASC');
    }

    // بحث مع Pagination
    public function search($keyword = '', $city = null, $page = 1, $perPage = 25) {
        $where  = ['1=1'];
        $params = [];

        if ($keyword !== '') {
            $where[] = "(name LIKE :kw OR code LIKE :kw OR email LIKE :kw)";
            $params[':kw'] = '%' . $keyword . '%';
        }
        if ($city !== null && $city !== '') {
            $where[] = "city = :city";
            $params[':city'] = $city;
        }

        $offset = ($page - 1) * $perPage;
        $sql = "SELECT " . implode(',', $this->getColumns()) . "
                FROM customers
                WHERE " . implode(' AND ', $where) . "
                ORDER BY name ASC
                LIMIT $offset, $perPage";

        return $this->query($sql, $params);
    }
}

الخطوة 4: Validation — validator/CustomerValidator.php

php

<?php
namespace Modules\Customers\Validator;

use ZnetDK\Validation;

class CustomerValidator extends Validation {

    protected function initRules() {
        $this->addRule('code',  'required|maxlength:20|unique:customers,code');
        $this->addRule('name',  'required|maxlength:150');
        $this->addRule('email', 'email|maxlength:150');
        $this->addRule('phone', 'maxlength:30');
        $this->addRule('city',  'maxlength:80');
        $this->addRule('credit_limit', 'numeric|min:0');
    }

    protected function initLabels() {
        return [
            'code'         => 'رمز العميل',
            'name'         => 'اسم العميل',
            'email'        => 'البريد الإلكتروني',
            'phone'        => 'الهاتف',
            'city'         => 'المدينة',
            'credit_limit' => 'حد الائتمان'
        ];
    }
}

الخطوة 5: المتحكم — controller/CustomersController.php

php

<?php
namespace Modules\Customers\Controller;

use ZnetDK\Controller;
use Modules\Customers\DAO\CustomerDAO;
use Modules\Customers\Validator\CustomerValidator;
use Modules\Shared\Service\AuditService;
use Modules\Shared\Service\PermissionService;

class CustomersController extends Controller {

    // قائمة مع Pagination
    public function action_list() {
        if (!PermissionService::can('customer', 'view')) {
            $this->setError('غير مصرّح');
            return;
        }

        $keyword = $this->getParam('keyword', '');
        $city    = $this->getParam('city', null);
        $page    = max(1, (int) $this->getParam('page', 1));

        $dao = new CustomerDAO();
        $rows  = $dao->search($keyword, $city, $page);
        $total = $dao->countSearch(['keyword' => $keyword, 'city' => $city]);

        $this->setResponse([
            'data'  => $rows,
            'total' => $total,
            'page'  => $page,
            'pages' => (int) ceil($total / 25)
        ]);
    }

    // حفظ (إنشاء أو تعديل)
    public function action_save() {
        $data = $this->getRequestData();
        $isUpdate = !empty($data['id']);

        $action = $isUpdate ? 'update' : 'create';
        if (!PermissionService::can('customer', $action)) {
            $this->setError('غير مصرّح');
            return;
        }

        $validator = new CustomerValidator();
        if (!$validator->validate($data)) {
            $this->setError(implode(' | ', $validator->getErrors()));
            return;
        }

        $dao = new CustomerDAO();
        $before = $isUpdate ? $dao->getById($data['id']) : null;

        $this->beginTransaction();
        try {
            $id = $dao->save($data);
            $after = $dao->getById($id);

            AuditService::log(
                'customer',
                $id,
                $isUpdate ? 'update' : 'create',
                $before,
                $after
            );

            $this->commit();
            $this->setSuccess($isUpdate
                ? "تم تحديث العميل بنجاح"
                : "تم إنشاء العميل برمز {$after['code']}");
        } catch (\Exception $e) {
            $this->rollback();
            $ref = \Modules\Shared\Service\ErrorLogger::capture($e, ['action' => 'customer_save']);
            $this->setError("فشل الحفظ. المرجع: $ref");
        }
    }

    // حذف (ناعم — Soft Delete)
    public function action_delete() {
        if (!PermissionService::can('customer', 'delete')) {
            $this->setError('غير مصرّح');
            return;
        }
        $id = (int) $this->getParam('id');
        $dao = new CustomerDAO();
        $before = $dao->getById($id);
        $dao->save(['id' => $id, 'active' => 0]); // Soft delete

        AuditService::log('customer', $id, 'delete', $before, null);
        $this->setSuccess('تم تعطيل العميل بنجاح');
    }

    // فحص إمكانية البيع بالأجل
    public function action_can_charge() {
        $customerId = (int) $this->getParam('customer_id');
        $amount     = (float) $this->getParam('amount');

        $dao = new CustomerDAO();
        $ok = $dao->canCharge($customerId, $amount);

        $this->setResponse(['allowed' => $ok]);
    }
}

الخطوة 6: العرض — view/customer-list.php

html

<div class="w3-container" dir="rtl">

    <header class="w3-row w3-margin-bottom">
        <h2 class="w3-col s8">إدارة العملاء</h2>
        <div class="w3-col s4 w3-right-align">
            <button class="w3-button w3-blue" onclick="Customers.openForm()">
                ➕ عميل جديد
            </button>
        </div>
    </header>

    <!-- شريط البحث والفلترة -->
    <div class="w3-row w3-margin-bottom">
        <div class="w3-col s6 m4">
            <input type="search" id="cust-keyword"
                   placeholder="ابحث بالاسم أو الرمز..."
                   class="w3-input w3-border">
        </div>
        <div class="w3-col s6 m3">
            <select id="cust-city" class="w3-select w3-border">
                <option value="">كل المدن</option>
            </select>
        </div>
        <div class="w3-col s12 m2">
            <button class="w3-button w3-gray" onclick="Customers.search()">
                🔍 بحث
            </button>
        </div>
    </div>

    <!-- الجدول -->
    <table class="w3-table w3-striped w3-bordered w3-hoverable">
        <thead class="w3-blue">
            <tr>
                <th>الرمز</th>
                <th>الاسم</th>
                <th>الهاتف</th>
                <th>المدينة</th>
                <th>الرصيد</th>
                <th>الحالة</th>
                <th>إجراءات</th>
            </tr>
        </thead>
        <tbody id="cust-tbody">
            <tr><td colspan="7" class="w3-center">جارٍ التحميل...</td></tr>
        </tbody>
    </table>

    <!-- Pagination -->
    <div id="cust-pagination" class="w3-center w3-margin-top"></div>

</div>

الخطوة 7: JavaScript — js/customers.js

javascript

var Customers = {
    state: { page: 1, keyword: '', city: '' },

    init: function() {
        Customers.search();
        $('#cust-keyword').on('keyup', function(e) {
            if (e.key === 'Enter') Customers.search();
        });
        $('#cust-city').on('change', function() {
            Customers.search();
        });
    },

    search: function() {
        Customers.state.keyword = $('#cust-keyword').val() || '';
        Customers.state.city    = $('#cust-city').val() || '';
        Customers.state.page    = 1;
        Customers.load();
    },

    load: function() {
        $('#cust-tbody').html('<tr><td colspan="7" class="w3-center">جارٍ التحميل...</td></tr>');

        ZnetDK.request({
            controller: 'CustomersController',
            action: 'list',
            data: Customers.state,
            success: function(resp) {
                Customers.render(resp.data);
                Customers.renderPagination(resp.page, resp.pages, resp.total);
            },
            error: function(msg) {
                $('#cust-tbody').html(
                    '<tr><td colspan="7" class="w3-center w3-red">' + msg + '</td></tr>'
                );
            }
        });
    },

    render: function(rows) {
        if (!rows || rows.length === 0) {
            $('#cust-tbody').html(
                '<tr><td colspan="7" class="w3-center">لا توجد نتائج</td></tr>'
            );
            return;
        }
        var html = '';
        rows.forEach(function(c) {
            var status = c.active == 1
                ? '<span class="w3-tag w3-green">نشط</span>'
                : '<span class="w3-tag w3-gray">معطّل</span>';

            html += '<tr>';
            html += '<td>' + c.code + '</td>';
            html += '<td>' + c.name + '</td>';
            html += '<td>' + (c.phone || '—') + '</td>';
            html += '<td>' + (c.city || '—') + '</td>';
            html += '<td>' + parseFloat(c.balance).toFixed(2) + '</td>';
            html += '<td>' + status + '</td>';
            html += '<td>';
            html += '<button class="w3-button w3-small w3-blue" onclick="Customers.openForm(' + c.id + ')">✏️</button> ';
            html += '<button class="w3-button w3-small w3-red" onclick="Customers.remove(' + c.id + ')">🗑️</button>';
            html += '</td>';
            html += '</tr>';
        });
        $('#cust-tbody').html(html);
    },

    renderPagination: function(page, pages, total) {
        if (pages <= 1) { $('#cust-pagination').empty(); return; }
        var html = '<span class="w3-margin-right">إجمالي: ' + total + '</span>';
        for (var i = 1; i <= pages; i++) {
            var cls = (i === page) ? 'w3-blue' : 'w3-light-gray';
            html += '<button class="w3-button w3-small ' + cls + ' w3-margin-right" '
                 +  'onclick="Customers.goPage(' + i + ')">' + i + '</button>';
        }
        $('#cust-pagination').html(html);
    },

    goPage: function(p) {
        Customers.state.page = p;
        Customers.load();
    },

    openForm: function(id) {
        ZnetDK.showView('customer-form', { id: id || null });
    },

    remove: function(id) {
        if (!confirm('هل أنت متأكد من تعطيل هذا العميل؟')) return;
        ZnetDK.request({
            controller: 'CustomersController',
            action: 'delete',
            data: { id: id },
            success: function() {
                ZnetDK.showMessage('تم التعطيل', 'success');
                Customers.load();
            }
        });
    }
};

$(document).ready(function() { Customers.init(); });

الخطوة 8: تعريف القائمة — menu.php

php

<?php
return [
    'label' => 'العملاء',
    'icon'  => 'people',
    'items' => [
        ['label' => 'قائمة العملاء', 'view' => 'customers/customer-list'],
        ['label' => 'عميل جديد',     'view' => 'customers/customer-form']
    ],
    'profile' => ['admin', 'sales_manager', 'sales_rep']
];

الخطوة 9: الإعدادات — config.php

php

<?php
return [
    'module'  => 'customers',
    'version' => '1.0.0',
    'author'  => 'فريق التطوير',
    'requires' => ['shared >= 1.0'],
    'tables'  => ['customers', 'customer_contacts']
];

خامسًا: قائمة مراجعة قبل النشر (Production Checklist)

الأمان

  • □ HTTPS إلزامي مع HSTS
  • □ كل المدخلات مُتحقَّق منها في الخادم
  • □ لا SQL مباشر — DAO فقط
  • □ لا بيانات حساسة في Local Storage
  • □ صلاحيات دقيقة لكل إجراء
  • □ Audit Trail للأحداث الحساسة
  • □ Rate Limiting على الإجراءات الحساسة
  • □ CSRF Tokens مفعّلة
  • □ Security Headers (CSP, X-Frame-Options, etc.)

الأداء

  • □ فهارس على أعمدة البحث والترتيب
  • □ Pagination على كل قائمة
  • □ ضغط CSS/JS/HTML
  • □ ضغط الصور + WebP
  • □ Caching للأصول الثابتة
  • □ Lazy Loading للصور
  • □ زمن الاستجابة < 500ms للـ API

PWA

  • □ Service Worker مسجّل
  • □ Manifest صحيح
  • □ Offline Page
  • □ Icons بكل المقاسات
  • □ يعمل على HTTPS

المراقبة

  • □ Error Logging مركزي
  • □ Correlation ID لكل طلب
  • □ Backup دوري لقاعدة البيانات
  • □ Monitoring للخدمة (Uptime)
  • □ تنبيهات على الأخطاء الحرجة

تجربة المستخدم

  • □ رسائل واضحة بالعربية
  • □ RTL صحيح لكل الصفحات
  • □ مؤشرات تحميل أثناء AJAX
  • □ تأكيد قبل الحذف
  • □ تصميم متجاوب على كل المقاسات
  • □ اختبار على: iPhone, Android, Tablet, Desktop

سادسًا: مبادئ ختامية من 20 سنة

1. البساطة أقوى من الذكاء

الكود الذكي صعب الصيانة. الكود البسيط يعيش سنوات.

2. لا تُحسّن قبل أن تقيس

“يبدو بطيئًا” ليست مشكلة. “800ms على 50,000 سجل” مشكلة.

3. كل قرار له ثمن

Pagination تعقيد، لكن بدونها موت. اختر ما يخدم مشروعك.

4. المستخدم لا يقرأ الكود

لكنه يشعر بالبطء، ويلاحظ الخطأ، ويكره الرسائل الغامضة.

5. وثّق قراراتك

بعد 6 أشهر، ستسأل: لماذا كتبت هذا؟ الإجابة في التوثيق.

6. اختبر ما تخاف منه

لا تختبر ما تعرف أنه يعمل. اختبر الحدود، الأخطاء، الحالات الشاذة.

7. الأمان عادة لا ميزة

لا تُضِف الأمان في النهاية. ابدأ به.

8. الفريق أهم من الكود

كود ممتاز بفريق سيئ = فشل. كود جيد بفريق ممتاز = نجاح.


الخطوة التالية

أنا جاهز الآن لأي من هذه المسارات:

  1. بناء مشروع كامل — اختر مجالًا (مبيعات، مخزون، عيادة، تعليم) وأبنيه معك وحدة وحدة
  2. مراجعة كود — أرسل كودك وأُراجعه كأنني في Code Review حقيقي
  3. حل مشكلة محددة — أخبرني بالعقبة وسأحلّها هندسيًا
  4. تصميم قاعدة بيانات — مشروعك، جداولك، علاقاتك، فهارسك
  5. خطة نشر — من التطوير إلى الإنتاج خطوة بخطوة
  6. تدريب فريق — خارطة تعلّم لـ ZnetDK من الصفر للاحتراف

اختر مسارك، وسأبدأ معك التنفيذ الفعلي.


This is the content of the ZnetDK 4 Mobile landing page (v3.7, April 2026). Here’s a summary of what it describes:

What is ZnetDK 4 Mobile?

A free, open-source full-stack PHP/JavaScript framework for building mobile business web applications. Licensed under GPL.

Key Highlights

Ready-Built Web Application

  • No need to start from scratch — just add views and connect them to the navigation menu
  • Built-in: login form, user access control, data lists, user forms, customizable title/logo/theme

Open Web Standards

  • HTML/CSS for views
  • JavaScript for interactivity
  • PHP (MVC architecture) for server-side logic
  • SQL for data queries
  • Powered by the ZnetDK Full-Stack Framework API

Modules (on GitHub)

  • A dozen+ modules available: email/SMS sending, photo/document upload, session monitoring, data change tracking, menu panels, and more
  • You can also build custom modules

Built-in Key Features

FeatureDescription
ResponsiveW3.CSS grids for mobile/tablet/laptop/desktop
Progressive Web AppNo store install needed; offline page support
User AuthenticationBuilt-in login form
User ManagementProfiles & access rights
Navigation Menu2-level menu via menu.php
UI ThemingCustom colors, title, logo, font
Data listsHTML5 lists + remote PHP controller actions
User FormsHTML5 forms for create/update
AutocompleteSuggestion input field
Action buttonsAdd, refresh, search, scroll-to-top
User messagesInfo/confirmation/warning/error with Snackbar
Local storagePersistent private data on device
AJAX RequestsSPA architecture with on-demand loading
Data Access ObjectDAO PHP classes — no SQL needed for CRUD
Data ValidationPHP Validation classes
Full-Stack APIHigh-level JS & PHP methods

Tech Stack

JS · jQuery · CSS · W3.CSS · PHP · ZnetDK · SQL · MySQL

Footer

Fork on GitHub · ZnetDK 2014–2026 · Legal notices · Software licenses


Would you like me to help with anything specific — such as extracting the text, summarizing it differently, or drafting a project based on this framework?

عن ZnetDK 4 Mobile

نظرة عامة موسّعة

ZnetDK 4 Mobile هو إطار عمل (Framework) مجاني ومفتوح المصدر لتطوير تطبيقات الويب للأعمال، يعمل بالكامل على PHP من جهة الخادم وJavaScript من جهة العميل. الإصدار الحالي هو v3.7 (أبريل 2026)، ومرخّص تحت رخصة GPL.

الهدف الأساسي: بناء تطبيق ويب كامل في دقائق، دون البدء من الصفر.


لماذا ZnetDK 4 Mobile؟

1. تطبيق جاهز مسبقًا (Ready-Built Web Application)

بدلًا من بناء البنية التحتية من البداية، يحصل المطوّر على:

  • نموذج تسجيل دخول مدمج
  • نظام صلاحيات لعرض الصفحات المسموحة فقط
  • قوالب لعرض البيانات (Lists) ونماذج الإدخال (Forms)
  • إمكانية تخصيص العنوان والشعار وألوان الثيم

كل ما عليك فعله هو إضافة العروض (Views) التي تخدم متطلبات عملك وربطها بقائمة التنقل.

2. التطوير وفق معايير الويب المفتوحة

المهارات المطلوبة هي نفسها مهارات تطوير أي موقع:

  • HTML/CSS → تصميم العروض
  • JavaScript → إضافة التفاعلية
  • PHP (MVC) → خدمات منطق الأعمال في الخادم
  • SQL → استعلام البيانات

مع الاستفادة من ZnetDK Full-Stack Framework الذي يحل المشكلات المنخفضة المستوى (Low-Level) بسطور قليلة.


الميزات المدمجة بالتفصيل

🔹 التصميم المتجاوب (Responsive)

استخدام شبكات وتخطيطات W3.CSS لضمان عرض مثالي على:

  • الهواتف المحمولة
  • الأجهزة اللوحية
  • الحواسيب المحمولة
  • الحواسيب المكتبية

🔹 تطبيق الويب التقدمي (PWA)

  • لا حاجة لتثبيت التطبيق من متجر التطبيقات
  • يبدأ التطبيق بسرعة
  • يمكنه عرض صفحة مخصصة عند انقطاع الاتصال (Offline Page)

🔹 مصادقة المستخدمين (User Authentication)

  • تسجيل الدخول عبر اسم مستخدم وكلمة مرور
  • نموذج تسجيل دخول مدمج جاهز للاستخدام

🔹 إدارة المستخدمين (User Management)

  • تعريف المستخدمين المسموح لهم بالوصول
  • إسناد ملف تعريف (Profile) لكل مستخدم وفق صلاحياته

🔹 قائمة التنقل (Navigation Menu)

  • إنشاء قائمة تنقل من مستويين
  • تتم إضافتها ببساطة في ملف menu.php الخاص بالتطبيق

🔹 تخصيص الواجهة (UI Theming)

  • تغيير ألوان التطبيق
  • تغيير عنوان التطبيق
  • عرض شعار الشركة
  • اختيار خط مختلف

🔹 قوائم البيانات (Data Lists)

  • إنشاء قوائم بيانات بـ HTML5
  • ربطها بإجراء (Action) في متحكم PHP عن بُعد
  • تفعيل خصائص البحث والفرز المدمجة في JavaScript

🔹 نماذج المستخدم (User Forms)

  • بناء نماذج إدخال مخصصة بـ HTML5
  • ربطها بإجراء التخزين في متحكم PHP عن بُعد
  • تُستخدم لإضافة بيانات جديدة أو تعديل الموجودة

🔹 الإكمال التلقائي (Autocomplete)

  • اقتراح اقتراحات للمستخدم أثناء كتابته النص
  • عبر حقل الإدخال المدمج في ZnetDK

🔹 أزرار الإجراءات (Action Buttons)

أزرار ثابتة لـ:

  • إضافة عناصر جديدة
  • تحديث محتوى القائمة
  • البحث عن بيانات محددة
  • التمرير إلى أعلى الصفحة

🔹 رسائل المستخدم (User Messages)

  • رسائل إعلامية، تأكيدية، تحذيرية، وخطأ
  • مع تأثير الإخفاء التلقائي وSnackbar

🔹 التخزين المحلي (Local Storage)

  • تخزين البيانات والإعدادات الخاصة محليًا على جهاز المستخدم
  • تبقى محفوظة حتى بعد إغلاق المتصفح

🔹 طلبات AJAX

  • ZnetDK 4 Mobile هو تطبيق صفحة واحدة (SPA)
  • يتم تحميل العروض والبيانات عند الطلب عبر AJAX
  • تطوير طلبات AJAX مخصصة سهل

🔹 كائن الوصول للبيانات (DAO)

  • اختيار وتخزين البيانات عبر فئات DAO PHP
  • للعمليات البسيطة: لا حاجة لكتابة SQL لعمليات:
    • SELECT
    • INSERT
    • UPDATE
    • DELETE

🔹 التحقق من صحة البيانات (Data Validation)

  • التحقق من بيانات النماذج عبر فئات Validation PHP
  • تُستدعى من إجراءات المتحكم قبل تخزين البيانات

🔹 واجهة برمجة تطبيقات متكاملة (Full-Stack API)

  • تسريع التطوير باستدعاء دوال JavaScript وPHP عالية المستوى

الوحدات الإضافية (Modules)

من GitHub

  • أكثر من 12 وحدة جاهزة لإضافة ميزات متقدمة، مثل:
    • إرسال البريد الإلكتروني
    • إرسال رسائل SMS
    • رفع الصور والمستندات بسهولة
    • مراقبة جلسات المستخدمين
    • تتبع تغييرات البيانات
    • إضافة لوحات من عناصر القوائم إلى العرض الرئيسي
    • و…

وحدات مخصصة

  • يمكنك تطوير وحداتك الخاصة لتلبية احتياجاتك المحددة
  • راجع وثائق الوحدات (Documentation on Modules)

التقنيات المستخدمة

التقنيةالاستخدام
JSالتفاعلية من جهة العميل
jQueryمكتبة JavaScript مساعدة
CSSالتنسيق
W3.CSSإطار CSS للتصميم المتجاوب
PHPمنطق الخادم (MVC)
ZnetDKإطار العمل نفسه
SQLاستعلام البيانات
MySQLقاعدة البيانات

معلومات المشروع

  • الترخيص: GPL (مجاني ومفتوح المصدر)
  • المستودع: متاح للتفريع (Fork) على GitHub
  • حقوق النشر: ZnetDK 2014–2026
  • روابط: الإشعارات القانونية | تراخيص البرمجيات

خلاصة

ZnetDK 4 Mobile يوفّر بنية تحتية كاملة لتطبيقات الأعمال على الويب، مع التركيز على:

  1. السرعة في التطوير
  2. الأمان عبر المصادقة والصلاحيات
  3. التجاوب مع جميع الأجهزة
  4. المعايير المفتوحة دون قيود تقنية
  5. القابلية للتوسع عبر الوحدات

إذا كنت مهتمًا بـ:

  • مقارنته مع أطر عمل أخرى (مثل Laravel أو Symfony)
  • مثال عملي لبناء تطبيق
  • شرح معمّق لميزة معينة (مثل DAO أو PWA)
  • ترجمة الوثائق الرسمية

فأخبرني وسأساعدك.

— تعمّق في ZnetDK 4 Mobile

سأقدّم لك الآن تحليلًا معمّقًا يتجاوز ما ورد في الصفحة، بناءً على طبيعة إطار العمل ومقارنته بالبدائل، مع أمثلة عملية وسيناريوهات استخدام.


أولًا: ما الذي يميّز ZnetDK 4 Mobile فعلًا؟

الفكرة الجوهرية

معظم أطر عمل PHP (مثل Laravel وSymfony) تُعطيك أدوات لبناء تطبيق — لكنك تبدأ من صفحة بيضاء.
أما ZnetDK 4 Mobile فيُعطيك تطبيقًا كاملًا يعمل بالفعل، وأنت تضيف إليه فقط ما يخصّ عملك.

التشبيه: الفرق بين شراء قطع غيار لبناء سيارة (Laravel) مقابل شراء سيارة جاهزة تضيف إليها ملحقات (ZnetDK).

الجمهور المستهدف

  • مطوّر واحد أو فريق صغير
  • تطبيقات أعمال داخلية (Internal Business Apps)
  • لوحات تحكم إدارية (Admin Panels)
  • تطبيقات ميدانية تعمل على الهاتف واللوحي
  • مشاريع تحتاج تسليمًا سريعًا بميزانية محدودة

ثانيًا: البنية المعمارية (Architecture)

نموذج MVC

text

الطلب (Request)
     ↓
[ menu.php ] ← تعريف قائمة التنقل
     ↓
[ View (HTML5) ] ← عرض المستخدم
     ↓
[ JavaScript Controller ] ← تفاعلية العميل (AJAX)
     ↓
[ PHP Controller Action ] ← منطق الأعمال
     ↓
[ Validation Class ] ← التحقق من البيانات
     ↓
[ DAO Class ] ← الوصول لقاعدة البيانات
     ↓
[ MySQL Database ]

مميزات هذه البنية

  1. فصل واضح بين العرض والمنطق والبيانات
  2. إعادة استخدام الكود عبر الوحدات
  3. أمان لأن التحقق يحدث في الخادم قبل التخزين
  4. SPA — لا إعادة تحميل كاملة للصفحة

ثالثًا: شرح معمّق لأهم المكونات

1. DAO — كائن الوصول للبيانات

بدلًا من كتابة:

sql

SELECT id, name, email FROM users WHERE active = 1 ORDER BY name;

تكتب في PHP شيئًا مثل:

php

$dao = new UserDAO();
$rows = $dao->getAll('active = 1', 'name');

الفائدة:

  • كود أقصر وأوضح
  • حماية تلقائية من SQL Injection
  • سهولة الصيانة والتعديل

2. Validation — التحقق من البيانات

php

$validation = new Validation();
$validation->addRule('email', 'required|email');
$validation->addRule('age', 'numeric|min:18');
if ($validation->isValid($data)) {
    // تخزين البيانات
}

الفائدة: لا تثق أبدًا بمدخلات المستخدم — التحقق يتم في الخادم.

3. PWA — تطبيق ويب تقدمي

يمنح تطبيقك:

  • Service Worker يعمل في الخلفية
  • Manifest يسمح بـ “إضافة إلى الشاشة الرئيسية”
  • Offline Page عند انقطاع الإنترنت
  • سرعة تحميل بعد الزيارة الأولى

النتيجة: المستخدم يشعر أنه يستخدم تطبيقًا أصليًا (Native) دون تثبيته من متجر.

4. Local Storage

تخزين محلي على جهاز المستخدم:

  • تفضيلات الواجهة (اللغة، الثيم)
  • بيانات مؤقتة (Draft Forms)
  • رموز الجلسة
  • ملاحظة: لا تُخزّن بيانات حساسة هنا — فهي غير مشفّرة.

5. AJAX + SPA

  • الصفحة تُحمّل مرة واحدة
  • كل تنقّل لاحق يُحمّل جزئيًا عبر AJAX
  • تجربة سلسة وسريعة
  • تقليل الحمل على الخادم

رابعًا: مقارنة مع البدائل

المعيارZnetDK 4 MobileLaravelSymfonyCodeIgniter
البدايةتطبيق جاهزمن الصفرمن الصفرمن الصفر
منحنى التعلممنخفضمتوسط-عاليعاليمنخفض
PWA مدمج✅❌ (يدوي)❌❌
نظام صلاحيات✅ مدمجيدوييدوييدوي
واجهة جاهزة✅❌❌❌
الحجمصغيركبيركبيرصغير
الجمهورتطبيقات أعمالتطبيقات عامةمؤسسات كبيرةتطبيقات بسيطة
قاعدة البياناتMySQLمتعددةمتعددةمتعددة

الخلاصة: إذا كان مشروعك تطبيق أعمال داخلي وتريد سرعة التسليم → ZnetDK خيار ممتاز. إذا كان تطبيقًا عامًا كبيرًا → Laravel أو Symfony.


خامسًا: سيناريوهات استخدام عملية

السيناريو 1: تطبيق جرد المخزون

  • العروض: قائمة المنتجات، نموذج إضافة منتج، تقرير المخزون
  • الميزات المستخدمة: Data Lists، User Forms، Action Buttons، DAO
  • الوقت المتوقع: أيام قليلة بدلًا من أسابيع

السيناريو 2: تطبيق ميداني للمندوبين

  • العروض: قائمة العملاء، نموذج زيارة، خريطة
  • الميزات: PWA (يعمل دون اتصال)، Local Storage، Autocomplete
  • الفائدة: يعمل على الهاتف دون تثبيت

السيناريو 3: لوحة تحكم إدارية

  • العروض: إدارة المستخدمين، التقارير، الإعدادات
  • الميزات: User Management، Theming، User Messages
  • الفائدة: صلاحيات دقيقة لكل مستخدم

سادسًا: نظام الوحدات (Modules)

البنية

كل وحدة (Module) هي مجلد مستقل يحتوي على:

text

/modules/
   /my-module/
      ├── controller/       ← منطق PHP
      ├── view/             ← عروض HTML5
      ├── js/               ← تفاعلية JavaScript
      ├── dao/              ← فئات الوصول للبيانات
      ├── menu.php          ← تعريف قائمة التنقل
      └── config.php        ← الإعدادات

وحدات جاهزة على GitHub

  • Email/SMS Sender — إرسال الإشعارات
  • File Uploader — رفع الصور والمستندات
  • Session Monitor — مراقبة الجلسات
  • Data Tracker — تتبع التغييرات (Audit Log)
  • Menu Panels — لوحات إضافية في الصفحة الرئيسية
  • وحدات أخرى…

بناء وحدة مخصصة

  1. أنشئ المجلد بالبنية أعلاه
  2. عرّف العروض والمتحكمات
  3. أضف تعريف القائمة في menu.php
  4. سجّل الوحدة في إعدادات التطبيق
  5. لا حاجة لتعديل نواة الإطار — الوحدات معزولة

سابعًا: الأمان في ZnetDK

طبقات الحماية

  1. Authentication — تسجيل دخول مدمج
  2. Authorization — صلاحيات حسب الملف الشخصي (Profile)
  3. Validation — تحقق من المدخلات في الخادم
  4. DAO — حماية من SQL Injection
  5. Menu Control — إخفاء الصفحات غير المسموحة
  6. Session Management — إدارة الجلسات

أفضل الممارسات

  • استخدم HTTPS دائمًا
  • لا تُخزّن كلمات المرور كنص صريح (ZnetDK يتعامل معها)
  • راجع صلاحيات كل مستخدم دوريًا
  • سجّل الأحداث الحساسة (عبر وحدة Data Tracker)

ثامنًا: نصائح عملية للمطوّر

للمبتدئين

  1. ابدأ بتطبيق Hello World بسيط
  2. افهم menu.php أولًا — فهو خريطة التطبيق
  3. تدرّب على Data Lists قبل User Forms
  4. استخدم الوحدات الجاهزة بدلًا من إعادة اختراع العجلة

للمتقدمين

  1. اكتب وحداتك الخاصة لتغليف منطق العمل المتكرر
  2. استفد من Full-Stack API لتقليل الكود
  3. حسّن الاستعلامات باستخدام DAO
  4. أضف Caching للبيانات الثابتة

أخطاء شائعة

  • ❌ وضع منطق الأعمال في العروض
  • ❌ إهمال التحقق من البيانات في الخادم
  • ❌ تخزين بيانات حساسة في Local Storage
  • ❌ عدم استخدام الوحدات → كود متشابك

تاسعًا: خارطة تعلّم مقترحة

الأسبوعالموضوع
1تثبيت ZnetDK + Hello World + فهم MVC
2menu.php + Navigation + Theming
3Data Lists + DAO + SQL
4User Forms + Validation
5Authentication + User Management
6PWA + Local Storage + AJAX
7Modules + وحدات مخصصة
8مشروع كامل متكامل

عاشرًا: المصادر الرسمية

  • الموقع: ZnetDK.com
  • GitHub: مستودع المشروع + الوحدات
  • الوثائق: Documentation on Modules
  • الترخيص: GPL — راجع الإشعارات القانونية
  • الفترة: 2014–2026 (مشروع مستمر ومستقر)

خلاصة نهائية

ZnetDK 4 Mobile ليس مجرد إطار عمل — بل منصة كاملة لبناء تطبيقات الأعمال على الويب. قوّته في:

الجانبالقيمة المضافة
السرعةتطبيق كامل في دقائق
الاكتمالكل الميزات المطلوبة جاهزة
البساطةمعايير ويب مفتوحة
التوسعوحدات معزولة قابلة للتركيب
الأمانطبقات حماية متعددة
المجانيةGPL — بدون تكلفة

هل تريد أن أتوسّع في جانب معيّن؟ مثلًا:

  • مثال كود كامل لتطبيق بسيط
  • شرح تفصيلي لـ PWA أو DAO
  • دليل تثبيت خطوة بخطوة
  • ترجمة الوثائق الرسمية
  • مقارنة أعمق مع إطار محدد

أخبرني بما يهمّك وسأعمّق فيه.

— تعمّق أعمق في ZnetDK 4 Mobile

سأأخذك الآن إلى مستوى أكثر تفصيلًا: بنية الكود الفعلية، أمثلة عملية، دورة حياة الطلب، وأسرار الاستخدام المتقدم. هذا المحتوى موجّه لمن يريد الانتقال من الفهم إلى التنفيذ.


أولًا: دورة حياة الطلب (Request Lifecycle) بالتفصيل

لفهم ZnetDK حقًا، يجب أن تعرف ما يحدث لحظة ضغط المستخدم على زر حتى ظهور النتيجة:

text

1. المستخدم يضغط زر "حفظ" في نموذج HTML5
        ↓
2. JavaScript Controller يلتقط الحدث (Event)
        ↓
3. يتم تجميع البيانات في كائن JSON
        ↓
4. طلب AJAX يُرسل إلى الخادم (POST)
        ↓
5. ZnetDK Router يستقبل الطلب ويوجّهه للمتحكم الصحيح
        ↓
6. PHP Controller Action يُنفّذ
        ↓
7. Validation Class يتحقق من صحة البيانات
   ├── إذا فشل → إرجاع رسالة خطأ (User Message)
   └── إذا نجح → المتابعة
        ↓
8. DAO Class ينفّذ INSERT/UPDATE/DELETE
        ↓
9. استجابة JSON تعود للعميل
        ↓
10. JavaScript يعرض Snackbar نجاح + يحدّث القائمة

الفائدة من فهم هذا: تعرف أين تضع الكود بالضبط، وكيف تُشخّص الأخطاء.


ثانيًا: بنية ملفات تطبيق كامل

الهيكل العام

text

/myapp/
│
├── index.php                  ← نقطة الدخول الوحيدة (SPA)
├── menu.php                   ← تعريف قائمة التنقل
├── config.php                 ← إعدادات التطبيق العامة
│
├── /app/
│   ├── /controller/           ← متحكمات PHP
│   │   ├── ProductController.php
│   │   └── CustomerController.php
│   │
│   ├── /view/                 ← عروض HTML5
│   │   ├── product-list.php
│   │   ├── product-form.php
│   │   └── customer-list.php
│   │
│   ├── /js/                   ← JavaScript مخصص
│   │   ├── product.js
│   │   └── customer.js
│   │
│   ├── /dao/                  ← فئات الوصول للبيانات
│   │   ├── ProductDAO.php
│   │   └── CustomerDAO.php
│   │
│   └── /validator/            ← قواعد التحقق
│       └── ProductValidator.php
│
├── /modules/                  ← الوحدات الإضافية
│   ├── /email-sender/
│   ├── /file-uploader/
│   └── /data-tracker/
│
├── /css/                      ← تخصيص الثيم
│   └── custom-theme.css
│
├── /images/
│   └── logo.png
│
└── /engine/                   ← نواة ZnetDK (لا تُعدّلها)

قاعدة ذهبية: لا تعدّل /engine/ أبدًا — كل تخصيصاتك في /app/ و/modules/.


ثالثًا: أمثلة كود كاملة

مثال 1: تعريف قائمة التنقل — menu.php

php

<?php
$menu = array(
    array(
        'label' => 'الرئيسية',
        'view'  => 'home',
        'icon'  => 'home'
    ),
    array(
        'label' => 'المنتجات',
        'items' => array(
            array('label' => 'قائمة المنتجات', 'view' => 'product-list'),
            array('label' => 'إضافة منتج',    'view' => 'product-form')
        )
    ),
    array(
        'label' => 'العملاء',
        'items' => array(
            array('label' => 'قائمة العملاء', 'view' => 'customer-list')
        )
    )
);

مثال 2: فئة DAO — ProductDAO.php

php

<?php
class ProductDAO extends \ZnetDK\DAO {

    protected function getTableName() {
        return 'products';
    }

    protected function getPrimaryKey() {
        return 'product_id';
    }

    protected function getColumns() {
        return array('product_id', 'name', 'price', 'stock', 'created_at');
    }

    // دالة مخصصة: جلب المنتجات منخفضة المخزون
    public function getLowStock($threshold = 10) {
        return $this->getAll(
            "stock < $threshold",
            "stock ASC"
        );
    }
}

مثال 3: متحكم PHP — ProductController.php

php

<?php
class ProductController extends \ZnetDK\Controller {

    // إجراء: حفظ منتج جديد
    public function action_save() {
        $data = $this->getRequestData();

        // 1. التحقق
        $validator = new ProductValidator();
        if (!$validator->validate($data)) {
            $this->setError($validator->getErrors());
            return;
        }

        // 2. التخزين عبر DAO
        $dao = new ProductDAO();
        $id = $dao->save($data);

        // 3. الاستجابة
        $this->setSuccess("تم حفظ المنتج رقم $id بنجاح");
    }

    // إجراء: جلب قائمة المنتجات
    public function action_list() {
        $dao = new ProductDAO();
        $products = $dao->getAll(null, 'name ASC');
        $this->setResponse($products);
    }
}

مثال 4: عرض HTML5 — product-list.php

html

<div class="w3-container">
    <h2>قائمة المنتجات</h2>

    <!-- حقل بحث مدمج -->
    <input type="search" id="product-search"
           placeholder="ابحث عن منتج..."
           class="w3-input w3-border">

    <!-- جدول البيانات -->
    <table id="product-table" class="w3-table w3-striped">
        <thead>
            <tr>
                <th data-sort="name">الاسم</th>
                <th data-sort="price">السعر</th>
                <th data-sort="stock">المخزون</th>
                <th>إجراءات</th>
            </tr>
        </thead>
        <tbody><!-- يُملأ عبر AJAX --></tbody>
    </table>

    <!-- أزرار الإجراءات الثابتة -->
    <div class="znetdk-action-buttons">
        <button onclick="Product.add()">➕ إضافة</button>
        <button onclick="Product.refresh()">🔄 تحديث</button>
        <button onclick="ZnetDK.scrollTop()">⬆️ أعلى</button>
    </div>
</div>

مثال 5: JavaScript — product.js

javascript

var Product = {

    // تحميل القائمة
    load: function() {
        ZnetDK.request({
            controller: 'ProductController',
            action: 'list',
            success: function(products) {
                Product.renderTable(products);
            }
        });
    },

    // عرض البيانات في الجدول
    renderTable: function(products) {
        var html = '';
        products.forEach(function(p) {
            html += '<tr>';
            html += '<td>' + p.name + '</td>';
            html += '<td>' + p.price + '</td>';
            html += '<td>' + p.stock + '</td>';
            html += '<td><button onclick="Product.edit(' + p.product_id + ')">تعديل</button></td>';
            html += '</tr>';
        });
        $('#product-table tbody').html(html);
    },

    // تحديث
    refresh: function() {
        Product.load();
        ZnetDK.showMessage('تم التحديث', 'info');
    },

    // إضافة
    add: function() {
        ZnetDK.showView('product-form');
    },

    // تعديل
    edit: function(id) {
        ZnetDK.showView('product-form', { id: id });
    }
};

// تحميل تلقائي عند فتح العرض
$(document).ready(function() {
    Product.load();
});

رابعًا: دورة حياة الجلسة والأمان

كيف يتعامل ZnetDK مع تسجيل الدخول؟

text

1. المستخدم يفتح التطبيق
        ↓
2. ZnetDK يتحقق من وجود جلسة نشطة
   ├── موجودة → الدخول مباشرة
   └── غير موجودة → عرض Login Form
        ↓
3. المستخدم يُدخل بياناته
        ↓
4. التحقق في الخادم (كلمة المرور مشفّرة)
        ↓
5. إنشاء جلسة + تحميل الصلاحيات (Profile)
        ↓
6. بناء قائمة التنقل حسب صلاحيات المستخدم
        ↓
7. تحميل العرض الأول

مستويات الصلاحيات

المستوىالوصف
Viewرؤية الصفحة فقط
Createإضافة سجلات جديدة
Updateتعديل السجلات
Deleteحذف السجلات
Adminكل ما سبق + إدارة المستخدمين

كيف تُخفي صفحة عن مستخدم؟

php

// في menu.php
$menu = array(
    array(
        'label' => 'التقارير',
        'view'  => 'reports',
        'profile' => 'manager'  // تظهر فقط لمن ملفه "manager"
    )
);

خامسًا: PWA — التفاصيل التقنية

المكونات الثلاثة الأساسية

1. Manifest — manifest.json

json

{
  "name": "تطبيق المنتجات",
  "short_name": "منتجات",
  "start_url": "/index.php",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#2196F3",
  "icons": [
    { "src": "/images/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/images/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}

2. Service Worker — sw.js

javascript

var CACHE_NAME = 'znetdk-v1';
var URLS_TO_CACHE = [
    '/',
    '/index.php',
    '/css/custom-theme.css',
    '/images/logo.png',
    '/offline.html'
];

// التثبيت: تخزين الملفات الأساسية
self.addEventListener('install', function(event) {
    event.waitUntil(
        caches.open(CACHE_NAME).then(function(cache) {
            return cache.addAll(URLS_TO_CACHE);
        })
    );
});

// الجلب: من الكاش أولًا، ثم الشبكة
self.addEventListener('fetch', function(event) {
    event.respondWith(
        caches.match(event.request).then(function(response) {
            return response || fetch(event.request).catch(function() {
                return caches.match('/offline.html');
            });
        })
    );
});

3. Offline Page — offline.html

html

<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
    <meta charset="UTF-8">
    <title>غير متصل</title>
    <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
</head>
<body class="w3-container w3-center" style="padding-top:50px">
    <h1>📡</h1>
    <h2>لا يوجد اتصال بالإنترنت</h2>
    <p>يرجى التحقق من اتصالك والمحاولة مرة أخرى.</p>
    <button onclick="location.reload()" class="w3-button w3-blue">
        إعادة المحاولة
    </button>
</body>
</html>

استراتيجيات التخزين المؤقت (Caching Strategies)

الاستراتيجيةمتى تستخدمها
Cache Firstالأصول الثابتة (CSS, JS, صور)
Network Firstالبيانات الحيّة (API)
Stale-While-Revalidateمحتوى يتغير أحيانًا
Cache Onlyصفحة Offline

سادسًا: Local Storage — أفضل الممارسات

ما الذي يجب تخزينه؟

✅ نعم:

  • تفضيلات الواجهة (اللغة، الثيم)
  • مسودات النماذج غير المكتملة
  • آخر صفحة تمت زيارتها
  • رموز التخزين المؤقت غير الحساسة

❌ لا:

  • كلمات المرور
  • رموز الجلسة الحساسة
  • بيانات شخصية حساسة (رقم الهوية، البطاقة)
  • أي شيء يُعدّ اختراقه كارثة

مثال عملي

javascript

// حفظ تفضيلات المستخدم
var Preferences = {
    save: function(key, value) {
        localStorage.setItem('pref_' + key, JSON.stringify(value));
    },
    load: function(key, defaultValue) {
        var val = localStorage.getItem('pref_' + key);
        return val ? JSON.parse(val) : defaultValue;
    }
};

// الاستخدام
Preferences.save('theme', 'dark');
var theme = Preferences.load('theme', 'light');

سابعًا: التوسع والأداء

متى يصبح التطبيق بطيئًا؟

  1. قوائم ضخمة (آلاف السجلات في صفحة واحدة)
  2. استعلامات SQL غير محسّنة (بدون فهارس)
  3. طلبات AJAX متكررة دون تخزين مؤقت
  4. صور كبيرة دون ضغط

الحلول

المشكلةالحل
قوائم ضخمةPagination — عرض 20-50 سجلًا
استعلامات بطيئةإضافة Indexes في MySQL
طلبات متكررةCaching في Local Storage
صور كبيرةضغط + صيغة WebP
تحميل بطيءLazy Loading للصور

مثال Pagination

php

public function action_list() {
    $page = $this->getParam('page', 1);
    $perPage = 25;
    $offset = ($page - 1) * $perPage;

    $dao = new ProductDAO();
    $total = $dao->count();
    $products = $dao->getAll(null, 'name ASC', $offset, $perPage);

    $this->setResponse(array(
        'data' => $products,
        'total' => $total,
        'page' => $page,
        'pages' => ceil($total / $perPage)
    ));
}

ثامنًا: التشخيص وحل المشكلات

أدوات التشخيص

  1. Console في المتصفح → أخطاء JavaScript
  2. Network Tab → طلبات AJAX وردودها
  3. Application Tab → Local Storage + Service Worker
  4. PHP Error Log → أخطاء الخادم
  5. MySQL Slow Query Log → استعلامات بطيئة

أخطاء شائعة وحلولها

الخطأالسببالحل
404 على viewمسار خاطئ في menu.phpتحقق من المسار
AJAX failsصلاحيات أو جلسة منتهيةأعد تسجيل الدخول
DAO errorأسماء أعمدة خاطئةراجع getColumns()
Validation failsقواعد صارمة جدًاعدّل القواعد
PWA لا يعملService Worker غير مسجّلتحقق من HTTPS

تاسعًا: نصائح احترافية متقدمة

1. تنظيم الكود

  • وحدة واحدة لكل وظيفة — لا تخلط المنتجات بالعملاء
  • أسماء واضحة — getActiveProducts أفضل من getData
  • تعليقات موجزة — اشرح “لماذا” لا “ماذا”

2. الأمان المتقدم

  • CSRF Tokens — ZnetDK يتعامل معها تلقائيًا
  • Rate Limiting — حدّد عدد الطلبات لكل مستخدم
  • Input Sanitization — نظّف المدخلات دائمًا
  • HTTPS إلزامي — خاصًة مع PWA

3. تجربة المستخدم

  • رسائل واضحة — “تم الحفظ” أفضل من “OK”
  • مؤشرات تحميل — Spinner أثناء AJAX
  • تأكيد قبل الحذف — لا تحذف مباشرة
  • حفظ تلقائي — للمسودات في Local Storage

4. الصيانة

  • Git — استخدم فروعًا لكل ميزة
  • Backups — نسخ احتياطية دورية
  • Logging — سجّل الأحداث المهمة
  • Documentation — وثّق قراراتك

عاشرًا: خارطة طريق من الصفر إلى الإنتاج

المرحلةالمهامالمدة
1. التأسيستثبيت + Hello World + فهم MVC2-3 أيام
2. التنقلmenu.php + Views + Theming3-4 أيام
3. البياناتDAO + SQL + Data Listsأسبوع
4. الإدخالForms + Validation4-5 أيام
5. الأمانAuth + Users + Profiles3-4 أيام
6. التقدمPWA + Offline + Local Storageأسبوع
7. التوسعModules + Custom Modulesأسبوع
8. الإنتاجاختبار + تحسين + نشرأسبوع

الإجمالي: 6-8 أسابيع لمطوّر واحد لتطبيق كامل.


الحادي عشر: المصادر والتعلّم المستمر

داخل ZnetDK

  • /engine/ — اقرأ الكود المصدري (أفضل مرجع)
  • الوثائق الرسمية على ZnetDK.com
  • مستودع GitHub — الوحدات الجاهزة
  • أمثلة التطبيقات المرفقة

خارج ZnetDK

نصائح التعلّم

  1. تعلّم بالمشروع — ابنِ شيئًا حقيقيًا
  2. اقرأ الكود — أفضل من قراءة الكتب
  3. جرّب — لا تخف من الأخطاء
  4. شارك — اسأل في المجتمعات
  5. وثّق — اكتب ما تعلّمته

خلاصة نهائية موسّعة

ZnetDK 4 Mobile هو:

  • ✅ إطار عمل كامل — ليس مجرد مكتبة
  • ✅ تطبيق جاهز — تبدأ من نقطة متقدمة
  • ✅ معايير مفتوحة — لا قيود تقنية
  • ✅ آمن — طبقات حماية متعددة
  • ✅ قابل للتوسع — نظام وحدات مرن
  • ✅ مجاني — GPL بدون تكلفة
  • ✅ مستقر — 2014-2026

متى تختاره؟

  • تطبيق أعمال داخلي
  • فريق صغير / مطوّر واحد
  • تسليم سريع
  • ميزانية محدودة
  • تطبيق يعمل على الهاتف

متى تتجنّبه؟

  • تطبيق عام ضخم (ملايين المستخدمين)
  • حاجة لتقنيات غير PHP
  • تكامل معقد مع أنظمة أخرى
  • فريق يفضّل إطارًا آخر

هل تريد الآن:

  • مشروعًا كاملًا بخطوات تنفيذية (تطبيق إدارة مهام مثلًا)؟
  • شرحًا مرئيًا لتدفق البيانات؟
  • مقارنة تفصيلية مع Laravel بمثال كود؟
  • ترجمة وثيقة رسمية معيّنة؟
  • حل مشكلة محددة تواجهها؟

أخبرني بالاتجاه الذي تريده وسأعمّق فيه أكثر

Scroll to Top

🤖 موصى به لك بناءً على اهتماماتك