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

البحث في الموقع

المحتوى عن 'artisan'.

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المحتوى


التصنيفات

  • الإدارة والقيادة
  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • السلوك التنظيمي في المؤسسات
  • عالم الأعمال
  • التجارة والتجارة الإلكترونية
  • نصائح وإرشادات
  • مقالات ريادة أعمال عامة

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • PHP
    • Laravel
    • ووردبريس
  • جافاسكربت
    • لغة TypeScript
    • Node.js
    • React
    • Vue.js
    • Angular
    • jQuery
    • Cordova
  • HTML
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • لغة C#‎
    • ‎.NET
    • منصة Xamarin
  • لغة C++‎
  • لغة C
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • لغة Rust
  • برمجة أندرويد
  • لغة R
  • الذكاء الاصطناعي
  • صناعة الألعاب
  • سير العمل
    • Git
  • الأنظمة والأنظمة المدمجة

التصنيفات

  • تصميم تجربة المستخدم UX
  • تصميم واجهة المستخدم UI
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب GIMP
    • كريتا Krita
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • مقالات DevOps عامة
  • خوادم
    • الويب HTTP
    • البريد الإلكتروني
    • قواعد البيانات
    • DNS
    • Samba
  • الحوسبة السحابية
    • Docker
  • إدارة الإعدادات والنشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
    • ريدهات (Red Hat)
  • خواديم ويندوز
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • شبكات
    • سيسكو (Cisco)

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح
  • مبادئ علم التسويق

التصنيفات

  • مقالات عمل حر عامة
  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • العمل الحر المهني
    • العمل بالترجمة
    • العمل كمساعد افتراضي
    • العمل بكتابة المحتوى

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
    • بريستاشوب
    • أوبن كارت
    • دروبال
  • الترجمة بمساعدة الحاسوب
    • omegaT
    • memoQ
    • Trados
    • Memsource
  • برامج تخطيط موارد المؤسسات ERP
    • تطبيقات أودو odoo
  • أنظمة تشغيل الحواسيب والهواتف
    • ويندوز
    • لينكس
  • مقالات عامة

التصنيفات

  • آخر التحديثات

أسئلة وأجوبة

  • الأقسام
    • أسئلة البرمجة
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات

التصنيفات

  • كتب ريادة الأعمال
  • كتب العمل الحر
  • كتب تسويق ومبيعات
  • كتب برمجة
  • كتب تصميم
  • كتب DevOps

ابحث في

ابحث عن


تاريخ الإنشاء

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


رشح النتائج حسب

تاريخ الانضمام

  • بداية

    نهاية


المجموعة


النبذة الشخصية

تم العثور على 11 نتائج

  1. يعتبر Laravel أشهر إطار ويب بلغة PHP، ولقد شهد إصداره الخامس العديد من التغييرات وتمت إضافة الكثير من المميزات الجديدة، ولذلك لتعلم أساسيات هذا الإطار سوف نقوم بإنشاء تطبيق Todo List، وسوف نتعلم من خلال هذا التطبيق الكثير من المفاهيم والطرق البرمجية حتى نتمكن من التعامل والتوسع مع إطار Laravel 5. ملاحظة: هذا الدرس طويل للغاية لذلك سوف يتم تقسيمه إلى أجزاء أصغر، ويمكنك الحصول على الكود المصدري لكل جزء من GitHub. في هذا الدرس سوف نتحدث عن تثبيت Laravel 5 وإعداده وعن بعض التقنيات الجديد كالتهجير migration والبذر seeding. قبل أن نبدأ أهم المصطلحات التي ستصادفنا في هذه الدروس: التهجير migration: نظام لإدارة قواعد البيانات، فهو يسمح للفريق بتعديل مخطط قواعد البيانات لجميع الأعضاء. البذر seeding: يقصد بها إدخال البيانات الأولية لتجربة التطبيق، أي أنه سيقوم بإدخال بيانات لمساعدتك على التطوير واكتشاف الأخطاء. هدف المشروع سوف يتكون تطبيقنا من مشروع أو أكثر، كل واحد لديه قائمة مهام خاصة به، وسوف تتمكن من إنشاء وعرض وتعديل وحذف المهام والمشاريع. في هذا الدرس سوف نقوم بـ: إعداد وتنصيب Laravel. تركيب حزم إضافية لتسهيل عملية التطوير. استخدام التهجير migration والبذر seeds. تعلم كيفية استخدام متحكِّمات الموارد resourceful controllers. تعلم كيفية استخدام العروض views. (بما في ذلك نظام blade ومخططات المحتوى) التعامل مع علاقات النماذج. التثبيت إن عملية تثبيت Laravel سهلة للغاية بفضل Composer، لذلك سوف نستخدمه في تثبيته. استخدام Composer والآن نقوم بتشغيل مثبت Laravel (سوف تحتاج إلى عمل هذا لمرة واحدة فقط): composer global require "laravel/installer=~1.1" والآن نقوم بعمل مشروعنا: laravel new l5todo الإعداد يستخدم Laravel 5 حزمة تدعى DotEnv تقوم بتخزين المعلومات الحساسة في ملفات ذات صيغة .env والتي يتم تحميلها كمتغيرات PHP عند التشغيل. قد يبدوا لك هذا معقدا لكن بكلمات أخرى، هذا يعني أن فقط الإعدادات الحسّاسة credentials يتم تخزينها في هذه الملفات وأما بقية إعداداتك فيتم تخزينها في ملفات الإعداد العادية. قاعدة البيانات سوف نحتاج إلى قاعدة بيانات، لذلك سنقوم بعمل واحدة ثم سنقوم بنسخ .env.example إلى .env وتحديث البيانات على الشكل التالي: DB_HOST=localhost DB_DATABASE=homestead DB_USERNAME=homestead DB_PASSWORD=secret في النهاية إذا لم تكن تستخدم MySQL فقُم بفتح config/database.php/ ثم قٌم بتغيير السطر الافتراضي: 'default' => 'mysql', تذكر أن تضيف ملفات بيئة عملك إلى .gitignore عن طريق إضافة سطر .env. خطواتنا الأولى كما ذكرنا في الأعلى، سوف يتكون تطبيق قائمة المهام على مشروع أو أكثر كل واحد مع قائمة المهام الخاصة به، ولذلك سوف نحتاج إلى مشروع، نماذج مهام، متحكِّمات، عروض views، التهجير migration، البذور seeds والطرق routes، ولقد قمنا بشرح بعض هذه المصطلحات في بداية الدرس. لنبدأ بالتعامل معهم كل واحد على حدة. التهجير migration أولا نريد أن نرى مخطط جدولنا، سوف يبدو مثل هذا: Projects +------------+------------------+------+-----+ | Field | Type | Null | Key | +------------+------------------+------+-----+ | id | int(10) unsigned | NO | PRI | | name | varchar(255) | NO | | | slug | varchar(255) | NO | | | created_at | timestamp | NO | | | updated_at | timestamp | NO | | +------------+------------------+------+-----+ Tasks +-------------+------------------+------+-----+ | Field | Type | Null | Key | +-------------+------------------+------+-----+ | id | int(10) unsigned | NO | PRI | | project_id | int(10) unsigned | NO | MUL | | name | varchar(255) | NO | | | slug | varchar(255) | NO | | | completed | tinyint(1) | NO | | | description | text | NO | | | created_at | timestamp | NO | | | updated_at | timestamp | NO | | +-------------+------------------+------+-----+ ثم سوف نقوم بعمل التهجير migration: php artisan make:migration create_projects_and_tasks_tables --create="projects" سوف نقوم بعمل كِلا الجدولين بتهجير migration واحد حتى نتمكن من حذفها بترتيب عكسي لتجنب سلامة خرق القيد integrity constraint violation، قُم بفتح: /database/migrations/createprojectsandtasks_tables.php وقُم بتحديث المعلومات حسب الآتي: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateProjectsAndTasksTables extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('projects', function(Blueprint $table) { $table->increments('id'); $table->string('name')->default(''); $table->string('slug')->default(''); $table->timestamps(); }); Schema::create('tasks', function(Blueprint $table) { $table->increments('id'); $table->integer('project_id')->unsigned()->default(0); $table->foreign('project_id')->references('id')->on('projects')->onDelete('cascade'); $table->string('name')->default(''); $table->string('slug')->default(''); $table->boolean('completed')->default(false); $table->text('description')->default(''); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('tasks'); Schema::drop('projects'); } } قم بتنفيذ الهجرة migration: php artisan migrate إذا قمت الآن بمراجعة قاعدة البيانات، يجب أن تكون الجداول مكتملة. البذور Seeds سوف نحتاج إلى بذر المشاريع/المهام ليكون لديك شيء للعمل معه عندما نصل إلى المتصفح، لذلك قم بإنشاء: /database/seeds/ProjectsTableSeeder.php و TasksTableSeeder.php كالتالي: // /database/migrations/seeds/ProjectsTableSeeder.php <?php use Illuminate\Database\Seeder; class ProjectsTableSeeder extends Seeder { public function run() { // Uncomment the below to wipe the table clean before populating DB::table('projects')->delete(); $projects = array( ['id' => 1, 'name' => 'Project 1', 'slug' => 'project-1', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 2, 'name' => 'Project 2', 'slug' => 'project-2', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 3, 'name' => 'Project 3', 'slug' => 'project-3', 'created_at' => new DateTime, 'updated_at' => new DateTime], ); // Uncomment the below to run the seeder DB::table('projects')->insert($projects); } } // /database/migrations/seeds/TasksTableSeeder.php <?php use Illuminate\Database\Seeder; class TasksTableSeeder extends Seeder { public function run() { // Uncomment the below to wipe the table clean before populating DB::table('tasks')->delete(); $tasks = array( ['id' => 1, 'name' => 'Task 1', 'slug' => 'task-1', 'project_id' => 1, 'completed' => false, 'description' => 'My first task', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 2, 'name' => 'Task 2', 'slug' => 'task-2', 'project_id' => 1, 'completed' => false, 'description' => 'My first task', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 3, 'name' => 'Task 3', 'slug' => 'task-3', 'project_id' => 1, 'completed' => false, 'description' => 'My first task', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 4, 'name' => 'Task 4', 'slug' => 'task-4', 'project_id' => 1, 'completed' => true, 'description' => 'My second task', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 5, 'name' => 'Task 5', 'slug' => 'task-5', 'project_id' => 1, 'completed' => true, 'description' => 'My third task', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 6, 'name' => 'Task 6', 'slug' => 'task-6', 'project_id' => 2, 'completed' => true, 'description' => 'My fourth task', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 7, 'name' => 'Task 7', 'slug' => 'task-7', 'project_id' => 2, 'completed' => false, 'description' => 'My fifth task', 'created_at' => new DateTime, 'updated_at' => new DateTime], ); //// Uncomment the below to run the seeder DB::table('tasks')->insert($tasks); } } ولا تنسَ إضافة أصناف البذور(seed classes) إلى database/seeds/DatabaseSeeder.php/ : use Illuminate\Database\Seeder; use Illuminate\Database\Eloquent\Model; class DatabaseSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { Model::unguard(); $this->call('ProjectsTableSeeder'); $this->call('TasksTableSeeder'); } } الآن نقوم بالبذر: composer dump-autoload ثم: php artisan db:seed # أو php artisan migrate:refresh --seed يجب الآن أن تكون قاعدة البيانات قد تم بذرها. mysql> select * from projects; +----+-----------+-----------+---------------------+---------------------+ | id | name | slug | created_at | updated_at | +----+-----------+-----------+---------------------+---------------------+ | 1 | Project 1 | project-1 | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | | 2 | Project 2 | project-2 | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | | 3 | Project 3 | project-3 | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | +----+-----------+-----------+---------------------+---------------------+ mysql> select * from tasks; +----+------------+--------+--------+-----------+----------------+---------------------+---------------+ | id | project_id | name | slug | completed | description | created_at | updated_at | +----+------------+--------+--------+-----------+----------------+---------------------+---------------+ | 1 | 1 | Task 1 | task-1 | 0 | My first task | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | 2 | 1 | Task 2 | task-2 | 0 | My first task | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | 3 | 1 | Task 3 | task-3 | 0 | My first task | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | 4 | 1 | Task 4 | task-4 | 1 | My second task | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | 5 | 1 | Task 5 | task-5 | 1 | My third task | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | 6 | 2 | Task 6 | task-6 | 1 | My fourth task | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | 7 | 2 | Task 7 | task-7 | 0 | My fifth task | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 +----+------------+--------+--------+-----------+----------------+---------------------+---------------+ النماذج Models للعمل مع جداول المشاريع والمهام نحتاج إلى نماذج مماثلة، سوف نقوم بإنشائها الآن: php artisan make:model Project php artisan make:model Task كان هذا سهلا استخدام Artisan – Tinker الآن لدينا المعلومات في قاعدة البيانات وسيكون من الرائع أن نتعلم حول واحدة من أهم ميزات artisan وهي Tinker الذي يوفر لك سطر أوامر للتعامل مع Laravel، وكمثال، لنستخدمه في معرفة عدد المشاريع في قاعدة البيانات الحالية: $ php artisan tinker > App\Project::count(); 3 > App\Task::count(); 7 وكما ترى يمكن أن يكون Tinker مفيدا للغاية، لذلك سوف نستخدمه عدة مرات في هذه السّلسلة. المُتحكِّمات Controllers الآن وصلنا إلى النقطة التي سوف نبدأ بعرض ما صنعناه على المتصفح، وسوف نحتاج إلى إنشاء بعض المتحكِّمات Controllers والطرق Routes. سنبدأ أولا بالمتحكِّمات: php artisan make:controller ProjectsController php artisan make:controller TasksController الموارد المُضمّنة نبدأ بإضافة موارد Project و Task إلى app/Http/routes.php/ : Route::get('/', 'WelcomeController@index'); //Route::get('home', 'HomeController@index'); // //Route::controllers([ // 'auth' => 'Auth\AuthController', // 'password' => 'Auth\PasswordController', //]); Route::resource('projects', 'ProjectsController');Route::resource('tasks', 'TasksController'); والآن دعونا نرى إحدى مميزات artisan والمُتمثّلة في route:list، في سطر الأوامر أكتب التالي: php artisan route:list لتكون النّتيجة +--------+----------+--------------------------+------------------+-------------------------------------------------+------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+----------+--------------------------+------------------+-------------------------------------------------+------------+ | | GET|HEAD | / | | App\Http\Controllers\WelcomeController@index | | | | GET|HEAD | projects | projects.index | App\Http\Controllers\ProjectsController@index | | | | GET|HEAD | projects/create | projects.create | App\Http\Controllers\ProjectsController@create | | | | POST | projects | projects.store | App\Http\Controllers\ProjectsController@store | | | | GET|HEAD | projects/{projects} | projects.show | App\Http\Controllers\ProjectsController@show | | | | GET|HEAD | projects/{projects}/edit | projects.edit | App\Http\Controllers\ProjectsController@edit | | | | PUT | projects/{projects} | projects.update | App\Http\Controllers\ProjectsController@update | | | | PATCH | projects/{projects} | | App\Http\Controllers\ProjectsController@update | | | | DELETE | projects/{projects} | projects.destroy | App\Http\Controllers\ProjectsController@destroy | | | | GET|HEAD | tasks | tasks.index | App\Http\Controllers\TasksController@index | | | | GET|HEAD | tasks/create | tasks.create | App\Http\Controllers\TasksController@create | | | | POST | tasks | tasks.store | App\Http\Controllers\TasksController@store | | | | GET|HEAD | tasks/{tasks} | tasks.show | App\Http\Controllers\TasksController@show | | | | GET|HEAD | tasks/{tasks}/edit | tasks.edit | App\Http\Controllers\TasksController@edit | | | | PUT | tasks/{tasks} | tasks.update | App\Http\Controllers\TasksController@update | | | | PATCH | tasks/{tasks} | | App\Http\Controllers\TasksController@update | | | | DELETE | tasks/{tasks} | tasks.destroy | App\Http\Controllers\TasksController@destroy | | +--------+----------+--------------------------+------------------+-------------------------------------------------+------------+ ستلاحظ أن كل من projects و tasks هي روابط ذات مستوى عالي، وفي تطبيقنا، المهام تتبع المشاريع، لذلك سيكون من الأفضل للروابط أن تكون مُضمّنة مثل /projects/1/tasks/3 بدلا من /tasks/، وهذا يمكن فعله باستخدام شيء اسمه الموارد المُضمّنة nested resources، وكما هو الحال مع أغلب الأشياء في Laravel، التعديل المطلوب سريع وبسيط، فقط قٌم بفتح /app/Http/routes.php وقٌم بالتعديلات التالية: // Route::resource('tasks', 'TasksController'); Route::resource('projects.tasks', 'TasksController'); هذا كل شيء، قُم بكتابة php artisan route:list لنعلم ماذا لدينا لحد الآن: php artisan route:list +--------+----------+----------------------------------------+------------------------+-------------------------------------------------+------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+----------+----------------------------------------+------------------------+-------------------------------------------------+------------+ | | GET|HEAD | / | | App\Http\Controllers\WelcomeController@index | | | | GET|HEAD | projects | projects.index | App\Http\Controllers\ProjectsController@index | | | | GET|HEAD | projects/create | projects.create | App\Http\Controllers\ProjectsController@create | | | | POST | projects | projects.store | App\Http\Controllers\ProjectsController@store | | | | GET|HEAD | projects/{projects} | projects.show | App\Http\Controllers\ProjectsController@show | | | | GET|HEAD | projects/{projects}/edit | projects.edit | App\Http\Controllers\ProjectsController@edit | | | | PUT | projects/{projects} | projects.update | App\Http\Controllers\ProjectsController@update | | | | PATCH | projects/{projects} | | App\Http\Controllers\ProjectsController@update | | | | DELETE | projects/{projects} | projects.destroy | App\Http\Controllers\ProjectsController@destroy | | | | GET|HEAD | projects/{projects}/tasks | projects.tasks.index | App\Http\Controllers\TasksController@index | | | | GET|HEAD | projects/{projects}/tasks/create | projects.tasks.create | App\Http\Controllers\TasksController@create | | | | POST | projects/{projects}/tasks | projects.tasks.store | App\Http\Controllers\TasksController@store | | | | GET|HEAD | projects/{projects}/tasks/{tasks} | projects.tasks.show | App\Http\Controllers\TasksController@show | | | | GET|HEAD | projects/{projects}/tasks/{tasks}/edit | projects.tasks.edit | App\Http\Controllers\TasksController@edit | | | | PUT | projects/{projects}/tasks/{tasks} | projects.tasks.update | App\Http\Controllers\TasksController@update | | | | PATCH | projects/{projects}/tasks/{tasks} | | App\Http\Controllers\TasksController@update | | | | DELETE | projects/{projects}/tasks/{tasks} | projects.tasks.destroy | App\Http\Controllers\TasksController@destroy | | +--------+----------+----------------------------------------+------------------------+-------------------------------------------------+------------+ جعل روابط ذات اسم لطيف الطرق routes الحالية ممتازة لكنها ذات عناوين يمكن تحسينها، مثل هذا العنوان /projects/1/tasks/2، فسيكون من الرائع للزوار أن يتم تغيير المعرف الرقمي ID بالاسم اللطيف للحقول، فمثلا سوف نحصل على /projects/my-first-project/tasks/buy-milk بدلا من /projects/1/tasks/2. قم بفتح ملف /app/Http/routes.php وضع به التالي: Route::bind('tasks', function($value, $route) { return App\Task::whereSlug($value)->first(); }); Route::bind('projects', function($value, $route) { return App\Project::whereSlug($value)->first(); }); هذه الشيفرة سوف تغير السلوك الافتراضي لـ tasks و projects في artisan routes وجعل الروابط ذات إسم لطيف slug. خاتمة اليوم لقد قمنا بعمل عدة أشياء وهي: تثبيت وإعداد Laravel. إضافة موردان اثنان resources. إنشاء التهجيرات migrations. إضافة بعض بيانات البذور seeds. تكوين هيكل الروابط. بكلمات أخرى، قمنا اليوم بوضع الأساس التي سوف نعتمد عليه في الدروس القادمة من خلال إنشاء جميع العناصر اللازمة حتى تعمل الواجهة الأمامية، ولدينا الآن بعض البيانات في قاعدة البيانات بالإضافة إلى الطرق، في الدرس القادم سوف نبدأ في الواجهة الأمامية للموقع. هنالك بضعة مواقع أخرى لتحصل على المزيد من المعلومات وآخر الأخبار: The Laravel Twitter feed: أخر الأخبار حول تطورات Laravel. Taylor Otwell’s Twitter feed: حساب تويتر لمُؤسّس Laravel. Laravel.io: لمتابعة آخر الأخبار و الدورات من مختلف مواقع الويب، بالإضافة إلى بودكاست أسبوعي يتحدث عن الأفكار والاهتمامات ذات علاقة إلى المنصة. Laravel Packages Registry: مكان جيد لإيجاد أفضل حزم Laravel. Code Bright: كتاب إلكتروني مجاني من المؤلف دايلي ريس Dayle Rees. ترجمة وبتصرف للمقال: Creating a Basic ToDo Application in Laravel 5 – Part 1. حقوق الصورة البارزة: Designed by Freepik.
  2. ثبّتنا في الدرس السابق من هذه السلسلة إطار Laravel وأعددناه. سنستخدم تلك الإعدادات كقاعدة لبناء تطبيق صغير عليها؛ ونرى العناصر الأساسية لتطبيق يعمل على إطار العمل Laravel. هذا الدرس جزء من سلسلة تعلم Laravel والتي تنتهج مبدأ "أفضل وسيلة للتعلم هي الممارسة"، حيث ستكون ممارستنا عبارة عن إنشاء تطبيق ويب للتسوق مع ميزة سلة المشتريات. يتكون فهرس السلسلة من التالي: مدخل إلى Laravel 5.تثبيت Laravel وإعداده على كلّ من Windows وUbuntu.أساسيات بناء تطبيق باستخدام Laravel. (هذا الدرس)إنشاء روابط محسنة لمحركات البحث (SEO) في إطار عمل Laravel. نظام Blade للقوالب.تهجير قواعد البيانات في Laravel.استخدام Eloquent ORM لإدخال البيانات في قاعدة البيانات، تحديثها أو حذفها.إنشاء سلة مشتريات في Laravel.الاستيثاق في Laravel.إنشاء واجهة لبرمجة التطبيقات API في Laravel.إنشاء مدوّنة باستخدام Laravel.استخدام AngularJS واجهةً أمامية Front end لتطبيق Laravel.الدوّال المساعدة المخصّصة في Laravel.استخدام مكتبة Faker في تطبيق Laravel لتوليد بيانات وهمية قصدَ الاختبار. يشمل الدرس المواضيع التالية: مفهوم التوجيه Routing وآلية عمله.أداة Artisan التي تعمل على سطر الأوامر.استخدام أداة Artisan لتوليد شفرة مصدرية نمطية للمتحكّمات Controllers (وهي المسؤولة عن الإجابة على الطّلبات التي تردها Requests).إنشاء نداء لمتحكّم في مسار Route.تمرير المتغيّرات من متحكّم إلى عرض View.أساسيات التوجيه في Laravelعند وصول طلب إلى تطبيق Laravel تتلقى آلية التوجيه الطلب ثم تختار طريقة التعامل معه. سنرى في هذه الفقرة كيفية عمل هذه الآلية. أول ما سنفعله هو إنشاء مسار جديد؛ لذا افتح ملف routes.php الموجود على المسار app\Http بمحرّر النصوص المفضّل لديك. ملحوظة1: جميع المسارات المذكورة في الدرس هي بالنسبة لمجلد تطبيق Laravel. يعني هذا أن المقصود بالمسار (على افتراض أنك اتبعت خطوات الإعداد في الدرس السابق) app/Http هو المسار C:\laragon\www\larashop\app\Http على وندوز و /var/www/html/larashop/ على لينكس. ملحوظة2: انتبه لحالة الأحرف، كبيرة أو صغيرة. أضف المحتوى التالي إلى الملف routes.php : Route::get('/hello',function(){ return 'Hello World!'; });تعرّف الشفرة السابقة مسارًا لتلقي الطلبات من نوع GET (الدّالة get الموجودة في الصنف Route). المعطى الأول للدّالة هو الرابط المعني بالمسار (أي /hello) أما الثاني فهو دالة مجهولة الاسم Anonymous function تعرّف طريقة التعامل مع الطلب. يعني هذا أنه عند تلقي طلب على العنوان http://larashop.dev/hello فإن التطبيق ينفذ الدالة مجهولة الاسم والتي تُرجِع العبارة: Hello World!. احفظ الملف ثم اذهب إلى المتصفح وأدخل العنوان: http://larashop.dev/helloيجب أن تظهر صفحة بيضاء مطبوعة عليها عبارة: Hello World!. ربما تكون لاحظت وجود الشفرة التالية في الملف routes.php: Route::get('/', function () { return view('welcome'); });تتبع هذه الشفرة نفس المبدأ حيث تعرّف مسارا للطلبات الآتية على الرابط / أي رابط الصفحة الرئيسية (http://larashop.dev) وتحيلها إلى عرض باسمwelcome بدلا من طباعة عبارة مباشرةً (سنرى في ما بعد كيفية ذلك). أداة ArtisanArtisan عبارة عن أداة تُستدعى من سطر الأوامر لأتممة تنفيذ أنشطة اعتيادية على Laravel. يمكن استخدام أداة Artisan لتنفيذ الأنشطة التالية من بين أخرى: توليد شفرة نمطية: إنشاء المتحكّمات، النماذج Models، وغيرها بيُسر.تهجير قواعد البيانات Database migrations: وهو نظام لإدارة الإصدارات الخاصة بقواعد البيانات يجعل من التعامل مع الكائنات في قواعد البيانات (إنشاء جداول أو حذفها مثلا) أسهل عبر وصفها دون الحاجة للتعديل عليها مباشرةً.بذر قواعد البيانات Database seeding: وهو مصطلح يشير إلى إضافة بيانات وهمية للتسجيلات Records في جداول قاعدة البيانات لأغراض الاختبار.التوجيه: وهي الآليّة المسؤولة عن تلقي الطلبات ثم اختيار طريقة التعامل معها (عادةً بإرسالها إلى متحكّم).ضبط التطبيق.تشغيل الاختبارات الأحاديّة Unit tests.كيف تستخدم أداة Artisanافتح سطر الأوامر ثم انتقل إلى مجلد التطبيق. على افتراض أنك موجود في أصل المستند (C:\laragon\www على وندوز أو /var/www/html/ على لينكس): cd larashopثم نفّذ الأمر التالي: php artisan listملحوظة: تأكّد من وجودك في مجلّد التطبيق (أي المجلّد larashp) قبل تنفيذ الأمر artisan. تظهر أوامر artisan المتاحة للتنفيذ. مثال على النتيجة: Laravel Framework version 5.1.26 (LTS) Usage: command [options] [arguments] Options: -h, --help Display this help message -q, --quiet Do not output any message -V, --version Display this application version --ansi Force ANSI output --no-ansi Disable ANSI output -n, --no-interaction Do not ask any interactive question --env[=ENV] The environment the command should run under. -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug Available commands: clear-compiled Remove the compiled class file down Put the application into maintenance mode env Display the current framework environment help Displays help for a command inspire Display an inspiring quote list Lists commands migrate Run the database migrations optimize Optimize the framework for better performance serve Serve the application on the PHP development server tinker Interact with your application up Bring the application out of maintenance mode أمر Artisan لتوليد شفرة متحكمسنستخدم Artisan لتوليد متحكم بسيط يجيب على طلب Http ويحمّل عرضا باسم hello. افتح سطر الأوامر ونفذ الأمر التالي (تأكد من وجودك في مجلّد التطبيق قبل تنفيذ الأمر): php artisan make:controller Helloاستخدمنا أمر make:controller الذي توفره أداة Artisan لتوليد شفرةِ متحكّم ومررنا له اسم المتحكم الذي نريد توليده Hello. يُنشئ الأمر أعلاه ملفا باسم Hello.php على المسار app/Http/Controllers. يجب أن تظهر رسالة في سطر الأوامر تفيد بنجاح توليد المتحكم. نفتح ملف Hello.php لرؤية محتواه: <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests; use App\Http\Controllers\Controller; class Hello2 extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { // } /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create() { // } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { // } /** * Display the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { // } /** * Show the form for editing the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function edit($id) { // } /** * Update the specified resource in storage. * * @param \Illuminate\Http\Request $request * @param int $id * @return \Illuminate\Http\Response */ public function update(Request $request, $id) { // } /** * Remove the specified resource from storage. * * @param int $id * @return \Illuminate\Http\Response */ public function destroy($id) { // } }يعرف الملف فضاء اسم Namespace خاصًّا بالمتحكم: namespace App\Http\Controllers.تستورد التعليمات التالية (use Illuminate\Http\Request; use App\Http\Requests; use App\Http\Controllers\Controller;) الأصناف Classes المطلوبة لتشغيل المتحكم.الصنف Hello هو تمديد للصنف Controller الذي يعرّفه إطار العمل.بالنسبة للدوال التي يعرفها المتحكم فاستخدامها يكون كالآتي: الدالة index هي الدالة الافتراضية للمتحكم: تتعامل مع الطلبات الموجهة للرابط الجذر الذي يجيب عنه المتحكم.الدالة create: إنشاء موارد جديدة مثل تسجيلة في جدول قاعدة البيانات.الدالة store: تخزين مورد جديد مُنشَأ بالدالة السابقة.الدالة show: العثور على مورد اعتمادا على المعرِّف ID الممرَّر للدالة.الدالة edit: تتيح إمكانية التعديل على المورد الممرّر للدالة.الدالة update: تحديث مورد مخزّن.الدالة destroy: حذف مورد مخزَّن.سنأتي تباعا لتفصيل استخدام هذه الدوال في الوقت المناسب. توجيه طلب إلى متحكمفي المسار الذي عرفناه سابقا تُطبع العبارة مباشرة من داخل دالة التوجيه، وهذا خلط بين عناصر نموذج MVC، لذا سنضع المسار بين علامات تعليق لتجاهله: /* Route::get('/hello',function(){ return 'Hello World!'; }); */ثم نضيف شفرة مصدرية جديدة تنشئ مسارا لنفس الرابط hello/ ولكنها بدلا من طبع سلسلة محارف ترسل نداءً إلى المتحكم Hello وبالتحديد دالة index: Route::get('hello', 'Hello@index');نفتح ملف المتحكم لتحرير الدالة index لتأخذ المحتوى التالي: public function index() { return 'hello world from controller : )'; }احفظ الملف. افتح الآن الرابط http://larashop.dev/hello، وستجد أن الرسالة التالية تظهر في الصفحة: hello world from controller :)ما حدث هنا هو أن المسار الخاص بالرابط hello تلقى الطلب من المتصفح ثم أحاله إلى المتحكم Hello الذي طبع سلسلة محارف على الشاشة. تحميل عرض View انطلاقا من متحكماحترام مبادئ MVC يقضي بألا يتداخل عمل المتحكمات والعروض؛ فتنسيق ما يراه الزائر من مسؤولية العروض، ولذا فعلى المتحكم توكيل عرض بهذه المهمة مع إمداده بما يحتاجه من متغيرات. وهو ما سنفعله في الفقرات التالية. سيتلخص عملنا في ثلاث نقاط: إنشاء مسار لتلقي الطلب على الرابط hello/ وإرساله إلى المتحكم المناسب (تم هذا الأمر).إنشاء متحكم للإجابة عن الطلب (تم).إنشاء عرض لتنسيق ما سيظهر على شاشة الزائر.تغيير المتحكم لإرسال نداء إلى العرض وتمرير المتغيرات المناسبة إليه.أنشئ ملفا باسم hello.blade.php في المسار resources/views/ وأضف إليه المحتوى التالي: <!DOCTYPE html> <html> <head> <title>Laravel</title> <link href="//fonts.googleapis.com/css?family=Lato:100" rel="stylesheet" type="text/css"> <style> html, body { height: 100%; } body { margin: 0; padding: 0; width: 100%; display: table; font-weight: 100; font-family: 'Lato'; } .container { text-align: center; display: table-cell; vertical-align: middle; } .content { text-align: center; display: inline-block; } .title { font-size: 96px; } </style> </head> <body> <div class="container"> <div class="content"> <div class="title">Hello {{$name}}, welcome to Laraland! : )</div> </div> </div> </body> </html>يحوي الملف كما يظهر شيفرة HTML عادية مع تغييرين أساسيين، هما لاحقة اسم الملف (blade.php) واستخدام المتغير name في الملف. تشير اللاحقة إلى أن العرض يستخدم نظام Blade للقوالب الذي يأتي مضمّنا في Laravel (سنخصص درسا للعروض والقوالب). نكتفي الآن بالقول إن {{$name}} تطبع قيمة المتغير name. افتح الملف app/Http/routes.php وأضف الشفرة التالية: Route::get('/hello/{name}', 'Hello@show');يعمل هذا السطر على إنشاء مسار للروابط ذات الصيغة /hello/value حيث value قيمة ما، تخزن هذه القيمة في المتغير name ثم تمرّر إلى الدالة show الموجودة في المتحكم Hello. نفتح ملف المتحكم Hello ونعدّل الدالة show($id) لتصبح على النحو التالي: public function show($name) { return view('hello',array('name' => $name)); }نعرف الدالة show التي تقبل المعطى name.تحمِّل الدالة عرضا باسم hello.blade.php (يُكتفى بذكر اسم العرض دون اللاحقة blade.php) ثم تمرر له مصفوفة تحوي متغيراتٍ وقيمَها. في حالتنا يوجد متغير واحد باسم name وقيمته هي المعطى الممرَّر للدالة show.احفظ التعديلات التي أجريناها في الفقرات السابقة ثم افتح الرابط http://larashop.dev/hello/HsoubAcademy في المتصفح. يمكن أن نلاحظ هنا أن HsoubAcademy هي قيمة المتغير name الموجود في تعريف المسار التالي: Route::get('/hello/{name}', 'Hello@show');إذن: طلب الزائر العنوان http://larashop.dev/hello/HsoubAcademy في المتصفح.وجد التطبيق أن المسار الموافق لهذا الرابط هو المسار /hello/{name{ وأعطى القيمة HsoubAcademy للمتغير name ثم وجّه الطلب إلى الدالة show في المتحكم Hello.تلقت الدالة show الطلب ثم أرسلته إلى العرض hello مع إنشاء متغير جديد بنفس الاسم المستخدم في العرض وحددت قيمته بالمعطى الذي تلقته من المسار. كان بإمكان الدالة show إجراء عمليات على المعطى قبل تمريره.بدأ نظام القوالب العمل ووجد أن العرض hello يحوي متغيرا باسم name وأن قيمة هذا المتغير كما مررها له المتحكم هي HsoubAcademy، فوضعها مكان المتغير وأرسل النتيجة إلى المتصفح.خاتمةتعرفنا في هذا الدرس على الآلية الأساسية التي تعمل بها تطبيقات Laravel. من المهم جدّا فهم محتويات هذا الدرس قبل الانتقال إلى الدروس الموالية. ترجمة -وبتصرّف- لمقال Laravel 5 Hello World لصاحبه Rodrick Kazembe.
  3. الأحداث Events هي إجراءات يمكن لبرنامج التعرّف عليها ومن ثمّ التعامل معها. يمكن أن نأخذ مثالا على ذاك عملية تسجيل مستخدم في الموقع. عندما يكمل زائر ملء حقول الاستمارة الخاصّة بالتسجيل في موقع فإنه يكون قد فعّل حدثا يمكننا تسميّته "التسجيل في الموقع"؛ نستطيع بعد ذلك التعامل مع هذا الحدث لنقرّر ما التالي. كانت الطريقة القديمة تقضي بكتابة شفرة برمجية مباشرةً في المتحكّم Controller للتعامل مع الحدث؛ إلا أن توسّع التطبيق وزيادة الحاجة للتعامل مع أحداث جديدة يجعل الحاجة أكبر لطريقة جديدة أنظف وأكثر قابلية للتمديد. يستجيب Laravel لهذه الحاجة بالصنف Event والأدوات المساعدة المتعلّقة به. لا تقتصر الأحداث على إرسال البريد؛ إلا أن هذا الأخير وسيلة سهلة لشرح المبدأ. سنرى في هذا الدرس الخطوات اللازمة لتعريف حدث واستخدامه. نفترض أن لدينا جدولا ببيانات الاتصال ببعض الأشخاص؛ ونريد أن نرسل لأحدهم بريدا في كل مرة يُعرَض فيها الرابط contacthandler/id/ حيث id معرّف من نرسل له البريد. سنستخدم في هذا الجدول نفس المشروع الذي أنشأناه في الدرس السابق ونزيد عليه بإنشاء نموذج Model وجدول بيانات لحفظ جهات الاتّصال. تهيئة المشروع لا تدخل الخطوات الواردة في هذا العنوان في صميم إدارة الأحداث في Laravel؛ إلا أننا سنحتاجها للحصول على نتيجة مرئية للمثال الذي نبنيه. سنمرّ على الخطوات دون الكثير من التفاصيل لورودها في دروس سابقة. إنشاء النموذج Contact ننفذ الأمر التالي في مجلّد المشروع لإنشاء نموذج باسم Contact مع ملف التهجير الخاصّ به: php artisan make:model Contact -m نعدّل ملف التهجيرعلى النحو التالي: public function up() { Schema::create('contacts', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('email')->unique(); $table->string('subject'); $table->text('body'); $table->timestamps(); }); } أضفنا الحقول التي نريد استخدامها في جدول البيانات. ننفذ أمر التهجير لإنشاء الجدول: php artisan migrate بذر البيانات سنحتاج لبيانات اتصّال للتجربة عليها. نستخدم معمل نماذج Model factory لبذر الجدول contacts. ننشئ ملفا باسم ContactFactory.php في المسار /app/factories ونضيف إليه المحتوى التالي: <?php $factory->define(App\Contact::class, function (Faker\Generator $faker){ return [ 'name' => $faker->name, 'email' => $faker->unique()->safeEmail, 'subject' => $faker->word, 'body' => $faker->text ]; }); يستدعي معمل النماذج مكتبة Faker لإدراج بيانات في جدول contacts عبر النموذج Contact. ثم ننشئ صنفا للبذر: php artisan make:seeder ContactTableSeeder نستورد النموذج Contact في صنف البذر ContactTableSeeder ونستخدم معمل النماذج لتوليد بيانات عشوائية: <?php use Illuminate\Database\Seeder; use App\Contact; class ContactTableSeeder extends Seeder { public function run() { Contact::truncate(); factory(Contact::class, 50)->create(); } } نأتي الآن للخطوة الأخيرة قبل تنفيذ أمر البذر وهي تحرير الملف DataBaseSeeder.php ليستدعي الصّنف ContactTableSeeder في الدالة run: $this->call(ContactTableSeeder::class); ثم ننفذ البذر: php artisan db:seed المسار والمتحكم نفتح ملف routes.php لإضافة مسار للرابط contacthandler/id/: Route::get('/contacthandler/{id}', 'HomeController@contactHandler'); يحيل ملف المسارات الطلب على الرابط إلى الدالة contacthandler في المتحكّم HomeController. نذهب للمتحكم ونضيف هذه الدالة: public function contactHandler($id) { } نترك الدالة خاوية لحد الساعة. التعامل مع الأحداث جهزنا المشروع ويمكننا الآن الانتقال إلى الأحداث والتعامل معها. نذكّر بأننا نريد إرسال بريد لصاحب جهة الاتصال عند طلب رابط به معرّفه. مثلا نرسل بريدا إلى جهة الاتصال ذات المعرّف 6 عند زيارة يُعرَض فيها الرابط contacthandler/6/. نحصُل على البريد الإلكتروني الذي سنرسل إليه الرسالة من خلال النموذج Contact؛ إضافة لاسمه وموضوع الرسالة وفحواها. ولدنا هذه البيانات تلقائيا أثناء البذر السابق. تعريف الحدث يتلخّص الأمر في أننا سننفّذ إجراءً عند وقوع الحدث "زيارة رابط المعرّف الخاص بجهة الاتصال". سنسميّ الصنف الموافق للحدث بـ ContactHandlerAction. نستعين بأداة artisan لإنشاء الحدث على النحو التالي: php artisan make:event ContactHandlerAction ستجد أن الأداة أنشأت الملف ContactHandlerAction.php على المسار app/Events وأضافت إليه المحتوى التالي (بعد نزع التعليقات): class ContactHandlerAction extends Event { use SerializesModels; public function __construct() { } public function broadcastOn() { return []; } } سنضيف خاصيّة إلى الصّنف ليصبح كالتالي: class ContactHandlerAction extends Event { use SerializesModels; public $contact; public function __construct($contact) { $this->contact = $contact; } public function broadcastOn() { return []; } } أضفنا متغيّرا جديدا لحفظ بيانات الاتصال واستخدامها عند الحاجة. نعود لدالّة contactHandler في المتحكّم HomeController ونعدّلها لتصبح كما يلي: public function contactHandler($id) { $contact = Contact::find($id); event (new ContactHandlerAction($contact)); return view('mail.success_view'); } نحتاج لاستيراد الصنفين المستخدمين في الدالة (النموذج والحدث) إلى المتحكّم: use App\Contact; use App\Events\ContactHandlerAction; عند استدعاء الدالة contactHandler نأخذ المعطى id المُمرَّر في الرابط ونحيله إلى الدالة find في النموذج Contact من أجل العثور عليه في قاعدة البيانات. في التعليمة التالية يبدأ عمل آلية إدارة الأحداث في Laravel: نستدعي الدالة المساعدة event ونمرّر لها كائنا من الحدث ContactHandlerAction الذي أنشأناه قبل قليل. نستخدم جهة الاتصال التي عثرنا عليها contact$ من أجل بناء الحدث. يعني هذا أننا أطلقنا الحدث “زيارة رابط المعرّف الخاص بجهة الاتصال” وزوّدناه بجهة الاتصال المعنيّة. مالذي يترتّب عن إطلاق الحدث؟ هذا هو ما سنراه الآن. التعامل مع الحدث توجد في Laravel ما تُعرَف بالمستمعات Listeners ومهمتها انتظار وقوع أحداث لتبدأ عملها. نعود لأداة artisan وننفّذ الأمر التالي: php artisan make:listener EmailContactListener --event="ContactHandlerAction" ينشئ الأمر صنفا باسم EmailContactListener للاستماع للأحداث ContactHandlerAction. يوجد الصّنف EmailContactListener في المجلّد /app/Listeners ومحتواه التالي: class EmailContactListener { public function __construct() { // } public function handle(ContactHandlerAction $event) { // } } لاحظ أن الدالة handle تنتظر معطى من نوع ContactHandlerAction. يشغّل المستمِع الدالة handle بعد تلقيه إخطارا بإطلاق الحدث ContactHandlerAction. سنعود بعد قليل للدالة handle بعد أن نعرّف الآلية التي سيُخطَر بها المستمِع بوقوع الحدث. ربط الحدث بآلية التعامل معه يوفّر Laravel مزوّد خدمة خاصًّا بالأحداث اسمه EventServiceProvider يوجد في المجلّد /app/Providers. إن فتحت الملف EventServiceProvider.php ستجد التالي (إن لم يسبق لأحد التعديل عليه): class EventServiceProvider extends ServiceProvider { protected $listen = [ 'App\Events\ContactHandlerAction' => [ 'App\Listeners\EmailContactListener', ], ]; public function boot(DispatcherContract $events) { parent::boot($events); // } } ما يهمّنا هنا هو الخاصيّة listen$. نعدّلها لتصبح على النحو التالي: protected $listen = [ 'App\Events\ContactHandlerAction' => [ 'App\Listeners\EmailContactListener', ], ]; يمكن تعريف أكثر من مستمِع لنفس الحدث (لاحظ أن قيمة App\Events\ContactHandlerAction في الخاصيّة هي مصفوفة Array). تمكننا الآن العودة إلى المنصِت بعد أن أعلمنا Laravel بالرابط بينه والحدث. تنفيذ آلية التعامل مع الحدث نعدّل الدالة handle لتصبح على النحو التالي: public function handle(ContactHandlerAction $event) { $data = [ 'name' => $event->contact->name, 'email' => $event->contact->email, 'subject' => $event->contact->subject, 'body' => $event->contact->body ]; Mail::send('mail.email_view',$data , function($message) use($data) { $message->from('hsoub@academy.com', 'Hsoub Academy'); $message->to($data['email'])->subject($data['subject']); }); if (Mail::failures()){ throw new EmailSendingException; } } نعرّف مصفوفةً باسم data$ تحوي اسم جهة الاتصال، بريدها الإلكتروني، موضوع الرسالة وفحواها. نعثُر على هذه البيانات من الكائن contact الذي مررناه للحدث event عند زيارة الرابط. نستخدم هذه البيانات لجدولة إرسال بريد عبر الدالة Mail::send. تأخذ الدالة Mail::send عرضا View تستخدمه لتنسيق الرسالة والبيانات التي أخذناها من جهة الاتصال. ثم نختبر أخطاء إرسال البريد الإلكتروني بالدالة Mail::failures؛ فإن وجدت أخطاء استخدمنا استثناءً Exception مخصّصًا لمعالجتها. يمكنك الحصول على هذا الاستثناء في الملف المرفق أو مراجعة الدرس الخاص بالاستثناءات. نحن الآن جاهزون تقريبا لتجربة الأحداث والتعامل معها في Laravel. بقي لنا إنشاء بضعة عروض لحاجات تطبيقنا المختلفة: mail.success_view في المتحكّم، mail.email_view لتنسيق الرسالة، إضافة لعروض أخرى قد تحتاجها أثناء تخصيص الاستثناء. يمكنك الحصول عليها في الملف المرفق. بقيت لنا خطوة أخيرة قبل اختبار التطبيق: إعداد Laravel لإرسال البريد. يوفّر Laravel وسيلة لمحاكاة إرسال البريد يمكننا استخدامها للتجربة. ابحث في ملف متغيّرات النظام env. عن المعطى MAIL_DRIVER وعيّن قيمته بـlog: MAIL_DRIVER=log نطلُب بهذه الطريقة من Laravel كتابة البريد في ملفّ السجل بدلا من إرساله. هذه الطريقة مفيدة أثناء الاختبار. افتح الرابط التالي في المتصفّح (ضع اسم المضيف المناسب بدلا من laraveltips): http://laraveltips.dev/contacthandler/45 ملحوظة: لا يتعدّى عدد جهات الاتصال التي أضفناها الخمسين؛ سيؤدي البحث عن جهة اتصال بمعرّف أكبر من 50 إلى خطأ يمكنك التقاطه في استثناء كما فعلنا في درس استخدام الاستثناءات Exceptions المخصَّصة في Laravel. ثم اذهب إلى ملف السجل storage/logs/laravel.log للتأكد من نجاح معالجة الحدث؛ ستجد في آخر الملف ما يشبه التالي: [2016-03-30 22:16:29] local.DEBUG: Message-ID: <8c79163440df2d3b5bbb3efd97ad549f@laraveltips.dev> Date: Wed, 30 Mar 2016 22:16:29 +0000 Subject: distinctio From: Hsoub Academy <hsoub@academy.com> To: fFranecki@example.net MIME-Version: 1.0 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable يليه محتوى العرض المستخدم لتنسيق الرسالة (mail.email_view في المثال). إن حدثت مشكلة في إرسال البريد فسيتولّى الاستثناء -إن أعددتَه - إظهار العرض المناسب للزائر؛ وفي حال سريان الأمور على الوجه الأمثل فسيُظهر المتحكّم العرض mail.success_view للدلالة على نجاح الإرسال. الملف المرفق: الأحداث في Laravel.zip ترجمة -وبتصرّف- للمقال Eventing in Laravel 5.1 لصاحبه Bill Keck.
  4. يجد الكثير من المبتدئين استخدام PHPUnit لاختبار Test الشيفرة المصدرية التي يكتبونها أمرا معقّدا للغاية؛ بدءًا من تثبيت مكتبات الاختبار وليس انتهاء بتشخيص أخطاء عملها. يسهّل Laravel 5 الكثير من هذه التعقيدات إذ يأتي مضمّنا مبدئيّا بمكتبة PHPUnit جاهزة للعمل. يمكنك التأكّد من ذلك بالدخول إلى مجلّد المشروع ثم تنفيذ الأمر التالي: vendor/bin/phpunit --version قد تظهر لك مشكلة في اعتمادات Composer إلا أنه يمكن تجاوزها بتنفيذ الأمر على النحو التالي: vendor/phpunit/phpunit/phpunit --version تظهر بعد تنفيذ الأمر المناسب النتيجة التالية (قد يختلف رقم الإصدار لديك): PHPUnit 4.8.24 by Sebastian Bergmann and contributors. استخدم الأمر الصحيح من بين الأمريْن أعلاه في بقية هذا الدرس. يأتي Laravel مبدئيّا بمثال لاختبار؛ سننظر في هذا المثال لنرى كيف تعمل اختبارات PHPUnit. افتح الملف ExampleTest.php الموجود في المجلّد tests المتفرّع عن مجلّد المشروع: <?php use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Foundation\Testing\DatabaseTransactions; class ExampleTest extends TestCase { /** * A basic functional test example. * * @return void */ public function testBasicExample() { $this->visit('/') ->see('Laravel 5'); } } يمكننا تجربة الاختبار بتنفيذ الأمر التالي الذي ينفّذ جميع الاختبارات الموجودة في الملف tests المذكور أعلاه: vendor/bin/phpunit يبحث الاختبار أعلاه عن سلسلة المحارف Laravel 5 في الصفحة الرئيسية للمشروع التي يشير إليها المسار /. إن كان مشروع Laravel الذي تعمل عليه جديدا فسيظهر سطران كالتالي: Time: 118 ms, Memory: 12.00Mb OK (1 test, 1 assertion) يخبر السطر الأول بالوقت الذي استغرقه الاختبار وحجم الذاكرة المستخدمة لتنفيذه. أما السطر الثاني فيُظهر عدد الاختبارات وعدد التأكيدات Assertions ونتيجتها. لدينا اختبار واحد (ExampleTest) وتأكيد واحد (الصفحة الرئيسية تحوي سلسلة المحارف Laravel 5). بما أن نتيجة الاختبار إيجابية فإن السطر الأخير يظهر باللّون الأخضر. فلنجرّب إخفاق الاختبار. نستبدل Laravel 6 بـ Laravel 5 في ملف الاختبار ثم نعيد تنفيذ الأمر: vendor/bin/phpunit ستظهر الكثير من المُخرجات تحوي الرسائل التالية إضافة لشفرة الصفحة الرئيسية التي نختبرها: Time: 115 ms, Memory: 12.00Mb There was 1 failure: Couldn't find [Laravel 6] on the page. Check content above. FAILURES! Tests: 1, Assertions: 1, Failures: 1. يخبر السطر الأول بالمدة التي استغرقها الاختبار والحجم الذي أخذه في الذاكرة، بينما يشرح السّطر الثالث سبب الإخفاق وهو عدم القدرة على إيجاد سلسلة المحارف Laravel 6 في الصفحة. يلخّص السّطر الأخير نتيجة الأمر التي تظهر باللون الأحمر دلالة على إخفاق الاختبارات. إن حصل خطأ في الشفرة المصدرية للاختبار نفسه (نسيت مثلا ; في نهاية تعليمة) فلن يُنفَّذ الاختبار وستظهر رسالة بما حدث. اختبار استمارة Form تسجيل مستخدم يقضي المطوّرون المبتدئون الكثير من الوقت لاختبار الاستمارات على تطبيقاتهم، إلا أن Laravel يجعل الأمر في منتهى السهولة. سنجرّب في هذه الفقرة اختبار استمارة لتسجيل مستخدم جديد في التطبيق. سنستعين بأداة Artisan لإنشاء أساسيات الاستيثاق (عروض Views التسجيل، الدخول وإعادة تعيين كلمة السر بالإضافة إلى المتحكّمات Controllers والمسارات Routes الضرورية): php artisan make:auth ينشئ الأمر السابق عرضا على المسار register/ به استمارة لتسجيل مستخدم جديد. سنستخدم PHPUnit لاختبار هذه الاستمارة. نستعين بأداة Artisan من جديد لإنشاء ملف للاختبار: php artisan make:test RegistrationTest ملحوظة: تأكّد من إعداد قاعدة البيانات في مشروعك. ينشئ الأمر ملفا باسم RegistrationTest.php في المجلّد tests. نفتح الملف ونعدّله على النحو التالي: <?php use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Foundation\Testing\DatabaseTransactions; class RegistrationTest extends TestCase { use DatabaseTransactions; public function testNewUserRegistration() { $this->visit('/register') ->type('bob', 'name') ->type('hello1@in.com', 'email') ->type('hello1', 'password') ->type('hello1', 'password_confirmation') ->press('Register') ->seePageIs('/'); } } لاحظ أن الشفرة تطلُب زيارة المسار register/، إدخال المعطيات المذكورة في الاستمارة باستخدام الدالة type، الضغط على الزّر Register ثم النظر في النتيجة هل هي المسار /. تأخذ الدالة type معطيين، الأول قيمة حقل الاستمارة والثاني اسم الحقل. يجب أن تكون أسماء المعطيات في الدالة موافقة لحقول الاستمارة. في حالتنا توجد أربعة حقول في الاستمارة. نستخدم الطّابع DatabaseTransactions Trait في الاختبار. يمكّننا هذا الطّابع من إلغاء إدراج تسجيلات جديدة في قاعدة البيانات حتى لا نملأها من غير جدوى. كما نستخدم الطّابع DatabaseMigrations للتراجع عن التهجيرات Migrations وبالتالي حذف الجدول من قاعدة البيانات. الطابع الثالث المستخدم في الاختبار هو طابع WithoutMiddleware الذي يسمح بتخطّي بعض إجراءات الحماية لأغراض الاختبار. يمكننا الآن تنفيذ الاختبار كما يلي: vendor/bin/phpunit tests/RegistrationTest.php النتيجة: Time: 401 ms, Memory: 16.50Mb OK (1 test, 5 assertions) يمكنك قراءة المزيد عن الاختبارات على موقع PHPUnit؛ فقط تذكّر أن الصنف الذي نمدّده في Laravel هو TestCase بدلا من PHPUnit_Framework_TestCase في تطبيقات PHP أخرى. ترجمة -وبتصرّف- لمقال How Use PHPUnit Test in Laravel 5.1 لصاحبه Bill Keck.
  5. يأتي Laravel مبدئيا بمعمل نماذج Model factory يُستخدَم لتسريع بناء النماذج واختبارها. سنرى في هذا المقال طريقتين لإدراج تسجيلات في جدول قاعدة بيانات باستخدام معمل النماذج. سنعتمد في الخطوات الموالية على النموذج الذي أنشأناه في الدرس السابق كيف تنشئ نموذجا (Model) في Laravel. الطريقة الأولى: بذر جدول البيانات نبدأ بفتح الملف database/factories/ModelFactory.php. يأتي الملف مبدئيا بدالّة لـبذر جدول المستخدمين: $factory->define(App\User::class, function (Faker\Generator $faker) { return [ 'name' => $faker->name, 'email' => $faker->email, 'password' => bcrypt(str_random(10)), 'remember_token' => str_random(10), ]; }); لاحظ استخدام مكتبة Faker عبر المتغيّر faker$. يجب تنفيذ التهجيرات المبدئية التي تأتي مع Laravel لإنشاء الجداول في قاعدة البيانات حتى يمكن إدراج تسجيلات فيها. سنعلّق الدّالة السابقة ونضيف دالة جديدة على النحو التالي: $factory->define(App\Widget::class, function ($faker) { return [ 'widget_name' => $faker->unique()->word, ]; }); تستدعي الشفرة السابقة مكتبة faker$ لتوليد كلمة word لإدراجها في حقل widget_name، نطلُب من المكتبة التأكد أن الكلمة وحيدة uniq لنوافق القيد الموجود على حقل الاسم في الجدول. بقيت لنا خطوة قبل بذر الجدول باستخدام أمر artisan. ننتقل إلى المجلّد database/seeds، نفتح الملف DatabaseSeeder.php ونعدّله ليصبح كالتالي: <?php use Illuminate\Database\Seeder; use Illuminate\Database\Eloquent\Model; use App\Widget; class DatabaseSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { Model::unguard(); Widget::truncate(); factory(Widget::class, 50)->create(); Model::reguard(); } } تعمل دالة unguard على تعطيل الحماية مؤقّتا على النموذج لحين إدراج التسجيلات، بينما تعيد دالة reguard تفعيلها. تعمل الدّالة truncate على حذف جميع التسجيلات في الجدول لتهيئة عمليّة البذر. نطلُب داخل دالة factory إدراج 50 تسجيلة في جدول النموذج المذكور Widget. نحن الآن جاهزون لتنفيذ أمر البذر: php artisan db:seed الأمر سهل للغاية. يمكنك إن أردت التراجع بنفس السهولة عن الأمر وحذف التسجيلات المدرجة بتنفيذ الأمر: php artisan migrate:rollback الطريقة الثانية: استخدام الاختبارات توجد طريقة أخرى غير السابقة لإدراج بيانات وهميّة في جدول بيانات. إن لم تكن لديك فكرة عن التّطوير الموجَّه بالاختبارات Test-driven development, TDD فيمكنك أخذ فكرة عن الأساسيات في مقال كيف تستخدم PHPUnit لاختبار تطبيقات Laravel. سيكون من الجيّد لك التعوّد على استخدام الاختبارات في أعمال التطوير، خصوصا أن Laravel يسهّل الأمر كثيرا. نبدأ بالانتقال إلى ملفّ tests/ExampleTest.php ثم ننشئ نسخة منه باسم WidgetTest.php ونعدّلها لتصبح على النحو التالي: <?php use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Foundation\Testing\DatabaseTransactions; use App\Widget; class WidgetTest extends TestCase { use DatabaseTransactions; /** * A basic functional test example. * * @return void */ public function testWidgetFactory() { $widgets = factory(Widget::class, 50)->create(); dd($widgets); } } أنشأنا دالة اختبار تستدعي دالة المعمل لإدراج تسجيلات الجدول. بما أننا نستخدم: use DatabaseTransactions; فإن التسجيلات لن تبقى مخزّنة في قاعدة البيانات أكثر من حاجة الاختبار. يؤدي استخدام الدالة dd إلى طباعة محتوى النماذج في الطرفيّة عند نجاح الاختبار: dd($widgets); سنحتاج قبل تنفيذ الاختبار إلى حذف محتوى الجدول الناتج عن الطريقة الأولى، لذا ننفذ أمر إرجاع التهجير: php artisan migrate:rollback ثم نعيد تنفيذ التهجير لإنشاء الجدول من جديد: php artisan migrate نحن الآن جاهزون لتنفيذ الاختبار: vendor/bin/phpunit tests/WidgetTest.php أو إن كان مسار PHPUnit مختلفا كما ذكرنا في درس كيف تستخدم PHPUnit لاختبار تطبيقات Laravel: vendor/phpunit/phpunit/phpunit tests/WidgetTest.php إن كنت ترغب في إبقاء بيانات الاختبار في الجدول فيمكنك تعليق استخدام الصنف التالي: // use DatabaseTransactions; يمكن أن تظهر أخطاء عند إعادة تنفيذ الاختبار بعد إبقاء بيانات الاختبار السابق في جدول البيانات. يعود السبب في ذلك إلى أن مكتبة Faker لا تعرف مالذي يوجد في جدول البيانات وبالتالي يمكن أن تولّد بيانات لا تحترم شرط عدم التكرار في محتوى الحقل widget_name. توجد خيارات عدّة لتجاوز هذا الأمر، إما بإرجاع التهجير لحذف الجدول ثم تنفيذ التهجير مرة أخرى لإنشاء الجدول من جديد وبعدها ينفَّذ الاختبار؛ أو استخدام دالة truncate لحذف التسجيلات من الجدول قبل توليد تسجيلات جديدة. في كلتا الحالتين تُفقَد البيانات السابقة على تنفيذ الاختبار. ترجمة -وبتصرّف- لمقال Using Model Factory to make Test Data in Laravel 5.1 لصاحبه Bill Keck.
  6. يقدّم هذا الدرس كيفية إنشاء نموذج Model قاعدي في Laravel ومن ثم استخدامه. النماذج هي الجزء من بنية MVC الذي تُعالَج فيه البيانات وتُنفَّذ عليها قواعد التطبيق. يتطلّب الدرس تثبيت Laravel وإعداده مع قاعدة البيانات. نبدأ بإنشاء النموذج باستخدام أمر artisan على النحو التالي: php artisan make:model Widget -m سمّينا النموذج بـWidget وأضفنا خيار m- لإنشاء التهجير في نفس الوقت. يختزل الأمر بهذه الطريقة الكثير من الوقت ويجعلنا نركّز على الأهم: عمل النموذج. ستجد بعد اكتمال تنفيذ الأمر ملفا باسم Widget.php في مجلّد app المتفرّع عن مجلّد المشروع، وملفًّا للتهجير في المجلّد database/migrations. يظهر اسما الملفّيْن في مخرجات تنفيذ الأمر السّابق. نفتح ملفّ Widget.php للنظر في محتواه: <?php namespace App; use Illuminate\Database\Eloquent\Model; class Widget extends Model { // } هذا كلّ ما يوجد الملف! هيكل نموذج يمكننا الاستفادة منه لإنشاء ما نريد. بالانتقال إلى مجلّد database/migrations نجد ملفًّا يشبه التالي: 2016_03_19_163722_create_widgets_table.php يظهر في بداية اسم الملف ختم زمني بتاريخ إنشائه. يفترض Laravel أن اسم النموذج كلمة مفردة (Widget مثلا) تبدأ بحرف كبير Uppercase، في حين يتوقّع أن يكون اسم الجدول Table جمعًا (widgets) يبدأ بحرف صغير. يمكن تفسير الأمر بأن النموذج يُرجِع نظيرا واحدا لتسجيلات الجدول. إذا نظرنا إلى ملفّ التهجيرات فسنجد أن لدينا قاعدة يمكننا البناء عليها لأمور أكثر تقدّما: use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateWidgetsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('widgets', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('widgets'); } } ينشئ Laravel تلقائيا حقل المعرّف في قاعدة البيانات id ويجعله يتقدّم تلقائيا فور إدراج تسجيلة جديدة في الجدول (autoincrement) وذلك باستخدام الدّالة increments. يضيف Laravel كذلك ختمين زمنيّين لتاريخَيْ إنشاء التسجيلة وتحديثها timestamps. سنضيف عمودا Column جديدا إلى قاعدة البيانات؛ لذا نضيف التعليمة التالية إلى دالّة up ضمن ملفّ التهجير: $table->string('widget_name')->unique(); يضيف السّطر أعلاه عمودا جديدا للجدول باسم widget_name من نوع string ويقيّده بـunique لكي لا توجد تسجيلتان في الجدول بنفس الاسم. تصبح دالّة up في ملف التهجير بعد إضافة العمود على النحو التالي: public function up() { Schema::create('widgets', function (Blueprint $table) { $table->increments('id'); $table->string('widget_name')->unique(); $table->timestamps(); }); } ثم ننفّذ التهجير: php artisan migrate ستلاحظ بعد تنفيذ الأمر إنشاءَ جدول جديد في قاعدة البيانات لديك. بما أننا نخطّط لإدراج تسجيلات إلى قاعدة البيانات بالتطبيق فيجب أن نضيف الخاصيّة التالية إلى النموذج Widget: /** * The attributes that are mass assignable. * * @var array */ protected $fillable = ['widget_name']; تخبر هذه التعليمة Laravel أن العمود widget_name يدعم الإسناد الشّامل Mass assignment (تحديد قيم معطياتٍ عدّةٍ مرة واحدة). إن لم نضف هذه التعليمة فلن يمكننا باستخدام التطبيق إدراجُ تسجيلات جديدة في الجدول. سنستخدم في الدرس التالي النموذج الذي أنشأناه أعلاه مع معمل النماذج Model factory في Laravel لملْء تسجيلات في جدول widgets. ترجمة -وبتصرّف- للمقال How to Make a Model in Laravel 5.1 لصاحبه Bill Keck.
  7. تلعب روابط URL دورا هامًّا في العثور على مواقع الويب؛ لذا من المهم تحسينها لمحركات البحث Search Engine Optimization. سنرى في هذا الدرس طريقةً لجعل روابط مشروع larashop محسّنة للمحركات. رأينا في الدرس السابق كيف تعمل المسارات والمتحكمات، سنبني على هذه المعرفة التي اكتسبناها من أجل الوصول إلى الهدف المحدّد. هذا الدرس جزء من سلسلة تعلم Laravel والتي تنتهج مبدأ "أفضل وسيلة للتعلم هي الممارسة"، حيث ستكون ممارستنا عبارة عن إنشاء تطبيق ويب للتسوق مع ميزة سلة المشتريات. يتكون فهرس السلسلة من التالي: مدخل إلى Laravel 5.تثبيت Laravel وإعداده على كلّ من Windows وUbuntu.أساسيات بناء تطبيق باستخدام Laravel.إنشاء روابط محسنة لمحركات البحث (SEO) في إطار عمل Laravel. (هذا الدرس)نظام Blade للقوالب.تهجير قواعد البيانات في Laravel.استخدام Eloquent ORM لإدخال البيانات في قاعدة البيانات، تحديثها أو حذفها.إنشاء سلة مشتريات في Laravel.الاستيثاق في Laravel.إنشاء واجهة لبرمجة التطبيقات API في Laravel.إنشاء مدوّنة باستخدام Laravel.استخدام AngularJS واجهةً أمامية Front end لتطبيق Laravel.الدوّال المساعدة المخصّصة في Laravel.استخدام مكتبة Faker في تطبيق Laravel لتوليد بيانات وهمية قصدَ الاختبار. سنغطّي في هذا الدرس موضوعين أساسيّين: العوامل المؤثّرة في التحسين لمحركات البحث.كيفية إنشاء روابط محسّنة لمحركات البحث في Laravel.العوامل المؤثرة في التحسين لمحركات البحثلا نهدف إلى تقديم دليل شامل عن التحسين لمحركات البحث؛ ما نريده هنا هو ذكر بضعة عوامل يجب على المطور أن يكون على اطّلاع عليها. في ما يلي عوامل تؤثر على تقويم محركات البحث مثل Google لصفحات الويب: سرعة الموقع: يحب الجميع أن تظهر صفحة الويب التي يزورها بسرعة، فلا أحد يحب الانتظار إلى ما لا نهاية حتى تظهر الصفحة التي يطلبها. من الأحسن ألا يتعدى زمن تنزيل الصفحة ثانيتين وكل ما قلّ كلّ ما كان الأمر أفضل. يجب عليك بوصفك مطوّرا اختبارُ سرعة تطبيقك وإجراء التحسينات اللازمة إن اقتضت الضرورة.إحصاءات الشبكات الاجتماعية: من الطبيعي، عند قراءتك شيئا مهمّا، مشاركتُه مع متابعيك وأصدقائك على الشبكات الاجتماعية؛ وهذا دليل على الأهمية بالنسبة لمحركات البحث. دورك كمطور هو توفير الأدوات التي تسهل على الزوار مشاركة محتوى الموقع.تصميم تجاوبي Responsive: يمثل مستخدمو الأجهزة المتنقلة جزءًا كبيرًا من مستخدمي خدمات الويب. من هذا المنطلق يجب التأكد من أن موقع الويب يظهر بشكل صحيح على أجهزة الجوّال، الأجهزة اللوحية وأجهزة سطح المكتب؛ إذ أن تجربة المستخدم من العوامل المؤثرة في تقويم محركات البحث.الكلمات المفتاحية Keywords: تصنف محركات البحث مليارات صفحات الويب حسب الكلمات الأساسية الواردة فيها. يتمثل دور المطور في التأكد من توفير آليات مثل الوسوم Tags، أوصاف meta، وعناوين HTML يمكن لكاتب المحتوى استخدامها لتمييز المحتوى المفتاحي.روابط URL الخاصة بالموقع: يجب أن تظهر الكلمات المفتاحية في روابط الموقع.كيفية إنشاء روابط محسنة لمحركات البحث في Laravelعرضنا لأساسيات تحسين محركات البحث مع ذكر دور المطور في تنفيذها. سنبدأ الآن في وضع هذه المبادئ موضع التنفيذ. سننشئ مسارات ونربطها بمت حكم. يُظهر الجدول التالي الروابط التي سيتكون منها متجرنا الإلكتروني. التسلسل الرابط الدالة الوصف1/indexالصفحة الرئيسية2/productsproductsصفحة المنتجات3/products/details/{id}product_details(id)صفحة المنتج ذي المعرّف id4/products/categoryproduct_categoriesعرض تصنيفات المنتجات5/products/brandsproduct_brandsعرض العلامات التجارية للمنتجات6/blogblogعرض فهرس بمنشورات المدونة7/blog/post/{id}blog_post($id)عرض محتوى التدوينة ذات المعرّف id8/contact-uscontact_usعرض صفحة الاتصال9/loginloginصفحة تسجيل الدخول10/logoutlogoutتسجيل خروج المستخدم11/cartcartعرض محتوى سلة المشتريات12/checkoutcheckoutصفحة الدفع13/search/{query}search($query)عرض نتائج البحث في الموقعتعريف مسارات الروابطسنعرّف مسارا لكل من الروابط الموجودة في الجدول أعلاه، لذا نفتح الملف app/Http/routes.php ونعدّل المحتوى بحيث يصبح التالي: <?php /* |-------------------------------------------------------------------------- | Application Routes |-------------------------------------------------------------------------- | | Here is where you can register all of the routes for an application. | It's a breeze. Simply tell Laravel the URIs it should respond to | and give it the controller to call when that URI is requested. | */ Route::get('/','Front@index'); Route::get('/products','Front@products'); Route::get('/products/details/{id}','Front@product_details'); Route::get('/products/category','Front@product_categories'); Route::get('/products/brands','Front@product_brands'); Route::get('/blog','Front@blog'); Route::get('/blog/post/{id}','Front@blog_post'); Route::get('/contact-us','Front@contact_us'); Route::get('/login','Front@login'); Route::get('/logout','Front@logout'); Route::get('/cart','Front@cart'); Route::get('/checkout','Front@checkout'); Route::get('/search/{query}','Front@search');احفظ الملف. يستدعي كلٌّ مسار دالة المتحكم Front الموافقة له، حسب الجدول أعلاه. بقي الآن إنشاء المتحكم وكتابة الدوال. نستخدم أداة Artisan لإنشاء شفرة نمطية لمتحكم Laravel. تأكد من وجودك في مجلد المشروع larashop ثم نفذ الأمر التالي: php artisan make:controller Front يعني ظهور الرسالة التالية أن الأمر نُفذ كما يجب: Controller created successfully.افتح ملف المتحكم Font الذي أنشأناه للتو (app/Http/Controllers/Front.php) وعدّل عليه بحيث يصبح محتواه التالي: <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class Front extends Controller { public function index() { return 'index page'; } public function products() { return 'products page'; } public function product_details($id) { return 'product details page'; } public function product_categories() { return 'product categories page'; } public function product_brands() { return 'product brands page'; } public function blog() { return 'blog page'; } public function blog_post($id) { return 'blog post page'; } public function contact_us() { return 'contact us page'; } public function login() { return 'login page'; } public function logout() { return 'logout page'; } public function cart() { return 'cart page'; } public function checkout() { return 'checkout page'; } public function search($query) { return "$query search page"; } }تعرّف الشفرة أعلاه الدوال التي تجيب على كل طلب يأتي من المسارات التي عرّفناها في ملف routes.php. اكتفينا -لحد الساعة- بجعل كل دالة ترجع اسم المسار الذي تجيب على طلباته. انتقل الآن للمتصفح وأدخل الرابط التالي في شريط العناوين: http://larashop.dev/search/bootsستحصل على صفحة بالمحتوى التالي: boots search pageجرب الروابط الأخرى أيضا: http://larashop.dev/ http://larashop.dev/products http://larashop.dev/products/details/7 http://larashop.dev/products/category http://larashop.dev/products/brands http://larashop.dev/blog http://larashop.dev/blog/post/3 http://larashop.dev/contact-us http://larashop.dev/login http://larashop.dev/logout http://larashop.dev/cart http://larashop.dev/checkout http://larashop.dev/search/Keywordترجمة -وبتصرّف- لمقال Laravel 5 SEO Friendly URLs لصاحبه Rodrick Kazembe.
  8. يوفر تهجير قواعد البيانات Database migration في Laravel آليات لإنشاء الجداول Tablesوالتعديل عليها بغض النظر نظام إدارة قواعد البيانات المستخدم. يعني هذا أنك لن تضطر للاهتمام بالاختلافات بين نظم إدارة قواعد البيانات في صياغة أوامر SQL. يمكّن التهجير أيضا من التراجع والعودة إلى ما كانت عليه قاعدة البيانات قبل آخر التعديلات. هذا الدرس جزء من سلسلة تعلم Laravel والتي تنتهج مبدأ "أفضل وسيلة للتعلم هي الممارسة"، حيث ستكون ممارستنا عبارة عن إنشاء تطبيق ويب للتسوق مع ميزة سلة المشتريات. يتكون فهرس السلسلة من التالي: مدخل إلى Laravel 5.تثبيت Laravel وإعداده على كلّ من Windows وUbuntu.أساسيات بناء تطبيق باستخدام Laravel.إنشاء روابط محسنة لمحركات البحث (SEO) في إطار عمل Laravel.نظام Blade للقوالب.تهجير قواعد البيانات في Laravel. (هذا الدرس)استخدام Eloquent ORM لإدخال البيانات في قاعدة البيانات، تحديثها أو حذفها.إنشاء سلة مشتريات في Laravel.الاستيثاق في Laravel.إنشاء واجهة لبرمجة التطبيقات API في Laravel.إنشاء مدوّنة باستخدام Laravel.استخدام AngularJS واجهةً أمامية Front end لتطبيق Laravel.الدوّال المساعدة المخصّصة في Laravel.استخدام مكتبة Faker في تطبيق Laravel لتوليد بيانات وهمية قصدَ الاختبار. يمكن النظر إلى تهجير قواعد البيانات كما لو كان نظام إدارة نسخ خاص بقواعد البيانات، إذ يتيح لفريق العمل سهولة تغيير مخطّط Schema البيانات وتشاركه. نكمل في هذا الدرس اعتمادا على ما أنشأناه في الدروس السابقة من السلسلة. يغطي الدرس المواضيع التالية: متطلبات التهجير.أمر Artisan لتهجير قواعد البيانات.بنية التهجير.إنشاء جدول بآلية التهجير.استخدام آلية التهجير للتراجع Rollback عن التعديلات.بذر قواعد البيانات Database seeding.بنية قاعدة البيانات الخاصة بمشروع Larashop.ملفات التهجير لقاعدة بيانات Larashop.متطلبات التهجيريجب أولا إنشاء قاعدة بيانات في نظام إدارة قواعد البيانات المستخدم (MySQL في حالتنا) وإعداد معطيات الاتصال بها في Laravel ولدى أداة سطر الأوامر Artisan. إنشاء قاعدة بياناتنفذ الأمر التالي في سطر أوامر MySQL أو استخدم التطبيق المفضّل لديك (PHPMyAdmin مثلا) لإنشاء قاعدة بيانات larashop: CREATE DATABASE `larashop`;إعداد Laravel للاتصال بقاعدة البياناتأعددنا Laravel في الدرس الأول من هذه السلسلة للاتصال بقاعدة بيانات باسم larashop. في ما يلي تذكير بخطوات الإعداد. افتح الملف config/database.php واعثر على الأسطُر التالية: 'mysql' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', 'localhost'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', 'strict' => false, ],حدّث القيم التالية لتوافق إعدادات MySQL لديك: 'database' => env('DB_DATABASE', 'larashop'), 'username' => env('DB_USERNAME', 'root'), 'password' => env('DB_PASSWORD', 'melody'), إعداد معطيات اتصال Artisan بقاعدة البياناتيواجه الكثير من المطورين رسالة الخطأ التالية عند العمل على تهجير قواعد البيانات باستخدام أداة Artisan: Access denied for user 'homestead'@' localhost' (using password: YES)ستظهر الرسالة أعلاه حتى ولو كانت معطيات الاتصال في الملف configuration/database.php صحيحة. يعود السبب في ذلك إلى أن Artisan يستخدم المعطيات الموجودة في الملف env.. الحل هو إذن تحرير الملف env. الواقع في مجلد التطبيق، ستجد ما يلي: APP_ENV=local APP_DEBUG=true APP_KEY=aqk5XHULL8TZ8t6pXE43o7MBSFchfgy2 DB_HOST=localhost DB_DATABASE=homestead DB_USERNAME=homestead DB_PASSWORD=secret CACHE_DRIVER=file SESSION_DRIVER=file QUEUE_DRIVER=sync MAIL_DRIVER=smtp MAIL_HOST=mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null حدث المتغيرات التالية: DB_HOST=localhost DB_DATABASE=homestead DB_USERNAME=homestead DB_PASSWORD=secretلتصبح: DB_HOST=localhost DB_DATABASE=larashop DB_USERNAME=root DB_PASSWORD=melody احرص على موافقة اسم قاعدة البيانات، اسم المستخدم وكلمة مروره للمعطيات لديك. احفظ التعديلات. تهجير قواعد البيانات بأداة Artisanينشئ أمر artisan ملفا على المسار database/migrations لكل عملية تهجير. يمكن تغيير المسار الخاص بحفظ ملفات التهجير إن أردت ولكننا هنا سنكتفي بالمسار المبدئي. ننفذ الأمر التالي لإنشاء أول ملف تهجير: php artisan make:migration create_drinks_tableتظهر رسالة باسم ملف التهجير الجديد. اخترنا اسم create_drinks_table للدلالة على أن التهجير ينشئ جدولا باسم drinks في قاعدة البيانات. نتيجة الأمر هي إنشاء ملف للتهجير بنفس الاسم الذي أعطيناه مع إضافة ختم زمني قبله، مثلا: 2015_12_21_215845_create_drinks_table.phpبنية ملف التهجيرندرس الآن محتوى ملف التهجير الذي أنشأناه للتو. افتح الملف التالي لرؤية محتواه (انتبه إلى أن اسم الملف يبدأ بختم زمني للحظة إنشائه): database/migrations/2015_12_21_215845_create_drinks_table.phpنجد ما يلي: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateDrinksTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { // } /** * Reverse the migrations. * * @return void */ public function down() { // } }يعرف ملف التهجير صنفا جديدا باسم CreateDrinksTable يمدد الصنف Migration: CreateDrinksTable extends Migrationداخل الصنف CreateDrinksTable توجد دالة باسم up. تنفّذ تعليمات الدالة up عند تشغيل التهجير. توجد أيضا دالة باسم down في الصنف CreateDrinksTable. تنفذ تعليمات الدالة down عند التراجع عن تغييراتِ تهجير.ملف تهجير لإنشاء جدول بقاعدة البياناتليمكن إنشاء جدول في قاعدة البيانات فجيب تعريف حقوله في ملف التهجير. نعيد فتح ملف التهجير السابق ونعدله ليصبح محتواه التالي : <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateDrinksTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('drinks', function (Blueprint $table) { $table->increments('id'); $table->string('name',75)->unique(); $table->text('comments')->nullable(); $table->integer('rating'); $table->date('juice_date'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('drinks'); } }في الدالة up: نستدعي الدالة create المعرَّفة في الصنف Schema ونمرر لها معطيين، الأول اسم الجدول الذي نريد إنشاءه drinks، والمعطى الثاني دالة غير محدّدة الاسم تعرّف حقول الجدول. نستخدم كائنا من صنف Blueprint لتعريف الجدول.نعرف أول حقل من الجدول وهو الحقل id. تعرف الدالة increments التابعة للصنف Blueprint عددا طبيعيا (عدد صحيح إشارته موجبة) يزداد تلقائيّا مع كل إدخال للبيانات في الجدول.الحقل الثاني هو حقل الاسم name، الذي نعرفه بالدالة string. تنشئ الدالة stringحقلا من سلسلة محارف مع تحديد طول السلسلة (75 في المثال). نعلّم الحقل name بالدالة unique \لجعله وحيدا وهو ما يعني أنه لا يمكن لتسجيلتين في الجدول أن تحويا نفس القيمة بالنسبة لهذا الحقل.الحقل الثالث comments نصي، وتستخدم الدالة text لتعريفه. نتيح إمكانية ألا يحوي الحقل بيانات باستخدام الدالة nullable.الحقل الرابع rating للتقيمات. نستخدم الدالة integer للإشارة إلى أنه عدد صحيح.ثم نضيف حقلا لتخزين تاريخ المشروب juice_date ونستخدم الدالة date لهذا الغرض.تُستخدم الدالة timestamps لإضافة حقلين هما created_at وupdated_at في الجدول تلقائيا. الحقلان عبارة عن ختم زمني ل، على التوالي، تاريخ إضافة التسجيلة إلى قاعدة البيانات وتاريخ آخر تحديث عليها.في الدالة down نحذف الجدول drinks من قاعدة البيانات في حالة وجوده.ننفذ بعد حفظ ملف التهجير الأمر التالي: php artisan migrateستظهر مخرجات في سطر الأوامر على النحو التالي: Migration table created successfully. Migrated: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_100000_create_password_resets_table Migrated: 2015_12_21_215845_create_drinks_tableإن نظرت في جداول قاعدة البيانات الآن فستجد التالي: ستلاحظ وجود أربعة جداول من بينها جدول drinks. الجداول الأخرى أنشأها Laravel لأن ملفات تهجيرها تأتي مبدئيا مع Laravel. التراجع عن التعديلاتيوفر التهجير إمكانية التراجع عن تعديلاته والعودة إلى حالة قاعدة البيانات قبل تنفيذه. أنشأنا في الفقرة السابقة جداول في قاعدة البيانات، ننفذ الأمر التالي للتراجع عن ذلك: php artisan migrate:rollbackتظهر الرسائل التالية: Rolled back: 2015_12_21_215845_create_drinks_table Rolled back: 2014_10_12_100000_create_password_resets_table Rolled back: 2014_10_12_000000_create_users_tableإن أعدت التحقق في MySQL سترى أن الجدول drinks لم يعد موجودا. نعيد إنشاء الجدول بتنفيذ التهجير مرة أخرى: php artisan migrateتمكن ملاحظة أن تنفيذ التهجير يكون بالتسلسل الزمني التصاعدي لتاريخ إنشاء ملفات التهجير (من الأقدم إلى الأحدث)، في ما يكون التراجع بتنفيذ ملفات التهجير حسب التسلسل الزمني التنازلي (من الأحدث إلى الأقدم). إدارة جداول البيانات في Laravel باستخدام التهجيرسنرى في هذه الفقرة كيفية استخدام التهجير للقيام بأشغال شائعة على جداول قواعد البيانات. إدراج بياناتسنرى الآن كيفية استخدام التهجير لإدراج بيانات في جدول أثناء إنشائه. ننشئ جدولا بالموظفين employees وندرج فيه 33 تسجيلة بالاعتماد على مكتبة Faker (سنخصص درسا لتفصيل استخدام Faker). نفذ الأمر التالي لإنشاء ملف تهجير لجدول employees: php artisan make:migration employeesنفتح الملف المنشأ للتو ونضيف الشفرة التالية: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class Employees extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('employees', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('email')->unique(); $table->string('contact_number'); $table->timestamps(); }); $faker = Faker\Factory::create(); $limit = 33; for ($i = 0; $i < $limit; $i++) { DB::table('employees')->insert([ //, 'name' => $faker->name, 'email' => $faker->unique()->email, 'contact_number' => $faker->phoneNumber, ]); } } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('employees'); } } ننشئ كائنا من صنف Faker بالتعليمة $faker = Faker\Factory::create();نحدد عدد التسجيلات التي نود إدراجها: limit$. نستخدم حلقة for التكرارية لإضافة التسجيلات إلى الجدول. تولد التعليمة faker->name$ اسما وهميّا، faker->unique()->email$ اسم بريد وحيد وfaker->phoneNumber$ رقم هاتف وهميا.الأمر التالي ينفذ التهجير: php artisan migrateتظهر الرسالة التالية دلالة على تهجير الجدول employees: Migrated: 2015_12_21_225233_employees إن بحثت الآن عن محتوى الجدول employees، مثلا بتنفيذ الاستعلام التالي في سطر أوامر MySQL: SELECT * FROM employees;ستحصُل على أسماء الموظفين، عناوينهم البريدية وأرقام هواتفهم. نتراجع عن إنشاء الجدول employees بتنفيذ الأمر: php artisan migrate:rollbackتظهر رسالة دلالة على التراجع عن إنشاء الجدول. نفتح ملف التهجير للتعديل عليه ثم نضع الشفرة الخاصة بإدراج بيانات وهمية بين علامتي تعليق، هكذا: /* $faker = Faker\Factory::create(); $limit = 33; for ($i = 0; $i < $limit; $i++) { DB::table('employees')->insert([ //, 'name' => $faker->name, 'email' => $faker->unique()->email, 'contact_number' => $faker->phoneNumber, ]); } */احفظ ملف التهجير ثم نفذ الأمر: php artisan migrateسيُنشأ جدول employees من جديد ولكن هذه المرة دون إدراج تسجيلات في الجدول. إضافة عمود إلى جدول أو حذفه منهنفرض أننا نود إضافة عمود جديد gender لتخزين جنس الموظّف، مباشرة بعد العمود contact_number. ننفذ الأمر التالي لإنشاء ملف تهجير باسم add_gender_to_employees مع تحديد الجدول الذي نريد العمل عليه وهو employees: php artisan make:migration add_gender_to_employees --table=employeesتشير التعليمة table=employees-- إلى أننا نريد العمل على الجدول employees الموجود في قاعدة البيانات. افتح ملف التهجير المنشأ بعد الأمر السابق، وعدله ليصبح على النحو التالي: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class AddGenderToEmployees extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('employees', function (Blueprint $table) { $table->string('gender')->after('contact_number'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('employees', function (Blueprint $table) { $table->dropColumn('gender'); }); } } في الدالة up أضفنا حقلا جديدا من نوع string (سلسلة محارف) وحددنا مكانه بأنه بعد العمود contact_number.في الدالة down نحذف الحقل gender.الآن عند تنفيذ أمر التهجير php artisan migrate ستلاحظ إضافة عمود جديد باسم gender بعد عمود contact_number. تغيير نوع عموديحتاج تغيير نوع العمود لتثبيت حزمة Doctrine Database Abstract Layer, DBAL. تُستخدم هذه الحزمة لتهجيرات التعديل على الجداول Alter table. سنستخدم أداة إدارة الاعتماديات Composer لتثبيت الحزمة. افتح ملف composer.json الذي يوجد في مجلد التطبيق. ابحث عن مقطع require: "require": { "php": ">=5.5.9", "laravel/framework": "5.2.*" },توجد في هذا المقطع حزم المكتبات التي يحتاجها تطبيقنا. حتى الآن توجد حزمتان فقط هما php وlaravel. يشير الجزء الأول (قبل النقطتين) إلى اسم الحزمة، في ما يشير الثاني لإصدارها. نضيف حزمة dbal` إلى هذه الاعتماديات، وذلك على النجو التالي: "require": { "php": ">=5.5.9", "laravel/framework": "5.2.*", "doctrine/dbal": "v2.4.2" }, لاحظ الفاصلة اللاتينية التي أضفناها بعد حزمة Laravel. نفذ الأمر التالي لتحديث المشروع: composer updateعند إنشاء العمود gender لم نحدد طول الحقل، أي أنه سيأخذ الطول المبدئي للحقول من نوع string وهو255 محرفا. ننشئ ملف تهجير جديدا لتعديل طول الحقل ليصبح 5 كحد أقصى. نعدل الملف على النحو التالي: php artisan make:migration modify_gender_in_employees --table=employeesقبول فراغ الحقول في الجدوليفترض Laravel عند إنشاء الحقول أنها لا تقبل فراغ القيمة، أي أنه يجب ذكر قيمة للحقل عند إدراج تسجيلات في الجدول. يمكننا تغيير هذا الإعداد المبدئي وجعل قيمة حقل ما اختيارية. سنأخذ الحقل gender للتمثيل به. ننشئ ملفا للتهجير: php artisan make:migration make_gender_null_in_employees --table=employeesثم نعدله على النحو التالي: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class MakeGenderNullInEmployees extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('employees', function (Blueprint $table) { $table->string('gender', 5)->nullable()->change(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('employees', function (Blueprint $table) { $table->string('gender', 5)->change(); }); } } تجعل الدالة nullable الحقل gender يقبل قيما فارغة.php artisan migrate إضافة مفتاح خارجي Foreign keyنصنف موظفينا حسب القسم الذي يعملون فيه. ننشئ جدولا للأقسام depts ثم نضيف مفتاحا خاريجا في جدول الموظفين employees. الأمر أدناه ينشئ ملف تهجير لجدول الأقسام: php artisan make:migration deptsعدل ملف التهجير: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class Depts extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('depts', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('depts'); } }ثم ننفذ أمر التهجير لإنشاء الجدول: php artisan migrateيُشترط لتصح علاقة عبر مفتاح خارجي بين جدولين أن يكون المفتاح الخارجي والمفتاح الرئيس Primary key متطابقين في النوع. استخدمنا في تعريف المفتاح الرئيس idضمن الجدول depts دالة increments التي تعطي النوع عددا طبيعيا من عشرة أرقام ;(unsigned integer INT(10 وهو ما يعني أننا سنعطي نفس النوع للمفتاح الخارجي الذي سننشئه في الجدول employees. الفرق أن المفتاح الخارجي لا يزداد تلقائيا لذا سنستخدم الدالة unsignedInteger التي لها نفس مفعول increments من حيث نوع الحقل وطوله، مع فرق أنها لا تضيف الازدياد التلقائي. ملحوظة: حتى تمكن إضافة مفتاح خارجي في الجدول employees يجب أن يكون الجدول فارغا (بدون تسجيلات). لهذا السبب علقنا في فقرة ماضية الشفرة الخاصة بـFaker. نفذ الأمر التالي لإنشاء ملف تهجير لإضافة حقل المفتاح الخارجي dept_id إلى الجدول employees: php artisan make:migration add_dept_id_in_employees --table=employeesثم نعدل ملف التهجير: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class AddDeptIdInEmployees extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('employees', function (Blueprint $table) { $table-> unsignedInteger ('dept_id')->after('gender'); $table->foreign('dept_id') ->references('id')->on('depts') ->onDelete('cascade'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('employees', function (Blueprint $table) { $table->dropColumn('dept_id'); }); } }ثم ننفذ التهجير: php artisan migrate بذر قواعد البياناتيشير مصطلح البذر Seeding إلى عملية إضافة بيانات وهمية لأغراض الاختبار في قواعد البيانات. نطبق هذا الإجراء على جدول drinks الذي أنشأناه في أول الدرس. نفذ الأمر التالي لإنشاء ملف للبذر: php artisan make:seeder DrinksTableSeederينشئ الأمر ملفا باسم DrinksTableSeeder.php على المسار database/seeds. افتح الملف: <?php use Illuminate\Database\Seeder; class DrinksTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { // } } يمدد الصنف DrinksTableSeeder الصنف Seeder ويعرّف الدالة run التي تُنفّذ عند تشغيل أمر البذر في Artisan. عدل الملف ليصبح محتواه التالي: <?php use Illuminate\Database\Seeder; class DrinksTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('drinks')->insert([ 'name' => 'Orange Juice', 'comments' => 'Rich in C vitamin', 'rating' => 9, 'juice_date' => '2015-12-20', ]); } أضفنا في الدالة run أمر إدراج في جدول البيانات drinks ومررنا مصفوفة توافق عناصرها حقول الجدول مع تحديد قيم عناصر المصفوفة. ننفذ الأمر أمر البذر لإضافة التسجيلة أعلاه إلى الجدول: php artisan db:seed --class=DrinksTableSeederنمرر لأمر البذر php artisan db::seed اسم الملف المراد تنفيذه. الآن عند التحقق نجد في جدول قاعدة البيانات التسجيلة التالية: قاعدة البيانات الخاصة بمشروع Larashopتعرفنا في الفقرات الماضية على أساسيات التهجير في Laravel. سنجعل هذه المعرفة موضع التطبيق لإنشاء قاعدة بيانات لمشروع Larashop. ستشنرك جميع الجداول في الحقول التالية التي أنشأناها لأغراض الفحص والتدقيق. التسلسلالحقلنوع البياناتالوصف1created_at Timestamp ختم زمني لتاريخ إدراج التسجيلة2updated_at Timestamp ختم زمني لتاريخ تحديث التسجيلة3created_at_ip (Varchar(45 عنوان IP المستخدم لإدراج التسجيلة4updated_at_ip (Varchar(45 عنوان IP المستخدم لتحديث التسجيلةجدول منشورات المدونةالتسلسلالحقلنوع البياناتالوصف1id INT مفتاح رئيس (AUTOINCREMENT) عدد طبيعي يزداد تلقائيا2url (Varchar(255 رابط الصفحة3title (Varchar(140 عنوان الصفحة4description (Varchar(170 وصف يظهر في محركات البحث5content Text محتوى الصفحة أو المنشور 6conblogtent (Tinyint(1 يحدد ما إذا كان المنشور صفحةجدول التصنيفاتالتسلسلالحقلنوع البياناتالوصف1id INT مفتاح رئيس (AUTOINCREMENT) عدد طبيعي يزداد تلقائيا2name (Varchar(255 اسم التصنيفجدول العلامات التجاريةالتسلسلالحقلنوع البياناتالوصف1id INT مفتاح رئيس (AUTOINCREMENT) عدد طبيعي يزداد تلقائيا2name (Varchar(255 اسم العلامة التجاريةجدول المنتجاتلكل منتج تصنيف وعلامة تجارية وحيدين. التسلسلالحقلنوع البياناتالوصف1id INT مفتاح رئيس (AUTOINCREMENT) عدد طبيعي يزداد تلقائيا2name (Varchar(255 اسم المنتج3title (Varchar(140 عنوان المنتج4description (Varchar(500 عنوان المنتج5price int ثمن المنتج6category_id int معرف تصنيف المنتج7brand_id int معرف العلامة التجارية للمنتجملفات التهجير لجداول قاعدة بيانات المشروعسننشئ في هذه الفقرة ملفات تهجير لجداول البيانات المذكورة أعلاه؛ سنضيف أيضا بعض البيانات الوهمية إلى الجداول باستخدام آلية البذر التي تعرفنا عليها سابقا. توليد ملفات التهجيرافتح سطر الأوامر ونفذ الأوامر التالية لتوليد ملفات الجداول: جدول منشورات المدونة: php artisan make:migration create_posts_table جدول تصنيفات المنتجات php artisan make:migration create_categories_tableجدول العلامات التجارية للمنتجات php artisan make:migration create_brands_tableجدول المنتجات php artisan make:migration create_products_tableتحرير ملفات التهجيرننتقل لتحرير كل ملف من ملفات التهجير. جدول منشورات المدونة <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreatePostsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('posts', function (Blueprint $table) { $table->increments('id'); $table->string('url', 255)->unique(); $table->string('title', 140); $table->string('description', 170); $table->text('content'); $table->boolean('blog'); $table->timestamps(); $table->string('created_at_ip'); $table->string('updated_at_ip'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('posts'); } } جدول التصنيفات <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateCategoriesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('categories', function (Blueprint $table) { $table->increments('id'); $table->string('name', 255)->unique(); $table->timestamps(); $table->string('created_at_ip'); $table->string('updated_at_ip'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('categories'); } } جدول العلامات التجارية <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateBrandsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('brands', function (Blueprint $table) { $table->increments('id'); $table->string('name', 255)->unique(); $table->timestamps(); $table->string('created_at_ip'); $table->string('updated_at_ip'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('brands'); } } جدول المنتجات <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateProductsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('products', function (Blueprint $table) { $table->increments('id'); $table->string('name', 255)->unique(); $table->string('title', 140); $table->string('description', 500); $table->integer('price'); $table->unsignedInteger('category_id'); $table->unsignedInteger('brand_id'); $table->timestamps(); $table->string('created_at_ip'); $table->string('updated_at_ip'); // مفتاح خارجي على جدول التصنيفات $table->foreign('category_id') ->references('id')->on('categories') ->onDelete('cascade'); // مفتاح خارجي على جدول العلامات التجارية $table->foreign('brand_id') ->references('id')->on('brands') ->onDelete('cascade'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('products'); } }بذر قاعدة بيانات المشروعندرج بيانات وهمية في جداول قاعدة البيانات قصدَ الاختبار. أنشئ ملفات البذر بتنفيذ الأوامر أدناه على التوالي: php artisan make:seeder CategoriesTableSeeder php artisan make:seeder BrandsTableSeeder php artisan make:seeder ProductsTableSeederيحوي جدول المنتجات مفتاحين خارجيين لجدولي التصنيف والعلامة التجارية. لذا يجب البدء بهما (لا يصح إدراج مفتاح خارجي لتسجيلة غير موجودة في الجدول الذي مثل المفتاح الخارجي مرجعا إليه). بذر جدول التصنيفات <?php use Illuminate\Database\Seeder; class CategoriesTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('categories')->insert(['name' => 'MENS']); DB::table('categories')->insert(['name' => 'WOMENS']); DB::table('categories')->insert(['name' => 'KIDS']); DB::table('categories')->insert(['name' => 'FASHION']); DB::table('categories')->insert(['name' => 'CLOTHING']); } } بذر جدول العلامات التجارية<?php use Illuminate\Database\Seeder; class BrandsTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('brands')->insert(['name' => 'ACNE']); DB::table('brands')->insert(['name' => 'RONHILL']); DB::table('brands')->insert(['name' => 'ALBIRO']); DB::table('brands')->insert(['name' => 'ODDMOLLY']); } } بذر جدول المنتجات<?php use Illuminate\Database\Seeder; class ProductsTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('products')->insert(['name' => 'Mini skirt black edition', 'title' => 'Mini skirt black edition','description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna','price' => 35,'category_id' => 1,'brand_id' => 1,]); DB::table('products')->insert(['name' => 'T-shirt blue edition', 'title' => 'T-shirt blue edition','description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna','price' => 64,'category_id' => 2,'brand_id' => 3,]); DB::table('products')->insert(['name' => 'Sleeveless Colorblock Scuba', 'title' => 'Sleeveless Colorblock Scuba','description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna','price' => 13,'category_id' => 3,'brand_id' => 2,]); } } ثم ننفذ أوامر البذر لكل جدول. خاتمةتعرفنا في هذا الدرس على تهجير قواعد البيانات وبذرها في Laravel. كما أننا حددنا هيكلة قاعدة بيانات المشروع الذي نعمل عليه. في الدروس القادمة سنتعرف على إطار عمل Eloquent الذي سنعتمد عليه للتخاطب مع قاعدة البيانات وعرض محتوياتها عند الاقتضاء. ترجمة -وبتصرّف- لمقال Laravel 5 Migrations لصاحبه Rodrick Kazembe.
  9. بعد أن انتهينا من إعداد قواعد البيانات والطرق (routes)، سوف نتحدث في هذا الجزء عن المتحكِّمات (Controllers)، النماذج (مع العلاقات)، والعروض views. (بما في ذلك نظام blade ومخططات المحتوى). مساعدات النماذج Form Helpers في Laravelبعد كل هذه الإعدادات، إذا ذهبنا الآن (في المتصفح) إلى projects/ فسوف نحصل على صفحة فارغة فما هو السبب؟ حسنا، لنكتشف ذلك، قم بتنفيذ الأمر php artisan route:list على سطر الأوامر مرة أخرى وأنظر إلى هذا السطر: +--------+----------+----------------------------------------+------------------------+-------------------------------------------------+------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+----------+----------------------------------------+------------------------+-------------------------------------------------+------------+ | | GET|HEAD | projects | projects.index | App\Http\Controllers\ProjectsController@index | | +--------+----------+----------------------------------------+------------------------+-------------------------------------------------+------------+يبدو أن رابط projects/ يقوم بتحميل الوظيفة Index الخاصة بـ ProjectsController، لذلك سنقوم بفتح app/Http/controllers/ProjectsController.php/ وسنقوم بتحديث التابع (method) لنقوم بربطه إلى عرض سوف نقوم بإنشائه. قم بإضافة السطر ;('return view('projects.index إلى الوظيفة ()index كالتالي: public function index() { return view('projects.index'); }بما أننا نستخدم في هذا الدرس نظام Blade للقَوْلَبة سنقوم بإنشاء ملف resources/views/projects/index.blade.php/ وسنكتب نص قصير داخله، ثم سنقوم مرة أخرى بالذهاب إلى projects/ في المتصفح، إذا كان كل شيء يعمل جيدا فسوف تجد النص الذي كتبته بالأعلى. والآن قُم بنفس الشيء لتابع المتحكِّم create عن طريق إضافة السطر ;('return view('projects.create إلى التابع ()create كالتالي: public function create() { return view('projects.create'); }إن إظهار محتويات العرض view هو أمر رائع، لكن ماذا لو كنا نملك أكثر من صفحة واحدة في موقعنا ؟ في هذه الحالة، سوف نحتاج إلى قالب ثابت لجميع الصفحات، أي أننا نحتاج إلى عرض محتويات العرض view داخل قالب HTML بسيط، وسنقوم بهذا عن طريق مخططات المتحكِّمات. من أجل عمل مخططات المتحكِّمات سنقوم بالخطوات التالية: إنشاء مخطط العرض، وبما أن Laravel توفر لنا واحدة جيدة تدعى app.blade.php لذلك سنوفر الوقت وسنقوم بإستخدامها. لاحظ أن قرب أسفل المخطط هنالك سطر ('@yield('content وهذه دالة ستقوم بتحميل محتوانا الحالي.الإشارة إلى مخططك في عرضك view باستخدام ('extends('app@ ولفها عن طريق كتلة ('section('content@، مثل التالي: @extends('app') @section('content') This is my /resources/views/projects/index.blade.php file! @endsection سنقوم الآن بتنفيذ هذه الخطوات بإنشاء عروض show.blade.php و create.blade.php و index.blade.php في مجلد resources/views/projects/ مع الشيفرات (markup) المذكورة أعلاه وقُم بتغيير اسم الملف إذا كان الأمر ضروريا، ثم قُم بعمل تحديث للصفحة projects/ في متصفحك، وسوف ترى هيكل app.blade.php حول محتويات عرضك (view). الربط بين الطريق (route) والنموذجسيوفر Laravel بشكل افتراضي معرف رقمي ID للعديد من توابع متحكِّمات الموارد مثل ()show و ()edit و ()update و ()destroy، وهذا الأمر سيضيف العديد من الشيفرات الجاهزة (boilerplate) التي نحتاج إلى كتابتها، مثل الحصول على مثيل النموذج والتأكد من وجودها وغيرها. ولهذا سوف نستخدم إحدى مميزات Laravel والتي تدعى الربط بين الطريق و النموذج route model binding، فبدل من توفير متغير id$، سوف نقوم بإعطاء الوظيفة (method) كائن project$ أو task$. قُم بفتح app/Http/routes.php/ وأضف هذين السطرين (أول سطرين بعد التعليق): // Provide controller methods with object instead of ID Route::model('tasks', 'Task'); Route::model('projects', 'Project'); // Use slugs rather than IDs in URLs Route::bind('tasks', function($value, $route) { return App\Task::whereSlug($value)->first(); }); Route::bind('projects', function($value, $route) { return App\Project::whereSlug($value)->first(); }); Route::resource('projects', 'ProjectsController'); Route::resource('projects.tasks', 'TasksController'); وفي TasksController و ProjectsController (ملفات app/Http/Controllers/ProjectsController.php/ و app/Http/Controllers/TasksController.php/) قُم باستبدال كل وظيفة (method) مُعرف بمرجع id$ بـ task$ أو project$ مثل التالي: // public function edit($id) public function edit(Project $project)لا تنسَ إضافة use App\Task و use App\Project في أعلى المتحكِّمات حتى نشير إلى هذه النماذج. لا تقلق إذا لم تفهم هذه التغييرات، فسنقوم بعرض الشيفرة البرمجة الكاملة لـ TasksController و ProjectsController لاحقا. وفي هذه المرحلة يمكنك تمرير الكائن (object) إلى عرضه في كل متحكِّم لوظيفة (method) الإظهار والتعديل والتحديث حتى نستطيع استخدامهم لاحقا: public function edit(Project $project) { return view('projects.show', compact('project')); }سوف يحتاج TasksController إلى بضعة تعديلات طفيفة أخرى، وبما أننا نستخدم الموارد المضمّنة nested resources، سيخبرنا php artisan route:list أن جميع طرق task تحتوي على قناع {projects} بالإضافة إلى أن بعضها يستقبل قناع {tasks} أيضا. و كنتيجة لذلك، سيتم تمرير مثيل Project كمُعامل أول لتوابع المتحكِّمات (controller methods)، لذلك قُم بتحديثها وقٌم بتمرير متغير project$ الجديد. إذا لم تفهم خطوات الإضافة لـ TasksController و ProjectsController فهذه هي الشيفرة الكاملة للملفين بعد كل التعديلات: // /app/Http/Controllers/ProjectsController.php <?php namespace App\Http\Controllers; use App\Project; use App\Http\Requests; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class ProjectsController extends Controller { /** * Display a listing of the resource. * * @return Response */ public function index() { $projects = Project::all(); return view('projects.index', compact('projects')); } /** * Show the form for creating a new resource. * * @return Response */ public function create() { return view('projects.create'); } /** * Store a newly created resource in storage. * * @return Response */ public function store() { // } /** * Display the specified resource. * * @param \App\Project $project * @return Response */ public function show(Project $project) { return view('projects.show', compact('project')); } /** * Show the form for editing the specified resource. * * @param \App\Project $project * @return Response */ public function edit(Project $project) { return view('projects.edit', compact('project')); } /** * Update the specified resource in storage. * * @param \App\Project $project * @return Response */ public function update(Project $project) { // } /** * Remove the specified resource from storage. * * @param \App\Project $project * @return Response */ public function destroy(Project $project) { // } } // /app/Http/Controllers/TasksController.php <?php namespace App\Http\Controllers; use App\Project; use App\Task; use App\Http\Requests; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class TasksController extends Controller { /** * Display a listing of the resource. * * @param \App\Project $project * @return Response */ public function index(Project $project) { return view('tasks.index', compact('project')); } /** * Show the form for creating a new resource. * * @param \App\Project $project * @return Response */ public function create(Project $project) { return view('tasks.create', compact('project')); } /** * Store a newly created resource in storage. * * @param \App\Project $project * @return Response */ public function store(Project $project) { // } /** * Display the specified resource. * * @param \App\Project $project * @param \App\Task $task * @return Response */ public function show(Project $project, Task $task) { return view('tasks.show', compact('project', 'task')); } /** * Show the form for editing the specified resource. * * @param \App\Project $project * @param \App\Task $task * @return Response */ public function edit(Project $project, Task $task) { return view('tasks.edit', compact('project', 'task')); } /** * Update the specified resource in storage. * * @param \App\Project $project * @param \App\Task $task * @return Response */ public function update(Project $project, Task $task) { // } /** * Remove the specified resource from storage. * * @param \App\Project $project * @param \App\Task $task * @return Response */ public function destroy(Project $project, Task $task) { // } }لاحظ أنه إذا قمت بتحديث صفحة projects/project-1/ سيبقى كل شيء يعمل. عرض نماذجناصفحة عرض قائمة المشاريع، حان الآن وقت البدء بعرض المشاريع والمهام، قٌم بفتح projects/ في متصفحك. وبالاعتماد على php artisan route:list، ستكون هذه الصفحة هي صفحة عرض المشاريع، لذلك قُم بفتح resources/views/projects/index.blade.php/ وأضف التالي: @extends('app') @section('content') <h2>Projects</h2> @if ( !$projects->count() ) You have no projects @else <ul> @foreach( $projects as $project ) <li><a href="{{ route('projects.show', $project->slug) }}">{{ $project->name }}</a></li> @endforeach </ul> @endif @endsectionسوف أشرح بعض النقاط في هذه الشفرة البرمجية: استخدمنا في هذه الشفرة لغة Blade للقَوْلَبة، لذلك فإن if و foreach تتحكم في تدفق flow الدوال بإضافة إلى أنها دالة طباعة (الأقواس المعقوفة المزدوجة).تأكدنا إن كان هنالك أي مشروع لعرضه، إن كان هنالك أية مشاريع فسيقوم بعرضها وإن لم يكن تظهر رسالة تخبرك بذلك.استدعينا مساعد ()route مع طريق ذا اسم (يمكنك رؤية قائمة الطرق ذات الاسم عن طريق php artisan route:list) لربط كل مشروع مع صفحة تفاصيله.سوف تحتاج أيضا إلى تمرير متغير projects$ لهذا العرض view وإلا ستحصل على خطأ متغير غير معرف undefined variable error. قُم بفتح app/Http/controllers/ProjectsController.php/ وقُم بتحديث الوظيفة ()index إلى: public function index() { $projects = Project::all(); return view('projects.index', compact('projects')); }قُم بتحديث الصفحة (Refresh) وستجد قائمة من مشاريعك. علاقات النموذج - صفحة تفاصيل المشروعفي صفحة تفاصيل المشروع نحتاج إلى عرض قائمة من مهام المشاريع، وللقيام بذلك نحتاج إلى تعريف علاقة "واحد إلى الكثير one-to-many" في نموذج Project للسماح له بالحصول على المهام tasks. قُم بفتح app/Project.php/ وأضف التابع ()tasks كالتالي: public function tasks() { return $this->hasMany('App\Task'); }بشكل عكسي، يمكننا إضافة علاقة "الكثير إلى واحد many-to-one" لنموذج المهام Task: public function project() { return $this->belongsTo('App\Project'); }للتأكد من أنها تعمل اكتب الأمر: $ php artisan tinkerوستكون النّتيجة كالتي حسب المدخلات: >App\Project::whereSlug('project-1')->first()->tasks->count(); 5 >App\Project::whereSlug('project-2')->first()->tasks->count(); 2 >App\Task::first()->project->name; Project 1ممتاز، يمكننا الآن تحديث عرض resources/views/projects/show.blade.php/: @extends('app') @section('content') <h2>{{ $project->name }}</h2> @if ( !$project->tasks->count() ) Your project has no tasks. @else <ul> @foreach( $project->tasks as $task ) <li><a href="{{ route('projects.tasks.show', [$project->slug, $task->slug]) }}">{{ $task->name }}</a></li> @endforeach </ul> @endif @endsectionاضغط على أي مشروع في صفحة عرض قائمة المشاريع في متصفحك وسوف يتم عرض المشروع مع جميع المهام المرتبطة به. وأخير تَبقى لدينا صفحة عرض المهام (resources/views/tasks/show.blade.php/)، وهذه سهلة للغاية: @extends('app') @section('content') <h2> {!! link_to_route('projects.show', $project->name, [$project->slug]) !!} - {{ $task->name }} </h2> {{ $task->description }} @endsectionملاحظة: كُن حذرا عندما تستخدم علاقات النماذج (models)، فإنه من السهل إنشاء عدد كبير من استعلامات SQL، وتسمى هذه المشكلة بـ "مشكلة N+1". الخاتمةاليوم تعلمنا الكثير من الأشياء الجديدة مثل: الربط بين الطريق (route) و النموذجالنماذج (مع علاقة واحد إلى الكثير one-to-many)المتحكِّمات (مع الربط بين الطريق و النموذج)العروض (مع لغة blade للقَوْلَبة والمخططات)أصبحت الصفحة تعرض لنا قائمة من المشاريع والمهام، وفي الدرس القادم سوف نركز على تعديل وإضافة وحذف المشاريع والمهام. ترجمة -وبتصرّف- للمقال Creating a Basic ToDo Application in Laravel 5 – Part 2. حقوق الصورة البارزة: Designed by Freepik.
  10. حتى الآن تعلمنا كيفية تثبيت وإعداد Laravel، بالإضافة إلى إعداد بعض المشاريع والمهام والموارد المضمّنة و عرضهم على المستخدم، كما أنشأنا وظائف الإنشاء والتعديل والحذف، وفي هذا الفصل سوف نختم الدرس عن طريق إضافة استمارة التحقق. استمارة التحقق من جانب الخادمعلى الرغم من أن استمارات الإنشاء والتعديل تعمل إلا أننا لم نقم بالتحقق من ما يتم إدخاله، وهذا ما سنقوم بعمله اليوم. هنالك طرق متعددة للتعامل مع استمارة التحقق، بعضها أفضل من الآخر، ولهذا التطبيق الصغير نقترح أن نستخدم وظيفة المتحكِّمات ()validate مع كائن Illuminate\Http\Request مثل هذا، في ملف app/Http/Controllers/ProjectsController.php/: // /app/Http/Controllers/ProjectsController.php use Illuminate\Http\Request; class ProjectsController extends Controller { protected $rules = [ 'name' => ['required', 'min:3'], 'slug' => ['required'], ]; /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return Response */ public function store(Request $request) { $this->validate($request, $this->rules); $input = Input::all(); Project::create( $input ); return Redirect::route('projects.index')->with('message', 'Project created'); } /** * Update the specified resource in storage. * * @param \App\Project $project * @param \Illuminate\Http\Request $request * @return Response */ public function update(Project $project, Request $request) { $this->validate($request, $this->rules); $input = array_except(Input::all(), '_method'); $project->update($input); return Redirect::route('projects.show', $project->slug)->with('message', 'Project updated.'); }وأما في ملف app/Http/Controllers/TasksController.php/: // /app/Http/Controllers/TasksController.php use Illuminate\Http\Request; class TasksController extends Controller { protected $rules = ['name' => ['required', 'min:3'], 'slug' => ['required'], 'description' => ['required'], ]; /** * Store a newly created resource in storage. * * @param \App\Project $project * @param \Illuminate\Http\Request $request * @return Response */ public function store(Project $project, Request $request) { $this->validate($request, $this->rules); $input = Input::all(); $input['project_id'] = $project->id; Task::create( $input ); return Redirect::route('projects.show', $project->slug)->with('Task created.'); } /** * Update the specified resource in storage. * * @param \App\Project $project * @param \App\Task $task * @param \Illuminate\Http\Request $request * @return Response */ public function update(Project $project, Task $task, Request $request) { $this->validate($request, $this->rules); $input = array_except(Input::all(), '_method'); $task->update($input); return Redirect::route('projects.tasks.show', [$project->slug, $task->slug])->with('message', 'Task updated.'); }سوف نحتاج أيضا إلى مكان لعرض أية أخطاء، لذلك قُم بفتح resources/views/app.blade.php/ وأضف السطور التالية أسفل ('yield('content@: <div class="content"> @if (Session::has('message')) <div class="flash alert-info"> <p>{{ Session::get('message') }}</p> </div> @endif @if ($errors->any()) <div class='flash alert-danger'> @foreach ( $errors->all() as $error ) <p>{{ $error }}</p> @endforeach </div> @endif @yield('content') </div>إذا أردت قائمة كاملة من قواعد التحقق، أنصحك بزيارة التوثيق الرسمي. من المفترض أن يعمل كل شيء الآن، وإذا كان عكس ذلك، فسوف تقوم ()this->validate$ بتوجيهك إلى الصفحة الحالية مع الأخطاء التي سوف تظهر على الصفحة. خاتمةلقد قمنا في هذه الدورة بتعلم الكثير من الأشياء حول Laravel، مثل كيفية تثبيت وإعداد Laravel 5 بالإضافة إلى بعض المفاهيم المتقدمة مثل الربط بين الطريق والنموذج route model binding والحماية من CSRF، وعلى الرغم من بساطة التطبيق الذي قمنا به إلا أنه بداية جيدة لكل من يريد احتراف Laravel. ترجمة -وبتصرّف- للمقال Creating a Basic ToDo Application in Laravel 5 – Part 4. حقوق الصورة البارزة: Designed by Freepik.
  11. حتى الآن تعلمنا كيفية تثبيت وإعداد Laravel، بالإضافة إلى أننا قمنا بـإعداد بعض موارد المشاريع والمهام وعرضهم للمستخدم. في هذا الدرس، سنتعلم كيفية إنشاء وتعديل وحذف الصفحات والإجراءات. إضافة روابط التصفحقبل أن نفعل أي شيء، سنقوم بإضافة روابط create/edit/delete/back إلى صفحات المشاريع والمهام: <!-- /resources/views/projects/index.blade.php --> @extends('app') @section('content') <h2>Projects</h2> @if ( !$projects->count() ) You have no projects @else <ul> @foreach( $projects as $project ) <li> {!! Form::open(array('class' => 'form-inline', 'method' => 'DELETE', 'route' => array('projects.destroy', $project->slug))) !!} <a href="{{ route('projects.show', $project->slug) }}">{{ $project->name }}</a> ( {!! link_to_route('projects.edit', 'Edit', array($project->slug), array('class' => 'btn btn-info')) !!}, {!! Form::submit('Delete', array('class' => 'btn btn-danger')) !!} ) {!! Form::close() !!} </li> @endforeach </ul> @endif <p> {!! link_to_route('projects.create', 'Create Project') !!} </p> @endsection <!-- /resources/views/projects/show.blade.php --> @extends('app') @section('content') <h2>{{ $project->name }}</h2> @if ( !$project->tasks->count() ) Your project has no tasks. @else <ul> @foreach( $project->tasks as $task ) <li> {!! Form::open(array('class' => 'form-inline', 'method' => 'DELETE', 'route' => array('projects.tasks.destroy', $project->slug, $task->slug))) !!} <a href="{{ route('projects.tasks.show', [$project->slug, $task->slug]) }}">{{ $task->name }}</a> ( {!! link_to_route('projects.tasks.edit', 'Edit', array($project->slug, $task->slug), array('class' => 'btn btn-info')) !!}, {!! Form::submit('Delete', array('class' => 'btn btn-danger')) !!} ) {!! Form::close() !!} </li> @endforeach </ul> @endif <p> {!! link_to_route('projects.index', 'Back to Projects') !!} | {!! link_to_route('projects.tasks.create', 'Create Task', $project->slug) !!} </p> @endsectionهذه الشفرة البرمجية مفهومة، الصعوبة تكمن في رابط الحذف، متحكِّمات الموارد تتطلب إرسال وظيفة HTTP DELETE، وهذا لا يمكن أن يتم مع رابط عادي، لذلك يتطلب نموذج يتم تقديمه إلى طريق route محدد. للمزيد من المعلومات قُم بزيارة إجراءات يتم التحكم بها عن طريق متحكِّمات الموارد. إنشاء صفحات الإضافة والتعديلبعد الانتهاء من صفحات عرض قائمة المشاريع، نحتاج إلى إمكانية إضافة وحذف المشاريع والمهام. استمارات الإضافة والحذف ستكون متشابهة كثيرا، لذلك بدلا من تكرارهم، سنقوم بوراثتهم من نموذج واحد. سوف نبدأ مع عروض create/edit للمشروع: <!-- /resources/views/projects/create.blade.php --> @extends('app') @section('content') <h2>Create Project</h2> {!! Form::model(new App\Project, ['route' => ['projects.store']]) !!} @include('projects/partials/_form', ['submit_text' => 'Create Project']) {!! Form::close() !!} @endsection <!-- /resources/views/projects/edit.blade.php --> @extends('app') @section('content') <h2>Edit Project</h2> {!! Form::model($project, ['method' => 'PATCH', 'route' => ['projects.update', $project->slug]]) !!} @include('projects/partials/_form', ['submit_text' => 'Edit Project']) {!! Form::close() !!} @endsectionقُم بعمل نفس الشيء مع المهام لكن لا تنسَ تحديث الطرق (routes): <!-- /resources/views/tasks/create.blade.php --> @extends('app') @section('content') <h2>Create Task for Project "{{ $project->name }}"</h2> {!! Form::model(new App\Task, ['route' => ['projects.tasks.store', $project->slug], 'class'=>'']) !!} @include('tasks/partials/_form', ['submit_text' => 'Create Task']) {!! Form::close() !!} @endsection <!-- /resources/views/tasks/edit.blade.php --> @extends('app') @section('content') <h2>Edit Task "{{ $task->name }}"</h2> {!! Form::model($task, ['method' => 'PATCH', 'route' => ['projects.tasks.update', $project->slug, $task->slug]]) !!} @include('tasks/partials/_form', ['submit_text' => 'Edit Task']) {!! Form::close() !!} @endsectionالآن توجد عدة مفاهيم جديدة: إضافة جزئيةالاستمارة تتطلب عدة وسوم، وبدلا من ذلك سنقوم بتقسيمها من جزئية _form ونضعها مباشرة في العرض، استمارة Add هي من نوع POST ترسل البيانات إلى طريق projects.store واستمارة Edit هي من نوع PATCH وترسل البيانات إلى projects.update. قد يبدوا هذا معقدا ومبهم لكن هذه هي الطريقة التي تعمل بها المتحكِّمات. لاحظ أننا استخدمنا ()Form::model، وتسمى هذه نموذج الاستمارة الملزمة و ليس لدى هذه أهمية كبيرة الآن، فهي تُستخدم للتعبئة التلقائية وسوف نحتاجها في وقت لاحقا عندما نضيف الحقول باستخدام ()Form::input.الحماية من CSRF:مساعدي الاستمارة (Form helpers) توفر العديد من الوظائف مجانا، فإذا ذهبت إلى projects/create/ ورأيت مصدر الصفحة ستجد شيئا مشابها لهذا: <form method="POST" action="http://l4todo.localhost.com/projects" accept-charset="UTF-8"> <input name="_token" type="hidden" value="Y8uOo7SeD5tQZExezDf5a7UwiYR4P6qIHEUKJNxI"> </form>هل ترى حقل token_ ؟ هذا هو الرمز المميز لـ CSRF مُنشئ تِلقائيا بواسطة استدعاء {{ ()Form::model }} الذي يمنع ثغرة CSRF. يكفي أن نقول أن هذا شيئ مفيد وأننا لم نقم بأي شيئ للحصول عليه. إنشاء استمارة التعديلنحن بحاجة إلى ترميز (markup) الاستمارة للمشاريع والمهام، وبفضل نموذج الاستمارة الملزمة يمكننا فقط استخدام مساعدي الاستمارة في Laravel لإخراج كافة الحقول التي نحتاجها. <!-- /resources/views/projects/partials/_form.blade.php --> <div class="form-group"> {!! Form::label('name', 'Name:') !!} {!! Form::text('name') !!} </div> <div class="form-group"> {!! Form::label('slug', 'Slug:') !!} {!! Form::text('slug') !!} </div> <div class="form-group"> {!! Form::submit($submit_text, ['class'=>'btn primary']) !!} </div> <!-- /resources/views/tasks/partials/_form.blade.php --> <div class="form-group"> {!! Form::label('name', 'Name:') !!} {!! Form::text('name') !!} </div> <div class="form-group"> {!! Form::label('slug', 'Slug:') !!} {!! Form::text('slug') !!} </div> <div class="form-group"> {!! Form::label('completed', 'Completed:') !!} {!! Form::checkbox('completed') !!} </div> <div class="form-group"> {!! Form::label('description', 'Description:') !!} {!! Form::textarea('description') !!} </div> <div class="form-group"> {!! Form::submit($submit_text) !!} </div>هذا كل شيئ، سوف تستطيع الأن التصفح في صفحات الإضافة والتعديل، الأمر كان سهلا للغاية. جعل الاستمارات تعمللدينا استمارات الإضافة والتعديل للمشروع و المهام، بالإضافة إلى استمارة مُضللة لحذفهم، الآن يجب علينا أن نجعلهم يعملون لأداء مهامهم. أولا، أضف هذا في أعلى المتحكِّمات: use Input; use Redirect;والآن لوظائف store و update و destroy: // ProjectsController public function store() { $input = Input::all(); Project::create( $input ); return Redirect::route('projects.index') ->with('message', 'Project created'); } public function update(Project $project) { $input = array_except(Input::all(), '_method'); $project->update($input); return Redirect::route('projects.show', $project->slug) ->with('message', 'Project updated.'); } public function destroy(Project $project) { $project->delete(); return Redirect::route('projects.index')->with('message', 'Project deleted.'); } // TasksController public function store(Project $project) { $input = Input::all(); $input['project_id'] = $project->id; Task::create( $input ); return Redirect::route('projects.show', $project->slug) ->with('message', 'Task created.'); } public function update(Project $project, Task $task) { $input = array_except(Input::all(), '_method'); $task->update($input); return Redirect::route('projects.tasks.show', [$project->slug, $task->slug]) ->with('message', 'Task updated.'); } public function destroy(Project $project, Task $task) { $task->delete(); return Redirect::route('projects.show', $project->slug)->with('message', 'Task deleted.'); }الشفرة البرمجية في الأعلى مفهومة ولا داعي لشرحها. إذا قمت الآن بتقديم إحدى الاستمارات فسوف تحصل على خطأ MassAssignmentException: _token، فالإحالة الكتلية تحدث عندما تقوم بتمرير مصفوفة بيانات إلى ()Model::create أو ()Model::update من المتحكِّمات بدل من وضع حقل واحد في نفس الوقت، ولإصلاح هذا، قم ببساطة بإضافة خاصية guarded إلى كل نموذج. class Project extends Model { protected $guarded = []; class Task extends Model { protected $guarded = [];رسائل الفلاشسوف تلاحظ من الشفرة السابقة أننا استخدمنا دالة ()with، وهذه الدالة تقوم بتمرير متغير فلاش (يستخدم لمرة واحدة) للجلسة session والتي يمكن قراءتها عند تحميل الصفحة التالية. والآن نحتاج إلى التأكد من تلك الرسالة وعرضها على المستخدم. قُم بفتح resources/views/app.blade.php/ وأضف التالي: ... <div class="content"> @if (Session::has('message')) <div class="flash alert-info"> <p>{{ Session::get('message') }}</p> </div> @endif @yield('content') </div> ...حاول إنشاء مشروع، هذه الرسالة ستظهر مهما حدّثت الصفحة وستذهب بعد فترة. خاتمةاليوم قمنا بأشياء مثيرة للغاية، فلقد أضفنا استمارات الإضافة والتعديل والحذف وتعلمنا حول CSRF و نموذج الاستمارة الملزمة بالإضافة إلى تعلمنا المزيد حول blade و الدوال. كل شيئ الآن يعمل، يمكن تجربة التطبيق وإنشاء وتعديل وحذف المهام والمشاريع، وفي أي وقت يمكنك كتابة الأمر: php artisan migrate:refresh –seed وسوف يتم إعادة تعيين قواعد البيانات. في المرحلة القادمة والتي ستكون النهائية سوف نقوم بالتحقق من الاستمارات. ترجمة -وبتصرّف- للمقال Creating a Basic ToDo Application in Laravel 5 – Part 3. حقوق الصورة البارزة: Designed by Freepik.
×
×
  • أضف...