سابقا في Jobeet
بالأمس قمتم بتوسيع معرفتكم ب symfony في الكثير من المجالات المختلفة: للاستعلام ب Doctrine, التجهيزات الثابتة، التوجيه، تصحيح الأخطاء وتعريف التكوين. و انتهينا بتحد صغير لهذا اليوم Jobeet .
أرجو أن تعملوا على صفحة الفئة لأن هذا اليوم التعليمي من جوبيت سيكون أكثر قيمة بالنسبة لكم .
مستعدين؟ فلنتحدث عن الطريقة التنفيذية المحتملة.
مسار Category
أولا ، نحن بحاجة إلى إضافة مسار لتحديد عنوان جيد لصفحة الفئة Category. أضفه في بداية ملف التوجيه:
# apps/frontend/config/routing.yml category: url: /category/:slug class: sfDoctrineRoute param: { module: category, action: show } options: { model: JobeetCategory, type: object }
tip
كلما بدأت بتنفيذ ميزة جديدة، من المستحسن التفكير في العنوان وخلق الطريق الذي يربط به. ومن الواجب إزالة قواعد التوجيه الافتراضي.
يمكن للمسار استخدام أي عمود من العناصر المرتبطة به على شكل متغير ويمكنه أيضا استخدام أي قيمة أخرى إذا كان هناك اتصال ب Accessor
محدد في عنصر الصنف .
لكى يعمل المسار, نحن بحاجة إلى إضافة accessor
افتراضى في JobeetCategory
لأن المتغير فى slug
ليس له أي عمود مقابل له في الجدولcategory
.
// lib/model/doctrine/JobeetCategory.class.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/doctrine/JobeetCategory.class.php public function countActiveJobs() { $q = Doctrine_Query::create() ->from('JobeetJob j') ->where('j.category_id = ?', $this->getId()); return Doctrine::getTable('JobeetJob')->countActiveJobs($q); }
تستعمل الدالة ()countActiveJobs دالة لا وجود لها حتى الآن في JobeetJobTable هى الدالة ()countActiveJobs . نقوم بتعويض محتوى الملف JobeetJobTable.php بالترميز البرمجي التالي :
// lib/model/doctrine/JobeetJobTable.class.php class JobeetJobTable extends Doctrine_Table { public function retrieveActiveJob(Doctrine_Query $q) { return $this->addActiveJobsQuery($q)->fetchOne(); } public function getActiveJobs(Doctrine_Query $q = null) { return $this->addActiveJobsQuery($q)->execute(); } public function countActiveJobs(Doctrine_Query $q = null) { return $this->addActiveJobsQuery($q)->count(); } public function addActiveJobsQuery(Doctrine_Query $q = null) { if (is_null($q)) { $q = Doctrine_Query::create() ->from('JobeetJob j'); } $alias = $q->getRootAlias(); $q->andWhere($alias . '.expires_at > ?', date('Y-m-d h:i:s', time())) ->addOrderBy($alias . '.expires_at DESC'); return $q; } }
كما ترى لتفادى التكرار, لقد قمنا بتعميل الترميز البرمجى بأكمله فى JobeetJobTable
لإدخال دالة جديدة و مشتركة هى الدالة ()addActiveJobsQuery
DRY (Don't Repeat Yourself).
tip
اول مرة نعيد فيها استعمال الترميز البرمجي قد يكون كافيا, لكن إذا اضطررنا إلى إعادة استعماله مرات عديدة ِعندها يستحسن تعميل جميع الاستعمالات في دالة كما فعلنا بالأعلى .
في الدالة ()countActiveJobs
عوض استعمال ()execute
ثم حساب عدد النتائج, استعملنا الدالة الاسرع ()Count
.
لقد قمنا بتغيير الكثير من الملفات بالنسبة لهذه الميزة فقط ٠لكن في كل مرة كنا نضيف فيها بعض الترميز البرمجى كنا نحاول وضعه في المكان الصحيح من البرنامج لكي نستطيع إعادة استعماله .
في هذه العملية قمنا بتعميل بعض الترميز المهم .
فهذا نموذج جيد عن كيفية العمل في مشروع symfony.
إنشاء الوحدة Job Category
حان الوقت لإنشاء الوحدة category
.
$ php symfony generate:module frontend category
إذا كان قد سبق لكم وأنشأتم وحدة فلابد أنكم قد إستعملتم doctrine:generate-module
.
هذا جيد لكننا لا نحتاج إلى 90% من الترميز البرمجي المولد لقد استعملت generate:module
وذلك لإنشاء وحدة فارغة.
tip
لمذا لم نقتصر على إضافة الاجراء category
إلى الوحدة job
؟
كان من الممكن فعل ذلك لكن بما أن الموضوع الرئيسي في صفحة category
هو category
كان من الطبيعي إنشاء الوحدة category.
عند الوصول إلى صفحة category
يتوجب على مسار category
العثور على الفئة category
المرتبطة بالمتغير المطلوب slug
,
لكن بما أن slug
غير مسجل في قاعدة البيانات و لأننا لا نستطيع إستنتاج إسم الفئة category
عن طريق slug,
فإنه لا توجد أي وسيلة للعثور على الفئة category
المرتبطة ب slug.
تحديث قاعدة البيانات
نحن بحاجة إلى إضافة عمود slug
إلى الجدول category:
يتكلف سلوك Doctrine
المسمى ب Sluggable
بالإعتناء بعمود slug
مانحتاج إليه ببساطة هو تشغيل سلوك النموذج JobeetCategory
وسيتكلف هو بالباقي .
# config/doctrine/schema.yml JobeetCategory: actAs: Timestampable: ~ Sluggable: fields: [name] columns: name: type: string(255) notnull: true
الآن بما أن slug
هو عمود حقيقي يجب حذف الدالة ()JobeetCategory
منgetSlug
.
note
يتم تحديد setting
لعمود slug
تلقائيا عند تسجيل قياس .
يتم تكوين slug
باستعمال قيمة name
و إعطائها للعنصر object .
لتحديث جداول قاعدة البيانات إستعمل doctrine:build-all-reload
ثم إملأ قاعدة البيانات بتركيباتنا fixtures .
$ php symfony doctrine:build-all-reload
لدينا هنا كل شيء لإنشاء الدالة ()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 وظيفة".
من أجل تقسيم قائمة من العناصر Doctrine,
يقدم لنا symfony
الصنف :
sfDoctrinePager
.
عوض تمرير العنصر job
في القالب templateshowSuccess
فإننا نمرر صفحة.
// apps/frontend/modules/category/actions/actions.class.php public function executeShow(sfWebRequest $request) { $this->category = $this->getRoute()->getObject(); $this->pager = new sfDoctrinePager( 'JobeetJob', sfConfig::get('app_max_jobs_on_category') ); $this->pager->setQuery($this->category->getActiveJobsQuery()); $this->pager->setPage($request->getParameter('page', 1)); $this->pager->init(); }
tip
تأخذ الدالة قيمة افتراضية على شكل متغير ثاني ()getParameter
.
في الترميز البرمجي للإجراء أعلاه, إذا لم يوجد المتغير المطلوب للصفحة فإن الدالة ()getParameter
ستعطي كنتيجة 1
يأخذ sfDoctrinePager
constructor
صنف النموذج و العدد الأقصى من items
للعرض في كل صفحة .
أضف القيمة الاخيرة في ملف وحدة التحكم:
# apps/frontend/config/app.yml all: active_days: 30 max_jobs_on_homepage: 10 max_jobs_on_category: 20
()sfDoctrinePager::setQuery
تستعمل العنصر Doctrine_Query
أضف الدالة ()getActiveJobsCriteria
// lib/model/doctrine/JobeetCategory.class.php public function getActiveJobsQuery() { $q = Doctrine_Query::create() ->from('JobeetJob j') ->where('j.category_id = ?', $this->getId()); return Doctrine::getTable('JobeetJob')->addActiveJobsQuery($q); }
بما أننا الآن حددنا الدالة ()getActiveJobsQuery
نستطيع تعميل الدوال الأخرى ل JobeetCategory
كي نستعملها .
// lib/model/doctrine/JobeetCategory.class.php public function getActiveJobs($max = 10) { $q = $this->getActiveJobsQuery() ->limit($max); return $q->execute(); } public function countActiveJobs() { return $this->getActiveJobsQuery()->count(); }
أخيرا لنقم بتحديث القالب 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
: تعطي كنتيجة جدول من العناصر Doctrine للصفحة الحالية .()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.