إطار العمل Yii2: الأداة GridView

التعرّف على أهم الخصائص التي تمكّننا من التحكّم بالأداة GridView

عدنان توتنيمنذ 5 سنوات

تُعتبر الأداة GridView من أهم الأدوات المستخدمة في إطار العمل Yii2، فمن خلالها يمكنك عرض البيانات على شكل جدول مع إمكانية ترتيب هذه العناصر والبحث عن عناصر معيّنة أو عرض عدد محدد من الأسطر في كل صفحة.

غالباً ما تجد هذه الأداة في صفحات الـ index؛ أي الصفحات التي تُعنى بعرض جميع البيانات الخاصة بجدول معيّن.

سنقوم في هذا المقال بالتعرّف على هذه الأداة وذكر أهم الخصائص المتعلّقة بها.

سنقوم باستخدام الـ model الذي قمنا بإنشائه في المقال السابق وهو Cars، والذي يحوي الكود التالي:

<?php

namespace app\models;

use Yii;

class Cars extends \yii\db\ActiveRecord{
	
	public static function tableName(){
		return 'cars';
	}

	public function rules(){
		return [
			[['brand', 'model', 'price', 'type'], 'required'],
			[['details'], 'safe'],
			[['price'], 'integer'],
		];
	}
	
}

?>

تأكّد من وجود جدول cars في قاعدة البيانات مرتبط بالـ Cars model.

 الآن في ملف الـ controller (CarsController) سنقوم بإضافة action جديدة اسمها index لجلب جميع البيانات الموجودة في جدول cars:

public function actionIndex(){
	return $this->render('index');
}

الـ action ستقوم مبدأياً بإعادة الصفحة index فقط، لذلك قم بإنشاء صفحة view اسمها index في المجلد views/cars.

سنكتب في هذه الصفحة الكود المبدأي التالي، حيث سنقوم بتضمين الأداة GridView، وسنقوم باستخدامها بشكلها المجرّد حتى نتعرّف على بعض الـ attributes الموجودة فيها:

<?php
    use yii\grid\GridView;
?>

<h1>All cars:</h1>

<?= GridView::widget([
	'dataProvider' => … 
	'columns' => [
	…
    ]
]); ?>

الخاصية dataProvider ستحوي جميع الـ records التي سنقوم بجلبها من جدول cars، ولكن نلاحظ هنا أنّنا نستخدم dataProvider ولم نستخدم خاصّية model مثل الأداة DetailView التي رأيناها في المقال السابق!

هنا نحن نتعامل مع عدّة records، أي أنّنا نتعامل مع عدّة objects من الـ model، بالإضافة إلى أنّ هذه الـ records سيتم إجراء عمليات عليها مثل الترتيب والبحث وعرض عدد محدد منها ضمن الصفحة الواحدة وغير ذلك، فمن أجل هذه الديناميكية في التعامل مع الـ objects نستخدم dataProvider والذي ستكون قيمته نسخة من ActiveDataProvider التي تسمح لنا بالقيام بالعمليات السابقة.

أما الخاصية columns فسنضع فيها الأعمدة التي سيتم عرضها في أداة GridView.

سنقوم بتعديل التابع index في الـ controller كي نقوم بإنشاء ActiveDataProvider من أجل جلب البيانات من جدول cars:

...

use app\models\Cars;
use yii\data\ActiveDataProvider;

...
...

public function actionIndex(){
	$dataProvider = new ActiveDataProvider([
		'query' => Cars::find(),
    ]);
	return $this->render('index' , ['dataProvider' => $dataProvider]);
} 

لاحظ أنّنا قمنا بتضمين الـ model الذي نتعامل معه وهو Cars، وقمنا أيضاً بتضمين الكلاس ActiveDataProvider.

في التابع index قمنا بتعريف data provider من خلال إنشاء object من الكلاس ActiveDataProvider والذي يأخذ كـ parameter مصفوفة من النمط key/value لنضع فيها الخيارات التي نريدها.

أول وأهم خيار هو الخيار query، حيث نضع فيه الاستعلام الذي ينبغي تنفيذه لجلب البيانات وعرضها في الجدول (أي الـ GridView)، وهو الاستعلام نفسه الذي سيتم إعادة تنفيذه عند التنقّل بين الصفحات (سيتم شرح مفهوم الصفحات لاحقاً).

الاستعلام الذي وضعناه في الخيار query هو عبارة عن جلب جميع البيانات من جدول cars، ولاحظ أنّنا لم نستخدم التابع all، فوضعنا فقط التابع find بغير شروط.

أخيراً قمنا بنقل المتحول من نمط ActiveDataProvider إلى صفحة العرض index من خلال التابع render.

الآن سنقوم بتعديل صفحة index لتقوم بالتعامل مع الـ dataProvider الذي قمنا بنقله إلى الصفحة، وسنقوم بعرض جميع الأعمدة الموجودة فيه:

<?php
    use yii\grid\GridView;
?>

<h1>All cars:</h1>

<?= GridView::widget([
	'dataProvider' => $dataProvider,
	'columns' => [
	    'id',
	    'brand',
	    'model',
	    'price',
	    'details',
	    'type'
    ]
]); ?>

الآن عند تنفيذ التابع index ستظهر لنا النتيجة التالية:

لاحظ أنّ أسماء الأعمدة قابلة للضغط، فعند الضغط عليها سيتم إعادة ترتيب الأسطر بشكلٍ تصاعدي أو تنازلي اعتماداً على العمود الذي قمتَ بالضغط عليه.

 

لنقم الآن بإضافة عمودَين آخرَين (معرّفَين مسبقاً في الـ Yii2) إلى الأعمدة السابقة، وهما عمود التعداد التسلسلي، وعمود الأوامر.

أما عمود التعداد التسلسلي فيقوم بوضع رقم السّطر في بداية كلّ سطر، وهو أمرٌ مهمٌ عندما يكون الجدول يحوي أسطراً كثيرةً وتريد أن ترى رقم السّطر بجواره، وأمّا عمود الأوامر فيحوي على ثلاث أيقوناتٍ تستطيع من خلالها أن تنفّذ الأوامر الأساسية لكلِ سطرٍ مثل الذهاب إلى صفحة العرض أو التعديل، أو حذف السطر (أي حذف الـ record)، بالإضافة إلى أوامر أخرى يمكنك إضافتها.

<?php
    use yii\grid\GridView;
?>

<h1>All cars:</h1>

<?= GridView::widget([
	'dataProvider' => $dataProvider,
	'columns' => [
        ['class' => 'yii\grid\SerialColumn'],
	    'id',
	    'brand',
	    'model',
	    'price',
	    'details',
	    'type',
        ['class' => 'yii\grid\ActionColumn']
    ]
]); ?>

 عندما تمرّر مؤشر الفأرة فوق إحدى الأيقونات ستلاحظ (أسفل يمين أو يسار المتصفح) ظهور المسار الذي إن ضغطتَ على الأيقونة سيتم التوجّه إليه، فسترى حينها أنّك إن مرّرتَ الفأرة على أيقونة سلّة المهملات فإنّ المسار سيكون cars/delete متبوعاً بالـ id الخاص بالسّطر (أي بالـ object)، وهنا ستلاحظ السهولة الكبيرة والوقت الكثير الذي تمّ توفيره عليك في حال قمتَ ببرمجة ذلك يدوياً.

ولكن ماذا لو أردتَ فعلاً أن تبرمج الأوامر بيدك؟ أو ماذا لو أردتَ أن تجعل تنفيذ أمر التعديل مثلاً يذهب بك إلى غير controller وينفّذ غير action؟

تستطيع تغيير طبيعة تنفيذ كل أمر أو إضافة أوامر جديدة أو تغيير الأيقونات من خلال عمل override للأوامر الموجودة مسبقاً، ولذلك دعنا نجرّب تغيير هذه الأوامر.

أولاً لنفرض أنّك تريد فقط في عمود الأوامر أن تعرضَ أيقونة التعديل، فلا تريد مثلاً أن يتم حذف أو مشاهدة الـ object.

للقيام بذلك قم بتغيير الخاصّية template الموجودة في عمود الأوامر كالتالي:

(لاحظ أنّ عمود الأوامر عبارة عن مصفوفة يمكن أن يأخذ عدّة خيارات).

'columns' => [
    [
	    'class' => 'yii\grid\ActionColumn',
	    'template' => '{update}'
    ],
    ...
    ...
]

عندما تضع في الخاصية template الأوامر التي تريد وضعها فيجب وضعها ضمن القوسَين { … }.

الآن لنقم بتغيير أيقونة التعديل والمسار الذي نريد التوجّه إليه عند الضغط على الأيقونة:

[
	'class' => 'yii\grid\ActionColumn',
	'template' => '{update}',
	'buttons' => [
		'update' => function($url, $model){
			return Html::a('<span class="glyphicon glyphicon-globe"></span>', ['any-controller/any-action', 'id' => $model->id]);
        }
    ]
]

قمنا بتغيير الخاصية buttons والتي من خلالها سنقوم بعمل override للزر update، حيث وضعنا الـ key والذي هو update وقيمته عبارة عن تابع يتم تفسيره أو ترجمته لإعادة القيمة المطلوبة كشكل الأيقونة والـ action التي يجب تنفيذها.

قمنا بإعادة زر (من خلال التابع المساعد a المُستدعى من الكلاس المساعد Html، لذلك لا تنسَ تضمينه في الصفحة)، وأول parameter له هو اسم الزر، ووضعنا هنا span تحوي glyphicon تزوّدنا بها مكتبة bootstrap المضمّنة بشكل تلقائي في الـ Yii2، ووضعنا الـ parameter الثاني المسار إلى الـ controller والـ action التي نريد تنفيذها، ومرّرنا الـ id الخاص بالـ object من خلال الوسيط model الذي يعبّر عن الـ object الخاص بالسّطر الحالي.

 

ولإضافة أيقونة جديدة فما علينا إلا أن نضيفها إلى الـ template ومن ثمّ نضيفها إلى الـ buttons ونتحكّم بخصائصها كما فعلنا مع الزر update:

[
	'class' => 'yii\grid\ActionColumn',
	'template' => '{update} {myAction}',
	'buttons' => [
		'update' => function($url, $model){
			return Html::a('<span class="glyphicon glyphicon-globe"></span>', ['any-controller/any-action', 'id' => $model->id]);
        },
		'myAction' => function($url, $model){
			return Html::a('<span class="glyphicon glyphicon-user"></span>', ['any-controller/any-action', 'id' => $model->id]);
        }

    ]
]

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

التحكّم بطريقة عرض البيانات في كل عمود:

رأينا أنّه من أجل عرض الأعمدة الموجودة في الـ model فيكفي أن نقوم بكتابة اسم العمود ضمن الخاصية columns، ولكن هذه الطريقة تمكّننا فقط من عرض القيمة الموجودة في العمود بدون إضافة أو تحكّمٍ كاملٍ بها.

من أجل أن يكون لدينا قدرة على وضع القِيَم التي نريدها والتعديل على خصائص العمود نقوم بكتابة العمود على الشكل التالي:

'columns' => [
	[
		'attribute' =>
		'formar' => 
		'value' => 
		'headerOptions' =>
    ],
]

قمنا بكتابة العمود الواحد على شكل مصفوفة من النمط key/value، حيث تكون الـ keys على الشكل التالي:

attribute: تمثّل اسم الحقل أو العمود الذي نريد عرضه.

format: تمثّل الطريقة التي نريد عرض القيمة فيها، يمكننا عرض القيمة على شكل نصّي، ويمكننا عرض عناصر HTML ضمن خانات الـ GridView كما سنرى لاحقاً.

value: القيمة التي نريد عرضها، حيث يمكن الاكتفاء بالقيمة الموجودة في قاعدة البيانات، أو يمكن تعديلها أو إضافة أمور أخرى إليها.

headerOptions: تمثّل الخيارات الأخرى التي يمكن وضعها للعمود، مثل الـ style أو الكلاسات الخاص به.

سنقوم بتطبيق بسيط حتى نشملَ الأفكار السّابقة، فسنجعل حقل الـ price متبوعاً برمز $، وسنجعل حقل الـ brand عبارة عن زر قابل للضغط نذهب من خلاله إلى صفحةٍ أخرى، وسنجعل عرض حقل الـ details ثابتاً بعرض 50% من عرض الجدول الكلّي.

'columns' => [
	[
	'attribute' => 'brand',
	'format' => 'raw',
	'value' => function($model){
		return Html::a($model->brand, ['/other-controller/any-action']);
	}
    ],
    'model',
    [
	    'attribute' => 'price',
	    'value' => function($model){
		    return $model->price . ' $'; 
	    } 
    ],
    [
	    'attribute' => 'details',
	    'headerOptions' => ['style' => 'width: 50%']
    ],
    'type'
]

لاحظ أنّ القيمة format => raw هي التي تسمح بعرض الزر بشكله الطبيعي، جرّب أن تحذف هذا السّطر وسترى أنّ القيمة التي سيتم عرضها عبارة عن الكود بدون تحويله إلى الصيغة النظامية لعرض عناصر HTML.

سنقوم أخيراً بذكر أمرٍ إضافيٍ يتعلّق بالأسطر هذه المرّة وليس الأعمدة، فلنفرض أنّنا نريد تلوين سطر معيّن في حال تحقق شرط ما في الـ object، فنستطيع ذلك عن طريق الخاصية rowOptions والتي نضعها ضمن خواص الـ GridView.

فمثلاً لتلوين الأسطر التي يكون فيها الـ type الخاص بالسّيارة هو Sedan:

<?= GridView::widget([
	'dataProvider' => $dataProvider,
	'rowOptions' => function($model){
		if($model->type == 'Sedan'){
			return ['style' => 'background-color: rgba(255, 0, 0, 0.3)'];
		}
    },
    'columns' => [
	…
    ]
]); ?>

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

لنعد الآن إلى الـ controller لنضيف أمرَين آخرَين، وهما عدد العناصر التي سيتم عرضها في كل صفحة، والحقول القابلة للترتيب.

عندما نتعامل مع عدد كبير من الـ records فإنّه من غير المجدي أن نقوم بعرض كل هذه الـ records في صفحةٍ واحدة، بل يجب تقسيمها في صفحاتٍ متعددة حتى يستطيع المستخدم التعامل معها بسلاسةٍ وتنظيمٍ أكبر، بالإضافة إلى تقليص عدد الـ records التي سيتم جلبها من قاعدة البيانات، فلو تمّ جلبها جميعاً دفعةً واحدة سيجعل الشبكة مثقلة بالبيانات التي يتم نقلها، أمّا عند تقسيم الـ records إلى صفحات فسيتم جلب عدد محدد منها فقط.

للقيام بذلك ما علينا إلا إضافة خاصّية جديدة للـ dataProvider المسؤول عن جلب البيانات في الـ controller، حيث نحدّد من خلاله عدد العناصر التي يتم جلبها في كل مرّة، هذا الأمر سيؤدّي إلى تقسيم عدد الـ records الكلّي الذي يحقق الـ query المُستخدمة، وسيتم إضافة pagination (أرقام تمثّل الصفحات) أسفل الـ GridView:

$dataProvider = new ActiveDataProvider([
    'query' => Cars::find(),
    'pagination' => [
        'pageSize' => 2
    ],
]);

هنا وضعنا القيمة pageSize مساويةً لـ 2، أي أنّه سيتم عرض سطرَين فقط في كل صفحة، وبما أنّ عدد العناصر الكلّي (الذي يحقق الـ query) هو 4 فقط، فسيتم تقسيم 4 على 2 (أي على قيمة pageSize)، وبالتالي سيظهر لدينا صفحتَين فقط.

 

أما بالنسبة للحقول القابلة للترتيب فيمكن التحكّم بها من خلال الخاصية sort، فربما لا نريد أن يقوم المستخدم بترتيب الأسطر تبعاً لحقولٍ محددة، فنستطيع منعه من ذلك من خلال تحديد الأعمدة القابلة للترتيب:

$dataProvider = new ActiveDataProvider([
    'query' => Cars::find(),
    'pagination' => [
        'pageSize' => 2
    ],
    'sort' => [
        'attributes' => [
            'brand',
            'price'
        ]
    ]
]);

نلاحظ عند وضع الحقلَين brand و price أنّهما الحقلان الوحيدان القابلان للضغط، أما بقية الحقول فليست كذلك.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

كلمات دليلية: gridview widget yii yii2
0
إعجاب
1537
مشاهدات
0
مشاركة
1
متابع

التعليقات (0)

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !