مقدمة إلى Livewire
مقدمة إلى Laravel Livewire
Laravel Livewire هو إطار عمل متكامل لـ Laravel يجعل بناء الواجهات الديناميكية بسيطاً، دون مغادرة بيئة Laravel المريحة. يسمح لك ببناء واجهات تفاعلية وديناميكية باستخدام العرض من جانب الخادم دون كتابة JavaScript. مكونات Livewire هي فئات PHP تعرض قوالب Blade.
الفوائد الرئيسية لـ Livewire:
- بناء واجهات ديناميكية دون كتابة JavaScript
- لا حاجة لـ API - اتصال مباشر مع الخلفية
- التحقق من صحة النماذج والتعامل مع الأخطاء تلقائياً
- تحديثات فورية دون إعادة تحميل الصفحة
- العرض من جانب الخادم لتحسين محركات البحث
- التكامل مع نظام Laravel البيئي (التحقق، الصلاحيات، الأحداث)
تثبيت Livewire
للبدء مع Livewire في تطبيق Laravel الخاص بك، قم بتثبيته عبر Composer:
# تثبيت Livewire
composer require livewire/livewire
# نشر إعدادات Livewire (اختياري)
php artisan livewire:publish --config
بعد التثبيت، قم بتضمين أصول JavaScript و CSS الخاصة بـ Livewire في ملف التخطيط الرئيسي:
<!DOCTYPE html>
<html>
<head>
<title>تطبيق Livewire الخاص بي</title>
@livewireStyles
</head>
<body>
{{ $slot }}
@livewireScripts
</body>
</html>
نصيحة احترافية: ضع @livewireStyles قبل وسم الإغلاق </head> و @livewireScripts قبل وسم الإغلاق </body> للحصول على أداء مثالي.
إنشاء مكون Livewire الأول
تتكون مكونات Livewire من فئة PHP وعرض Blade. استخدم أمر artisan لإنشاء كليهما:
# إنشاء مكون Livewire
php artisan make:livewire Counter
# هذا ينشئ:
# app/Livewire/Counter.php
# resources/views/livewire/counter.blade.php
# يمكنك أيضاً الإنشاء في مجلدات فرعية
php artisan make:livewire Blog/PostList
إليك مثال بسيط لمكون عداد:
<?php
// app/Livewire/Counter.php
namespace App\Livewire;
use Livewire\Component;
class Counter extends Component
{
// خاصية عامة متاحة تلقائياً في العرض
public $count = 0;
// دالة يتم استدعاؤها من العرض
public function increment()
{
$this->count++;
}
public function decrement()
{
$this->count--;
}
public function render()
{
return view('livewire.counter');
}
}
<!-- resources/views/livewire/counter.blade.php -->
<div>
<h1>العدد: {{ $count }}</h1>
<button wire:click="increment">+</button>
<button wire:click="decrement">-</button>
</div>
لاستخدام المكون في أي عرض Blade:
<!-- استخدام صيغة الوسم -->
<livewire:counter />
<!-- استخدام توجيه Blade -->
@livewire('counter')
<!-- تمرير المعاملات -->
<livewire:counter :initial-count="10" />
الخصائص وربط البيانات
يوفر Livewire ربط بيانات ثنائي الاتجاه قوي باستخدام التوجيه wire:model. الخصائص العامة في فئة المكون يتم مزامنتها تلقائياً مع العرض.
<?php
// app/Livewire/SearchUsers.php
namespace App\Livewire;
use Livewire\Component;
use App\Models\User;
class SearchUsers extends Component
{
public $search = '';
public $results = [];
// يتم استدعاء هذه الدالة عند تغيير $search
public function updatedSearch()
{
$this->results = User::where('name', 'like', '%'.$this->search.'%')
->limit(10)
->get();
}
public function render()
{
return view('livewire.search-users');
}
}
<!-- resources/views/livewire/search-users.blade.php -->
<div>
<input type="text" wire:model.live="search" placeholder="البحث عن المستخدمين...">
<ul>
@foreach($results as $user)
<li>{{ $user->name }}</li>
@endforeach
</ul>
</div>
معدلات ربط البيانات:
wire:model.live- تحديث عند كل حدث إدخال (فوري)wire:model.blur- تحديث عند فقدان التركيزwire:model.debounce.500ms- تحديث بعد 500 مللي ثانية من عدم الكتابةwire:model.lazy- تحديث عند حدث "التغيير"
الإجراءات والدوال
تسمح لك إجراءات Livewire باستدعاء دوال على مكونك من الواجهة الأمامية باستخدام wire:click و wire:submit وتوجيهات الأحداث الأخرى.
<?php
// app/Livewire/TodoList.php
namespace App\Livewire;
use Livewire\Component;
use App\Models\Todo;
class TodoList extends Component
{
public $newTodo = '';
public $todos = [];
public function mount()
{
$this->todos = Todo::where('user_id', auth()->id())
->latest()
->get();
}
public function addTodo()
{
$this->validate([
'newTodo' => 'required|min:3|max:255'
]);
Todo::create([
'user_id' => auth()->id(),
'title' => $this->newTodo,
'completed' => false,
]);
$this->newTodo = '';
$this->mount(); // تحديث القائمة
session()->flash('message', 'تم إضافة المهمة بنجاح!');
}
public function toggleComplete($todoId)
{
$todo = Todo::find($todoId);
$todo->update(['completed' => !$todo->completed]);
$this->mount();
}
public function deleteTodo($todoId)
{
Todo::find($todoId)->delete();
$this->mount();
}
public function render()
{
return view('livewire.todo-list');
}
}
<!-- resources/views/livewire/todo-list.blade.php -->
<div>
@if (session()->has('message'))
<div class="alert alert-success">{{ session('message') }}</div>
@endif
<form wire:submit.prevent="addTodo">
<input type="text" wire:model="newTodo" placeholder="إضافة مهمة جديدة">
@error('newTodo') <span class="error">{{ $message }}</span> @enderror
<button type="submit">إضافة</button>
</form>
<ul>
@foreach($todos as $todo)
<li>
<input type="checkbox"
wire:click="toggleComplete({{ $todo->id }})"
@if($todo->completed) checked @endif>
<span class="{{ $todo->completed ? 'line-through' : '' }}">
{{ $todo->title }}
</span>
<button wire:click="deleteTodo({{ $todo->id }})">حذف</button>
</li>
@endforeach
</ul>
</div>
خطافات دورة الحياة
تحتوي مكونات Livewire على عدة خطافات دورة حياة تسمح لك بتنفيذ الكود في نقاط محددة من دورة حياة المكون.
خطافات دورة الحياة المتاحة:
mount()- يُستدعى عند تهيئة المكون (مثل المُنشئ)hydrate()- يُستدعى في الطلبات اللاحقة قبل أي إجراءupdating($name, $value)- يُستدعى قبل تحديث خاصيةupdated($name, $value)- يُستدعى بعد تحديث خاصيةupdatingPropertyName()- يُستدعى قبل تحديث خاصية محددةupdatedPropertyName()- يُستدعى بعد تحديث خاصية محددة
<?php
namespace App\Livewire;
use Livewire\Component;
class ProductForm extends Component
{
public $name = '';
public $price = 0;
public $category = '';
// يُستدعى عند تهيئة المكون
public function mount($productId = null)
{
if ($productId) {
$product = Product::find($productId);
$this->name = $product->name;
$this->price = $product->price;
$this->category = $product->category;
}
}
// يُستدعى في كل طلب (بعد mount في التحميل الأول)
public function hydrate()
{
// إعادة تعيين أخطاء التحقق
}
// يُستدعى قبل تحديث أي خاصية
public function updating($name, $value)
{
// تنظيف المدخلات
if ($name === 'name') {
return ucfirst($value);
}
}
// يُستدعى بعد تحديث أي خاصية
public function updated($name, $value)
{
$this->validateOnly($name, [
'name' => 'required|min:3',
'price' => 'required|numeric|min:0',
'category' => 'required'
]);
}
// يُستدعى قبل تحديث $price
public function updatingPrice($value)
{
// التأكد من أن السعر موجب
return max(0, $value);
}
// يُستدعى بعد تحديث $price
public function updatedPrice($value)
{
// حساب السعر المخفض تلقائياً
$this->discountedPrice = $value * 0.9;
}
public function render()
{
return view('livewire.product-form');
}
}
أفضل ممارسة: استخدم mount() للتهيئة لمرة واحدة و hydrate() للمنطق الذي يحتاج للتشغيل في كل طلب. استخدم الخطافات الخاصة بالخصائص مثل updatedPrice() للتحقق والتحويل المستهدف.
حالات التحميل
يوفر Livewire مؤشرات حالة تحميل مدمجة باستخدام التوجيه wire:loading:
<div>
<button wire:click="save">
حفظ
<span wire:loading wire:target="save">جاري الحفظ...</span>
</button>
<!-- إظهار دوار أثناء أي إجراء -->
<div wire:loading>
جاري المعالجة...
</div>
<!-- إظهار دوار لإجراء محدد -->
<div wire:loading wire:target="save">
جاري حفظ التغييرات...
</div>
<!-- إزالة العنصر أثناء التحميل -->
<div wire:loading.remove>
محتوى مخفي أثناء التحميل
</div>
<!-- حالات التحميل للخصائص -->
<input wire:model="search">
<div wire:loading wire:target="search">جاري البحث...</div>
</div>
مأزق شائع: لا تنس تضمين كل من @livewireStyles و @livewireScripts في التخطيط الخاص بك. فقدان هذه سيتسبب في عدم عمل مكونات Livewire بشكل صحيح.
تمرين تطبيقي 1: مكون نموذج الاتصال
أنشئ مكون Livewire لنموذج اتصال بالمتطلبات التالية:
- الحقول: الاسم، البريد الإلكتروني، الموضوع، الرسالة
- التحقق الفوري بينما يكتب المستخدم (استخدم wire:model.blur)
- عرض أخطاء التحقق أسفل كل حقل
- إظهار حالة التحميل عند الإرسال
- عرض رسالة نجاح بعد الإرسال
- مسح النموذج بعد الإرسال الناجح
تلميح: استخدم $this->validate() في دالة الإرسال و wire:loading لحالة التحميل.
تمرين تطبيقي 2: مكون البحث المباشر
ابنِ مكون بحث مباشر:
- يبحث عن المنتجات بالاسم أثناء كتابة المستخدم
- يستخدم التأخير (500 مللي ثانية) لتجنب الكثير من الطلبات
- يعرض "لم يتم العثور على نتائج" عندما يعود البحث فارغاً
- يظهر مؤشر تحميل أثناء البحث
- يسمح بالنقر على نتيجة لعرض التفاصيل
- ينفذ دالة لمسح نتائج البحث
إضافة: أضف حد أدنى لعدد الأحرف (3) قبل إجراء البحث.
تمرين تطبيقي 3: آلة حاسبة ديناميكية
أنشئ مكون آلة حاسبة بسيطة:
- مدخلان للأرقام (wire:model.live)
- قائمة منسدلة لاختيار العملية (+، -، *، /)
- عرض النتيجة تلقائياً عند تغيير المدخلات
- التعامل مع القسمة على صفر بشكل صحيح
- إظهار سجل الحسابات (آخر 5 حسابات)
- زر لمسح السجل
تحدي: استخدم خطافات دورة الحياة للتحقق من المدخلات وتنسيق النتيجة إلى رقمين عشريين.
الخلاصة
في هذا الدرس، تعلمت عن Laravel Livewire، إطار عمل قوي لبناء واجهات ديناميكية دون كتابة JavaScript. النقاط الرئيسية تشمل:
- تثبيت وإعداد Livewire في تطبيقات Laravel
- إنشاء مكونات Livewire بفئات PHP وعروض Blade
- استخدام الخصائص العامة وربط البيانات ثنائي الاتجاه مع wire:model
- تنفيذ الإجراءات والدوال مع wire:click وتوجيهات الأحداث الأخرى
- الاستفادة من خطافات دورة الحياة للتهيئة وتحديثات الخصائص
- إضافة حالات التحميل لتجربة مستخدم أفضل
- التحقق من صحة النماذج والتعامل مع الأخطاء في الوقت الفعلي
يربط Livewire الفجوة بين العرض التقليدي من جانب الخادم والواجهات التفاعلية الحديثة، مما يسمح لك ببناء تطبيقات ديناميكية مع البقاء في نظام Laravel البيئي. في الدرس التالي، سنستكشف بناء ميزات الوقت الفعلي مع البث و WebSockets.