忍者ブログ
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



2017/07/21 17:55 |
Symfony-6日目その2
まずは結合です。
単なる結合だけから始めればいいのに、なんでソートとか書いて騙すようなわかりにくいことするんだろうか?

内容としては、最初から記事を取得するのではなく、まずカテゴリリストを取得し、その後各カテゴリ毎に記事を取得していって表示する、という内容になります。

まずコントローラを、記事リストの取得ではなく、カテゴリリストの取得を行うように変更します。
 apps/frontend/modules/job/actions/actions.class.php
1
2
3
public function executeIndex(sfWebRequest $request){
  $this->categories = Doctrine::getTable('JobeetCategory')->getWithJobs();
}

JobeetCategoryTableモデルクラスに、有効なジョブを含むカテゴリリストの取得を行うメソッドを追加します。
 lib/model/doctrine/JobeetCategoryTable.class.php
1
2
3
4
5
public function getWithJobs(){
  $q = $this->createQuery('c')
    ->leftJoin('c.JobeetJobs j')->where('j.expires_at > NOW() ');
  return $q->execute();
}

JobeetCategoryテーブルに対し、JobeetJobテーブルをLEFT JOINするという内容です。
スキーマでリレーションを設定してあるテーブルの場合、このようにjoin系のメソッドで簡単に結合を行えます。

最後の条件はチュートリアルではdate()とか使ってますがSQL任せでいいじゃん。


この時点で、テンプレートから$categoriesでカテゴリリストを取得することができます。
簡単にテンプレートを書いて確認してみます。

 apps/frontend/modules/job/templates/indexSuccess.php
1
2
3
4
<?php 
    foreach($categories as $category){
        print($category->name."<br />");
    }


Design
Programming

2行が表示されました。
この2カテゴリのみが、現在有効な求人を持っているからです。

実行されたSQLは以下のようになります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT
  j.id AS  j__id, j.name AS  j__name, j.created_at AS  j__created_at
  , j.updated_at AS  j__updated_at, j2.id AS  j2__id
  , j2.category_id AS  j2__category_id, j2.type AS  j2__type
  , j2.company AS  j2__company, j2.logo AS  j2__logo
  , j2.url AS  j2__url, j2.position AS  j2__position
  , j2.location AS  j2__location, j2.description AS  j2__description
  , j2.how_to_apply AS  j2__how_to_apply, j2.token AS  j2__token
  , j2.is_public AS  j2__is_public, j2.is_activated AS  j2__is_activated
  , j2.email AS  j2__email, j2.expires_at AS  j2__expires_at
  , j2.created_at AS  j2__created_at, j2.updated_at AS  j2__updated_at
  FROM jobeet_category j
    LEFT JOIN jobeet_job j2 ON j.id = j2.category_id 
  WHERE j2.expires_at > NOW()


さて、ここからがSymfonyの便利な点ですが、SQLの時点で呼ばれてない内容にも、リレーションが設定されている限り進んでいくことができます。
テンプレートをちょっと変更してみます。

 apps/frontend/modules/job/templates/indexSuccess.php
1
2
3
4
5
6
7
<?php 
    foreach($categories as $category){
        $jobeetjobs=$category->getJobeetJobs();
        foreach($jobeetjobs as $jobeetjob){
            print($jobeetjob->token.'<br />');
        }
    }


取得されたjobeet_categoryテーブルから、そこに含まれるjobeet_jobテーブルの内容を引っ張ってきて全部表示する、という内容です。

各モデルには、get+相手のモデル名で、リレーションのあるテーブルの中身を全部取得するというメソッドが予め定義されています。
JobeetCategoryモデルには他の3テーブルから直接リレーションが張ってあるのですが、スキーマの書き方によって呼び出し方が少しだけ違います。
relationsが単数形のJobeetCategoryで定義されているjobeet_jobsやjobeet_category_affiliateはgetJobeetJobs()getJobeetCategoryAffiliate()で呼ぶことができ、複数形のJobeetCategoriesで定義されているjobeet_affiliateテーブルはgetJobeetAffiliates()で呼ぶことができます。
今回はありませんが、テーブルa→b→cなんて関連があった場合でも、$a->getB()->getC()といったふうにすることができるので非常に便利です。

上で使用したgetJobeetJobs()メソッドは、関連するカラムを全て引っ張ってくるという内容ですので、たとえばexpired_atが切れているようなものがあったとしても気にせず呼び出してしまいます。
job_extreme_sensio
job_sensio_labs
job_expired

の3行が表示されることになりま………………されないぞ?
job_expiredが表示されません。
本来getJobeetJobs()の際は、リレーション以外の条件を気にしないはずなのですが、ログを見てみると、getJobeetJobs()の際に新たなSQLが発行されず、上記SQLの実行結果をそのまま使い回していました。
そりゃ表示されるわけ無いな。

しかしこの動作では、チュートリアルのように以下のJobeetCategory::getActiveJobs()メソッドを作成する意味が全く無いのですが。


まあいいや、気を取り直して。
本来はget+モデル名のメソッドを呼び出した場合、リレーション以外の条件を全く気にせずにカラムを拾ってきてしまいます。
上記のgetJobeetJobs()で拾わなかったのは偶然です。たぶん。きっと。おそらく。

ということでexpired_at切れを確実に排除するために、getJobeetJobs()のかわりにgetActiveJobs()メソッドを作成することにします。
前JobeetJobTableクラスにgetActiveJobs()を作成しましたが、今回はJobeetCategory::getActiveJobs()です。
二者の違いは、前者はカテゴリを気にせず、後者はカテゴリを気にするということです。
チュートリアルではいきなり分けてますが、わかりにくいのでとりあえずJobeetCategoryに全部書いてしまうことにします。

 lib/model/doctrine/JobeetCategory.class.php
1
2
3
4
5
6
7
8
public function getActiveJobs(){
  $q = Doctrine_Query::create()
    ->from('JobeetJob j')
    ->addWhere('j.category_id = ?', $this->getId())
    ->addWhere('j.expires_at > NOW()')
    ->addOrderBy('j.expires_at DESC');
  return $q->execute();
}

$this->getId()の$thisは一見何処からやってくるのかさっぱりなのですが、きちんとテンプレートでループ中の$category.idを取得してきてくれます。
メソッドを作成したのでテンプレートに反映します。

 apps/frontend/modules/job/templates/indexSuccess.php
1
2
3
4
5
6
7
<?php 
    foreach($categories as $category){
        $jobeetjobs=$category->getActiveJobs();
        foreach($jobeetjobs as $jobeetjob){
            print($jobeetjob->token.'<br />');
        }
    }


job_extreme_sensio
job_sensio_labs

の2件がやっぱり表示されました。
こちらの場合はSQLを使い回しているわけではなく、ループのたびにJobeetCategory::getActiveJobs()が呼ばれ、SQLが実行されています。
今回はカテゴリが2件しかなかったので追加発行されるSQLも2件なのですが、今後カテゴリが1000個に増えたよ、みたいなことになれば発行回数がえらいことになってしまいます。
なのでこの設計はいまいちよろしくないのですが、とりあえずまあいいや。

どうでもいいのですがDoctrineのマニュアルではWHERE句追加のメソッドはandWhereです。
確かにor系メソッドとの対比上andが正しいのでしょうが、よく使われるaddも使用できるようになっています。


さて、jobeet_jobテーブルに関する処理内容がJobeetCategoryに書かれているという事態は好ましくありません。
ということで適切なクラスにメソッドを分けてしまいましょう。

まずJobeetCategory。
 lib/model/doctrine/JobeetCategory.class.php
1
2
3
4
5
6
public function getActiveJobs(){
  $q = Doctrine_Query::create()
    ->from('JobeetJob j')
    ->where('j.category_id = ?', $this->getId());
  return Doctrine::getTable('JobeetJob')->getActiveJobs($q);
}

この$qの持ち回しがポイントです。
呼ばれる側のJobeetJobTable::getActiveJobs()を修正。

 lib/model/doctrine/JobeetJobTable.class.php
1
2
3
4
5
6
7
8
9
public function getActiveJobs(Doctrine_Query $q = null){
  if (is_null($q))  {
    $q = Doctrine_Query::create()
      ->from('JobeetJob j');
  }
  $q->andWhere('j.expires_at > NOW()')
    ->addOrderBy('j.expires_at DESC');
  return $q->execute();
}

引数があればそのクエリにWHERE句を付け足し、無ければ単純にJobeetJobモデルを作成して返すということになります。
単に先ほどのJobeetCategory::getActiveJobs()を二つに分けただけなので、動作結果も全く変わりません。

しかし、このTableが付くか付かないかのモデルの使い分けがよくわからん。


Symfonyの記事一覧
PR


2010/01/04 20:44 | Comments(0) | TrackBack(1) | PHP

トラックバック

トラックバックURL:
その2で終わるかと思ってたらその3だよ。   チュートリアルは最初に表示の上限を設定するようになっていますが、こちらでは先にその下のフィクスチャから行います。 フィクスチャはYAMLでデータベースの中身を書いとけば下記コマンドでデータベースに内容を登録できるよ、というものです。 >php symfony doctrine:data-load で、実はYAMLファイル内にPHPコードを書くことができます。 まずチュートリアルのjobs.ymlフィクスチャをコピーし、...
弱小PHPerの憂鬱 | 2010/01/08 19:47

コメント

コメントを投稿する






Vodafone絵文字 i-mode絵文字 Ezweb絵文字 (絵文字)



<<ゲームレビュー:タユタマ | HOME | 買ったものリスト 2010/01/03>>
忍者ブログ[PR]