اذهب إلى المحتوى

كل الأنشطة

تحدث تلقائيًا

  1. الساعة الماضية
  2. لحل المشكلة يمكنك تعطيل الكود في ملف 'AuthServiceProvider.php' مؤقتا حتى يتم إنشاؤه بنجاح أثناء عملية الترحيل والبذر لأنه يستخدم جدول 'permissions' كما هو موضح هنا: /* Permission::whereIn('name', ['edit-post', 'delete-post', 'add-post'])->get()->map(function($per) { Gate::define($per->name, function($user, $post) use ($per) { return $user->hasAllow($per->name) && ($user->id == $post->user_id || $user->isAdmin()); }); }); Permission::whereIn('name', ['edit-user', 'delete-user', 'add-user'])->get()->map(function($per) { Gate::define($per->name, function($user) use ($per) { return $user->hasAllow($per->name) && $user->isAdmin(); }); }); */ ثم قم بتشغيل الأمر التالي لإنشاء مفتاح التطبيق: php artisan key:generate ثم تأكد من إدخالك لمعلومات الاتصال الصحيحة في ملف `.env` مثلما هو موضح هنا: DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=اسم_قاعدة_بياناتك DB_USERNAME=اسم_مستخدم_قاعدة_بياناتك DB_PASSWORD=كلمة_مرور_قاعدة_بياناتك والآن يمكنك القيام بتشغيل الأمر التالي لترحيل الجداول وتعبئتها بالبيانات الأولية: php artisan migrate:fresh --seed وبعد الانتهاء من الترحيل والبذر بنجاح، افتح ملف `AuthServiceProvider.php` مرة أخرى وقم بإزالة التعليق عن الأسطر التي علقتها سابقا. Permission::whereIn('name', ['edit-post', 'delete-post', 'add-post'])->get()->map(function($per) { Gate::define($per->name, function($user, $post) use ($per) { return $user->hasAllow($per->name) && ($user->id == $post->user_id || $user->isAdmin()); }); }); Permission::whereIn('name', ['edit-user', 'delete-user', 'add-user'])->get()->map(function($per) { Gate::define($per->name, function($user) use ($per) { return $user->hasAllow($per->name) && $user->isAdmin(); }); }); وشغل المشروع مباشرة: php artisan serve ففي هذه العملية، قمنا بتعليق الكود مؤقتا لتجنب حدوث خطأ عند تشغيل الأوامر المتعلقة بتهيئة قاعدة البيانات. فالكود الذي يتم تعليقه يستخدم جدول permissions، وهو جدول لم يتم إنشاؤه بعد. تعليق الكود مؤقتا هنا سيسمح لك بإتمام عملية الترحيل والبذر بنجاح وبمجرد أن يتم إنشاء الجدول من خلال عملية الترحيل والبذر، يمكن إزالة التعليق وإعادة تفعيل الكود لتشغيله بشكل طبيعي.
  3. اليوم
  4. دليل شامل لـ انشاء متجر على شوبيفاي من الصفر 2024 هل تمتلك فكرة متجر إلكتروني ولديك منتجات ترغب في بيعها من خلاله؟ فإن انشاء متجر على شوبيفاي سيسهل عليك كثيراً ويمكنك من امتلاك متجر إلكتروني ناجح. وتُعَد التجارة الإلكترونية في الوقت الحالي واحدة من أكثر أنواع المشاريع نجاحاً، خاصة بعدما أصبح إنشاء المتاجر الإلكترونية أكثر سهولة وأقل تكلفة مقارنة بإنشاء متجر فعلي على أرض الواقع. كما أن نسبة المتسوقين عبر الإنترنت في تزايد مستمر، حيث بات الكثير من الأفراد يعتمدون على الإنترنت في البحث عن المنتجات التي يرغبون بها وشرائها أونلاين. وفي هذه المقالة سوف نتناول بالتفصيل كيفية انشاء متجر على شوبيفاي، وكذلك التكلفة التي قد تتحملها للحصول على متجر. ما هو موقع شوبيفاي Shopify؟ وكيف يعمل؟ إن موقع شوبيفاي Shopify هو أحد المنصات الإلكترونية التي تتيح للمستخدمين من خلالها إمكانية انشاء وتصميم متجر إلكتروني بسهولة. ويتيح لك موقع شوبيفاي كافة الأدوات التي قد تحتاجها حتى تتمكن من تأسيس متجرك الإلكتروني بنفسك دون الحاجة لامتلاك خبرة حول البرمجة. فكل ما ستحتاجه حتى تتمكن من انشاء متجر على شوبيفاي هو تحديد فكرة المنتجات التي ستعرضها ثم اختيار الخطة التي تناسبك على الموقع وبدء تصميم متجرك الخاص من خلاله بسهولة. ومن ثَم تتمكن من الدخول في عالم التجارة الإلكترونية الذي أصبح في الفترة الأخيرة من أكثر أشكال الأعمال ربحية على الإنترنت. أما بالنسبة إلى آلية عمل الموقع، فشوبيفاي يوفر لك مختلف الأدوات التي تحتاجها لتصميم متجرك الإلكتروني بنفسك بدون الحاجة لاستخدام أكواد برمجية. كما أنه يوفر لك بعض الخصائص المجانية للاستخدام لانشاء المتجر، والبعض الآخر تكون أكثر تقدماً ولكنها مدفوعة أي تحتاج لشرائها لإضافتها واستخدامها في متجرك. ويوفر لك متجر شوبيفاي أيضاً الكثير من خيارات الشحن المحلية والدولية وكذلك بوابات دفع مدمجة من خلاله كما يمكنك أن تقوم بربط متجرك بشركات شحن خارجية أو بوابات دفع خارجية أيضاً. اكمل الليل من هنا
  5. المشروع يستعمل في ملف AuthServiceProvider.php الجدول permissions لإنشاء بعض البوابات (Gates) لتعريف التصريح لبعض العمليات ('edit-post', 'delete-post', 'add-post') لذلك ستحتاج مبدئياً إلى فتح الملف و تعطيل الأسطر: Permission::whereIn('name', ['edit-post', 'delete-post', 'add-post'])->get()->map(function($per) { Gate::define($per->name, function($user, $post) use ($per) { return $user->hasAllow($per->name) && ($user->id == $post->user_id || $user->isAdmin()); }); }); Permission::whereIn('name', ['edit-user', 'delete-user', 'add-user'])->get()->map(function($per) { Gate::define($per->name, function($user) use ($per) { return $user->hasAllow($per->name) && $user->isAdmin(); }); }); أي تعليقها: /* Permission::whereIn('name', ['edit-post', 'delete-post', 'add-post'])->get()->map(function($per) { Gate::define($per->name, function($user, $post) use ($per) { return $user->hasAllow($per->name) && ($user->id == $post->user_id || $user->isAdmin()); }); }); Permission::whereIn('name', ['edit-user', 'delete-user', 'add-user'])->get()->map(function($per) { Gate::define($per->name, function($user) use ($per) { return $user->hasAllow($per->name) && $user->isAdmin(); }); }); */ ثم إكمال عملية تهيئة المشروع: php artisan key:generate إنشاء قاعدة بيانات ثم ربط معلومات الإتصال بها في الملف env. بعد ذلك تنفيذ أمر التهجير و البذر: php artisan migrate:fresh --seed يمكنك بعد ذلك الرجوع إلى الملف السابق وإزالة التعليق من الأسطر السابقة. و أخيراً تشغيل المشروع
  6. عند عمل clone لمشروع لارافيل نظام إدارة المحتوى https://github.com/HsoubAcademy/laravel_cms.git ومن ثم عمل composer update يظهر هذا الخطأ Generating optimized autoload files > Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Illuminate\Database\QueryException SQLSTATE[HY000] [1045] Access denied for user 'forge'@'localhost' (using passw ord: NO) (SQL: select * from `permissions` where `name` in (edit-post, delete-po st, add-post)) at C:\Users\Mohsen\ALaravel\laravel_cms\vendor\laravel\framework\src\Illuminat e\Database\Connection.php:760 756▕ // If an exception occurs when attempting to run a query, we'll format the error 757▕ // message to include the bindings with SQL, which will make th is exception a 758▕ // lot more helpful to the developer instead of just the databa se's errors. 759▕ catch (Exception $e) { ➜ 760▕ throw new QueryException( 761▕ $query, $this->prepareBindings($bindings), $e 762▕ ); 763▕ } 764▕ } 1 [internal]:0 Illuminate\Foundation\Application::Illuminate\Foundation\{closure}(Object( App\Providers\AuthServiceProvider)) 2 C:\Users\Mohsen\ALaravel\laravel_cms\vendor\laravel\framework\src\Illumina te\Database\Connectors\Connector.php:70 PDOException::("SQLSTATE[HY000] [1045] Access denied for user 'forge'@'loc alhost' (using password: NO)") Script @php artisan package:discover --ansi handling the post-autoload-dump even t returned with error code 1 وعند عمل الامر php artisan migrate --seed يظهر هذا الخطأ
  7. للتأسيس الجيد في البرمجة والفهم العميق للمفاهيم الأساسية، يفضل البدء بإحدى اللغات التالية: 1. بايثون (Python): سهلة التعلم والقراءة، تستخدم كثيرا في التعليم والبحث، لديها مكتبات قوية وشاملة، تدعم مجموعة واسعة من التطبيقات من تطوير الويب إلى الذكاء الاصطناعي. تساعد في فهم المفاهيم الأساسية للبرمجة مثل المتغيرات، الحلقات، الشروط، والدوال بطريقة بسيطة وواضحة. 2. جافا (Java): لغة قوية ومنظمة، تستخدم بشكل واسع في التطبيقات التجارية وتطوير التطبيقات الكبيرة، تعتمد على البرمجة الشيئية بشكل كامل. تتيح لك فهم المفاهيم الأساسية للبرمجة الشيئية، إدارة الذاكرة، والتعامل مع البيانات بطرق منظمة. 3. C++: توفر أداء عاليا وتتحكم بشكل دقيق في موارد النظام. تدعم البرمجة الشيئية، مما يسمح بإنشاء برامج معقدة بطريقة منظمة. تتضمن ميزات لغة C، مما يسمح بفهم عميقا لهيكلية الكمبيوتر وإدارة الذاكرة، والتي تعتبر مهارات أساسية لأي مبرمج. مقارنة بين اللغات التي ذكرتها: التوصية: إذا كنت مبتدئاً تماماً وترغب في تعلم أساسيات البرمجة بسهولة، فإن Python هي الخيار المثالي. إذا كنت مهتماً بتطوير الويب وترغب في رؤية نتائج ملموسة بسرعة، ابدأ بـ JavaScript(لا انصح الا للنتائج السريعه). إذا كنت ترغب في فهم عميق لمفاهيم البرمجة الأساسية وتطبيقات الأداء العالي، فإن ++C ستكون مفيدة. نصيحتي الاخيره لك هي ان تتعلم اساسيات البرمجه بلغه Python لكي تفهم المفاهيم العامه ثم الانتقال الي لغه ++C لكي تتعمق بها اكثر وتدرس البرمجه كائنيه التوجه وان تتعلم حل المشكلات بهذه اللغه شئ عظيم اذا حققت هذا سيمكنك الانتقال بسهوله بين كل مجالات البرمجه في وقت قصير وبسهوله عكس عندما تدرس اول لغه لك JS ستجد ان كل شئ سهل وستصل لنتائج بسرعه ولكن لا يمكنك الانتقال لمجال اخر غير بصعوبه شديده.
  8. السلام عليكم اريد اضافة قائمة منسدلة بمعنى حقل به قيم يجب ان يختار منها المستخدم ولا يستطيع كتابة اي قيم اخري فى html ثانيا اريد الحصول غلي القيمة التى اختارها المستخدم من القائمة المنسدلة باستخدام الجانغو وتخزنها في قاعدة بيانات شكرا لكم
  9. نعم، يمكنك استخدام منصات مثل Glide أو AppSheet أو Power Apps لتحويل ملف بصيغة Excel يحتوي على معادلات إلى تطبيق موبايل يعمل على نظام أندرويد. لتحويل ملفك باستخدام Glide، قم بتحويل ملف Excel إلى Google Sheets برفعه على Google Drive، ثم الذهاب إلى موقع Glide وإنشاء مشروع جديد بربطه بملف Google Sheets. لاستخدام Glide يمكنك التوجه للموقع موقع GLIDE والتسجيل باستخدام حساب Google ثم: الضغط على "New Project" لإنشاء مشروع جديد. اختيار "Google Sheets" كمصدر البيانات، ثم اختيار جدول البيانات الذي قمت بإنشائه سابقا. بعد ربط جدول البيانات، سيتم توجيهك إلى واجهة تصميم التطبيق في Glide. يمكنك تخصيص شاشات التطبيق، الأزرار، النماذج، والعناصر الأخرى حسب احتياجاتك. والتأكد من أن جميع البيانات والمعادلات تظهر وتعمل بشكل صحيح داخل التطبيق. بعد الانتهاء من تخصيص التطبيق، اختبره جيدا للتأكد من أن كل شيء يعمل كما هو متوقع فإذا كان كل شيء صحيحا، اضغط على "Publish" لنشر التطبيق وستتمكن من مشاركة رابط التطبيق أو رمز الاستجابة السريعة (QR code) للوصول إليه على جهاز أندرويد.
  10. دورة علوم الحاسوب لا تستهدف مجالًا محددًا في البرمجة، إلا أنها تقدم أساسًا قويًا في علوم الحاسوب والبرمجة باستخدام Python و JavaScript. وبعد دراسة مفاهيم الدورة، سيكون لديك فهم أفضل للبرمجة وأساليب تطوير البرامج وقواعد البيانات والويب وغيرها من المواضيع الأساسية في مجال علوم الحاسوب وذلك مايميز المبرمج المحترف عن الهاوي. ومع ذلك بعد الإنتهاء من هذه الدورة ربما يكون الأمر صعباً نسبياً نظراً لعدم التخصص في مجال محدد وهذا ما تقوم به الأكاديمية بعد الإنتهاء من الدورة بتوجيهك لمصادر لتعلم مجال يناسبك للتخصص فيه والعمل به ويفضل الإطلاع على الإجابات التالية حيث تجيب على هذا السؤال بشكل مفصل
  11. هناك اكثر من طريقة تستطيع عمل ذلك بها اولا يمكنك الدخول الى موقع Microsoft الرسمي حيث ان هناك توثيق لعمل ذلك بالتفصيل خطوة بخطوة https://learn.microsoft.com/ar-sa/power-apps/maker/canvas-apps/get-started-create-from-data كما ان هناك العديد من المواقع الاخرى يمكنها تحقيق ذلك ساترك لك روابطها يمكنك اختيار ما يناسبك appsheet Convertigo glide kodular appgyver وبالتوفيق ان شاء الله
  12. سمعت ان هناك مواقع للذكاء الصناعي يقوم بتحويل شيت الاكسل الى تطبيق موبايل وانا لدي ملف يحتوي على معادلات لاستخراج بعض القيم فهل يمكن احد يساعدني في تحويل هذا الملف بحيث يمكن تشغيله على الهاتف الذكي اندرويد
  13. انا لو بدأت بدورة علوم الحاسوب مع العلم عدم وجود خبره سابقه ، هل هقدر ادخل سوق العمل وأعرف اشتغل بعد الأنتهاء منها ، ولا لازم أخد دورة تاني ؟
  14. أولاً، عليك بتنظيم هيكل المجلدات في مشروعك إنشاء مجلد templates في جذر المشروع، وداخله مجلدات للقوالب المختلفة. /your-project /templates /template1 header.tpl footer.tpl index.tpl /template2 header.tpl footer.tpl index.tpl /admin edit_template.php index.php template_engine.php ثم كتابة كود بسيط لتحميل القوالب في ملف وليكن باسم TemplateEngine.php وسيكون مسؤول عن ترجمة ملفات .tpl واستبدال الـ placeholders ببيانات فعلية. class TemplateEngine { private $templateDir; public function __construct($templateDir) { $this->templateDir = $templateDir; } public function render($templateName, $data) { $templateFile = $this->templateDir . '/' . $templateName . '.tpl'; if (!file_exists($templateFile)) { throw new Exception("Template file not found: $templateFile"); } $templateContent = file_get_contents($templateFile); foreach ($data as $key => $value) { $templateContent = str_replace('{' . $key . '}', $value, $templateContent); } return $templateContent; } } ثم إنشاء مدير للقوالب TemplateManager ليكون مسؤول عن إنشاء وتعديل وحذف القوالب: class TemplateManager { private $templateDir; private $templateEngine; public function __construct($templateDir) { $this->templateDir = $templateDir; $this->templateEngine = new TemplateEngine($templateDir); } public function getTemplates() { $templates = scandir($this->templateDir); $templates = array_filter($templates, function ($file) { return substr($file, -4) === '.tpl'; }); return $templates; } public function getTemplateContent($templateName) { $templateFile = $this->templateDir . '/' . $templateName . '.tpl'; return file_get_contents($templateFile); } public function saveTemplateContent($templateName, $content) { $templateFile = $this->templateDir . '/' . $templateName . '.tpl'; file_put_contents($templateFile, $content); } public function renderTemplate($templateName, $data) { return $this->templateEngine->render($templateName, $data); } } بعد ذلك تستطيع استخدام TemplateManager في إدارة القوالب، قم بإنشاء نسخة من الكلاس لاستخدام الميثودز الخاصة به. مثلاً تستطيع إنشاء صفحة في لوحة التحكم تقوم بها بعرض جميع القوالب، ثم السماح للمديرين بتعديل القالب بالضغط عليه وعند الإنتهاء من التعديل تستخدم ميثود saveTemplateContent لحفظ التعديلات والقالب، وهكذا.
  15. نكمل في هذا المقال ما بدأناه في مقالنا السابق الذي يتحدث عن بناء واجهة برمجية REST، وكنا قد خصصناه لشرح النقاط التالية: استخدام npm في إنشاء واجهة خلفية من الصفر. تحضير الاعتماديات اللازمة مثل TypeScript. استخدام الوحدة البرمجية debug المضمنة في بيئة Node.js. بناء هيكلية مشروع Express.js وتسجيل اﻷحداث التي تقع أثناء تشغيل التطبيق باستخدام Winston. إن كنت تشعر أن المفاهيم التي تحدثنا عنها واضحة بالنسبة إليك، انسخ رابط المشروع، وانتقل إلى الفرع toptal-article-01 ثم تابع القراءة خدمات الواجهة البرمجية ريست والبرمجيات الوسيطة والمتحكمات ونماذج البيانات سنفصّل في مقالنا هذا النقاط التالية: الخدمات Services: التي تجعل الشيفرة أكثر وضوحًا بترتيب العمليات المنطقية ضمن دوال يمكن للبرمجيات الوسيطة والمتحكمات استدعاءها. البرمجيات الوسيطة Middleware: التي تقيِّم حالة المتطلبات prerequisites قبل أن تستدعي Express.js دالة المتحكم المناسب. المتحكمات Controllers: تستخدم الخدمات لمعالجة الطلبات قبل إرسال نتيجة الطلب إلى العميل. نماذج البيانات Models: تصف بياناتنا وتساعد في عمليات التحقق التي نجريها أثناء تصريف التطبيق. سنضيف أيضًا قاعدة بيانات بسيطة ولا تصلح بالطبع لنسخة اﻹنتاج، فغايتها الوحيدة تسهيل فهم تطبيقنا ومتابعته، وتمهّد للمقال التالي الذي يتحدث عن الاتصال بقاعدة البيانات، والتكامل مع القاعدة MongoDB والمكتبة Mongoose. الخطوات اﻷولى للعمل مع DAOs و DTOs وقاعدة بياناتنا المؤقتة لن تستخدم قاعدة البيانات في هذه المرحلة ملفات لتخزين البيانات، بل ستحتفظ ببيانات المستخدمين ضمن مصفوفة، أي أن البيانات ستزول بمجرد إغلاق Node.js. ستدعم قاعدة البيانات العمليات اﻷساسية CRUD ( إنشاء Create، قراءة Read، تحديث Update، حذف Delete). ونشير إلى مفهومين نستخدمهما هنا وهما: كائنات الوصول إلى البيانات Data access Objects واختصارًا DAOs. كائنات نقل البيانات Data transfer Objects واختصارًا DTOs. يُستخدم DAO للاتصال بقاعدة بيانات محددة وتنفيذ عمليات CRUD عليها، بينما يحتوي DTO البيانات الخام التي يرسلها DAO أو التي يستقبلها من قاعدة البيانات. وبعبارة أخرى، تتوافق كائنات DTO مع أنواع نماذج البيانات، بينما تُعد كائنات DAO خدمات تستخدم كائنات DTO. وهكذا قد تكون كائنات DTO أكثر تعقيدًا وفقًا لمستوى تداخل البيانات في بنية قاعدة البيانات، بينما تكون نسخة واحدة من كائن DAO مسؤولة عن فعل محدد على سطر واحد فقط من قاعدة البيانات. لماذا تُستخدم كائنات DTO يساعد استخدام كائنات DTO كي تتوافق كائنات TypeScript مع نماذج البيانات الخاصة بنا في الحفاظ على تناسق قاعدة البيانات كما سنرى لاحقًا. لكن هنالك نقص واضح، فلا يمكن أن تقدم كائنات DTO ولا حتى TypeScript نفسها أي نوع من التحقق التلقائي مما يُدخله المستخدم، لأن ذلك يحدث أثناء تنفيذ التطبيق. فعندما يصل أحد المدخلات إلى نقطة وصول الواجهة البرمجية، فقد يكون لهذا المُدخل: حقول زائدة. حقول مطلوبة مفقودة (كتلك التي لا تبدأ بالمحرف ?). نوع بيانات الحقل لا تطابق نوع البيانات المحدد في نموذج البيانات الذي يعتمد على TypeScript. فلن تتحقق TypeScript (أو جافا سكريبت التي ستُنقل الشيفرة إليها) من تلك المدخلات، لهذا من المهم ألا ننسى عمليات التحقق، وخاصة عندما تكون الواجهة البرمجية متاحة للعموم. قد تساعدك في ذلك حزم مثل ajv، لكنها تعمل عادة بتعريف كائنات لها تخطيط مخصص لمكتبة محددة بدلًا من كائنات TypeScript الأصلية (ستلعب مكتبة Mongoose هذا الدور كما سنلاحظ في مقال تالٍ). وقد يخطر في بالك السؤال التالي: "هل علي استخدام كلًا من كائنات DAO و DTO إن توفّر ما هو أبسط؟"من اﻷفضل تفادي استخدام كائنات DTO في مشاريع Express.js/TypeScript حقيقة صغيرة إلا في الحالة التي تخطط فيها توسيع هذه المشاريع لتصبح متوسطة الحجم. لكن حتى لو لم تكن بصدد استخدامها في نسخ اﻹنتاج، يبقى هذا التطبيق فرصة مفيدة على طريق احتراف إنشاء واجهات برمجية باستخدام TypeScript. فمن الجيد التمرّن على توسيع استخدام أنواع TypeScript لتشمل أساليب أخرى، والعمل مع كائنات DTO لتقارنها مع أساليب أكثر بساطة عند إضافة مكوّنات ونماذج بيانات. نموذج المستخدم في الواجهة البرمجية REST على مستوى TypeScript نعرّف بداية ثلاث كائنات DTO للمستخدم، لهذا ننشئ مجلدًا يُدعى dto ضمن المجلد user، ثم ننشئ ملفًا يُدعى create.user.dto.ts يضم الشيفرة التالية: export interface CreateUserDto { id: string; email: string; password: string; firstName?: string; lastName?: string; permissionLevel?: number; } يعني ذلك أنه كلما انشأنا مستخدمًا جديدًا، وبصرف النظر عن قاعدة البيانات، لا بد أن يمتلك معرّفًا id وكلمة مرور password وبريد إلكتروني email وحقلين اختياريين هما الاسم اﻷول والثاني. يمكن لهذه المتطلبات أن تتغير وفقًا لمتطلبات العمل على مشروع محدد. ولا بد من تحديث الكائن بأكمله عند استخدام الاستعلام PUT، وسيكون الحقلان الاختياريين اﻵن ضروريان. لهذا أنشئ الملف put.user.dto.ts في نفس المجلد السابق ليضم الشيفرة التالية: export interface PutUserDto { id: string; email: string; password: string; firstName: string; lastName: string; permissionLevel: number; } وبالنسبة إلى طلبات PATCH، باﻹمكان استخدام الميزة partial من TypeScript والتي تنشئ نوعًا جديدًا بنسخ نوع آخر وجعل كل حقوله اختيارية. وهكذا ستكون شيفرة الملف patch.user.dto.ts هي فقط الشيفرة التالية: import { PutUserDto } from './put.user.dto'; export interface PatchUserDto extends Partial<PutUserDto> {} لننشئ اﻵن قاعدة البيانات المؤقتة في الذاكرة، لهذا ننشئ أولًا المجلد daos داخل المجلد user ومن ثم نضيف الملف users.dao.ts. ندرج أولًا كائنات DTO التي أنشأناها: import { CreateUserDto } from '../dto/create.user.dto'; import { PatchUserDto } from '../dto/patch.user.dto'; import { PutUserDto } from '../dto/put.user.dto'; وللتعامل مع معرّفات المستخدمين IDs، سنضيف المكتبة shortid باستخدام الطرفية: npm i shortid npm i --save-dev @types/shortid بالعودة إلى الملف users.dao.ts، سندرج المكتبة shortid: import shortid from 'shortid'; import debug from 'debug'; const log: debug.IDebugger = debug('app:in-memory-dao'); بإمكاننا اﻵن إنشاء صنف يُدعى UserDao يبدو كالتالي: class UsersDao { users: Array<CreateUserDto> = []; constructor() { log('Created new instance of UsersDao'); } } export default new UsersDao(); سنستخدم في هذا الصنف نمط التصميم المتفرد singleton وبالتالي سيقدم هذا الصنف نفس النسخة، ونفس مصفوفة المستخدمين users عندما ندرجه ضمن ملفات أخرى. والسبب أن Node.js تخزّن هذا الملف مؤقتًا كلما أُدرج، وتجري كل عمليات الإدراج عند إقلاع التطبيق. أي سيُسلم كل ملف يشير إلى الملف users.dao.ts مرجعًا إلى النسخة ()new UsersDao التي صُدِّرت في أول مرة يعالج فيها Node.js هذا الملف. سنرى طريقة العمل هذه عندما نستخدم الصنف لاحقًا في المقال، ونستخدم هذا النمط من اﻷصناف الشائعة في TypeScript/Express.js مع تقدمنا في تطوير التطبيق. ملاحظة: من سلبيات استخدام نمط التصميم singleton صعوبة كتابة اختبارات وحدة لها، لكننا لن نلاحظ هذه السلبية في الكثير من حالات استخدامنا لهذه الأصناف، لأنها لا تضم متغيرات أعضاء تحتاج إلى إعادة ضبط قيمها. أما بالنسبة للحالات التي يجب فيها إعادة ضبط المتغيرات اﻷعضاء في singleton سنترك اﻷمر كتمرين للقارئ كي يفكّر في انتهاج طريقة للحل تعتمد على فكرة حقن الاعتمادية dependency injection. أما اﻵن، سنضيف العمليات اﻷساسية للتعامل مع قواعد البيانات CRUD إلى الصنف كدوال، وستكون بداية دالة إنشاء مستخدم كالتالي: async addUser(user: CreateUserDto) { user.id = shortid.generate(); this.users.push(user); return user.id; } وستأتي دالة استرجاع أسماء المستخدمين بأسلوبين: اﻷول هو "قراءة كل الموارد (جميع المستخدمين المسجلين)" واﻵخر "استرداد مستخدم من خلال المعرّف ID فقط": async getUsers() { return this.users; } async getUserById(userId: string) { return this.users.find((user: { id: string }) => user.id === userId); } أما الدالة التي تحدّث سجلات المستخدمين فقد تعيد كتابة الكائن بالكامل (الاستعلام PUT) أو جزء منه (PATCH? async putUserById(userId: string, user: PutUserDto) { const objIndex = this.users.findIndex( (obj: { id: string }) => obj.id === userId ); this.users.splice(objIndex, 1, user); return `${user.id} updated via put`; } async patchUserById(userId: string, user: PatchUserDto) { const objIndex = this.users.findIndex( (obj: { id: string }) => obj.id === userId ); let currentUser = this.users[objIndex]; const allowedPatchFields = [ 'password', 'firstName', 'lastName', 'permissionLevel', ]; for (let field of allowedPatchFields) { if (field in user) { // @ts-ignore currentUser[field] = user[field]; } } this.users.splice(objIndex, 1, currentUser); return `${user.id} patched`; } وكما ذكرنا سابقًا، فعلى الرغم من التصريح عن UserDto في طريقة تعريف الدوال السابقة، لا يقدم TypeScript أي طريقة للتحقق من اﻷنواع في زمن التنفيذ، ويعني ذلك أن: الدالة ()putUserById ستحتوي ثغرة تسمح لمستخدمي الواجهة البرمجية بتخزين قيم لحقول ليست جزءًا من النموذج الذي يعرّفه كائن DTO. ()patchUserById تعتمد هذه الدالة على على قائمة مكررة من أسماء الحقول والتي يجب أن تبقى متزامنة مع النموذج. وبدون وجود تزامن بين هذه القائمة والنموذج قد يستخدم النموذج القائمة التي يمثلها الكائن الذي حُدِّث، وبالتالي سيتجاهل قيم الحقول التي هي في الواقع جزء من النموذج الذي عرّفه كائن DTO لكنها لم تخزّن ضمنه سابقًا. سنعالج هاتين الحالتين بالشكل الصحيح لاحقًا عندما نتعامل مع تطبيقنا على مستوى قاعدة البيانات. أما العملية اﻷخيرة فهي عملية الحذف، وستكون دالتها كالتالي: async removeUserById(userId: string) { const objIndex = this.users.findIndex( (obj: { id: string }) => obj.id === userId ); this.users.splice(objIndex, 1); return `${userId} removed`; } وكنقطة إضافية، نعلم أن من شروط التسجيل الصحيح لمستخدم جديد هو عدم تكرار البريد اﻹلكتروني، لهذا سنضيف الدالة getUserByEmail: async getUserByEmail(email: string) { const objIndex = this.users.findIndex( (obj: { email: string }) => obj.email === email ); let currentUser = this.users[objIndex]; if (currentUser) { return currentUser; } else { return null; } } ملاحظة: في الحالات الحقيقة، قد تتصل بقاعدة البيانات من خلال مكتبات موجودة مسبقًا مثل Mongoose و Sequelize والتي تقدّم آلية لتنفيذ كل العمليات اﻷساسية التي تحتاجها. لهذا لن نتوسّع في شرح طريقة إنجاز الدوال السابقة. طبقة الخدمات في الواجهة البرمجية REST لتطبيقنا بعد أن أنشأنا كائن DAO أساسي مقيم في الذاكرة، بإمكاننا إنشاء خدمة تستدعي دوال CRUD. وطالما أن هذه الدوال مطلوبة لكل خدمة تتصل بقاعدة بيانات، سننشئ الواجهة CRUD التي تضم التوابع التي نحتاجها في كل مرة ننفذ فيها خدمة جديدة. تتمتع معظم بيئات التطوير المتكاملة التي نعمل عليها حاليًا ميزة توليد الشيفرة التي تمكننا من إضافة الدوال التي ننجزها في كل مرة نحتاجها مما يقلل كمية الشيفرة المكررة التي علينا كتابتها. أنشئ ضمن المجلد common مجلدّا بالاسم interfaces ثم أنشئ الملف crud.interface.ts وأضف إليه الشيفرة التالية: export interface CRUD { list: (limit: number, page: number) => Promise<any>; create: (resource: any) => Promise<any>; putById: (id: string, resource: any) => Promise<string>; readById: (id: string) => Promise<any>; deleteById: (id: string) => Promise<string>; patchById: (id: string, resource: any) => Promise<string>; } لننشئ اﻵن المجلد services ضمن المجلد users وضمنه الملف users.service.ts ونزوّده بالشيفرة التالية: import UsersDao from '../daos/users.dao'; import { CRUD } from '../../common/interfaces/crud.interface'; import { CreateUserDto } from '../dto/create.user.dto'; import { PutUserDto } from '../dto/put.user.dto'; import { PatchUserDto } from '../dto/patch.user.dto'; class UsersService implements CRUD { async create(resource: CreateUserDto) { return UsersDao.addUser(resource); } async deleteById(id: string) { return UsersDao.removeUserById(id); } async list(limit: number, page: number) { return UsersDao.getUsers(); } async patchById(id: string, resource: PatchUserDto) { return UsersDao.patchUserById(id, resource); } async readById(id: string) { return UsersDao.getUserById(id); } async putById(id: string, resource: PutUserDto) { return UsersDao.putUserById(id, resource); } async getUserByEmail(email: string) { return UsersDao.getUserByEmail(email); } } export default new UsersService(); كانت خطوتنا الأولى إدراج الكائن DAO ثم اعتماديات الواجهة ثم نوع TypeScript الخاص بكل كائن DTO. سنعمل اﻵن على إنجاز الخدمة UserService كصنف متفرّد كما فعلنا مع الكائن DAO. تستدعي جميع دوال الواجهة CRUD الدوال التي تقبلها من UsersDao، وبالتالي عندما يحين الوقت لاستبدال الكائن DAO، لن نغيّر أي شيء في المشروع، ما عدا بعض التعديلات في هذا الملف حيث تُستدعى دوال DAO. أي لن نضطر إلى تتبع كل استدعاء للدالة ()list مثلًا والتجقق من محتواها قبل استبدالها، وهذه هي فائدة هذه الطبقة مقابل بعض الشيفرة اﻷساسية البسيطة التي رأيتها سابقًا. التعليمتان Async/Await في Node.js قد ترى أن استخدام async مع دوال الخدمة بلا معنى، وهذا صحيح حاليًا. فجميع هذه الدوال تعيد قيمها مباشرة، دون استخدام الوعود promise أو await داخليًا. لكننا فقط أردنا تحضير الشيفرة اﻷساسية للخدمات التي ستستخدم async. وبالمثل ستجد أن جميع الاستدعاءات لهذه الدوال تستخدم await. ستجد في نهاية المقال تطبيقًا قابلًا للتنفيذ والتجريب. وسيكون من الجيد توليد أخطاء مختلفة في أماكن مختلفة من الشيفرة، ومراقبة ما يحدث عند التصريف والاختبار، مع الانتباه إلى أن اﻷخطاء في استخدام async بالتحديد لن تظهر كما قد تتوقع! و اﻵن وقد انتهينا من إنجاز الكائن DAO والخدمات، سنعود إلى المتحكم بالمستخدم. بناء متحكم خاص بالواجهة البرمجية REST إن الفكرة من استخدام المتحكمات كما أشرنا سابقًا هي فصل إعدادات الوجهات عن الشيفرة التي تعالج في النهاية الطلب الذي يصل إلى الوجهة المطلوبة. وبالتالي لا بد أن تجري جميع عمليات التقييم قبل أن يصل الطلب إلى المتحكم. وكل ما يحتاجه المتحكم هو معرفة ما الذي سيفعله مع الطلب الفعلي، لأن الطلب الذي وصل إلى هذه المرحلة لابد وأن يكون صالحًا. يستدعي المتحكم بعد ذلك الخدمة التي تتوافق مع كل طلب تتعامل معه. علينا قبل أن نبدأ تثبيت مكتبة لتأمين تشفير كلمة المرور: npm i argon2 لنبدأ بإنشاء مجلد يُدعى controllers ضمن المجلد users وننشئ ضمنه الملف users.controller.ts: //ﻹضافة الأنواع إلى كائنات الطلب والاستجابة express ندرج //العائدة إلى دوال المتحكم import express from 'express'; //ندرج خدمة المستخدم التي أنشأناها مؤخرًا import usersService from '../services/users.service'; //لتشفير كلمة المرور argon2 ندرج المكتبة import argon2 from 'argon2'; //وفق سياق مخصص كما شرحنا في المقال السابق debug نستخدم المكتبة import debug from 'debug'; const log: debug.IDebugger = debug('app:users-controller'); class UsersController { async listUsers(req: express.Request, res: express.Response) { const users = await usersService.list(100, 0); res.status(200).send(users); } async getUserById(req: express.Request, res: express.Response) { const user = await usersService.readById(req.body.id); res.status(200).send(user); } async createUser(req: express.Request, res: express.Response) { req.body.password = await argon2.hash(req.body.password); const userId = await usersService.create(req.body); res.status(201).send({ id: userId }); } async patch(req: express.Request, res: express.Response) { if (req.body.password) { req.body.password = await argon2.hash(req.body.password); } log(await usersService.patchById(req.body.id, req.body)); res.status(204).send(); } async put(req: express.Request, res: express.Response) { req.body.password = await argon2.hash(req.body.password); log(await usersService.putById(req.body.id, req.body)); res.status(204).send(); } async removeUser(req: express.Request, res: express.Response) { log(await usersService.deleteById(req.body.id)); res.status(204).send(); } } export default new UsersController(); ملاحظة: تعيد اﻷسطر السابقة الاستجابة HTTP 204 No Content وتعني أن الطلب قد أنجز، لكن لا يوجد محتوى إضافي لإعادته مع جسم الاستجابة. بعد اﻹنتهاء من كتابة شيفرة المتحكم على شكل متفرّد، أصبحنا جاهزين لكتابة شيفرة الوحدة الوسيطة، وهي الوحدة البرمجية اﻷخرى التي تعتمد على نموذج كائن الواجهة البرمجية REST التجريبي وخدمته وهي الأداة الوسيطة. أداة وسيطة REST باستخدام Node.js و Express.js ما الذي يمكن أن تقدمه أداة وسيطة مبنية باستخدام Express.js ؟ بداية عمليات التحقق من صحة البيانات وهذا أمر شديد اﻷهمية، لنبدأ إذًا بإضافة آليات تحقق بسيطة من الطلبات قبل وصولها إلى متحكم المستخدم: التأكد من وجود حقول معينة لبيانات المستخدم مثل email و password وهي ضرورية ﻹنشاء مستخدم أو تحديث بياناته. التأكد من عدم استخدام البريد اﻹلكتروني المدخل من قبل. التحقق من عدم تغيير حقل البريد اﻹلكتروني بعد إنشاء المستخدم (لأننا سنستخدمه للسهولة كمعرّف أساسي للمستخدم). التحقق من وجود مستخدم محدد مسبقًا. ولتعمل آليات التحقق السابقة مع Express.js، لابد من كتابتها على شكل دوال تتوافق مع نمط Express.js وذلك ﻹدارة نقل التحكم باستخدام الدالة ()next كما شرحنا في المقال السابق. لهذا سنتحتاج إلى ملف جديد users/middleware/users.middleware.ts نضع فيه الشيفرة التالية: import express from 'express'; import userService from '../services/users.service'; import debug from 'debug'; const log: debug.IDebugger = debug('app:users-controller'); class UsersMiddleware { } export default new UsersMiddleware(); نضيف اﻵن بعض دوال اﻷداة الوسيطة إلى جسم الصنف: async validateRequiredUserBodyFields( req: express.Request, res: express.Response, next: express.NextFunction ) { if (req.body && req.body.email && req.body.password) { next(); } else { res.status(400).send({ error: Missing required fields email and password, }); } } async validateSameEmailDoesntExist( req: express.Request, res: express.Response, next: express.NextFunction ) { const user = await userService.getUserByEmail(req.body.email); if (user) { res.status(400).send({ error: User email already exists }); } else { next(); } } async validateSameEmailBelongToSameUser( req: express.Request, res: express.Response, next: express.NextFunction ) { const user = await userService.getUserByEmail(req.body.email); if (user && user.id === req.params.userId) { next(); } else { res.status(400).send({ error: Invalid email }); } } //بالشكل الصحيح this نستخدم هنا الدالة السهمية كي نربط التعليمة validatePatchEmail = async ( req: express.Request, res: express.Response, next: express.NextFunction ) => { if (req.body.email) { log('Validating email', req.body.email); this.validateSameEmailBelongToSameUser(req, res, next); [إضغط و إسحب للتحريك] } else { next(); } }; async validateUserExists( req: express.Request, res: express.Response, next: express.NextFunction ) { const user = await userService.readById(req.params.userId); if (user) { next(); } else { res.status(404).send({ error: User ${req.params.userId} not found, }); } } ولكي نسهل على مستثمري الواجهة البرمجية تنفيذ طلبات إضافية على المستخدم الذي أنشئ حديثًا، سننشئ دالة مساعدة تستخلص الحقل userId من معاملات الطلب التي تصل من عنوان URL للطلب نفسه، ومن ثم نضيف الحقل إلى جسم الطلب، حيث تتواجد بقية بيانات المستخدم. والغاية من ذلك هي استخدام جسم الطلب كاملًا عندما نريد تحديث معلومات المستخدم، دون القلق من ضرورة الحصول على معرّف المستخدم في كل مرة، بل ستهتم اﻷداة الوسطية بهذا الموضوع. ستبدو الدالة بالشكل التالي: async extractUserId( req: express.Request, res: express.Response, next: express.NextFunction ) { req.body.id = req.params.userId; next(); } إضافة إلى منطق التنفيذ، ستجد أن الاختلاف الرئيسي بين اﻷداة الوسيطة المتحكم هو استخدام اﻷداة الوسيطة الدالة ()next لتمرير التحكم عبر سلسلة من الدوال المهيّأة مسبقًا حتى يصل إلى وجهته النهائية وهي المتحكم في حالتنا. تجميع كل الوحدات: إعادة تشكيل الوجهات بعد أن انتهينا من إنجاز مختلف نواحي معمارية التطبيق، سنعود إلى الملف users.routes.config.ts الذي عرّفناه في المقال السابق، والذي يستدعي اﻷداة الوسيطة والمتحكمات وكلاهما يعتمد على خدمة المستخدم والتي تتطلب بدورها نموذج المستخدم. سيكون الملف بشكله النهائي كالتالي: import { CommonRoutesConfig } from '../common/common.routes.config'; import UsersController from './controllers/users.controller'; import UsersMiddleware from './middleware/users.middleware'; import express from 'express'; export class UsersRoutes extends CommonRoutesConfig { constructor(app: express.Application) { super(app, 'UsersRoutes'); } configureRoutes(): express.Application { this.app .route(`/users`) .get(UsersController.listUsers) .post( UsersMiddleware.validateRequiredUserBodyFields, UsersMiddleware.validateSameEmailDoesntExist, UsersController.createUser ); this.app.param(`userId`, UsersMiddleware.extractUserId); this.app .route(`/users/:userId`) .all(UsersMiddleware.validateUserExists) .get(UsersController.getUserById) .delete(UsersController.removeUser); this.app.put(`/users/:userId`, [ UsersMiddleware.validateRequiredUserBodyFields, UsersMiddleware.validateSameEmailBelongToSameUser, UsersController.put, ]); this.app.patch(`/users/:userId`, [ UsersMiddleware.validatePatchEmail, UsersController.patch, ]); return this.app; } } أعدنا هنا تعريف الوجهات بإضافة أداة وسيطة لتقييم منطق العمل واختيار دوال المتحكم المناسبة لمعالجة الطلب إن كان كل شيء صحيحًا. كما استخدمنا الدالة ()param التي تقدمها Express.js لاستخلاص قيمة الحقل userId. كما مررنا الدالة validateUserExists العائدة للأداة الوسيطة UserMiddleware في جميع الدوال ()all. كي تُستدعى قبل وصول أي طلب GET أو PUT أو PATCH أو DELETE إلى نقطة الوصول user/:usersId/. أي لا حاجة أن تكون validateUserExists ضمن مصفوفة الدوال اﻹضافية التي نمررها إلى ()put. أو ()patch.، إذ تُستدعى قبل هذه الدوال. كما عززنا قابلية الاستخدام المتكرر للأداة الوسيطة بطريقة أخرى أيضًا، وذلك بتمرير الدالة UsersMiddleware.validateRequiredUserBodyFields كي تُستخدم ضمن سياق استخدام POST و PUT، إذ نعيد دمجها في دوال وسيطة أخرى. تنبيه ﻹخلاء المسؤولية: ما فعلناه اﻵن هو آلية بسيطة للتحقق من صحة المدخلات، لكن عليك التفكير بكل القيود التي يجب وضعها في الشيفرة عندما تتعامل مع مشاريع حقيقية. ولكي نتوخى البساطة، افترضنا أن المستخدم ليس قادرا على تغيير بريده اﻹلكتروني. اختبار الواجهة البرمجية REST المبنية باستخدام Express/TypeScript نستطيع اﻵن تصريف وتشغيل تطبيق Node.js، وبمجرد أن يعمل سنكون قادرين على اختبار وجهات الواجهة البرمجية باستخدام عميل REST مثل Postman أو cURL. سنجرّب أولاً الحصول على قائمة المستخدمين: curl --request GET 'localhost:3000/users' \ --header 'Content-Type: application/json' إن الاستجابة التي سنحصل عليها حاليًا هي مصفوفة فارغة، وهذا صحيح، لذك علينا إنشاء المستخدم الأول كالتالي: curl --request POST 'localhost:3000/users' \ --header 'Content-Type: application/json' لاحظ كيف ستكون النتيجة هي خطأ يرسله التطبيق من خلال اﻷداة الوسيطة: { "error": "Missing required fields email and password" } وﻹصلاح اﻷمر، سنرسل طلبًا صحيحًا إلى المورد users/: curl --request POST 'localhost:3000/users' \ --header 'Content-Type: application/json' \ --data-raw '{ "email": "marcos.henrique@toptal.com", "password": "sup3rS3cr3tPassw0rd!23" }' سنرى هذه المرة استجابة شبيهة بالتالي: { "id": "ksVnfnPVW" } إن هذا المعرف id هو المعرّف الخاص بالمستخدم الجديد وقد يكون مختلفًا على جهازك. ولتسهيل بقية الاختبارات، يمكنك تنفيذ بقية اﻷوامر باستخدام المعرّف الذي حصلت عليه (على افتراض أنك تستخدم بيئة تشغيل شبيه بنظام لينكس): REST_API_EXAMPLE_ID="put_your_id_here" بإمكانك أن ترى اﻵن الاستجابة التي تحصل عليها عند تنفيذ الطلب GET باستخدام المتغّير السابق: curl --request GET "localhost:3000/users/$REST_API_EXAMPLE_ID" \ --header 'Content-Type: application/json' وتستطيع أيضًا تعديل المورد (المستخدم) بأكمله من خلال تنفيذ الطلب PUT: curl --request PUT "localhost:3000/users/$REST_API_EXAMPLE_ID" \ --header 'Content-Type: application/json' \ --data-raw '{ "email": "marcos.henrique@toptal.com", "password": "sup3rS3cr3tPassw0rd!23", "firstName": "Marcos", "lastName": "Silva", "permissionLevel": 8 }' كما تستطيع اختبار آلية التحقق بتغيير عنوان البريد اﻹلكتروني، ومن المفترض عندها ظهور رسالة خطأ. لاحظ أيضًا أن استخدام الطلب PUT لتحديث مورد ذو معرّف محدد، لا بد لنا -كمستخدمين للواجهة البرمجية- أن نرسل كائن الطلب بأكمله كي يتوافق مع معايير نموذج REST. فلو أردنا مثلًا تعديل الاسم اﻷخير فقط lastName، باستخدام PUT، لا بد من إرسال الكائن بأكمله لتنجح عملية التحديث. لكن من السهل في حالة كهذه استخدام الطلب PATCH لأنه يعمل ضمن قيود REST، وبإمكانك عندها إرسال قيمة lastName فقط: curl --request PATCH "localhost:3000/users/$REST_API_EXAMPLE_ID" \ --header 'Content-Type: application/json' \ --data-raw '{ "lastName": "Faraco" }' وتذكر أن التمييز بين PUT و PATCH في شيفرتنا اﻷساسية عائد إلى أسلوب إعداد الوجهات عن طريق استخدام دوال الأداة الوسيطة التي أضفناها. هل نستخدم PUT أو PATCH أو كلاهما؟ قد ترى أنه لا ضرورة ملحة لاستخدام PUT نظرًا لمرونة PATCH، وبالفعل تتبنى بعض الواجهات البرمجية الفكرة. وقد يصّر البعض على دعم PUT كي تتوافق الواجهة البرمجية تمامًا مع REST. مع ذلك، قد يكون إنشاء وجهات تدعم PUT أمرًا مناسبًا لبعض حالات الاستخدام الشائعة. وفي واقع اﻷمر هذه النقاط هي موضع نقاشات أعمق، لهذا دعمنا في تطبيقنا استخدام PUT وكذلك PATCH، لكننا نشجعك على الاطلاع والبحث أكثر عندما تكون مستعدًا. إن حاولت الحصول على قائمة المستخدمين مجددًا، سترى المستخدم الجديد وقد حّدثت بياناته: [ { "id": "ksVnfnPVW", "email": "marcos.henrique@toptal.com", "password": "$argon2i$v=19$m=4096,t=3,p=1$ZWXdiTgb922OvkNAdh9acA$XUXsOHaRN4uVg5ltIwwO+SPLxvb9uhOKcxoLER1e/mM", "firstName": "Marcos", "lastName": "Faraco", "permissionLevel": 8 } ] بإمكاننا أخيرًا اختبار حذف مستخدم كالتالي: curl --request DELETE "localhost:3000/users/$REST_API_EXAMPLE_ID" \ --header 'Content-Type: application/json' إن حاولت اﻵن الحصول على قائمة المستخدمين مجددًا، فلن ترى المستخدم الذي أنشأته سابقًا. وهكذا نكون قد أنجزنا جميع العمليات اﻷساسية CRUD. الخلاصة استكشفنا في هذا المقال المكمّل للمقال السابق مفاهيم أساسية في بناء واجهة برمجية REST باستخدام Express.js. إذ جزءنا شيفرتنا إلى خدمات وأداة وسيطة ومتحكمات ونماذج بيانات، ولكل منها دوال تنفذ مهامًا محددة كالتحقق من صحة المدخلات وتنفيذ عمليات منطقية أو معالجة الطلبات الصحيحة والاستجابة لها. كما أنشأنا بنية شديدة البساطة لتخزين البيانات هدفها الوحيد تنفيذ بعض الاختبارات في هذه المرحلة، ومن ثم ستُستبدل بشيء عملي أكثر في مقالات قادمة. وما سنتعرف عليه في المقال القادم، بعد توخي البساطة الشديدة في إنشاء واجهتنا البرمجية، هو خطوات إضافية لجعل التطبيق أسهل صيانة وأكثر قابلية للتوسع وكذلك أكثر أمانًا مثل: استبدال قاعدة البيانات المؤقتة بقاعدة بيانات MongoDB، واستخدام المكتبة Mongoose لتسهيل كتابة الشيفرة. إضافة طبقة أمان والتحكم بالوصول من خلال مقاربة لا تعتمد على حالة التطبيق باستخدام JWT. إعداد اختبارات مؤتمتة تسمح لنا بتوسيع تطبيقنا. بإمكانك اﻵن الاطلاع على الشيفرة النهائية حتى هذه المرحلة من هنا. ترجمة -وبتصرف- للمقال Building a Node.js TypeScript REST API,Part2: Models, Middleware and Services اقرأ أيضًا المقال السابق: بناء واجهة برمجية متوافقة مع REST باستخدام Express.js -الجزء الأول دليلك لربط واجهة OpenAI API مع Node.js مدخل إلى إطار عمل الويب Express وبيئة Node
  16. ستحتاج إلى دراسة دورة تطوير التطبيقات باستخدام JavaScript لأنك ستتمكن من تعلم لغة جافاسكريبت ومن خلالها تستطيع تطوير الواجهة الأمامية والخلفية وبناء تطبيق لسطح المكتب، وبناء تطبيقات للهواتف أيضًا تعمل على منصتي أندرويد و iOS وذلك من خلال إطار React Native. وستتعلم بها ما يلي: الأسس البرمجية السليمة للغة JavaScript الإضافات الحديثة في نسخة ES6 من اللغة تطوير تطبيقات الخادم باستخدام بيئة Node.js أساسيات مكتبة React.js وبناء تطبيق ملاحظات باستخدامها أساسيات مكتبة React Native و Expo تطوير تطبيق جوال للوصل بين الأطباء والمرضى بالاعتماد على React Native التعرف على التطبيقات التي تتطلب اتصالًا مستمرًا بين المتصفح والخادم بناء تطبيق محادثة يشبه تطبيق WhatsApp إنشاء تطبيق أسئلة وأجوبة ونقاشات مع تصيير من جهة الخادم Server-side Rendering عبر إطار العمل Next.js بناء واجهات أمامية باستخدام React.js مع مكتبة Material-UI إنشاء تطبيقات سطح مكتب باستخدام JavaScript باستخدام إطار العمل Electron.js بناء تطبيق جوال لشبكة تواصل اجتماعي باستخدام إطار العمل Ionic ومكتبة React.js لذا تستطيع دراسة جافاسكريبت ثم دراسة مسار React Native فقط إذا كنت تريد تطوير التطبيقات للهواتف فقط.
  17. السلام عليكوم ورحمة الله و بركاته لوسمحتم انا ابرمج سكربت php و العميل يحتاج يضيف خيار في لوحة التحكم للتحكم بشكل الموقع فأنا افكر بعمل نظام قوالب بسيط يستخدم ملفات tpl و ايضا يمكنة التعديل على كود القالب من لوحة التحكم بحيث يكون في جذر الموقع مجلد template و بداخلة مجلدات القوالب. كيف يمكنني إضافة الميزة في السكربت ؟؟
  18. كيف يمكنني تعلم موبايل ابلكيشن عل اكادميه حاسوب كيف يمكنني تعلم موبايل ابلكيشن عل اكادميه حاسوب
  19. دورة علوم الحاسب هي الدورة الأفضل في أكاديمية حسوب للبداية في مجال البرمجة عموماً إذا لم يكن لديك خبرة في علوم الحاسوب ولكن يمكنك الإشتراك في أي دورة من دورات حسوب بدون أن يكون لديك خبرة سابقة حيث أن كل دورة مستقلة وتقوم بتعليمك اللغات البرمجية والتقنيات المطلوبة للمجال أو المسار الخاص بالدورة لذلك يفضل إختيار المجال المناسب لك من خلال الإطلاع على المقالة التالية ثم بعد ذلك يمكنك الإطلاع على المحتوى الخاص بالدورة المناسبة لك
  20. الأغلب أن مشكلة الألوان عند الطباعة يمكن حلها من خلال ضبط إعدادات الطابعة على نوع الورق اللاصق الذي تستخدمه وهذا الإعداد موجود في خصائص الطابعة ضمن خيارات الطباعة مع تنزيل وتثبيت ملف تعريف الألوان المناسب للطابعة والورق اللاصق من موقع الشركة المصنعة للطابعة. هذا سيساعد الطابعة على معالجة الألوان بشكل صحيح. الأمر ثاني، وهو من خلال معايرة الشاشة لضمان عرض الألوان بشكل صحيح، يمكن القيام بذلك باستخدام أدوات المعايرة المدمجة في نظام التشغيل أو برامج متخصصة مثل X-Rite أو Datacolor. على نظام Windows، من خلال الذهاب إلى "لوحة التحكم" ثم "إدارة الألوان" واختيار "معايرة الشاشة". الحل الآخر الممكن من خلال تحويل الصورة إلى نموذج الألوان CMYK، حيث تستخدم الطابعات هذا النموذج للطباعة. في Adobe Photoshop، يمكنك القيام بذلك عن طريق الذهاب إلى "صورة" ثم "نمط" واختيار "CMYK". في Adobe Illustrator، اذهب إلى "ملف" ثم "نمط المستند" واختر "CMYK". مثل الصورة:
  21. في الوقت الحالي أصبح لا يمكن تخطي الأساسيات، وعليك التركيز عليها بشدة، لذا يجب التأسيس بشكل جيد وعدم تخطيها للإنتقال إلى التقنيات (المكتبات والإطارات) أقصد دراسة علوم الحاسوب واللغة البرمجية التي ستختارها بشكل مُعمق وليس التقنيات المبنية عليها. فحاليًا يجب أن تكون متميز ولديك فهم عميق للأساسيات وليس نسخة من شخص آخر موجود في سوق العمل، فمن لديهم تلك الدراية قليلون، لأن الأمر ليس سهل ويحتاج إلى صبر ومجهود. ستحتاج إلى الممارسة العملية بشكل مُكثف وليس الدراسة النظرية فقط. لذا دورة علوم الحاسوب ستؤهلك لدخول مجال البرمجة ثم بعدها تستطيع تحديد المجال الذي تريد التخصص به، وذلك بناءًا على المطلوب في سوق العمل الذي تريد العمل به.
  22. انا عندما اطبع صور يكون لونها في اول الصفحه باللون الوردي و ي وسط الصفحه يكون لونها بني و حتى ان الالوان لا تكون مطابقه لما اراه في شاشة الحاسوب باختلاف كبيرر على سبيل المثال اضع لون وردي او بني يصبح مائل الى اللون الاصفر وانا لا اعلم السبب لا اعلم ان اوصلت مشكلتي بشكل مناسب للمزيد من الشرح انا اواجه مشكلتين 1-ان الالوان في الوال الصفحه تتغير بشكل كبير عن الصوره 2- ان الالوان التي في شاشة الحاسوب ايضا غير مشابه في الورقه المطبعه نوع الجهاز generic universal PCL و استخدم ورق لاصق للهدايا ليس ورق عادي و النموذج اللوني CMYK و استخدم تطبيق AL ارجو المساعده.........
  23. مرحبا إبراهيم . إذا لم يكن لديك أى خلفية مطلقا عن البرمجة أو التعامل مع الانترنت وفهم آلية عمله فإن دورة علوم الحاسوب هى المناسبة لك . ولكنك ستحتاج إلى دورة أخرى بعدها حيث إن هذه الدورة فقط للأساسيات وستساعدك على فهم المجال و يمكنك بعدها أن تقرر في أى مجال سوف تريد أن تعمل . سواء ك Full Stack او MERN Stack و Back-End و Front-End و أيضا يمكنك إختيار لغة البرمجة التى تحب أن تعمل بها . أما إذا كان لديك أساسيات بسيطة عن مجال البرمجة و معرفة ليست بالقوية فى مجال الانترنت فيمكنك إختيار أى دورة أخرى من الدورات المتاحة على حسب المجال الذى تريده و بعد الإنتهاء من واحدة أو أكثر من هذه الدورات ستكون لديك المعرفة والقدرة على البحث عن عمل ومزاولة مجال البرمجة , يمكنك الذهاب لهذا الرابط حيث توجد جميع دورات أكادمية حسوب ويمكنك قراءة ما يتم شرحه في كل دورة . وهذه بعض الأجوبة الأخرى يمكنك قرائتها بعناية حتى تقرر أى مجال تريد العمل فيه أو تحبه .
  24. ليس لدي أي خبرة سابقة ، في أي دوره تنصحني لكي ابدأ بها ؟
  25. البارحة
  26. الطريقة الاولى لم تنجح معي لم يظهر لي اي ملف بالنسة للاستضافة لدي حساب المسؤول و يمكنني الوصول الى ssh هل يمكنك ان تخبرني بالمزيد اخي مصطفى؟
  27. لست بحاجة إلى التلخيص فالمستندات موجودة وجوجل موجود لديك، المهم هو الاستيعاب والحفظ في البداية ولا أقصد حفظ الأكواد كما هي بل حفظ ما الدالة المسؤولة عن ذلك وهكذا، وستنسى بعض الأمور لا مشكلة طالما أنك قمت باستيعابها والتطبيق عليها فتستطيع البحث عنها واستخدامها مباشرًة، ومع الوقت ستترسخ لديك الأمور التي تستخدمها بكثرة. بعد كل درس نقوم بالتطبيق والمهم هو التطبيق العملي بمفردك ولا يكفي التطبيق مع الشرح، فعند التطبيق بمفردك تجبر نفسك على التركيز وتسترجع ما قمت باستيعابه لتنفيذه عمليًا، هناك أمور أخرى وبشكل مُفصل ذكرتها هنا السابق كان تلخيص بسيط:
  1. عرض المزيد
×
×
  • أضف...