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

إنشاء تطبيق Todo List بسيط باستخدام Laravel 5 - الجزء الأول


هشام رزق الله

يعتبر Laravel أشهر إطار ويب بلغة PHP، ولقد شهد إصداره الخامس العديد من التغييرات وتمت إضافة الكثير من المميزات الجديدة، ولذلك لتعلم أساسيات هذا الإطار سوف نقوم بإنشاء تطبيق Todo List، وسوف نتعلم من خلال هذا التطبيق الكثير من المفاهيم والطرق البرمجية حتى نتمكن من التعامل والتوسع مع إطار Laravel 5.

laravel5-todo-app_(1).thumb.png.e3c2af36

ملاحظة: هذا الدرس طويل للغاية لذلك سوف يتم تقسيمه إلى أجزاء أصغر، ويمكنك الحصول على الكود المصدري لكل جزء من 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.


تفاعل الأعضاء

أفضل التعليقات



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...