اليوم 7 :صفحة Category
سابقا في Jobeet
بالأمس قمتم بتوسيع معرفتكم ب symfony في الكثير من المجالات المختلفة: للاستعلام ب Propel, التجهيزات الثابتة، التوجيه، تصحيح الأخطاء وتعريف التكوين. و انتهينا بتحد صغير لهذا اليوم Jobeet .
أرجو أن تعملوا على صفحة الفئة لأن هذا اليوم التعليمي من جوبيت سيكون أكثر قيمة بالنسبة لكم .
مستعدين؟ فلنتحدث عن الطريقة التنفيذية المحتملة.
مسار Category
أولا ، نحن بحاجة إلى إضافة مسار لتحديد عنوان جيد لصفحة الفئة Category. أضفه في بداية ملف التوجيه:
# apps/frontend/config/routing.yml category: url: /category/:slug class: sfPropelRoute param: { module: category, action: show } options: { model: JobeetCategory, type: object }
tip
كلما بدأت بتنفيذ ميزة جديدة، من المستحسن التفكير في العنوان وخلق الطريق الذي يربط به. ومن الواجب إزالة قواعد التوجيه الافتراضي.
يمكن للمسار استخدام أي عمود من العناصر المرتبطة به على شكل متغير ويمكنه أيضا استخدام أي قيمة أخرى إذا كان هناك اتصال ب Accessor
محدد في عنصر الصنف .
لكى يعمل المسار, نحن بحاجة إلى إضافة accessor
افتراضى في JobeetCategory
لأن المتغير فى slug
ليس له أي عمود مقابل له في الجدولcategory
.
// lib/model/JobeetCategory.php public function getSlug() { return Jobeet::slugify($this->getName()); }
رابط Category
الآن لنغير الدالة indexSuccess.php فى template و المتواجدة فى الوحدة job و ذلك من أجل إضافة رابط لصفحة Category
<!-- some HTML code --> <h1><?php echo link_to($category, 'category', $category) ?></h1> <!-- some HTML code --> </table> <?php if (($count = $category->countActiveJobs() - sfConfig::get('app_max_jobs_on_homepage')) > 0): ?> <div class="more_jobs"> and <?php echo link_to($count, 'category', $category) ?> more... </div> <?php endif; ?> </div> <?php endforeach; ?> </div>
نضيف هذا الرابط فقط إذا كان لدينا أكثر من 10 وظائف للعرض فى كل فئة category . يحتوى الرابط على الوظائف غير المعروضة. لكى يعمل template نحتاج إلى إضافة الدالة ()countActiveJobs إلى JobeetCategory .
// lib/model/JobeetCategory.php public function countActiveJobs() { $criteria = new Criteria(); $criteria->add(JobeetJobPeer::CATEGORY_ID, $this->getId()); return JobeetJobPeer::countActiveJobs($criteria); }
تستعمل الدالة ()countActiveJobs دالة لا وجود لها حتى الآن في JobeetJobPeer هى الدالة ()countActiveJobs . نقوم بتعويض محتوى الملف JobeetJobPeer.php بالترميز البرمجي التالى :
// lib/model/JobeetJobPeer.php class JobeetJobPeer extends BaseJobeetJobPeer { static public function doSelectActive(Criteria $criteria) { return self::doSelectOne(self::addActiveJobsCriteria($criteria)); } static public function getActiveJobs(Criteria $criteria = null) { return self::doSelect(self::addActiveJobsCriteria($criteria)); } static public function countActiveJobs(Criteria $criteria = null) { return self::doCount(self::addActiveJobsCriteria($criteria)); } static public function addActiveJobsCriteria(Criteria $criteria = null) { if (is_null($criteria)) { $criteria = new Criteria(); } $criteria->add(self::EXPIRES_AT, time(), Criteria::GREATER_THAN); $criteria->addDescendingOrderByColumn(self::CREATED_AT); return $criteria; } }
كما ترى لتفادى التكرار, لقد قمنا بتعميل الترميز البرمجى باكمله فى JobeetJobPeer لادخال دالة جديدة و مشتركة هى الدالة ()addActiveJobsCriteria.
DRY (Don't Repeat Yourself). (DRY (Don't Repeat Yourself)(http://en.wikipedia.org/wiki/Don%27t_repeat_yourself.
tip
اول مرة نعيد فيها استعمال الترميز البرمجي قد يكون كافيا, لكن إذا اضطررنا إلى إعادة استعماله مرات عديدة ِعندها يستحسن تعميل جميع الاستعمالات في دالة كما فعلنا بالأعلى .
في الدالة ()countActiveJobs
عوض استعمال ()doSelect
ثم حساب عدد النتائج, استعملنا الدالة الاسرع ()doCount
.
لقد قمنا بتغيير الكثير من الملفات بالنسبة لهذه الميزة فقط ٠لكن في كل مرة كنا نضيف فيها بعض الترميز البرمجى كنا نحاول وضعه في المكان الصحيح من البرنامج لكي نستطيع إعادة استعماله .
في هذه العملية قمنا بتعميل بعض الترميز المهم .
فهذا نموذج جيد عن كيفية العمل في مشروع symfony.
إنشاء الوحدة Job Category
حان الوقت لإنشاء الوحدة category
.
$ php symfony generate:module frontend category
إذا كان قد سبق لكم وأنشأتم وحدة فلابد أنكم قد إستعملتم propel:generate-module
.
هذا جيد لكننا لا نحتاج إلى 90% من الترميز البرمجي المولد لقد استعملت generate:module
وذلك لإنشاء وحدة فارغة.
tip
لمذا لم نقتصر على إضافة الاجراء category
إلى الوحدة job
؟
كان من الممكن فعل ذلك لكن بما أن الموضوع الرئيسي في صفحة category
هو category
كان من الطبيعي إنشاء الوحدة category.
عند الوصول إلى صفحة category
يتوجب على مسار category
العثور على الفئة category
المرتبطة بالمتغير المطلوب slug
,
لكن بما أن slug
غير مسجل في قاعدة البيانات و لأننا لا نستطيع إستنتاج إسم الفئة category
عن طريق slug,
فإنه لا توجد أي وسيلة للعثور على الفئة category
المرتبطة ب slug.
تحديث قاعدة البيانات
نحن بحاجة إلى إضافة عمود slug
إلى الجدول category:
# config/schema.yml propel: jobeet_category: id: ~ name: { type: varchar(255), required: true } slug: { type: varchar(255), required: true, index: unique }
الآن بما أن slug
هو عمود حقيقي يجب حذف الدالة ()JobeetCategory
منgetSlug
.
في كل مرة يتغير إسم category
علينا تغيير slug
أيضا.
لنغير الدالة ()setName
:
// lib/model/JobeetCategory.php public function setName($name) { parent::setName($name); $this->setSlug(Jobeet::slugify($name)); }
لتحديث جداول قاعدة البيانات إستعمل propel:build-all-load
ثم إملأ قاعدة البيانات بتركيباتنا fixtures .
$ php symfony propel:build-all-load
لدينا هنا كل شيء لإنشاء الدالة ()executeShow
قم بتغيير محتوى ملف الإجراءت category
بالترميز البرمجي التالي:
// apps/frontend/modules/category/actions/actions.class.php class categoryActions extends sfActions { public function executeShow(sfWebRequest $request) { $this->category = $this->getRoute()->getObject(); $this->jobs = $this->category->getActiveJobs(); } }
note
بما أننا حذفنا الدالة المولدة ()executeIndex
نستطيع أيضا حذف القالب المولد تلقائياً indexSuccess.php
.
(apps/frontend/modules/category/templates/indexSuccess.php
).
الخطوة الأخيرة هي إنشاء القالب showSuccess.php
.
// apps/frontend/modules/category/template/showSuccess.php <?php use_stylesheet('jobs.css') ?> <?php slot('title', sprintf('Jobs in the %s category', $category->getName())) ?> <div class="category"> <div class="feed"> <a href="">Feed</a> </div> <h1><?php echo $category ?></h1> </div> <table class="jobs"> <?php foreach ($category->getActiveJobs() as $i => $job): ?> <tr class="<?php echo fmod($i, 2) ? 'even' : 'odd' ?>"> <td class="location"><?php echo $job->getLocation() ?></td> <td class="position"><?php echo link_to($job->getPosition(), 'job_show_user', $job) ?></td> <td class="company"><?php echo $job->getCompany() ?></td> </tr> <?php endforeach; ?> </table>
الجزئيات
لاحظ أننا قمنا بنسخ و إلصاق <table>
من القالب indexSuccess.php
للوحدة (job)
والتي تعمل على إنشاء قائمة الوظائف .
هذا ليس جيدا, لنتعلم طريقة جديدة. عندما تكون بحاجة إلى إعادة استعمال بعض الأجزاء من القالب template
عليك إذن إنشاء الجزء partial.
الجزء هو قطعة من الترميز البرمجي الموجود في القالب template والذي يمكن أن تتشاركه مجموعة من القوالب . الجزء هو مجرد قالب يبدأ ب '_'.
أنشئ الملف list.php_
:
// apps/frontend/modules/job/templates/_list.php <table class="jobs"> <?php foreach ($jobs as $i => $job): ?> <tr class="<?php echo fmod($i, 2) ? 'even' : 'odd' ?>"> <td class="location"><?php echo $job->getLocation() ?></td> <td class="position"><?php echo link_to($job->getPosition(), 'job_show_user', $job) ?></td> <td class="company"><?php echo $job->getCompany() ?></td> </tr> <?php endforeach; ?> </table>
يمكن إضافة الجزء و ذلك باستعمال المساعد ()include_partial
helper:
<?php include_partial('job/list', array('jobs' => $jobs)) ?>
المتغير الأول في الدالة ()include_partial هو إسم الجزء ( مكون من إسم الوحدة, و / و إسم الجزء مع حذف '_').
المتغير الثاني في الدالة هو جدول من المتغيرات التي يجب تمريرها إلى الجزء .
note
لماذا لا نستعمل الدالة البانية ()include
بدلا من استعمال المساعد helper()include_partial
؟
الفرق الرئيسي بينهما هو أن المساعد ()include_partial
يتحمل built-in cache
قم بتعويض الترميز البرمجي الموجود في table>
HTML>
لكلا القالبين templates
وذلك باستدعاء : ()include_partial
.
// in apps/frontend/modules/job/templates/indexSuccess.php <?php include_partial('job/list', array('jobs' => $category->getActiveJobs(sfConfig::get('app_max_jobs_on_homepage')))) ?> // in apps/frontend/modules/category/templates/showSuccess.php <?php include_partial('job/list', array('jobs' => $category->getActiveJobs())) ?>
قائمة تقسيم الصفحات
حسب متطلبات اليوم 2:
"تقسم القائمة إلى صفحات في كل صفحة 20 وظيفة".
من أجل تقسيم قائمة من العناصر Propel,
يقدم لنا symfony
الصنف :
sfPropelPager
.
عوض تمرير العنصر job
في القالب templateshowSuccess
فإننا نمرر صفحة.
// apps/frontend/modules/category/actions/actions.class.php public function executeShow(sfWebRequest $request) { $this->category = $this->getRoute()->getObject(); $this->pager = new sfPropelPager( 'JobeetJob', sfConfig::get('app_max_jobs_on_category') ); $this->pager->setCriteria($this->category->getActiveJobsCriteria()); $this->pager->setPage($request->getParameter('page', 1)); $this->pager->init(); }
tip
تأخذ الدالة قيمة افتراضية على شكل متغير ثاني ()getParameter
.
في الترميز البرمجي للإجراء أعلاه, إذا لم يوجد المتغير المطلوب للصفحة فإن الدالة ()getParameter
ستعطي كنتيجة 1
يأخذ sfPropelPager
constructor
صنف النموذج و العدد الأقصى من items
للعرض في كل صفحة .
أضف القيمة الاخيرة في ملف وحدة التحكم:
# apps/frontend/config/app.yml all: active_days: 30 max_jobs_on_homepage: 10 max_jobs_on_category: 20
عندما نختار items
من قاعدة البيانات
فإن الدالة ()sfPropelPager::setCriteria
تستعمل العنصر Criteria
أضف الدالة ()getActiveJobsCriteria
// lib/model/JobeetCategory.php public function getActiveJobsCriteria() { $criteria = new Criteria(); $criteria->add(JobeetJobPeer::CATEGORY_ID, $this->getId()); return JobeetJobPeer::addActiveJobsCriteria($criteria); }
بما أننا الآن حددنا الدالة ()getActiveJobsCriteria
نستطيع تعميل الدوال الاخرى ل JobeetCategory
كي نستعملها .
// lib/model/JobeetCategory.php public function getActiveJobs($max = 10) { $criteria = $this->getActiveJobsCriteria(); $criteria->setLimit($max); return JobeetJobPeer::doSelect($criteria); } public function countActiveJobs() { $criteria = $this->getActiveJobsCriteria(); return JobeetJobPeer::doCount($criteria); }
أخيرا لنقم بتحديث القالب template:
<!-- apps/frontend/modules/category/templates/showSuccess.php --> <?php use_stylesheet('jobs.css') ?> <div class="category"> <div class="feed"> <a href="">Feed</a> </div> <h1><?php echo $category ?></h1> </div> <?php include_partial('job/list', array('jobs' => $pager->getResults())) ?> <?php if ($pager->haveToPaginate()): ?> <div class="pagination"> <a href="<?php echo url_for('category', $category) ?>?page=1"> <img src="/legacy/images/first.png" alt="First page" /> </a> <a href="<?php echo url_for('category', $category) ?>?page=<?php echo $pager->getPreviousPage() ?>"> <img src="/legacy/images/previous.png" alt="Previous page" title="Previous page" /> </a> <?php foreach ($pager->getLinks() as $page): ?> <?php if ($page == $pager->getPage()): ?> <?php echo $page ?> <?php else: ?> <a href="<?php echo url_for('category', $category) ?>?page=<?php echo $page ?>"><?php echo $page ?></a> <?php endif; ?> <?php endforeach; ?> <a href="<?php echo url_for('category', $category) ?>?page=<?php echo $pager->getNextPage() ?>"> <img src="/legacy/images/next.png" alt="Next page" title="Next page" /> </a> <a href="<?php echo url_for('category', $category) ?>?page=<?php echo $pager->getLastPage() ?>"> <img src="/legacy/images/last.png" alt="Last page" title="Last page" /> </a> </div> <?php endif; ?> <div class="pagination_desc"> <strong><?php echo $pager->getNbResults() ?></strong> jobs in this category <?php if ($pager->haveToPaginate()): ?> - page <strong><?php echo $pager->getPage() ?>/<?php echo $pager->getLastPage() ?></strong> <?php endif; ?> </div>
معظم هذا الترميز البرمجي يتعلق بروابط صفحات أخرى . لديكم هنا قائمة الأساليب المستخدمة في هذا template :
()getResults
: تعطي كنتيجة جدول من العناصر Propel للصفحة الحالية .()getNbResults
: تعطي كنتيجة العدد الإجمالي للنتائج .()haveToPaginate
: تعطي كنتيجة 'صحيح' 'true' إذا كان هناك أكثر من صفحة واحدة .()getLinks
: تعطي كنتيجة قائمة روابط الصفحة المعروضة .()getPage
: تعطي كنتيجة رقم الصفحة الحالية .()getPreviousPage
: تعطي كنتيجة رقم الصفحة السابقة .()getNextPage
: تعطي كنتيجة رقم الصفحة الموالية .()getLastPage
: تعطي كنتيجة رقم الصفحة الأخيرة .
نراكم غدا إن شاء الله
إذا عملتم البارحة على تنفيذكم الخاص و أحسستم أنكم لم تتعلموا الكثير هذا اليوم فهذا يعني أنكم بدأتم تعتادون على فلسفة symfony .
عملية إضافة مميزات جديدة لموقع إلكتروني مصمم ب symfony تكون دائما نفسها: يجب التفكير في الروابط, إنشاء بعض الإجراءات ِ, تحديث النموذج وكتابة بعض templates وإذااستطعت تطبيق بعض الممارسات الجيدة في التطوير لهذا المزيج ، ستصبح محترفا في symfony سريعا جدا .
غدا سيكون بداية أسبوع جديد. للإحتفال سنتحدث عن موضوع جديد :آليات الإختبار
يحتوي The release_day_07
subversion tag
على الترميز البرمجي لهذا اليوم.
http://svn.jobeet.org/tags/release_day_07/
This work is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.