忍者ブログ
[PR]
×

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



2024/11/21 22:30 |
Symfony1.2 10日目その3
前回の続き

現状では
http://symfony.localhost/frontend_dev.php/job/1/edit
というURLで誰でも求人情報を編集できてしまいます。
これでは求人改竄し放題ですので、投稿者以外編集できないように保護しましょう。
方針としては、初回投稿時にランダムな文字列を作成しjobeet_job.tokenに保存、その文字列を知らないとエディットできない、というふうにします。
チュートリアルとはちょっと違った順番で行ってみます。
基本を修めず創作料理は核煮の第一歩ですが気にしない。


まずルーティングを変更。
今のままの/job/1/editでは到達できないように、ルーティングに引っかかるカラムを変更します。

apps/frontend/config/routing.yml
1
2
3
class:        sfDoctrineRouteCollection
  options:      { model: JobeetJob, column: token }
  requirements: { token: \w+ }

ルーティングはデフォルトではJobeetJob.idに結びつけられていますが、options:column:を追加することで任意のカラムに変更することができます。
今回はJobeetJob.tokenに結びつけました。
従って、
http://symfony.localhost/frontend_dev.php/job/job_sensio_labs/edit
といったURLでエディット画面に行くことが出来るようになります。


さて、この状態でサイトトップから求人詳細を見ようとするとサーバエラーになります。
ルーティングが変更になったのでエディット画面へのリンクがtokenを必要とすることになったのに、テンプレに
1
<a href="<?php echo url_for('job/edit?id='.$job->getId()) ?>">Edit</a>
という行があるのでURLを解決できないのです。
このリンクを修正します。

apps/frontend/modules/job/templates/showSuccess.php
1
<a href="<?php echo url_for('job/edit?token='.$job->getToken()) ?>">Edit</a>

エディット画面へのリンクが正しく表示されるようになりました。
まあ、そもそもこのリンクは今後表示してはいけないのですが。

さらに引数を直接与えるのではなく、url_for()を使用して解決することも出来ます。

apps/frontend/modules/job/templates/showSuccess.php
1
<a href="<?php echo url_for('job_edit', $job) ?>">Edit</a>

ルーティングの設定によりトークンでアクセスできるようになりましたが、現在トークンはユーザが任意に登録できるようになっています。
ここでaaaaaとか入れられると困りますのでフォームに出さないようにし、トークンは自動生成するようにします。
JobeetJob::save()にトークン自動発行ルーチンを挿入。

lib/model/doctrine/JobeetJob.class.php
1
2
3
4
5
<?php
//トークンが無ければ自動生成
if (!$this->getToken()){
    $this->setToken(sha1($this->getEmail().rand(11111, 99999)));
}

フォームからトークン入力欄を削除。
lib/form/doctrine/JobeetJobForm.class.php
1
2
3
4
5
6
<?php
    unset(
      $this['created_at'], $this['updated_at'],
      $this['expires_at'], $this['is_activated']
      ,$this['token']
    );

これで初回登録時に自動で40桁のトークンが発行されるようになりました。
今後投稿したデータの編集を行う際は、
http://symfony.localhost/frontend_dev.php/job/ef59db48055f6cdde9819f3121d9e267f00f8f65/edit
というような第三者にはわからないアドレスを使用することになります。
どうでもいいですが、トークンに無理矢理日本語を入れると今後一切エディット不能になります。


現在求人情報は、
http://symfony.localhost/frontend_dev.php/job/n-a/n-a/1/n-a
という形式でルーティングのjob_show_user:に引っかかるタイプ、
http://symfony.localhost/frontend_dev.php/job/ef59db48055f6cdde9819f3121d9e267f00f8f65
という形式でjob:に引っかかるタイプの2種類のURLから閲覧することが出来ます。

前者の人は閲覧専用とし、エディット画面へのリンクは表示させたくありません。
一方、後者の人には編集、削除を行わせる必要があります。
テンプレートを修正します。

apps/frontend/modules/job/templates/showSuccess.php
1
2
3
<?php if ($sf_request->getParameter('token') == $job->getToken()): ?>
    <a href="<?php echo url_for('job_edit', $job) ?>">Edit</a>
<?php endif; ?>

リクエストパラメータにtokenがあって、それがjobeet_job.tokenと等しければエディット画面へのリンクを表示します。
元あったエディット画面へのリンクは削除しましょう。


さて、エディット以外にも削除やなにやら色々行いたいので部品化し、admin専用のメニューテンプレートを作成します。

apps/frontend/modules/job/templates/showSuccess.php
1
2
3
<?php if ($sf_request->getParameter('token') == $job->getToken()): ?>
    <?php include_partial('job/admin', array('job' => $job)) ?>
<?php endif; ?>

インクルードされる中身を作成します。

apps/frontend/modules/job/templates/_admin.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<div id="job_actions">
  <h3>Admin</h3>
  <ul>
    <?php if (!$job->getIsActivated()): ?>
      <li><?php echo link_to('Edit', 'job_edit', $job) ?></li>
      <li><?php echo link_to('Publish', 'job_edit', $job) ?></li>
    <?php endif; ?>
    <li><?php echo link_to('Delete', 'job_delete', $job, 
      array('method' => 'delete', 'confirm' => 'Are you sure?')) ?></li>
    <?php if ($job->getIsActivated()): ?>
      <li<?php $job->expiresSoon() and print ' class="expires_soon"' ?>>
        <?php if ($job->isExpired()): ?>
          Expired
        <?php else: ?>
          Expires in <strong>
          <?php echo $job->getDaysBeforeExpires() ?></strong> days
        <?php endif; ?>
 
        <?php if ($job->expiresSoon()): ?>
         - <a href="">Extend</a> for another 
           <?php echo sfConfig::get('app_active_days') ?> days
        <?php endif; ?>
      </li>
    <?php else: ?>
      <li>
        [Bookmark this <?php echo link_to('URL', 'job_show', $job, true) ?>
          to manage this job in the future.]
      </li>
    <?php endif; ?>
  </ul>
</div>

作成した_adminパーシャルには、$job->expiresSoon()$job->getDaysBeforeExpires()といったメソッドが書かれています。
ここらへんのメソッドは存在しないのでモデルに実装を追加します。
単に日付の比較などを行っているだけなのでテンプレートで行っても問題無いと言えば無いですが。

lib/model/doctrine/JobeetJob.class.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
  /*
   * テンプレートで利用するショートカット関数
   * */
      //JobeetJob.typeを取得
    public function getTypeName(){
      $types = Doctrine::getTable('JobeetJob')->getTypes();
      return $this->getType() ? $types[$this->getType()] : '';
    }
    //期限切れであればtrue
    public function isExpired(){
      return $this->getDaysBeforeExpires() < 0;
    }
     
    //期限切れ間近であればtrue
    public function expiresSoon(){
      return $this->getDaysBeforeExpires() < 5;
    }
     
    //期限切れまでの日数
    public function getDaysBeforeExpires(){
      return floor((strtotime($this->getExpiresAt()) - time()) / 86400);
    }
}

さて、公開されているか否かのチェックに$job->getIsActivated()を使用していますが、jobeet_job.is_activatedは本日の最初にフォームから削除しました。
すなわち、現状$job->getIsActivated()は常にfalseです。
本来この求人は公開されていない状態になっているということです。
ということで求人を公開するアクションを作成しましょう。
まずはルーティングを編集。

apps/frontend/config/routing.yml
1
2
3
4
5
6
7
8
job:
  class:   sfDoctrineRouteCollection
  options:
    model:          JobeetJob
    column:         token
    object_actions: { publish: put }
  requirements:
    token: \w+

job:options:object_actionsを追加しました。

>object_actionsは与えられたオブジェクト用の追加アクションの配列をとります。
意味がわかりません。
PUTにすればobject_actions:publishになるってことなんだろうか?
テンプレートのPublishへのリンクを変更します。

apps/frontend/modules/job/templates/_admin.php
1
2
<li><?php echo link_to('Publish', 'job_publish', $job,
     array('method' => 'put')) ?></li>

リンク先のPublishアクションを作成します。

apps/frontend/modules/job/actions/actions.class.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
    /*
     * フォーム、Publishリンクを押したら公開状態に
     */
    public function executePublish(sfWebRequest $request){
      $request->checkCSRFProtection();
     
      $job = $this->getRoute()->getObject();
      $job->publish();
     
      $this->getUser()->setFlash('notice', 
        sprintf('Your job is now online for %s days.',
         sfConfig::get('app_active_days')));
     
      $this->redirect($this->generateUrl('job_show_user', $job));
    }

lib/model/doctrine/JobeetJob.class.php
1
2
3
4
5
6
<?php
    //jobeet_job.is_activateをtrueに
    public function publish(){
      $this->setIsActivated(true);
      $this->save();
    }

以上で求人フォームの投稿→公開が完成しました。
ただ問題点として、現在はjobeet_job.is_activatedがfalseの求人も全部公開されています。
非公開な求人は表示しないようにしましょう。
単に検索条件をひとつ追加するだけです。

lib/model/doctrine/JobeetJobTable.class.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
  /*
   * 検索内容に現在有効なJobeetJobを追加する
   */
  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 > NOW()')
      ->addOrderBy($alias . '.created_at DESC')
      ->andWhere($alias . '.is_activated = ?', 1);
    return $q;
  }

jobeet_job.is_activatedが立っていない求人は全部消えました。
めでたし。

PR


2010/10/29 22:20 | Comments(0) | TrackBack() | PHP

トラックバック

トラックバックURL:

コメント

コメントを投稿する






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



<<今週の実績 2010/10/31 | HOME | PHP技術者認定初級試験ベータ試験 合格発表>>
忍者ブログ[PR]