Jobeetをやってみる 7日目

カテゴリページを作る

ルーティングの設定

# apps/frontend/config/routing.yml
category:
  url:       /category/:slug
  class:    sfDoctrineRoute
  param:  { module: category, action: show }
  options: { model: JobeetCategory, type: object }

:slugパラメータはカテゴリテーブルに対応していないので、アクセサを用意する

<?php
// lib/model/doctrine/JobeetCategory.class.php
public function getSlug()
{
  return Jobeet::slugify($this->getName());
}

カテゴリページへのリンク

<?php /* apps/frontend/modules/job/templates/indexSuccess.php */ ?>
<!-- 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>

カテゴリ毎のjob数をカウントするメソッドが必要

<?php
// 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);
}

次にJobeetJobTableに表示するjobをカウントするメソッドを作成するんだけど、DRYに違反しないように、クエリだけを生成するメソッドを作る。
その時、利用しているところによってテーブルの別名が異なる可能性もあるので、getRootAlias()を使って吸収する

<?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'))
      ->addOrderBy("{$alias}.expires_at DESC");
 
    return $q;
  }
}

categoryモジュールを作成する

空のモジュールを作る

$ ./symfony generate:module frontend category

DBスキーマを更新

slugカラムを作るためにSluggableというビヘイビアを利用する

# config/doctrine/schema.yml
JobeetCategory:
  actAs:
    Timestampable: ~
    Sluggable:
      fields: [name]
  columns:
    name:
      type: string(255)
      notnull:  true

slugカラムができたので、JobeetCategory::getSlug()を削除する必要がある

設定をリロード

$ ./symfony doctrine:build-all-reload

ページの作成

<?php
// apps/frontend/modules/category/actions/actions.class.php
class categoryActions extends sfActions
{
  public function executeShow(sfWebRequest $request)
  {
    $this->category = $this->getRoute()->getObject();
  }
}
<?php /* apps/frontend/modules/category/templates/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>

Partialの利用

<?php /* apps/frontend/modules/category/templates/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>

パーシャルの使い方は

<?php include_partial('job/list', array('jobs' => $jobs)) ?>
  • 第一引数 : パーシャルファイル名
    • スラッシュ"/"で区切ることで、module/partial といった指定が可能
    • ファイル名にあるアンダーバー"_"は必要ない
  • 第二引数 : パーシャル内で利用する変数の指定
    • array('変数名' => '値')
<?php /* apps/frontend/modules/job/templates/indexSuccess.php at 13 */ ?>
<?php include_partial('job/list', array('jobs' => $category->getActiveJobs(sfConfig::get('app_max_jobs_on_homepage')))) ?>

<?php /* apps/frontend/modules/category/templates/showSuccess.php at 12 */ ?>
<?php include_partial('job/list', array('jobs' => $category->getActiveJobs())) ?>

ページャー

1ページに20個ずつ表示させる
このページング処理を行なうために、sfDoctrinePagerクラスが提供されている

<?php
// 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();
}
  • sfRequest::getParameter()は、パラメータが存在しなかったときのデフォルト値を第二引数で設定することができる。
  • 指定しない場合はnullになる。

app設定値が追加されたので、設定

# apps/frontend/config/app.yml
all:
  active_days:          30
  max_jobs_on_homepage: 5
  max_jobs_on_category: 5
JobeetCategory::getActiveJobsQuery()の実装

getActiveJobsQuery()を実装するので、ついでにリファクタリング

<?php
// lib/model/doctrine/JobeetCategory.class.php
class JobeetCategory extends BaseJobeetCategory
{
  public function getActiveJobs($max = 10)
  {
    $q = $this->getActiveJobsQuery()
      ->limit($max);

    return $q->execute();
  }

  public function countActiveJobs()
  {
    return $this->getActiveJobsQuery()->count();
  }

  public function getActiveJobsQuery()
  {
    $q = Doctrine_Query::create()
      ->from('JobeetJob j')
      ->where('j.category_id = ?', $this->getId());

    return Doctrine::getTable('JobeetJob')->addActiveJobsQuery($q);
  }
}
テンプレートの記述

テンプレート内では以下のメソッドが利用できる

メソッド名 結果
getResult() Doctrineオブジェクトの配列のリスト
getNbResult() リストの個数
gethavToPaginate() 2ページ以上かどうか
getLinks() ページングのリンク
getPage() 現在のページ番号
getPreviousPage() 1ページ前の番号
getNextPage() 次のページ番号
getLastPage() 最後のページ番号(総ページ数)
<?php /* apps/frontend/modules/category/templates/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>
 
<?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="/images/first.png" alt="First page" />
    </a>
 
    <a href="<?php echo url_for('category', $category) ?>?page=<?php echo $pager->getPreviousPage() ?>">
      <img src="/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="/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="/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>