忍者ブログ
[PR]
×

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



2017/03/29 20:07 |
Symfony1.2 10日目
ずいぶん久しぶりに再開。

投稿フォームを作成します。
通常の投稿フォームでは、まず入力画面を作成し、入力のバリデーションを作成し、保存を行うといった流れで開発すると思います。
Symfonyでは先に投稿フォーム用のクラスを作成し、画面描写もバリデーションもそちらに任せてしまうことになります。

Symfonyのフォームは、画面上の入力欄を決定するヴィジェットと、バリデーションを行うバリデータの二つに分かれています。
フォームは基本的にデータベースと直結しており、データベースを作成したときにフォームの基礎も一緒に作成されています。
lib/form/doctrine/base/BaseJobeetJobForm.phpを見てみると、BaseJobeetJobForm::setWidgets()でヴィジェットが、BaseJobeetJobForm::setValidators()でバリデータが定義されているのがなんとなくわかります。


とりあえずデフォルトのフォームを見てみます。
まずはこれまで機能していなかった「POST A JOB」リンクを編集。

apps/frontend/templates/layout.php
1
<a href="<?php echo url_for('@job_new') ?>">Post a Job</a>

ルーティングの一番上にあるjob:class:sfDoctrineRouteCollectionに引っかかり、/job/newへのリンクに変化します。
リンクを辿るとjobActions::executeNew()が実行されます。

中身は前作成したこれだけです。

apps/frontend/modules/job/actions/actions.class.php
1
2
3
4
<?php
    public function executeNew(sfWebRequest $request){
      $this->form = new JobeetJobForm();
    }

$formにJobeetJobFormクラスをそのまま突っ込んでいます。
setTemplate()が無いのでデフォルトのnewSuccess.phpが適用され、echo $formでJobeetJobFormの全てが表示されます。


これからデフォルトのフォームを改造していきます。
フォームを触る場合はlib/form/doctrine/base/BaseJobeetJobForm.phpではなくlib/form/doctrine/JobeetJobForm.phpを利用し、これらの初期値を上書きしていく形でフォームを整えていきます。

その前にとりあえず修正前のフォームを見てみると、Category idがテキスト入力欄ではなくカテゴリ名選択になっています。
スキーマではcategory_id:{ type: integer, notnull: true }となっており明らかにテキストなのですが、勝手にリレーションを辿ってJobeetCategoryに行き着くようです。
そこまではまあいいのですが、何処にもjobeet_category.nameを表示せよみたいなことを書いた覚えはないのですが、一体何処からどうやってjobeet_job.category_idjobeet_category.nameを結び付けると判断したのかさっぱりだ。


デフォルトでは全てのカラムが表示されるようになっています。
どのような入力欄となるかはスキーマから自動的に決められます。
主キーであるidはhiddenとなり、varchar型であるtypeやcompany等はtextとなり、bool型はチェックボックスとなり、datetime型は年月日時分選択フォームとなります。
それは便利ですが、created_atやexpires_atなんかはシステム側で自動的に登録させたいので入力欄を表示させたくありません。
フォームから入力欄を削除したい場合、単にフォームクラスからフィールドを削除します。

lib/form/doctrine/JobeetJobForm.class.php
1
2
3
4
5
6
7
8
9
10
11
12
13
<?phpclass JobeetJobForm extends BaseJobeetJobForm{
  /*
   * 初期設定
   */
  public function configure(){
      //不要な入力欄を表示しない
      unset(
      $this['created_at'], $this['updated_at'],
      $this['expires_at'], $this['is_activated']
    );
  }}

バリデータもスキーマから自動的に決定されます。
jobeet_job.companyはスキーマ上ではemail:{ type: string(255), notnull: true }となっており、バリデータにも予め「最大255文字」というバリデーションが設定されています。
単純なバリデーションであればこのままで問題無いですが、もっと複雑なバリデーションを行いたい場合もあります。
jobeet_job.emailはメールアドレスの登録欄ですが、現状ではjobeet_job.company同様255文字以内というバリデータしか設定されていません。
バリデータをメールアドレス形式のみを受け付ける形に差し替えます。

lib/form/doctrine/JobeetJobForm.class.php
1
2
3
4
5
<?phppublic function configure(){
  //メアドのバリデータ
  $this->validatorSchema['email'] = new sfValidatorEmail();
}

Symfonyには多くのバリデータが用意されており、基本的にバリデータクラスを選択するだけでバリデーションを行うことができます。
sfValidatorEmailはメールアドレス形式のバリデーションを行うことができます。
まあ、'...@a.aa'みたいなRFC違反のメールアドレスも通してしまうアバウトなものですが。

上記のバリデータはBaseJobeetJobFormで定義されているバリデータを上書きしています。
元々あった255文字以内というバリデータは消滅してしまうのです。
sfValidatorAndクラスを使うことで、一つのフィールドに複数のバリデータを定義することができます。

lib/form/doctrine/JobeetJobForm.class.php
1
2
3
4
5
6
7
8
9
10
<?phppublic function configure(){
  //メアドのバリデータ
  $this->validatorSchema['email'] = new sfValidatorAnd(
    array(
       $this->validatorSchema['email']
      ,new sfValidatorEmail()
    )
  );
}

元のバリデータにsfValidatorEmailをプラスしています。
これで「255文字以内、かつメールアドレス形式」というバリデーションが完成しました。
残念なことにフォームタグにmaxlength属性は付加されませんが、255文字以上の文字列を突っ込むとバリデーションエラーが発生し、エラーメッセージが表示されます。


現在jobeet_job.typeはvarchar型ですが、ここを特定の文字列しか入れられないようにしたい、というバリデーションを作成してみます。
特定の文字列しか許されないという具体的な内容はバリデータではなくテーブルに持たせるべきですので、JobeetJobTableモデルに記述する必要があります。
現在のJobeetJobTableに以下を追加。

lib/model/doctrine/JobeetJobTable.class.php
1
2
3
4
5
6
7
8
9
<?phpstatic public $types = array(
  'full-time' => 'Full time',
  'part-time' => 'Part time',
  'freelance' => 'Freelance',
);
public function getTypes(){
  return self::$types;
}

フォームのヴィジェットをデフォルトのテキストからラジオボタンに変更。

lib/form/doctrine/JobeetJobForm.class.php
1
2
3
4
5
<?php$this->widgetSchema['type'] = new sfWidgetFormChoice(array(
  'choices'  => Doctrine::getTable('JobeetJob')->getTypes(),
  'expanded' => true,
));
sfWidgetFormChoiceはラジオボタン、もしくはチェックボックスを意味するクラスです。
引数は配列になり、'choices'に具体的な選択肢を配列で与えます。
'multiple'及び'expanded'の引数を与えるとドロップダウンリストやチェックボックス等に変化します。

今変更したのはヴィジェットだけなので、選択肢以外の値を無理矢理送ったとしてもバリデーションを通過してしまいます。
バリデータもヴィジェットに合わせて修正する必要があります。

lib/form/doctrine/JobeetJobForm.class.php
1
2
3
4
<?php$this->validatorSchema['type'] = new sfValidatorChoice(array(
  'choices' => array_keys(Doctrine::getTable('JobeetJob')->getTypes()),
));

ヴィジェットと違うところとしてarray_keys()に注意が必要ですが、これで簡単にヴィジェットとバリデーションを合わせることが出来ました。
選択肢以外の値を送るとバリデーションエラーが発生するようになりました。


jobeet_job.logoは、ロゴ画像をアップロードした場合、そのファイル名を格納する場所になるということだそうです。
ということでファイルアップロード用のヴィジェットとバリデータを設定する必要があります。

まずアップロードしたファイルを置く場所を設置します。
web/uploads/jobsディレクトリを新規作成し、Apacheに書き込み権限を与えます。

ヴィジェットをファイルアップロードフォームに変更。

lib/form/doctrine/JobeetJobForm.class.php
1
2
3
4
<?php$this->widgetSchema['logo'] = new sfWidgetFormInputFile(array(
  'label' => 'Company logo',
));

ファイルのアップロード用フォームが設定されます。
また、このヴィジェットを設定すると<form> に自動的にenctype="multipart/form-data"が追加されます。

バリデータを追加。

lib/form/doctrine/JobeetJobForm.class.php
1
2
3
4
5
6
<?php$this->validatorSchema['logo'] = new sfValidatorFile(array(
  'required'   => false,
  'path'       => sfConfig::get('sf_upload_dir').'/jobs',
  'mime_types' => 'web_images',
));
これでファイルのアップロードフォームが完成しまし……エラーになりました。

> 500 | Internal Server Error | InvalidArgumentException
> This form is multipart, which means you need to supply a files array as the bind() method second argument.

jobActions::processForm()内でフォームに投稿された値をバインドしていますが、現状は以下のようになっていました。

apps/frontend/modules/job/actions/actions.class.php
1
2
3
4
5
6
7
8
<?php
  protected function processForm(sfWebRequest $request, sfForm $form){
    $form->bind($request->getParameter($form->getName()));
    if ($form->isValid()){
      $jobeet_job = $form->save();
      $this->redirect('job/edit?id='.$jobeet_job->getId());
    }
  }

一方チュートリアル的にはこうなっているはずだったみたいです。

1
2
3
4
5
6
7
8
9
10
11
<?phpprotected function processForm(sfWebRequest $request, sfForm $form){
  $form->bind(
    $request->getParameter($form->getName()),
    $request->getFiles($form->getName())
  );
  if ($form->isValid()){
    $job = $form->save();
    $this->redirect($this->generateUrl('job_show', $job));
  }}

こんなところ触った覚えないんだが何故違うんだろう?
ファイルアップロードフォームは他のフォームと内容がかなり違うので、バインドもファイルだけ独立して行うことになっているようです。
jobActions::processForm()をチュートリアルに合わせて修正。


>バリデーターはデータベースに相対パスを保存するので、showSuccessテンプレートで使われているパスを次のように変更します

4日目で既に全く同じ記述にしてあるので、この節は意味がわかりません。

この時点で試しに投稿してみると、投稿が正しく処理されフォームから削除した値も正しく登録され画像も保存され投稿内容と画像をフロントから閲覧することが出来ます。
うひょお、気持ちわりい。


実は10日目までは1.2のころに作成していたりするので1.4の作りと違いがありますが気にしない。
ていうか、これ書いたころはJobeetJobPeerなんて影も形もなかったはずなんだが…?


PR


2010/10/22 23:33 | Comments(0) | TrackBack(1) | PHP

トラックバック

トラックバックURL:
現在の投稿画面は必要な入力欄が並んでいるだけの殺伐とした構成になっています。 装飾を行いましょう。 まず、デフォルトではスキーマのカラム名がそのまま入力項目名として表示され、「Category id」や「Is public」等になっています。 ヴィジェットのsetLabels()メソッドで入力項目名を変更することができます。 JobeetJobForm.class.phpに追加。 lib/form/doctrine/JobeetJobForm.class.php ...
弱小PHPerの憂鬱 | 2010/10/25 22:27

コメント

コメントを投稿する






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



<<今週の実績 2010/10/24 | HOME | システムアーキテクト試験を受けてきた>>
忍者ブログ[PR]