إنشاء وتشغيل مشروع Zend Framework (Laminas) في ServBay
نظرة عامة
يُعد Zend Framework (الذي أصبح الآن جزءاً من مشروع Laminas) إطار عمل PHP مفتوح المصدر وقوي، ويوفر مجموعة من المكونات عالية الجودة والموجهة للكائنات لبناء تطبيقات وخدمات الويب الحديثة. يشتهر الإطار بمرونته وتصميمه المعياري وأدائه العالي، ما يجعله خياراً مثالياً لإنشاء المواقع البسيطة أو التطبيقات المؤسسية المعقدة.
ServBay هو بيئة تطوير ويب محلية صُممت خصيصاً لـ macOS. تدمج ServBay PHP وعدة خوادم ويب (مثل Caddy وNginx)، وقواعد بيانات (مثل MySQL وPostgreSQL وMongoDB)، وخدمات التخزين المؤقت (مثل Redis وMemcached)، بالإضافة إلى أدوات تطوير أخرى. تتيح ServBay طريقة سهلة لإعداد وإدارة هذه الحزم البرمجية، مما يبسط عملية إعداد وتشغيل مشاريع PHP المختلفة محلياً.
سيرشدك هذا الدليل خطوة بخطوة لإنشاء وتشغيل مشروع Zend Framework (Laminas) داخل بيئة ServBay، وكذلك كيفية ربط واستخدام قواعد البيانات وخدمات التخزين المؤقت التي توفرها ServBay.
المتطلبات الأساسية
قبل البدء، تأكد من توفر ما يلي:
- تثبيت ServBay: يجب أن تكون قد ثبتَّ ServBay على نظام macOS ويعمل بشكل صحيح. إذا لم يتم التثبيت بعد، تفضل بزيارة الموقع الرسمي لـ ServBay للحصول على دليل التثبيت والتنزيل.
- حزم ServBay البرمجية: تحقق من تثبيت جميع الحزم المطلوبة عبر ServBay وتشغيلها، وهي:
- إصدار PHP واحد على الأقل (يفضل PHP 8.x أو أعلى، لأن نسخ Laminas الحديثة تتطلب إصدارات أحدث).
- خادم ويب (Caddy أو Nginx).
- Composer (يأتي غالباً مثبتاً مع ServBay).
- قواعد البيانات وخدمات التخزين المؤقت التي ستستخدمها (مثل MySQL، PostgreSQL، Memcached، Redis). يمكنك تفعيل هذه الخدمات بسهولة من لوحة تحكم ServBay.
إنشاء مشروع Zend Framework
توصي ServBay بوضع كل مشاريع المواقع ضمن المسار /Applications/ServBay/www لتسهيل الإدارة التلقائية للمواقع.
الدخول إلى مجلد جذر المواقع
افتح تطبيق الطرفية (Terminal) واذهب إلى مجلد المواقع المقترح:
bashcd /Applications/ServBay/www1إنشاء المشروع باستخدام Composer
يأتي Composer مثبتاً مع ServBay، فلا حاجة للتثبيت منفصلاً. لإنشاء مشروع جديد باستخدام الهيكل العظمي لـ Laminas، نفذ الأمر التالي لإنشاء المشروع في مجلد باسم
servbay-zend-app:bashcomposer create-project laminas/laminas-skeleton-application servbay-zend-app1سيقوم الأمر بتحميل الهيكل العظمي لمشروع Laminas مع جميع الاعتمادات اللازمة إلى مجلد
servbay-zend-app.الدخول إلى مجلد المشروع
اذهب إلى مجلد المشروع الذي أنشأته:
bashcd servbay-zend-app1
تهيئة خادم الويب
حتى تتمكن من الوصول إلى مشروع Zend Framework من المتصفح، عليك تهيئة موقع على ServBay.
- فتح لوحة تحكم ServBay: قم بتشغيل تطبيق ServBay.
- الدخول إلى إعدادات المواقع: توجه إلى تبويب المواقع (Websites) في لوحة تحكم ServBay.
- إضافة موقع جديد: اضغط على زر
+في الزاوية السفلية اليسرى لإضافة إعداد جديد لموقع. - أدخل معلومات الموقع:
- الاسم (Name): اختر اسماً يسهل تمييزه، مثل
My Zend Dev Site. - اسم النطاق (Domain): أدخل اسم النطاق الذي ستستخدمه في المتصفح. لتجنب التعارض مع النطاقات الفعلية، يفضل استخدام لاحقة
.localأو.testمثلservbay-zend-test.local. سيقوم ServBay تلقائياً بإعداد التوجيه المحلي. - نوع الموقع (Website Type): اختر
PHP. - إصدار PHP: اختر الإصدار المرغوب (مثال:
8.3). تأكد من تثبيته وتشغيله في ServBay. - المجلد الجذري للموقع (Document Root): يجب أن يكون مسار المجلد الجذري نحو مجلد
publicفي مشروعك:/Applications/ServBay/www/servbay-zend-app/public.
- الاسم (Name): اختر اسماً يسهل تمييزه، مثل
- الحفظ وإعادة التشغيل: اضغط على حفظ (Save). سيطلب منك ServBay تأكيد التطبيق، ثم يعيد خادم الويب تحميل الإعدادات لتفعيل الموقع الجديد.
للحصول على إرشادات مفصلة، راجع إضافة أول موقع في وثائق ServBay.
مثال أساسي "Hello ServBay!"
لنقم بتعديل الكود لعرض "Hello ServBay!" عند زيارة المسار الجذري (/).
تهيئة المسارات (routes) والمتحكم (Controller) في module.config.php
حرر ملف
module/Application/config/module.config.phpوأضف أو تأكد من وجود الإعدادات التالية:php<?php declare(strict_types=1); namespace Application; use Laminas\Router\Http\Literal; use Laminas\Router\Http\Segment; use Laminas\ServiceManager\Factory\InvokableFactory; return [ 'router' => [ 'routes' => [ 'home' => [ 'type' => Literal::class, 'options' => [ 'route' => '/', 'defaults' => [ 'controller' => Controller\IndexController::class, 'action' => 'index', ], ], ], // ... إعدادات مسارات أخرى ], ], 'controllers' => [ 'factories' => [ Controller\IndexController::class => InvokableFactory::class, ], ], 'view_manager' => [ 'display_not_found_reason' => true, 'display_exceptions' => true, 'doctype' => 'HTML5', 'not_found_template' => 'error/404', 'exception_template' => 'error/index', 'template_map' => [ 'layout/layout' => __DIR__ . '/../view/layout/layout.phtml', 'application/index/index' => __DIR__ . '/../view/application/index/index.phtml', 'error/404' => __DIR__ . '/../view/error/404.phtml', 'error/index' => __DIR__ . '/../view/error/index.phtml', ], 'template_path_stack' => [ __DIR__ . '/../view', ], ], // ... إعدادات أخرى ];1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49ملاحظة: عليك دمج هذه القطعة البرمجية مع مصفوفة الإعدادات الموجودة مسبقاً. تأكد من وجود المسار
'home'ومُعرّف مصنعController\IndexController::class.إنشاء أو تعديل المتحكم (IndexController.php)
حرر أو أنشئ ملف
module/Application/src/Controller/IndexController.phpوتأكد من وجود الدالةindexActionبالشكل التالي:php<?php declare(strict_types=1); namespace Application\Controller; use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\ViewModel; class IndexController extends AbstractActionController { /** * Default action to display the welcome page. */ public function indexAction() { // إرجاع ViewModel وتمرير متغير 'message' للعرض return new ViewModel([ 'message' => 'Hello ServBay!', ]); } // ... دوال أخرى }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24إنشاء أو تعديل ملف العرض (index.phtml)
حرر أو أنشئ
module/Application/view/application/index/index.phtmlلعرض المتغير القادم من المتحكم:php<h1><?php echo $this->message; ?></h1>1هنا نستخدم المساعد الخاص بالعرض
$this->messageضمن Laminas لعرض الرسالة.
زيارة الموقع
افتح متصفح الويب واذهب إلى اسم النطاق الذي أعددته مثل https://servbay-zend-test.local.
إذا سار كل شيء كما هو متوقع، ستشاهد رسالة Hello ServBay!، مما يعني أن كل شيء يعمل كما ينبغي.
أمثلة تكامل قواعد البيانات والتخزين المؤقت
تقدم ServBay العديد من قواعد البيانات وخدمات التخزين المؤقت. سنوضح أدناه ربط Laminas مع Memcached، Redis، MySQL، وPostgreSQL.
تنويه مهم: الأمثلة القادمة لكل قاعدة بيانات وخدمة تخزين مؤقت هي أمثلة توضيحية مستقلة. في التطبيق الفعلي، ستختار قاعدة بيانات واحدة (وربما أكثر من حل تخزين مؤقت) حسب الحاجة وتدير عملية الربط عبر الاعتماد على مبدأ الحقن (Dependency Injection) وغيره. تأكد من تفعيل خدمات قواعد البيانات والتخزين المؤقت المطلوبة من ServBay قبل تجربتها.
مثال تفاعل مع قاعدة البيانات - إنشاء جدول
سنبدأ بتوضيح كيفية التفاعل مع قاعدة البيانات باستخدام Laminas DB، بما في ذلك إنشاء جدول بسيط.
تثبيت Laminas DB
نفذ الأمر التالي داخل مجلد مشروعك لتثبيت المكون:
bashcomposer require laminas/laminas-db1إنشاء قاعدة بيانات يدوياً
قبل تجربة الكود، أنشئ قاعدة بيانات باسم
servbay_zend_appفي ServBay عبر أداة الإدارة المناسبة (phpMyAdmin, pgAdmin، إلخ). اسم المستخدم الافتراضي لقاعدة بيانات MySQL/MariaDB في ServBay هوrootوكلمة المرورpassword، أما PostgreSQL فاسم المستخدم والكلمة الافتراضيان أيضاً هماrootوpassword.كتابة سكربت إنشاء الجدول (مثال)
أنشئ ملف PHP (مثال:
create_users_table.php) وضع فيه الكود التالي:php<?php // create_users_table.php use Laminas\Db\Adapter\Adapter; use Laminas\Db\Sql\Sql; // نعمل هنا مع MySQL أو MariaDB $adapter = new Adapter([ 'driver' => 'Pdo_Mysql', // أو 'Pdo_Pgsql' 'database' => 'servbay_zend_app', 'username' => 'root', 'password' => 'password', // كلمة المرور الافتراضية 'hostname' => '127.0.0.1', // 'port' => 3306, // المنفذ الافتراضي لـ MySQL // 'port' => 5432, // المنفذ الافتراضي لـ PostgreSQL ]); $sql = new Sql($adapter); // تعريف كود إنشاء جدول المستخدمين $create = $sql->createTable('users') ->addColumn(new \Laminas\Db\Sql\Ddl\Column\Integer('id', false, null, ['AUTO_INCREMENT' => true])) ->addColumn(new \Laminas\Db\Sql\Ddl\Column\Varchar('name', 255)) ->addColumn(new \Laminas\Db\Sql\Ddl\Column\Varchar('email', 255, ['UNIQUE' => true])) ->addConstraint(new \Laminas\Db\Sql\Ddl\Constraint\PrimaryKey('id')); echo "Executing SQL:\n"; echo $sql->buildSqlString($create, $adapter->getPlatform()) . "\n"; try { // تنفيذ الأمر SQL $adapter->query( $sql->buildSqlString($create, $adapter->getPlatform()), Adapter::QUERY_MODE_EXECUTE ); echo "Table 'users' created successfully.\n"; } catch (\Exception $e) { echo "Error creating table: " . $e->getMessage() . "\n"; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38ملاحظة: هذا مجرد سكربت يدوي، في العادة تدار الهجرات عبر أدوات إدارة قواعد البيانات في Laminas.
نفذ السكربت باستخدام CLI:
bashphp create_users_table.php1
مثال تكامل MySQL
يشرح المثال كيفية ربط المتحكم بـ MySQL وقراءة البيانات منه.
ضبط إعدادات الاتصال بقاعدة البيانات
قم بتحرير الملف
config/autoload/global.phpوضبط الإعدادات:php<?php // config/autoload/global.php return [ 'db' => [ 'driver' => 'Pdo_Mysql', 'database' => 'servbay_zend_app', 'username' => 'root', 'password' => 'password', 'hostname' => '127.0.0.1', 'port' => 3306, 'charset' => 'utf8mb4', ], // ... إعدادات أخرى ];1
2
3
4
5
6
7
8
9
10
11
12
13
14إعداد مصنع المتحكم (module.config.php)
لتتمكن من حقن محول قاعدة البيانات في المتحكم، عدل جزء
controllersكما يلي:php<?php // module/Application/config/module.config.php namespace Application; use Laminas\ServiceManager\Factory\InvokableFactory; use Laminas\Db\Adapter\AdapterInterface; return [ // ... 'controllers' => [ 'factories' => [ Controller\IndexController::class => function($container) { $adapter = $container->get(AdapterInterface::class); return new Controller\IndexController($adapter); }, ], ], 'service_manager' => [ 'aliases' => [ AdapterInterface::class => 'Laminas\Db\Adapter\Adapter', ], 'factories' => [ 'Laminas\Db\Adapter\Adapter' => \Laminas\Db\Adapter\AdapterServiceFactory::class, ], ], // ... ];1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27ملاحظة: هذه الشفرة تدمج يدويا مع التهيئة الموجودة لديك.
ضبط المسارات (module.config.php)
أضف المسارات الخاصة بالعمليات في
routes:php<?php // module/Application/config/module.config.php namespace Application; use Laminas\Router\Http\Literal; return [ 'router' => [ 'routes' => [ // ... 'mysql-add' => [ 'type' => Literal::class, 'options' => [ 'route' => '/mysql-add', 'defaults' => [ 'controller' => Controller\IndexController::class, 'action' => 'mysqlAdd', ], ], ], 'mysql' => [ 'type' => Literal::class, 'options' => [ 'route' => '/mysql', 'defaults' => [ 'controller' => Controller\IndexController::class, 'action' => 'mysql', ], ], ], ], ], // ... ];1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34إضافة دوال المتحكم (IndexController.php)
أضف التكوين والدوال إلى المتحكم:
php<?php declare(strict_types=1); namespace Application\Controller; use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\ViewModel; use Laminas\Db\Adapter\AdapterInterface; use Laminas\Db\Sql\Sql; class IndexController extends AbstractActionController { private $adapter; public function __construct(AdapterInterface $adapter) { $this->adapter = $adapter; } public function indexAction() { return new ViewModel([ 'message' => 'Hello ServBay!', ]); } public function mysqlAddAction() { $sql = new Sql($this->adapter); $insert = $sql->insert('users') ->values([ 'name' => 'ServBay Demo User', 'email' => 'demo-mysql@servbay.test', ]); $statement = $sql->prepareStatementForSqlObject($insert); $result = $statement->execute(); $message = $result->getAffectedRows() > 0 ? 'MySQL User added successfully.' : 'Failed to add MySQL user.'; return new ViewModel([ 'message' => $message, ]); } public function mysqlAction() { $sql = new Sql($this->adapter); $select = $sql->select('users'); $statement = $sql->prepareStatementForSqlObject($select); $result = $statement->execute(); $users = []; foreach ($result as $row) { $users[] = $row; } return new ViewModel([ 'users' => json_encode($users, JSON_PRETTY_PRINT), ]); } // ... دوال أخرى }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65إنشاء ملفات العرض
أنشئ الملف
module/Application/view/application/index/mysql-add.phtml:php<h1><?php echo $this->message; ?></h1>1وأنشئ الملف
module/Application/view/application/index/mysql.phtml:php<h1>MySQL Users</h1> <pre><?php echo $this->users; ?></pre>1
2تجربة التكامل
تأكد من تشغيل MySQL من خلال ServBay.
- زر
https://servbay-zend-test.local/mysql-addلإضافة مستخدم (ستظهر رسالة نجاح). - زر
https://servbay-zend-test.local/mysqlلعرض المستخدمين بصيغة JSON.
- زر
مثال تكامل PostgreSQL
تماما كما في تكامل MySQL، لكن مع تهيئة الاتصال لـ PostgreSQL:
تعديل تهيئة الاتصال
في الملف
config/autoload/global.phpضع الآتي:php<?php return [ 'db' => [ 'driver' => 'Pdo_Pgsql', 'database' => 'servbay_zend_app', 'username' => 'root', 'password' => 'password', 'hostname' => '127.0.0.1', 'port' => 5432, ], // ... ];1
2
3
4
5
6
7
8
9
10
11
12مصنع المتحكم وتثبيتاته
إذا اتبعت تعليمات MySQL سابقاً، لن تحتاج لأي تعديل إضافي هنا.إضافة المسارات
داخل مصفوفة المسارات:
php'pgsql-add' => [ 'type' => Literal::class, 'options' => [ 'route' => '/pgsql-add', 'defaults' => [ 'controller' => Controller\IndexController::class, 'action' => 'pgsqlAdd', ], ], ], 'pgsql' => [ 'type' => Literal::class, 'options' => [ 'route' => '/pgsql', 'defaults' => [ 'controller' => Controller\IndexController::class, 'action' => 'pgsql', ], ], ],1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20إضافة دوال المتحكم
أضف في المتحكم:
phppublic function pgsqlAddAction() { $sql = new Sql($this->adapter); $insert = $sql->insert('users') ->values([ 'name' => 'ServBay Demo User', 'email' => 'demo-pgsql@servbay.test', ]); $statement = $sql->prepareStatementForSqlObject($insert); $result = $statement->execute(); $message = $result->getAffectedRows() > 0 ? 'PostgreSQL User added successfully.' : 'Failed to add PostgreSQL user.'; return new ViewModel([ 'message' => $message, ]); } public function pgsqlAction() { $sql = new Sql($this->adapter); $select = $sql->select('users'); $statement = $sql->prepareStatementForSqlObject($select); $result = $statement->execute(); $users = []; foreach ($result as $row) { $users[] = $row; } return new ViewModel([ 'users' => json_encode($users, JSON_PRETTY_PRINT), ]); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35إنشاء ملفات العرض
ملف
pgsql-add.phtml:php<h1><?php echo $this->message; ?></h1>1و
pgsql.phtml:php<h1>PostgreSQL Users</h1> <pre><?php echo $this->users; ?></pre>1
2تجربة التكامل
- تأكد من تشغيل PostgreSQL في ServBay.
- ادخل
https://servbay-zend-test.local/pgsql-addلإضافة مستخدم. - ادخل
https://servbay-zend-test.local/pgsqlلعرض جميع المستخدمين.
مثال تكامل Memcached
يُوضح هذا القسم كيفية استخدام Memcached مع Laminas.
تثبيت محول Memcached
أضف إلى
composer.json:json{ "require": { "laminas/laminas-skeleton-application": "^1.0", "laminas/laminas-cache-storage-adapter-memcached": "^2.0" } }1
2
3
4
5
6ثم نفذ:
bashcomposer update1ملحق
memcachedمفعّل في ServBay بشكل افتراضي.إضافة المسار
أضف الآتي في مصفوفة المسارات:
php'memcached' => [ 'type' => Literal::class, 'options' => [ 'route' => '/memcached', 'defaults' => [ 'controller' => Controller\IndexController::class, 'action' => 'memcached', ], ], ],1
2
3
4
5
6
7
8
9
10دالة المتحكم
أضف إلى المتحكم:
phpuse Laminas\Cache\StorageFactory; use Laminas\Cache\Storage\StorageInterface; public function memcachedAction() { // إعداد خدمة Memcached $cache = StorageFactory::factory([ 'adapter' => [ 'name' => 'memcached', 'options' => [ 'servers' => [ ['127.0.0.1', 11211], ], 'ttl' => 300, ], ], 'plugins' => [ 'serializer', 'exception_handler' => ['throw_exceptions' => false], ], ]); $cacheKey = 'my_memcached_data'; $cachedData = $cache->getItem($cacheKey, $success); if (!$success) { $cachedData = 'Hello Memcached! (Data from source, cached at ' . date('Y-m-d H:i:s') . ')'; $cache->setItem($cacheKey, $cachedData); $cachedData .= ' - CACHE MISS'; } else { $cachedData .= ' - CACHE HIT'; } return new ViewModel([ 'message' => $cachedData, ]); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37ملف العرض
أنشئ
memcached.phtml:php<h1>Memcached Example</h1> <p><?php echo $this->message; ?></p>1
2التجربة
تأكد من تشغيل خدمة Memcached.
- ادخل إلى
https://servbay-zend-test.local/memcached. - أول زيارة تظهر "CACHE MISS"، ثم في غضون 300 ثانية ستظهر "CACHE HIT".
- ادخل إلى
مثال تكامل Redis
سنوضح كيف يُستخدم Redis للتخزين المؤقت أو تخزين البيانات.
تثبيت محول Redis
أضف إلى
composer.json:json{ "require": { "laminas/laminas-skeleton-application": "^1.0", "laminas/laminas-cache-storage-adapter-redis": "^2.0", "ext-redis": "*" } }1
2
3
4
5
6
7ثم:
bashcomposer update1المسار
في تعريف المسارات:
php'redis' => [ 'type' => Literal::class, 'options' => [ 'route' => '/redis', 'defaults' => [ 'controller' => Controller\IndexController::class, 'action' => 'redis', ], ], ],1
2
3
4
5
6
7
8
9
10دالة المتحكم
أضف إلى المتحكم:
phpuse Laminas\Cache\StorageFactory; use Laminas\Cache\Storage\StorageInterface; public function redisAction() { $cache = StorageFactory::factory([ 'adapter' => [ 'name' => 'redis', 'options' => [ 'server' => [ 'host' => '127.0.0.1', 'port' => 6379, // 'database' => 0, // 'password' => null, ], 'ttl' => 300, ], ], 'plugins' => [ 'serializer', 'exception_handler' => ['throw_exceptions' => false], ], ]); $cacheKey = 'my_redis_data'; $cachedData = $cache->getItem($cacheKey, $success); if (!$success) { $cachedData = 'Hello Redis! (Data from source, cached at ' . date('Y-m-d H:i:s') . ')'; $cache->setItem($cacheKey, $cachedData); $cachedData .= ' - CACHE MISS'; } else { $cachedData .= ' - CACHE HIT'; } return new ViewModel([ 'message' => $cachedData, ]); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39ملف العرض
أنشئ ملف
redis.phtml:php<h1>Redis Example</h1> <p><?php echo $this->message; ?></p>1
2التجربة
تأكد من تشغيل خدمة Redis.
- ادخل إلى
https://servbay-zend-test.local/redis. - أول زيارة تظهر "CACHE MISS"، وزيارة ثانية خلال 300 ثانية تظهر "CACHE HIT".
- ادخل إلى
الخلاصة
من خلال هذه الخطوات، أتممت إعداد وتشغيل مشروع Zend Framework (Laminas) على بيئة ServBay المحلية، وعرفت كيف تضبط المواقع وتوجه خادم الويب نحو المجلد المناسب، وكذلك دمج واستخدام قواعد البيانات MySQL وPostgreSQL، وأنظمة التخزين المؤقت Memcached وRedis.
تسهل ServBay عملية الإعداد والإدارة لبيئة التطوير المحلية، ما يسمح لك بالتركيز على تطوير البرمجيات دون عناء التهيئة، مع إمكانية محاكاة بيئة الإنتاج محلياً لزيادة إنتاجيتك وكفاءتك كمطور.
