سابقا في Jobeet
أمس كان يوما عظيما . هل تعلم كيفية إنشاء عناوين جميلة ، وكيفية استخدام الإطار symfony في جعل الكثير من الامور بالنسبة لك تعمل أتوماتيكيا .
اليوم, نحن سوف نحسن موقع Jobeet من خلال التغيير والتبديل في الرمز هنا وهناك . عمليا , ستتعلم المزيد حول كافة الميزات لدينا أكثر مما عرض خلال الأيام الخمسة الأولى من هذا البرنامج التعليمي .
The Doctrine Query Object
من احتياجات يوم 2 :
"عندما يدخل المستخدم على موقع Jobeet, يرى قائمة الوظائف النشطة"
ولكن من الآن, يتم عرض جميع الوظائف , سواء كانت نشطة أم لا:
class jobActions extends sfActions { public function executeIndex(sfWebRequest $request) { $this->jobs = Doctrine::getTable('JobeetJob') ->createQuery('a') ->execute(); } // ... }
الوظيفة النشيطة هي التي نشرت أقل من 30 يوم. الدالة ()Doctrine_Query::execute
سوف تقدم طلبا إلى قاعدة البيانات. في أعلى هذا الرمز , نحن لم نحدد أي شرط فيها و هذا يعني أن كل السجلات سيتم إسترجاعها من قاعدة البيانات .
دعونا نغير و نقم باختيار الوظائف النشيطة فقط :
public function executeIndex(sfWebRequest $request) { $q = Doctrine_Query::create() ->from('JobeetJob j') ->where('j.created_at > ?', date('Y-m-d h:i:s', time() - 86400 * 30)); $this->jobs = $q->execute(); }
تصحيح أخطاء Doctrine وليدت SQL
بما أنك لا تكتب باليد بيانات SQL, سيتولى Doctrine الاختلافات بين محركات قواعد البيانات و اختيار توليد بيانات SQL الأمثل لمحرك قاعدة البيانات خلال 3 أيام .
ولكن في بعض الأحيان , يمثل عونا كبيرا لرؤية SQL التي تولدها Doctrine; على سبيل المثال , لتصحيح إستعلام إذا كان لا يعمل كما هو متوقع .
في المحيط dev
, يسجل symfony الاستعلامات (مع الكثير ) في المجلد /log
. هناك ملف الدخول واحد لكل مزيج من التطبيق و المحيط . الملف الذي نتطلع اليه هو بإسم frontend_dev.log
:
# log/frontend_dev.log Dec 04 13:58:33 symfony [info] {sfDoctrineLogger} executeQuery : SELECT j.id AS j__id, j.category_id AS j__category_id, j.type AS j__type, j.company AS j__company, j.logo AS j__logo, j.url AS j__url, j.position AS j__position, j.location AS j__location, j.description AS j__description, j.how_to_apply AS j__how_to_apply, j.token AS j__token, j.is_public AS j__is_public, j.is_activated AS j__is_activated, j.email AS j__email, j.expires_at AS j__expires_at, j.created_at AS j__created_at, j.updated_at AS j__updated_at FROM jobeet_job j WHERE j.created_at > ? (2008-11-08 01:13:35)
يمكنك أن ترى بنفسك أن Doctrine لديها شرط للعمود created_at
(? > WHERE j.created_at
).
note
السلسلة ?
في الاستعلام تشير إلى أن Doctrine يولد إعدادات البيانات .
القيمة الفعلية ل ?
('2008-11-06 15:47:12' في المثال أعلاه )
تنتقل أثناء تنفيذ الاستعلام بشكل صحيح إنطلاقا من محرك البحث. استخدام البيانات المعدة يقلل بشكل كبير من تعرض
SQL injection الخاص بك للهجمات .
وهذا جيد , لكن الامر مزعج بعض الشيء للتبديل بين المتصفح ,the IDE , و ملف التسجيل في كل مرة كنت بحاجة لاختبار تغيير ما .
يرجع الفضل في ذلك إلى شريط أدوات تصحيح الويب symfony, كل المعلومات التي تحتاجها متوفرة ويدخل هذا كله ضمن راحة متصفحك :
تسلسل العناصر
حتى لو كانت تعمل على الرمز, هو أبعد ما يكون عن الكمال لأنها لا تأخذ في الحسبان بعض الاحتياجات من يوم 2.
"يمكن للمستخدم إعادة تنشيط أو تمديد صلاحية الاعلان عن الوظائف ل 30 يوما إضافيا ..."
ولكن كما ذكرنا أعلاه يعتمد الرمز على قيمة created_at
, و بما أن هذا العمود يخزن تاريخ الإنشاء فإننا لا نستطيع تلبية الاحتياجات المذكورة أعلاه .
ولكن إذا كنت تتذكر مخطط قاعدة البيانات الذي قمنا بوصفه خلال الأيام الثلاثة السابقة , علينا أيضا أن نحدد العمود expires_at
. حاليا هذه القيمة فارغة دائما على اعتبار أنها ليست لها مهمة أساسية في بيانات fixture. ولكن عندما يتم إنشاء وظيفة, يأخذ تلقائيا 30 يوم بعد التاريخ الحالي .
عندما تحتاج لعمل شيء تلقائيا قبل أن يتسلسل عنصر Doctrine مع قاعدة البيانات , يمكنك تفادي الدالة ()save
لنموذج العنصر :
// lib/model/doctrine/JobeetJob.class.php class JobeetJob extends BaseJobeetJob { public function save(Doctrine_Connection $conn = null) { if ($this->isNew() && !$this->getExpiresAt()) { $now = $this->getCreatedAt() ? strtotime($this->getCreatedAt()) : time(); $this->setExpiresAt(date('Y-m-d h:i:s', $now + 86400 * 30)); } return parent::save($conn); } // ... }
الدالة ()isNew
تعطي النتيجة true
عندما يكون العنصر متسلسل مع قاعدة لبيانات و false
غير ذلك .
والآن , دعونا نغير في action استخدام العمود expires_at
بدلا من العمود created_at
لاختيار الوظائف النشطة :
public function executeIndex(sfWebRequest $request) { $q = Doctrine_Query::create() ->from('JobeetJob j') ->where('j.expires_at > ?', date('Y-m-d h:i:s', time())); $this->jobeet_job_list = $q->execute(); }
نحن نقيد الاستعلامات في إختيار فقط الوظائف مع تاريخ expires_at
في المستقبل .
المزيد مع Fixtures
تحديث الصفحة Jobeet في متصفحك لن يغير شيئا لأن الوظيفة في قاعدة البيانات التي تم نشرها قبل بضعة أيام فقط . دعونا نغيير fixtures لاضافة وظيفة منتهية بالفعل :
# data/fixtures/jobs.yml JobeetJob: # other jobs expired_job: JobeetCategory: programming company: Sensio Labs position: Web Developer location: Paris, France description: Lorem ipsum dolor sit amet, consectetur adipisicing elit. how_to_apply: Send your resume to lorem.ipsum [at] dolor.sit is_public: true is_activated: true created_at: '2005-12-01 00:00:00' token: job_expired email: job@example.com
note
كن حذرا عند نسخ ولصق الرمز في ملف fixture لعدم كسر تسنن . يجب أن تترك مساحتين قبل expired_job
.
كما ترون في الوظيفة أضفناها لدينا في ملف fixture, يمكن تعريف قيمة العمود created_at
حتى لو كان تشغيلها تلقائيا من طرف Doctrine. وقد حددت القيمة القصوى لواحد تلقائيا.
أعد تحميل fixture أعد تحديث متصفحك للتأكد من أن الوظائف القديمة لا تظهر :
$ php symfony doctrine:data-load
يمكنك أيضا تنفيذ الاستعلام الاتي للتأكد من أن العمود expires_at
يعمل تلقائيا بواسطة الدالة ()save
, معتمدا على قيمة العمود created_at
:
SELECT `position`, `created_at`, `expires_at` FROM `jobeet_job`;
عوامل التهيئة
في الدالة ()JobeetJob::save
, من الصعب علينا تشفير عدد أيام الوظيفة التي إنتهت مدة صلاحيتها . كان من الأفضل أن نستطيع التحكم في 30 يوما
يوفر الإطار symfony يوفر ملف تهيئة مدمج في التطبيق بإعدادات محددة , ملف app.yml
. هذا الملف YAML يمكن أن يحتوي على جميع الإعدادات التي تريدها :
# apps/frontend/config/app.yml all: active_days: 30
في التطبيق , نحصل على هذه الإعدادات من خلال النموذج الجامع sfConfig
:
sfConfig::get('app_active_days')
نبدأ اسم الإعداد ب app_
لأن النموذج sfConfig
يمكنكم إيضا من الوصول إلى إعدادات symfony كما سنرى لاحقا .
دعونا نقوم بتحديث الرمز بالأخذ بعين الاعتبار هذا الإعداد الجديد:
public function save(Doctrine_Connection $conn = null) { if ($this->isNew() && !$this->getExpiresAt()) { $now = $this->getCreatedAt() ? strtotime($this->getCreatedAt()) : time(); $this->setExpiresAt(date('Y-m-d h:i:s', $now + 86400 * sfConfig::get('app_active_days'))); } return parent::save($conn); }
ملف التهيئة app.yml
طريقة متميزة لتجميع جميع إعدادات تطبيقك.
Refactoring
بالرغم من أننا نتوفر على رمز مكتوب بشكل جيد , هو ليس صحيحا بعد إلى حدّ بعيد . هل يمكنك حل المشكلة ؟
رمز Doctrine_Query
لا ينتمي إلى action ( طبقة المراقبة ), انه ينتمي الى طبقة النموذج .
في النموذج MVC يعرف النموذج منطق جميع الأعمال , و المراقب هو الوحيد الذي ينادي النموذج لاسترجاع البيانات منه . نحصل من الرمز كنتيجة على مجموعة من الوظائف , دعنا ننتقل من رمز الصنف JobeetJobTable
و أنشأ الدالة ()getActiveJobs
:
// lib/model/doctrine/JobeetJobTable.class.php class JobeetJobTable extends Doctrine_Table { public function getActiveJobs() { $q = $this->createQuery('j') ->where('j.expires_at > ?', date('Y-m-d h:i:s', time())); return $q->execute(); } }
الآن يمكن استخدام هذه الدالة في رمز action لاستعادة الوظائف النشطة.
public function executeIndex(sfWebRequest $request) { $this->jobeet_job_list = Doctrine::getTable('JobeetJob')->getActiveJobs(); }
بعض فوائد refactoring على الرمز السابق :
- المنطق أن نحصل على الوظائف النشطة الآن من النموذج حيث تنتسب .
- يكون الرمز في المراقب مقروءا أكثر .
- الدالة
()getActiveJobs
صالحة لإعادة الاستعمال ( على سبيل المثال في مكان آخر ). - الرمز النموذجيي الآن وحدة قابلة للاختبار .
لنقم بتصنيف الوظائف حسب قيمة العمود expires_at
:
public function getActiveJobs() { $q = $this->createQuery('j') ->where('j.expires_at > ?', date('Y-m-d h:i:s', time())) ->orderBy('j.expires_at DESC'); return $q->execute(); }
الدالة orderBy
تضيف الشرط ORDER BY
لل SQL المولد
( وتوجد أيضا ()addOrderBy
)
الفئات على الصفحة الرئيسية
المتطلبات من اليوم2 :
" تصنف الوظائف بالفئة ثم تاريخ الإعلان عنها ( الوظائف الأحدث في المقدمة ) "
حتى الآن, لم نأخذ بعين الإعتبار مهمة الفئة . من الاحتياجات , يجب على الصفحة الرئيسية عرض الوظائف حسب الفئة . أولا , نحن بحاجة للحصول على كل الفئات على الأقل مع وظيفة مفعلة .
إفتح الصنف JobeetCategoryTable
و أضف الدالة ()getWithJobs
:
// lib/model/doctrine/JobeetCategoryTable.class.php class JobeetCategoryTable extends Doctrine_Table { public function getWithJobs() { $q = $this->createQuery('c') ->leftJoin('c.JobeetJob j') ->where('j.expires_at > ?', date('Y-m-d h:i:s', time())); return $q->execute(); } }
نغير action index
وفقا لذلك:
// apps/frontend/modules/job/actions/actions.class.php public function executeIndex(sfWebRequest $request) { $this->categories = Doctrine::getTable('JobeetCategory')->getWithJobs(); }
في القالب , نحن بحاجة إلى تكرار جميع الفئات خلاله وعرض الوظائف النشطة:
// apps/frontend/modules/job/indexSuccess.php <?php use_stylesheet('jobs.css') ?> <div id="jobs"> <?php foreach ($categories as $category): ?> <div class="category_<?php echo Jobeet::slugify($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> </div> <?php endforeach; ?> </div>
note
لعرض اسم الفئة في القالب قد استخدمنا echo $category
.
هل هذا الصوت غريب؟ category$
هو عنصر, كيف يمكن ل echo
السحرية عرض اسم الفئة؟
كان الرد خلال اليوم 3 عندما عرفنا سحر الدالة ()toString__
لجميع نماذج الأصناف .
لهذا العمل ، نحن بحاجة إلى إضافة الدالة ()getActiveJobs
إلى الصنف JobeetCategory
:
// lib/model/doctrine/JobeetCategory.class.php public function getActiveJobs() { $q = Doctrine_Query::create() ->from('JobeetJob j') ->where('j.category_id = ?', $this->getId()); return Doctrine::getTable('JobeetJob')->getActiveJobs($q); }
تستخدم الدالة ()JobeetCategory::getActiveJobs
الدالة()Doctrine::getTable('JobeetJob')->getActiveJobs
لاستعادة الوظائف النشطة لفئة معينة .
عندما ننادي ()Doctrine::getTable('JobeetJob')->getActiveJobs
, نريد أن نتقيد بشرط أكثر حتى من الفئة المقدمة . بدلا من المرور على العنصر الفئة قررنا المرور بالعنصر Doctrine_Query
و هذه هي أفضل طريقة لنضمن الشرط العام.
تحتاج ()getActiveJobs
لدمج عوامل Doctrine_Query
مع معاييره الخاصة. كما أن Doctrine_Query
هو عنصر , وهذا بسيط جدا :
// lib/model/doctrine/JobeetJobTable.class.php public function getActiveJobs(Doctrine_Query $q = null) { if (is_null($q)) { $q = Doctrine_Query::create() ->from('JobeetJob j'); } $q->andWhere('j.expires_at > ?', date('Y-m-d h:i:s', time())) ->addOrderBy('j.expires_at DESC'); return $q->execute(); }
تحديد النتائج
لا يزال هناك شرط واحد لتنفيذ في الصفحة الرئيسية لائحة الوظائف :
" بالنسبة لكل فئة ، لا يظهر في القائمة سوى أول 10 وظيفة و وصلة تسمح ببيان جميع الوظائف لفئة معينة "
هذا بسيط جدا نضيف إلى الدالة ()getActiveJobs
:
// lib/model/doctrine/JobeetCategory.class.php public function getActiveJobs($max = 10) { $q = Doctrine_Query::create() ->from('JobeetJob j') ->where('j.category_id = ?', $this->getId()) ->limit($max); return Doctrine::getTable('JobeetJob')->getActiveJobs($q); }
مناسبة الشرط LIMIT
الآن صعبة التشفير إلى نموذج ولكن من الافضل أن نستطيع التحكم فيها. غير القالب لتمرير الحد الأقصى لعدد الوظائف المحدد في app.yml
:
<!-- apps/frontend/modules/job/indexSuccess.php --> <?php foreach ($category->getActiveJobs(sfConfig::get('app_max_jobs_on_homepage')) as $i => $job): ?>
و أضف إعدادا جديدا في app.yml
:
all: active_days: 30 max_jobs_on_homepage: 10
حركية Fixtures
لن ترى أي فرق إلا إذا كنت قد قمت بتخفيض قيمة max_jobs_on_homepage
و وضعت واحدا. نحن بحاجة إلى إضافة مجموعة من الوظائف في fixture.
إذا يمكنك نسخ ولصق الوظيفة الموجودة عشرة أو عشرين مرة من جهة... ولكن هناك طريقة أفضل .
الازدواجية سيئة حتى في ملفات fixture.
symfony المنقذ! ملفات YAML في symfony يمكن أن تحتوي على شفرة PHP التي سيتم تقييمها قبل تحليل الملف. حرر
ملف fixtures jobs.yml
و أضف ما يلي في نهاية الرمز :
JobeetJob: # Starts at the beginning of the line (no whitespace before) <?php for ($i = 100; $i <= 130; $i++): ?> job_<?php echo $i ?>: JobeetCategory: programming company: Company <?php echo $i."\n" ?> position: Web Developer location: Paris, France description: Lorem ipsum dolor sit amet, consectetur adipisicing elit. how_to_apply: | Send your resume to lorem.ipsum [at] company_<?php echo $i ?>.sit is_public: true is_activated: true token: job_<?php echo $i."\n" ?> email: job@example.com <?php endfor; ?>
كن حذرا , لن يروق ل YAML أن تعبث بتسننه. نأخذ في الاعتبار النصائح البسيطة التالية عند إضافة رمز PHP لملف YAML:
البيانات
<?php ?>
يجب أن تبدأ دائما بالخط أو أن تكون جزءا لا يتجزأ من قيمتهاإذا كان البيان
<?php ?>
ينتهي بخط , تحتاج ضمنيا إلى انتاج خط جديد ("\n").يمكنك الآن إعادة تحميل fixtures بواسطة
doctrine:data-load
ونرى ما إذا كانت فقط 10 وظائف معروضة على الصفحة الرئيسية لفئةProgramming
. في ما يلي لقطة للشاشة ، علينا تغيير الحد الأقصى لعدد الوظائف إلى خمس لجعل الصورة أصغر :تأمين صفحة الوظيفة
.حين تنتهي مدة صلاحية الوظيفة, يجب أن لا يكون من الممكن الوصول إليه بعد الآن ، حتى لو كنت تعرف عنوان الموقع
حول عنوان موقع الوظائف المنتهية
( ضع مكان id
, id
الحالي في قاعدة بياناتك - ()SELECT id, token FROM jobeet_job WHERE expires_at < NOW
) :
/frontend_dev.php/job/sensio-labs/paris-france/ID/web-developer-expired
بدلا من عرض هذه المهمة ، نحن بحاجة إلى إعادة توجيه المستخدم إلى 404 صفحة . ولكن كيف لنا ان نفعل ذلك لأن إسترجاع الوظيفة يتم تلقائيا من طرف التوجيه ؟
# apps/frontend/config/routing.yml job_show_user: url: /job/:company_slug/:location_slug/:id/:position_slug class: sfDoctrineRoute options: model: JobeetJob type: object method_for_query: retrieveActiveJob param: { module: job, action: show } requirements: id: \d+ sf_method: [GET]
note
الإعداد method_for_query
لا يعمل قبل إطلاق الإصدار 1.2.2
الدالة retrieveActiveJob
ستتلقى العنصر Doctrine_Query
بناء على التوجيه :
// lib/model/doctrine/JobeetJobTable.class.php class JobeetJobTable extends Doctrine_Table { public function retrieveActiveJob(Doctrine_Query $q) { $q->andWhere('a.expires_at > ?', date('Y-m-d h:i:s', time())); return $q->fetchOne(); } // ... }
الآن ، إذا أنت حاولت الحصول على وظيفة إنتهت صلاحيتها ، فسوف تحال إلى صفحة 404
وصلة إلى صفحة الفئة
والآن ، دعونا نضيف رابط لصفحة الفئة على الصفحة الرئيسية ، و ننشأ صفحة الفئة .
.لكن إنتظر دقيقة. الساعة لم تنته بعد ونحن لم نعمل كثيرا . وهكذا ، لديك الكثير من وقت الفراغ و المعرفة ما يكفي لتنفيذ ذلك بنفسك! و يمكنك إعتباره كتمرين لك . و سنتحقق جدا مما قمت بتنفيذه
نراكم غدا إن شاء الله
قم بالعمل على تنفيذ على مشروع Jobeetمحليا. من فضلك ، أكثر من إستعمال وثائق الإنترنت API وجميع الوثائق المتاحة على موقع symfony لمساعدتك في الخروج بحلول .
حظ سعيد!
This work is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.