المقدمة
هذا الدليل السريع سيزودك بمقدمة متوسطة عن إطار عمل لارافيل ويضمن التالي:
- ترحيل لقاعدة البيانات Migrations
- مخطط الكائن العلائقي (ORM (object-relational mapper
- المسارات Routes
- الوثوقية والتخويل Authentication / Authorization
- حقن التبعية Dependency Injection
- المصادقة Validation
- العروض views و قوالب Blade
هذه نقطة بداية رائعة في حال كنت تملك معرفة بأساسيات لارافيل أو حتى أي من أطر العمل الخاصة بالـphp عموماً.
لتمثيل إختيار أساسي لميزات لارافيل سوف نقوم ببناء تطبيق إدارة مهام to-do لنصل إلى هدف ما ينص عليه التطبيق وهو إدارة للمهام التي يجب أن تنجزها في حياتك، على عكس ما احتواه تطوير تطبيق المهام للمبتدئين سنضيف في هذا المقال إمكانية المستخدمين من إنشاء حسابهم الخاص وتوثيق دخولهم إلى التطبيق (تسجيل الدخول)، وطبعاً الناتج النهائي لهذا المقال التعليمي موجود على الغيت هب GitHub.
التنصيب
بالطبع أول أمر تحتاجه هو تحميل نسخة جديدة من إطار العمل لارافيل. من الممكن استخدام Homestead Virtual Machine في حال رغبتك لمعرفة المزيد حول تنصيبها يمكنك مشاهدة هذا الفيديو. أو عبر مخدم PHP منصب بشكل محلي على حاسوبك باستخدام أحد التطبيقات الشهيرة او ما يعادلها WAMP MAMP XAMPP …الخ.
حالما تكون بيئة تطويرك جاهزة يمكنك تنصيب لارافيل عن طريق الكومبوسر Composer
في حال عدم معرفتك بالكومبسور أو أردت إسترجاع معلومة ما، الرجاء متابعة الفيديو الخاص به.
composer create-project laravel/laravel quickstart –prefer-dist
انت حر تماماً في حال أحببت أن تقرأ خطوة بخطوة هذا الدليل، او نسخ المستودع Repository الذي يحوي على هذا التطبيق بإصداره النهائي وتنصيب كل ما يحتاجه من شفرات تبعية (يعتمد عليها) من الـ GitHub، عبر تشغيل الأوامر التالية:
git clone https://github.com/laravelme/quickstart-intermediate.git cd quickstart composer install php artisan migrate
تحضير قاعدة البيانات
١- ترحيل قاعدة البيانات Migrations
أولاً دعونا نستخدم ترحيل ليقوم بتعرف جدولاً فيه جميع المهام. جزئية الترحيل في لارافيل تجعل من أمور مثل تعريف جدول وبنيته وتعديلاته من أسهل ما يكن بإستخدام شفرات بي إتش بي php طليقة ومُعبرة، بدلاً من إخبار فريقك ــ أو زميلك المختص في هذا المجال أو المسؤول عنه ــ على أن يقوم بالتعديل بشكل يدوي على الأعمدة وقاعدة البيانات الموجودة على أجهزتهم، يمكنهم ( لفريقك أو لزميلك) ببساطة أن يقوم بتشغيل أمر المايغريشن Migration الذي قمت بإرساله لهم عن طريق تحديث المستودع الخاص بكم على أحد مواقع الـتي تدعم Git أو بإرساله لهم بطريقة ما.
جدول المستخدمين users
لطالما أننا نخطط لإضافة ميزة إنشاء حساب لزائر تطبيقنا سنحتاج إلى جدول يُخزن بداخله جميع مستخدمينا، ولحسن الحظ، لارافيل تأتي مسبقاً مع ترحيل لإنشاء جدول مستخدمين أساسي وبالتالي لن نحتاج إلى إنشاءه يدوياً. الترحيل الإفتراضي لجدول users موجود داخل المجلد
database/migrations
جدول المهام tasks
بخطوتنا التالية دعونا نبني ملف تهجير لبناء جدول المهام والذي سيقوم بإستيعاب جميع المهام. يمكن إستخدام الحرفي (واجهة سطر الأمر) أرتيسان Artisan CLI لتوليد طيف واسع من الكلاسات Classes وسيوفر عليك الكثير من الكتابة في الوقت الذي تبني فيه مشروعاً باستخدام لارافيل ، في هذه الحالة دعونا نستخدم الأمر make:migration لتوليد ملف جديد ترحيل لقاعدة البيانات الخاص بجدول المهام tasks .
php artisan make:migration create_tasks_table –create=tasks
سيتم وضع ملف الترحيل المولد في مشروعك داخل المجلد database/migrations كما من الممكن أنك قد لاحظت أن الأمر make:migration قد ولد لك عامود ترقيم تلقائي لـ ID بالإضافة إلى توليد الطوابع الزمنية timestamps والتي هي عبارة عن عامودين crated_at و updated_at
دعونا نحرر هذا الملف لنضيف عاموداً آخر من نوع سلسلة نصية string ليحمل اسم المهام التي سنقوم بإنشائها، بالإضافة إلى عامود آخر user_id والذي سيعمل على ربط الجدولين users و tasks ببعضها البعض، ويكون شكل الملف:
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateTasksTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('tasks', function (Blueprint $table) { $table->increments('id'); $table->integer('user_id')->index(); $table->string('name'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('tasks'); } }
لتنفيذ ترحيلات تطبيقنا سنستخدم الأمر migrate الخاص ب الأداة أرتيسان Artisan ، في حال إستخدامك للهومستيد Homestead عليك تشغل الأمر التالي من داخل الهومستيد والسبب أن قاعدة البيانات ليست على جهازك المُستضيف وإنما على بيئة التطوير هومستيد والتي هي نظام متكامل يعمل وهمياً.
php artisan migrate
هذا الأمر سيقوم بإنشاء جميع جداول قاعدة البيانات، اذا قمت بتفحص جداول قاعدة البيانات بإستخدام أداة انت تختارها، يجب أن ترى جدولين tasks و users واللذان يحملان بداخلها الحقول التي عرفناها مسبقاً.
٢- موديلات إلكونت Eloquent Models
العم بليغ Eloquent وهو إصطلاحاً مخطط الكائن العلائقي (ORM (object-relational mapper وهو الإفتراضي في لارافيل للربط مابين جداول قاعدة البيانات وتطبيقك ويكون على شكل كائن Object تستخدمه برمجياً كما أي كائن قمت ببناءه في تطبيقك وبالتالي يٌشعرك برفع الكلفة ويُبقيك مركزاً على البرمجة بحد ذاتها. عادةً كل كائن بليغ Eloquent يرتبط بشكل مباشر بجدول مقابل له في قاعدة البيانات.
موديل المستخدم User model
أولاً نحن نحتاج إلى مودل لجدول المستخدم users في قاعدة البيانات، ولكن اذا ألقيت نظرة على مجلد app في مشروعك سترى أنّ لارافيل مسبقاً تأتي مع مودل User وبالتالي لا نحتاج إلى توليد واحداً يدوياً.
موديل المهمة Task model
دعونا نعرف مودل Task والذي يدل على جدول المهام tasks في قاعدة البيانات الذي قمنا بإنشاءه قبل قليل، مرة أخرى يمكننا إستخدام أمر توليد مودل من الحرفي Artisan ،في هذه الحالة سنستخدم الأمر make:model
كما في السطر :
php artisan make:model Task
المودل Model المولد سيتم وضعه في مجلد app داخل مشروعك. في الحالة الإفتراضية الصف Class لهذا المودل فارغ. لا نحتاج عنوةً لإخبار إلكونت Eloquent أي جدول هو المقابل لك في قاعدة البيانات، لأنه سيقوم بإفتراض أنّ اسم الجدول في قاعدة البيانات هو الجمع لاسم المودل، وبالتالي مودل المهمة Task سيفترض إلكونت أن الجدول المقابل له هو tasks، وفي حالتنا نعم فهو كذلك 🙂
دعونا نضيف بعض الأمور إلى هذه المودل، أولاً سنجعل حالة اسم المهمة name قابل للإسناد الهائل mass-assignable
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Task extends Model { /** * The attributes that are mass assignable. * * @var array */ protected $fillable = ['name']; }
سنتعلم سويتاً بشكل أكبر كيف نستخدم مودلات إلكونت Eloquent Models حالما نضيف المسارات الخاصة بمشروعنا.
٣- علاقات إلكونت Eloquent Relations
الآن بعد أن قمنا بتعريف الموديلات التي نحتاجها نحتاج إلى أن نربطهم ببعضهم البعض، مثلاً: مستخدمنا يمكنه تملك عدد من المهام، بينما المهمة فهي مسندة إلى مستخدم واحد. تعريف العلاقة سوف يمنحنا حرية الحركة مابين الموديلات كما في هذا المثال:
$user = App\User::find(1); foreach ($user->tasks as $task) { echo $task->name; }
علاقة المهام tasks
بدايةً دعونا نعرف علاقة المهام في موديل المستخدم User . علاقات إلكونت معرفة على شكل دوال داخل الموديلات. إلكونت يدعم عدة أنواع مختلفة من العاقات وبالتالي تأكد من المرور على الشرح الكامل في موقع إطار عمل اللارافيل. في هذه الحالة سنقوم بتعريف دالة tasks داخل موديل المستخدم User والذي بدوره يستدعي الدالة hasMany المزودة من قِبل إلكونت:
<?php namespace App; // Namespace Imports... class User extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract { use Authenticatable, Authorizable, CanResetPassword; // Other Eloquent Properties... /** * Get all of the tasks for the user. */ public function tasks() { return $this->hasMany(Task::class); } }
علاقة المستخدم user
في خطوتنا التالية دعونا نقوم بتعريف علاقة المستخدم User مع موديل المهمة Task ، أيضاً سنقوم بتعريف هذه العلاقة على شكل دالة داخل مويل المهمة Task ، في هذه الحالة سنستخدم الدالة belongsTo المزودة من قبل إلكونت Eloquent لتعريف العلاقة:
<?php namespace App; use App\User; use Illuminate\Database\Eloquent\Model; class Task extends Model { /** * The attributes that are mass assignable. * * @var array */ protected $fillable = ['name']; /** * Get the user that owns the task. */ public function user() { return $this->belongsTo(User::class); } }
رائع! وبما أنه تم تعريف العلاقات التي نحتاجها في تطبيقنا يمكننا البدء في بناء المتحكمات Controllers التي سنحتاجها
المسارات
في المقال السابق تطوير تطبيق إدارة المهام Todo قمنا بتعريف كل المنطق بإستخدامات دالات مجهولة ( دالة غير معرفة Anonymous Functions) داخل ملف المسارات Routes.php ، ولأسباب تتعلق بحجم التطبيق سنقوم بإستخدام متحكمات Controllers لتنظيم مساراتنا.
المتحكمات ستسمح لنا بإستلام طلب الـ HTTP و بمعالجة ما يحتاج من منطق على عدة ملفات وهذا لأغراض تنظيمية أفضل!
إظهار عرض view
سنقوم بإستخدام مسار واحد فيه دالة مجهولة/غير معرفة وهو مسار الجذر الخاص بتطبيقنا laravelme.app/
ومن هذا المسار سنقوم بتصيير قالب HTML يحتوي على صفحة الترحيب Welcome
في لارافيل جميع قوالب الـ HTML مخزنة في المجلد resources/views ويمكننا إستخدام الدالة المساعدة view() ليعيد لنا أحد هذه القوالب من داخل مسارنا:
Route::get('/', function () { return view('welcome'); });
طبعاً نحتاج فعلياً إلى بناء هذا العرض وسنقوم بفعل هذا بعد قليل.
الوثوقية Authentication
تذكر نريد أيضاً أن ندع المستخدمين ينشؤون حساباتهم وأن يقومو بتسجيل الدخول إلى تطبيقنا! يجدر هنا بالذكر أن جزئية إنشاء طبقة وثوقية كاملة في تطبيق ويب هي مهمة مضجرة ، وبما أن مثل هذا الأمر ذو احتياج واسع، لارافيل تحاول أن تجعل من هذه العملية عملية غير متعبة إطلاقاً.
لنبدأ دعونا نلاحظ وجود متحكم وثوقية موجود مسبقاً مضمناً داخل تطبيق اللارافيل الخاص بك.
app/Http/Controllers/AuthController.php
هذا المتحكم يستخدم تريت (سمة) trait خاص والذي يحوي بدوره على كل ما قد تحتاج من من المنطق اللازم لإنشاء وتوثيق مستخدمين.
مسارات الوثوقية Authentication Routes
في هذه النقطة برأيكم ما هو الباقي علينا من إنجاز؟
مازلنا في الحاجة إلى إنشاء قوالب لستجيل دخول و إنشاء حسابات جديدة للمستخدمين بالإضافة إلى تعريف مسارات تصل روابط موقعنا مع المتحكمات المناسبة! دعونا نقوم بفعل هذا أولاً ، لنحرر ملف المسارت:
app/Http/routes.php
// Authentication Routes... Route::get('auth/login', 'Auth\[email protected]'); Route::post('auth/login', 'Auth\[email protected]'); Route::get('auth/logout', 'Auth\[email protected]'); // Registration Routes... Route::get('auth/register', 'Auth\[email protected]'); Route::post('auth/register', 'Auth\[email protected]');
عروض التوثيق Authentication Views
جزئية التوثيق تحتم علينا إنشاء عرضين login.blade.php و register.blade.php الموجودين بدورهم في المجلد resources/views/auth وبالطبع موضوع شكل ومظهر هذه القوالب ليس بالمهم، ولكن على الأقل يجب أن يحتوا على حقول بسيطة.
قالب التسجيل register.blade.php يجب أن يحتوي على الحقول التالية: name ، email ، password بالإضافة إلى password_confirmation
ويجب أن يكون مضبوط على أن يرسل طلب من نوع بوست POST إلى المسار
laravelme.com/auth/register
أما بالنسبة لقالب تسجيل الدخول login.blade.php يجب أن يحتوي على الحقول:
email ، password
وعلى أن يكون مضبوط على إرسال طلب من نوع بوست POST إلى laravelme.com/auth/login
ملاحظة: في حال رغبتك بمشاهدة الشفرات الكاملة لا تنسى أنه يمكنك زيارة المستودع الخاص بهذا المقال على الغيت هاب GitHub
متحكم المهمة TaskController
لطالما نعلم أننا سنقوم بجلب المهام المسجلة مسبقاً دعونا ننشئ متحكم للمهام TaskController بإستخدام أرتيسان Artisan والذي سيقوم بوضع ملف (متحكم) داخل المجلد الخاص به في المسار app/Http/Controllers
php artisan make:controller TaskController –plain
الآن وبعد أن تم توليد ملف متحكم المهمة TaskController دعونا نخطو إلى الأمام ونقوم بإنشاء مزيد من المسارات لتطبيقنا داخل ملف المسارات لنشير بها إلى متحكمنا الوليد:
Route::get('/tasks', '[email protected]'); Route::post('/task', '[email protected]'); Route::delete('/task/{task}', '[email protected]');
توثيق جميع مسارات المهمة Task
لغرض هذا التطبيق نريد إضافة طلب وثوقية من قبل المستخدم لجميع المسارات المتعلقة بالمهمة Task ، بكلمات أخرى، يتحتم على المستخدم تسجيل الدخول لكي يكون بمقدوره إنشاء مهمة جديدة ، وبالتالي نحتاج إلى قصر الوصول إلى روابط المهمة task للمستخدمين المسجلين دخولهم مسبقاً، لارافيل تجعل من هذا الأمر قمة في السهولة عبر إستخدام الطبقات الوسيطة Middlewares.
لإضافة الحاجة إلى مستخدمين مسجلين لجميع الأحداث على متحكم ما، نستطيع إضافة إستدعاء إلى دالة middleware داخل الباني او الكونستركتور Contstructor لهذا المتحكم. جميع روابط الطبقات الوسيطة middleware route يتم تعريفها بشكل مسبق في ملف النواة الموجود في app/Http/Kernel.php ، في هذه الحالة نريد إسناد مدلوير الوثوقية auth middleware إلى جميع دالات هذا المتحكم:
<?php namespace App\Http\Controllers; use App\Http\Requests; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class TaskController extends Controller { /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('auth'); } }
بناء الواجهات والعروض
هذ التطبيق يحوي على واجهة واحدة فقط والتي تحتوي على نموذج لإضافة مهام جديدة بالإضافة إلى سرد المهام الموجودة بشكل مسبق في تطبيقنا ولمساعدة توضيح هذا الأمر بشكل رسومي في الأسفل سترى كيف هو شكل هذا العرض عندما نتهي منه مع إستخدامنا لمكتبة Bootstrap
تعريف الواجهة Layout
نسبياً جميع المواقع تتشارك نفس الواجهة أو البنية في جميع صفحاتها، مثلاً : هذا التطبيق يحتوي على شريط تنقل علوي والذي بدوره سيكون موجوداً في كل صفحة ( في حال كان في تطبيقنا أكثر من واحدة ). لارافيل تجعل من السهل جداً مشاركة الميزات المشتركة ـ كالشريط العلوي ـ في جميع الصفحات عبر إستخدام ميزة الواجهات من محركها الخاص بالقوالب Blade
كما ناقشنا سابقا فإن لارافيل تقوم بتخزين جميع العروض في المجلد resources/views
وبالتالي دعونا نعرف واجهة جديدة أو عرض جديد تحت المسار والمسمى resources/views/layouts/app.blade.php
الإمتداد blade.php يُعلم إطار العمل على لزوم استخدام محرك القوالب Blade لتصيير العرض، بالطبع يمكنك استخدام قوالب PHPعادية مع لارافيل! ولكن بليد Balde يزود شفرات مختصرة لكتابة قوالب نظيفة.
العرض app.blade.php يجب أن يكون محتواه كالتالي:
// resources/views/layouts/app.blade.php <!DOCTYPE html><span class="hiddenSpellError" pre="" data-mce-bogus="1">Laravel</span> Quickstart - Intermediate
@yield('content')
لاحظ الجزئية @yield هذه تعليمة خاصة بمحرك Blade والتي تساعده على تحديد جميع الصفحات التي ستقوم بوراثة هذا القالب الأب أي تمكن لهذه الصفحات الوريثة من حقن محتواهم الخاص، وبالتالي دعونا نقوم بتعريف واجهة وريثة (ابن) تقوم بإستخدام القالب الأساسي (الأب) وبحقن محتواها الأساسي تماماً مكان هذه التعليمة المذكورة في الأعلى
تعريف عرض وريث ( ابن ) Child View
عظيم! واجهات تطبيقنا انتهت، لنقوم ببناء عرض يحتوي على النموذج الخاص بالإنشاء بالإضافة لجدول لسرد المهام وليكن:
resources/views/index.blade.php
والذي يطابق الدالة index في المتحكم TaskController
سنقوم هنا بتخطي بعض الأمور المتعلقة بالبنية المسبقة التي تأتي مع Bootstrap CSS وبالمقابل سنقوم بالتركيز على الأمور الأهم ، وتذكر أنه يمكنك تحميل شفرة المصدر للتطبيق هذا بشكل كامل من المستودع الخاص بها على الـ GitHub.
// resources/views/tasks/index.blade.php @extends('layouts.app') @section('content') <!-- Bootstrap Boilerplate... --> <div class="panel-body"> <!-- Display Validation Errors --> @include('common.errors') <!-- New Task Form --> <form action="/task" method="POST" class="form-horizontal"> {{ csrf_field() }}
@endsection
بعض الشروحات
قبل أن نكمل في تطوير تطبيقنا دعونا نتحدث قليلاً عن القالب المسرود في الأعلى، أولاً الأمر @extends يقوم بإطلاع لارافيل على أننا نستخدم الواجهة المعرفة في
resources/views/layouts/app.blade.php
جميع المحتوى المحصور مابين
@section(‘content’) و @endsection
سيتم حقنه (إستبداله) في السطر
@yield(‘contents’)
الموجود في الواجهة
app.blade.php
الآن قد عرفنا عرض و واجهة أساسية لتطبيقنا دعونا نستمر بإعادة هذه الواجهة عند طلب الدالة index في متحكم المهمة TaskController
/** * Display a list of all of the user's task. * * @param Request $request * @return Response */ public function index(Request $request) { return view('tasks.index'); }
نحن الأن جاهزون لإضافة إرسال طلب من نوع بوست POST على الرابط
laravelme.app/task
ليقوم بإستلام الطلبات القادمة من نموذج اللإضافة والتي هي حقول الإدخال الموجودة بنموذج الإضافة Inputs وبالتالي إضافة مهمة جديدة على قاعدة البيانات
ملاحظة: السطر
@include('common.errors')
سيقوم بإضافة محتوى القالب الموجود في
resources/views/common/errors.blade.php
لم نقم بعد بتعريف محتوى هذا القالب! ولكن سنقوم بالخطوات القليلة القادمة 🙂
إضافة مهام
المصادقة Validation
الآن نحن نملك نموذج في عرضنا ونحتاج إلى إضافة شفرات إلى الدالة store الموجودة داخل متحكم المهمة TaskController لتقوم بمصادقة الطلب القادم من النموذج وإضافة مهمة جديدة، ولكن أولاً دعونا نقوم بمصادقة محتوى الدخل.
لهذا النموذج سنقوم بإنشاء حقل name على أن يكون مطلوباً وعلى أن يحوي عدد محارف أقل من 255 و في حال فشل هذه المصادقة سنقوم بإعادة التوجيه إلى صفحة المهام
laravelme.com/tasks
وسنقوم بإسناد القيم القديمة للدخل مع الخطأ داخل محتول الجلسة Session:
/** * Create a new task. * * @param Request $request * @return Response */ public function store(Request $request) { $this->validate($request, [ 'name' => 'required|max:255', ]); // Create The Task... }
في حال متابعتك للسلسة السابقة للمبتدئين ستلاحظ أنّ شفرات المصادقة هذه مختلفة بشكل بسيط! لطالما أنّنا نكتب شفراتنا داخل متحكم يمكننا الإستفادة من التسهيلات ValidatesRequests وهي عبارة عن سمة (تريت trait في حال عدم معرفتك بها قد بزيارة هذا الرابط أو اترك لي تعليقاً، ساقوم بالرد مباشرةً) والذي بدوره مُدرج مسبقاً كجزء من متحكمات لارافيل، هذه السمة تعرض دالة بسيطة validate والتي تأخذ الطلب و مصفوفة من قواعد المصادقة على الدخل القادم في الطلب كمعاملات.
لن نحتاج إلى التحديد يدوياً في ما إذا فشلت المصادقة أو القيام بإعادة توجيه يدوياً. اذا فشلت المصادقة للقواعد المعطاه، سيتم إعادة توجيه المستخدم للخلف من حيث أتى بشكل أوتوماتيكي وسيتم سرد الأخطاء إلى المتغير المعرف بشكل عام سيشن session ، جميل أليس كذلك؟!
متحول الأخطاء Errors
الآن دعونا نتذكر سويتاً أننا قمنا بإستخدام التعليمة
@include(‘common.errors’)
في العرض الذي أنشأناه ليقوم بتصيير الأخطاء الخاصة بالمُصادقة على النموذج. التعليمة common.errors ستمكننا بشكل مبسط من عرض الاخطاء جميعها بنفس الشكل في جميع الصفحات. دعونا نعرف محتوى هذا العرض الآن:
// resources/views/common/errors.blade.php @if (count($errors) > 0) <!-- Form Error List --> <div class="alert alert-danger"> <strong>Whoops! Something went wrong!</strong> <br><br> <ul> @foreach ($errors->all() as $error)
- {{ $error }}
لاحظ أنه من الممكن الوصول إلى المتحول $errors في جميع عروض Views تطبيقنا ببساطة هو عبارة عن مشتق Instance لـ ViewErrorBag في حال عدم وجود أية أخطاء خلال عملية المصادقة
إضافة مهام
الآن وبما أنه تمت مصادقة الدخل Input Validation ، لنقوم الآن بشكل حقيقي بإنشاء مهمة جديدة عبر إكمال محتوى ملف المسارات Routes ، عندما يتم إكمال عملية إنشاء مهمة جديدة سنقوم بإعاة التوجيه إلى صفحة المهام
laravelme.app/tasks
لإنشاء المهمة الجديدة ( تخزينها في قاعدة البيانات ) سنقوم بالإستفادة من قوة علاقات إلكونت Eloquent . أغلب علاقات لارافيل تعرض الدالة save والتي تستقبل بدورها إنستنس Instance ـ لندعوه مشتق ـ من مودل آخر على علاقة بمودل المهمة، في حالتنا مودل المستخدمين، دالة التخزين هذه ستقوم بإسناد الترقيم التلقائي للمستخدم user_id الذي يقوم بإنشاء المهمة بشكل تلقائي إلى الموديل المطبقة عليه، ويمكننا الوصول للمستخدم عبر الطلب
$request->user()
ليكون الناتج كالتالي:
/** * Create a new task. * * @param Request $request * @return Response */ public function store(Request $request) { $this->validate($request, [ 'name' => 'required|max:255', ]); $request->user()->tasks()->create([ 'name' => $request->name, ]); return redirect('/tasks'); }
عظيم يمكننا الآن إنشاء مهام جديدة بنجاح، الخطوة القادمة لتكن بإكمال إنشائنا لهذا العرض، عن طريق سرد الأعمال التي تم إنشاءها من قبل.
عرض المهام الموجودة
أولاً نحتاج إلى تحرير الدالة [email protected] لتمرر لنا جميع المهام الموجودة إلى العرض View ، بكل سهولة الدالة view() تأخذ قيمة إضافية غير الاولى ( اسم ملف العرض ذو الإمتداد Blade) والتي هي عبارة عن مصفوفة من البيانات التي ستكون متاحة للإستخدام في العرض View بحيث كل مفتاح في هذه المصفوفة سيصبح متحول في العرض View . وبالتالي يمكننا كتابة الشفرات كما يلي:
/** * Display a list of all of the user's task. * * @param Request $request * @return Response */ public function index(Request $request) { $tasks = Task::where('user_id', $request->user()->id)->get(); return view('tasks.index', [ 'tasks' => $tasks, ]); }
ومع ذلك دعونا نستكشف بعض إمكانيات الحقن التَبَعيّ dependency injection لـلارافيل، وذلك لحقن TaskRepository إلى TaskController والذي سنستخدمه لاحقاً في جميع عمليات الوصول إلى بياناتنا
حقن التبعية Dependency Injection
لارافيل في وعاء الخدمة تقتن أهم وأقوى ميزة من ميزاتها كإطار عمل، تأكد من قراءة المستندات الخاصة على موقعها الرسمي. سأقوم بتغطيته في الفيديوهات والقراءات القادمة إن شاء الله 🙂
إنشاء المستودع Repository
من المهم التوضيح من البداية أن المستودع هنا ليس هو نفسه مستودع الـGit الذي يحوي على ملفات مشروعك، سنقوم هنا باستخدام مفهموم برمجي وحل مشهور رائع للمشاريع الضخمة يدعى المستودعات، وكما ذكرنا في وقت سابق نريد تعريف مستودع مهام TaskRepository والذي سيحوي بدوره كل المنطق للوصول إلى البيانات الخاصة بالمودل Task، هذا سيكون مفيد جداً خاصةً إذا توسع تطبيقنا واحتجنا إلى مشاركة استعلامات Quiries عبر تطبيقنا.
بالتالي دعونا ننشئ مجلد مستودعات داخل مجلد التطبيق app/Repositories ونضيف بداخله كلاس TaskRepository، وتذكر أنّ جميع المجلدات داخل المجلد app يتم تحميلهم تلقائياً بإستخدام المعيار PSR-4 للتحميل التلقائي وبالتالي انت حر بإنشاء ما شئت من المجلدات الإضافية على حسب حاجتك إليها:
<?php namespace App\Repositories; use App\User; use App\Task; class TaskRepository { /** * Get all of the tasks for a given user. * * @param User $user * @return Collection */ public function forUser(User $user) { return Task::where('user_id', $user->id) ->orderBy('created_at', 'asc') ->get(); } }
حقن المستودع Injecting The Repository
حالما يتم تعريف مستودعنا نستطيع ببساطة كتابة تلميحه “type-hint” في الباني Constructor الخاص بـ TaskController ومن ثم نستخدمه في المسار index ، منذ أن لارافيل تستخدم وعاء الخدمة لفصل جميع المتحكمات، تبعياتُنا جميعها سيتم حقنها بشكل أوتوماتيكي مع أي إنستنس instance يتم إنشاءه من هذا المتحكم:
<?php namespace App\Http\Controllers; use App\Task; use App\Http\Requests; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use App\Repositories\TaskRepository; class TaskController extends Controller { /** * The task repository instance. * * @var TaskRepository */ protected $tasks; /** * Create a new controller instance. * * @param TaskRepository $tasks * @return void */ public function __construct(TaskRepository $tasks) { $this->middleware('auth'); $this->tasks = $tasks; } /** * Display a list of all of the user's task. * * @param Request $request * @return Response */ public function index(Request $request) { return view('tasks.index', [ 'tasks' => $this->tasks->forUser($request->user()), ]); } }
عرض المهمات
حالما يتم تمرير المعطيات نستطيع أن نقوم بالدوران على المهام وبالتالي عرضها في جدول داخل العرض tasks/index.blade.php ، التعليمة @foreach تمكننا من بناء حلقة موجزة والتي بدورها ستتحول بعد تنفيذ تطبيقنا إلى شفرات بي اتش بي عادية سريعة جداً تنفذ كما تشتعل النار بالهشيم!
@extends('layouts.app') @section('content') <!-- Create Task Form... --> <!-- Current Tasks --> @if (count($tasks) > 0) <div class="panel panel-default"> <div class="panel-heading"> Current Tasks </div> <div class="panel-body"> <table class="table table-striped task-table"> <!-- Table Headings --> <thead> <th>Task</th> <th> @foreach ($tasks as $task)
@endforeach @endif @endsection
تطبيقنا شارف على الإنتهاء، ولكن لا يوجد عندنا طريقة لحذف المهام الموجودة مسبقاً عندما ننتهي منهم، لنضف سويةً هذه الميزة.
حذف المهام
إضافة زر الحذف
لقد قدمنا مسبقاً بترك مهمة لتنفذ لاحقاً داخل شفراتنا كسطراً في المكان الذي يجب أن نقوم بإنشاء زر الحذف فيه وكانت على شكر سطر عندما كتبنا “To-Do” وبالتالي دعونا نضيف هذا الزر إلى كل سطر من سطور جدول المهام الموجود في العرض tasks/index.blade.php سنقوم بإنشاء نموذج يحوي على زر وحيد لكل مهمة في السرد هذا، وعندما يتم النقر على هذا الزر طلب من نوع حذف سيتم إرساله إلى الرابط laravelme.app/task والذي سيقوم بتحريض دالة الحذف على العمل [email protected]
<tr> <!-- Task Name --> <td class="table-text"> <div>{{ $task->name }}</div> </td> <!-- Delete Button --> <td> <form action="/task/{{ $task->id }}" method="POST"> {{ csrf_field() }} {{ method_field('DELETE') }} <button>Delete Task</button> </form> </td> </tr>
ملاحظة حول التحايل في المسارات
لاحظ أن الطريقة المختارة في نموذج زر الحذف هي Post ، علماً اننا نقوم بالرد على الطلب بإستخدام رابط Route::delete
وهذا يعود إلى أنّ نماذج HTML تسمح فقط للنوعين GET و POST كمتحولات ترسل عبر بروتوكول HTTP وبالتالي احتجنا لطريقة لنحتال ونستخدم نوع حذف لطلب نموذج الحذف.
نستطيع الإحتيال على طلب الحذف عن طريق عرض ناتج الدالة
method_field(‘DELETE')
داخل نموذجنا، هذه الدالة تقوم بتوليد دخل خفي للنموذج Hidden input والذي تقوم لارافيل بالتعرف عليه والذي سيستخدم كتجاوز على الطلب الأساسي والذي هو HTTP POST.
الناتج المولد يكون على هذا الشكل ( في حال أحببت عدم استخدام الدالة في الأعلى ، استخدم الناتج التالي )
<input type="hidden" name="_method" value=“DELETE">
ربط المودل بالمسار
والآن شارفنا على إنهاء تعريف دالة الحذف destroy ، على متحكم المهام TaskController ولكن أولاً دعونا نزور تعريفنا لهذا الرابط في ملف المسارات routes.php
Route::delete('/task/{task}', '[email protected]');
من دون إضافة أي شفرات إضافية لارافيل ستقوم بحقن ترقيم تلقائي للمهمة إلى دالة الحذف [email protected] كالشكل التالي:
/** * Destroy the given task. * * @param Request $request * @param string $taskId * @return Response */ public function destroy(Request $request, $taskId) { // }
ومع ذلك فإذن أول شي نحتاج إلى فعله في هذه الدالة هو جلب نسخة أي ( إنستنس instance ) للمهمة من قاعدة البيانات عبر الرقم التسلسلي المُعطى، وبالتالي ألن يكون جميلاً في حال استطاعت لارافيل أن تقوم بحقن نسخة من المهمة تتماشى مع الرقم التسلسلي المعطى في المرتبة الأولى؟ دعونا نجعل لهذه السطور وجوداً!
في الدالة الخاصة باللإقلاع boot للملف app/Providers/RouteServiceProvider.php دعونا نضيف السطر التالي:
$router->model('task', 'App\Task');
هذا السطر الصغير من الكود سيقوم بالإشارة للارافيل على أنها ستجلب نسخة من موديل المهمة المقابل للترقيم التلقائي ID في كل مرة ترى فيها لارافيل {task} داخل تعريفات المسارات الموجودة في ملف المسارات routes.php
والآن يمكننا تعريف دالة حذف مهمة على الشكل التالي:
/** * Destroy the given task. * * @param Request $request * @param Task $task * @return Response */ public function destroy(Request $request, Task $task) { // }
التخويل Authorization
الآن نملك إنستنس من المهمة محقون إلى دالة الحذف، ومع ذلك لا نملك ضماناً أن المستخدم المُسجل للدخول فعلياً يملك المهمة التي سيتم حذفها، على سبيل المثال طلب خبيث طبخ محاولة حذف لمهمة تابعة لمستخدم آخر عبر تمرير رقم ID عشوائي إلى الرابط /tasks/{task} . وهكذا فنحن نحتاج إلى ميزة التخويل في لارافيل لتقوم بالتأكيد على أن المستخدم الذي يحاول أن يقوم بعملية الحذف هو مستخدم مالك لهذه المهمة بشكل فعلي.
إنشاء سياسة Creating A Policy
لارافيل تستخدم السياسات policies لتنظيم التخويلات على شكل منطق مبسط و كلاسات صغيرة. عادة كل سياسة تقابل موديل وبالتالي دعونا ننشئ TaskPolicy بإستخدام أوامر الحرفي أرتيسنا والتي ستقوم بوضع الشفرات المُولدة في app/Policies/TaskPolicy.php
php artisan make:policy TaskPolicy
في خطوتنا التالية دعونا نضيف دالة حذف destroy إلى السياسة. هذه الدالة ستستقبل نسخة من المستخدم ونسخة من المهمة. المهمة ببساطة ستقوم بفحص في ما إذا كان رقم ID المستخدم مطابقاً للحقل user_id الموجود في نسخة المهمة، في الحقيقة، جميع دالات السياسة يجب عليهم إرجاع true أو false
HandlesAuthorization; class TaskPolicy { use HandlesAuthorization; /** * Determine if the given user can delete the given task. * * @param User $user * @param Task $task * @return bool */ public function destroy(User $user, Task $task) { return $user->id === $task->user_id; } }
وأخيراً نحن نحتاج إلى مشاركة موديل المهمة Task مع سياسة المهمة TaskPolicy. يمكننا فعل ذلك عبر إضافة سطر إلى خاصية السياسات داخل الملف app/Providers/AuthServiceProvider.php . هذا سيعلم لارافيل أي سياسة يجب إستخدامها كلما سنقوم بتجربة تخويل حدث على نسخة من المهمة:
/** * The policy mappings for the application. * * @var array */ protected $policies = [ Task::class => TaskPolicy::class, ];
تخويل حدث Authorizing The Action
الآن وبعد كتابة سياستنا دعونا نستخدم الدالة حذف، جميع متحكمات لارافيل مؤهلة لإستدعاء الدالة تخويل authorize والتي هي مكشوفة لها عبر استخدام التريت triat المسمى AuthorizesRequest وبالتالي يكون استخدام السياسة كما في الأسفل:
/** * Destroy the given task. * * @param Request $request * @param Task $task * @return Response */ public function destroy(Request $request, Task $task) { $this->authorize('destroy', $task); // Delete The Task... }
لننظر في هذا الإستدعاء لدالة التخويلauthorize لبره من الوقت، المعامل (البراميتر) الأول المُعطى لها هو اسم دالة المكتوب داخل السياسة التي نريد استدعاءها. المعامل الثاني هو نسخة من المهمة ـ هنا توضع إهتمامنا ـ تذكر لقد قمنا مؤخراً بإخبار لارافيل أن موديل المهمة يقابله TaskPolicy وهكذا إيطار العمل يعلم أي دالة حذف destroy يجب عليه أن ينفذ.
المستخدم الحالي سيتم إرساله بشكل أوتوماتيكي إلى دالة السياسية، وبالتالي لن نحتاج إلى إرساله بشكل يدوي هنا.
في حال كان الحدث هو مُخول فإذن شفراتنا ستكمل في التنفيذ بشكل طبيعي، ولكن في حال كان الحدث غير مخول ( أي الدالة حذف داخل السياسة قد قامت بإرجاع قيمة false) إستثناء (إكسبشن exception ) ٤٠٣ سيتم رميه وصفحة خطأ سوف تظهر للمستخدم.
ملاحظة: يوجد العديد من الطرق للتفاعل مع الناتج الصادر عن خدمات التخويل في لارافيل. تأكد من إكمال محتوى مستندات التخويل.
حذف المهمة
وأخيراً! دعونا ننهي إضافة المنطق إلى دالة الحذف الموجودة في متحكم المهام، لنقم فعلياً بالحذف المهمة المعطا إلينا، يمكننا إستخدام دالة الحذف الخاصة بإيلكونت لحذف نسخة الموديل المعطاة من قاعدة البيانات. و حالما يتم حذف السجل سنقوم بإعادة التوجيه المستخدم للوراء إلى رابط المهام laravelme.com/tasks
/** * Destroy the given task. * * @param Request $request * @param Task $task * @return Response */ public function destroy(Request $request, Task $task) { $this->authorize('destroy', $task); $task->delete(); return redirect('/tasks'); }
لقد انتهينا، هنيئاً لكم على إكمال هذه القراءة وأشكركم من كل قلبي
هذا التعليم جزء من مستندات لارافيل، تمت ترجمته بعد موافقة الأخ تايلور آوت ويل