今回のエントリは相変わらず特に酷かったので取り上げてみます。
天国と地獄じゃなくて、地獄と地獄なんだよね。@さあ
|
任天堂は『脳トレ』以後、それを持続させるための新しいゲームを作れませんでした。『Newマリオ』にしても、脳トレ層ではなくて、回帰層が購入しているだけです。回帰層+子供ですよね。『ドラクエ9』も回帰層に加えて、『モンスターバトルロード』で取り込んだ子供層です。中古で売られにくい設計のため、従来中古で売れていた分が新作として買われるようになり、史上最高の売上に達したわけです。 |
こんな頭の腐った分析してる業界人など見たことない。
きっと脳味噌が沸騰してるんでしょうね。
マーケットの現実を見てみますと、年末年始は毎年任天堂の一人勝ちです。
2009年末は4位にFF13が入った以外全てDSとWii。
| 順位 | タイトル | ハード | 週販 | 累計 |
| 1 | Newスーパーマリオブラザーズ | Wii | 506000 | 2440000 |
| 2 | ゼルダの伝説 大地の汽笛 | DS | 291000 | 291000 |
| 3 | トモダチコレクション | DS | 227000 | 2317000 |
| 4 | ファイナルファンタジーXIII | PS3 | 189000 | 1690000 |
| 5 | ポケモンハートゴールド/ソウルシルバー | DS | 125000 | 3465000 |
| 6 | Wii Fit Plus | Wii | 114000 | 1315000 |
| 7 | イナズマイレブン2 驚異の侵略者 | DS | 99000 | 908000 |
| 8 | Wii Sports Resort | Wii | 72000 | 1568000 |
| 9 | 太鼓の達人Wiiドドーンと2代目 | Wii | 64000 | 200000 |
| 10 | レイトン教授と魔神の笛 | DS | 64000 | 559000 |
ちなみにトモダチコレクションは発売直後に週販5万本程度でありながら、ぶつ森や脳トレを上回るペースで延々売れ続けているDS特有のロングセラー商品。
トモコレが一過性とか目眩がしすぎて失笑すら起こせないレベル。
元ネタの文意も相変わらず全く読み取れていません。
元ネタはどう読んでも、
「任天堂がせっかくユーザを囲ったのに他のメーカーが使いこなせてない」
なのですが、
これがDAKINIフィルターを通すと
>「みんなにウケる真ん中のソフト」言い換えると「みんなのゲーム」なんて幻想だって事ですよね
になります。
一体何処を読んでいるんでしょうか。
毎回きちんとトラバ送ってるんですが今回もきっと無視されるんでしょうね。笑
360のギャルゲと2DSTGは制覇するよ計画発動中。
タユタマ
実績1000/1000
とりあえず
アメリうぜー
エロゲ移植なので、ググって一番最初に出てくるサイトが18禁です。
私は360版で初プレイなのですが、360版には当然エロシーンなんて出て来ませんので、サンプルCGとか見るとものすごい違和感を感じてしまう。
実際描写はされずとも性交渉をしたという事実自体はわりとあからさまに提示されるのですが、それとこれとは別というか同じものとして考えられないというか。
つうかあの足でとかどんなシチュだよ。
新アイデア
フロントに出ない後ろの方でボイスだけで別の会話が進行するという面白いシステムが採用されています。
地の文で主人公の内心や情景描写を行っている際、あるいは会話している際に、その裏でシナリオ進行にはさほど関係ない他キャラたちの会話が続くのです。
わざわざテキストに起こして読ませるほどの内容ではない、でもそのキャラについてもっと知りたい、もっと掘り下げたい、などの要求を満たすのによい手法だと思います。
ただ声が小さいので聞き取りにくいのですが。
シナリオ所感
父親の声優はもっと評価されていい。十分されてるか。
一貫して「泣き熄む」とすごい字が使用されています。
主人公はバイクマニア。
ただ全然バイクを知らない人に対して専門用語で語りかけてしまう少々イタめの人。
志雄と違って将来のことを多少は考えている。
優柔不断であるのは相変わらず。
ファーストプレイは自然とましろ。
ましろいい娘すぎる。
あと三九もいい奴すぎる。空気も読めるし。
せっかくのデートイベントなのに服が一緒。服くらい買ってやれ。
フローレスは超設定にも程がある。何処の要塞だよ。
何故そこでラブホ。
しかし11eyesのときもそうだったんだが、ノベル形式でバトルされてもかったるいだけなのだがどうにかならんもんかね。
何千日も何万日も…?
いやあ、一万日って27年ちょいなんですが…
そこは何千年何万年じゃないのか。
うん、メモが断片的すぎてなんのことだかさっぱりわからない。
最後の超御都合以外は良いシナリオでした。
人間と太転依の共存というテーマに関してはスルー気味で、結局裕理とましろだけしか共存していないような気もしないでもないですがそこはそれ、愛なんですよきっと。
…ってあれ、なんか選択肢が全然無かったような気がするんですが……
調べてみたら8個しかなかった。
実は本作のシナリオは完全な一本道です。
序盤にいくつかの選択を行ったらあとは最後まで見てるだけです。
全キャラクターのシナリオで。
さて、とりあえずメインであろうましろシナリオ終えたんだが、これ他のシナリオどーすんだ?
とても整合性取れない気がするんだが。
一旦ましろとラブラブになっておきながら鵺に浮気とかしやがったらブチキれんぞおい。
アメリうぜえ。
適当に選択肢選んだらセカンドプレイはアメリ。
ましろとはそんな決着の付け方が……あるのかなあ?
ましろ以外の全シナリオそうなんだけどましろが可哀相です。
幾ら幼馴染みでも、邪魔者を実力で排除しようとかするような奴を好きになるものなのかね。
つうか好きになっていいものなのかね。
アメリは生理的に受け付けない。
ヤンデレとか俺には理解できん。病院に隔離しとけ。
ということで途中で放り出してしまい、気がついたら放置すること一ヶ月。
いいかげん復帰しようとがんばってクリアした。
太転依帰っちゃった。バッドエンドじゃねえか。
ゆみなはえらい極端だな。
てか、このシナリオでは特に裕理の思考の幼さというかわりと口だけって感じが目立ちました。
おまえ男女の仲を示唆されて数日しか経ってないのにいきなりなんでそこまで思考が飛ぶんだよ。
朝日が上がる前に洗濯…無理じゃね?
つうか制服の予備無し?
お、応龍すっ飛ばされたぞ。
この状態で今更アメリに出しゃばってこられてもウザいだけなのでいいんだけど。
だが話の整合性はどうなるんだ?
三強の出てくる順番が変わりました。
このアイデアは目新しく感じてなかなか面白かった。
が、そのせいなのかなんなのか同じテキストが未読扱いになっていて読むのが大変でした。
アメリの出てこないシーンならまだいいんだけどねえ。
つうか、こんなことやらかしてしまったうえで元の日常が戻って来たりするわけなんか無いって誰か言えよ。
最後は太転依とも非常によい形で収まったしエンディングもやたら力入っているし設定的にベストエンド?
しかし集合写真からハブられる鳳凰可哀相です><
さて残るは美冬ルートなわけですが、このルートって存在自体が他のルートで力説していた美冬の覚悟を台無しにしてしまう気がするんだがどうなんだ。
まあ読んでみよう。
…おお、美冬、智代を全否定しやがった。
実に清々しい。
しかし展開急すぎるだろ。
全然こう互いが惹かれあってゆくという描写があまり無かったんですが。
他の3キャラはまあ設定上急接近しても仕方がないという面はあったんですが、美冬に関してはそれまでの積み重ねというものが存在しないのに一気にのぼせ上がりすぎでしょう。
まあ美冬の側は箱入りだったから仕方ないかもしれないが、裕理のほうがなあ。
アメリうぜえ。
メモを読み返したらアメリうぜえって10回くらい書いてあった。どんだけ。
そのかわりかどうか知らんが三九が格好良すぎて困る。
勾玉の真の力が明らかに。
つうかそんな危険なものを他シナリオでもホイホイ渡してたんかい。
結局解決は相手からかい。
終盤はなんという超設定、超歴史、超展開。
いやまあ智世への答えを出そうとすればこうなってしまうのが正しいのかもしれないんだがなんともこう。
鵺シナリオ。
うん、外伝で良かった。
普通にヒロイン扱いで出たらどうしようかと思ったぞ。
鵺攻略を待ち望んでいた他機種からのファンには申し訳ないが、鵺はこの形が一番だと思う。
実に良いシナリオでした。
しかし火事の家に素人が突っ込んでいくとか実際は邪魔でしかないよな。
あと退魔の霊能についてはこのシナリオではスルーなのが非常に残念。
美冬→鵺ルートでは力の源が何なのか知る由もなく、またましろルートではあんだけ苦労して人外になってまでようやくどうにかすることのできた退魔の霊能がなんとなくちょっと瞑想しただけでどうにかなるとかどうなのよ。
どうせ奇跡で済ますのであれば、火事の中裕理が鵺を助けねばならないというお約束な展開に持っていった方が、よりすっきりしていたと思う。
お約束
ましろ>美冬=ゆみな
ただ、三九がもし女だったら確実に惚れていたレベル。
あと鵺は異性としては見ることはできませんが、娘としては目に入れても痛くない可愛さ。
システム
他の5pb作品と比べると少々難があります。
なんといってもEDムービーとアイキャッチを見れないのがひどい。
シーン別リストがありません。
選択肢が序盤にしか存在しないので意味がないと言えばないのかもしれませんが残念。
面白い機能として、任意のシーンを自由に録画して再生できるRECモードがあります。
ただこれ、シーン別リストがあれば全く不要な機能です。
またシーンタイトルも細かに入っているのですが、シーン別リストがあるわけでもなく、ロード画面に表示されるわけでもなく、メニューを出さない限り見ることができないのでほとんど意味がありません。
結果として、システムが全体として少々噛み合っていない、という感じを受けました。
あと個人的感想ですが、音楽鑑賞モードには作詞作曲歌コメント等を入れてほしいです。
タイトルだけだと、いくらいい曲でも無味乾燥な印象を受けてしまいます。
総合
システムの細かな部分に5pb作品としては少々難あり。
ロード時間、スキップ等は非常にとまでは言えないが速いほう。
シナリオは超展開が一部見られますが、概ね良いレベルだと思います。
ギャルゲ好きで360を持っているのならとりあえずプレイしても損はない。
アメリがウザいのには頑張って耐えてください。
最後に
本作最大の問題点はあれだ、本作の全実績のうち635を2010/01/01に解除してしまった私の人生にあるのではなかろうかと。
過去のゲームレビュー
単なる結合だけから始めればいいのに、なんでソートとか書いて騙すようなわかりにくいことするんだろうか?
内容としては、最初から記事を取得するのではなく、まずカテゴリリストを取得し、その後各カテゴリ毎に記事を取得していって表示する、という内容になります。
まずコントローラを、記事リストの取得ではなく、カテゴリリストの取得を行うように変更します。
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
|
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
|
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
|
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の記事一覧
笑点見逃した。
ぼのぼの33
☆☆☆☆☆
架空生物ネタの回は正直微妙にいまいちな内容が多かったのですが、今回は非常に良かった。
レギュラーキャラ総出演で一巻まるごとひとつの話になっているというこれまでにない形で、綺麗にまとまっていました。
ラブプラス恋歌三重奏
☆☆
「どうでもいい」
この言葉がぴったり。
面白いかつまらないかどちらか選べと言われたらつまらないを選びます。
ただ、積極的につまらないというわけでは決してありません。
約一名除いて。
ただただ低レベルな自己満足同人誌のような、読んでも読まなくてもどうでもいい内容しかありませんでした。
唯一YUGだけはちょっとだけよかったかもしれない。
昔から二次創作というものが嫌いなのですが、その理由のひとつがクリムゾンのような「その作品である意味が全く無い」もので、もうひとつの理由が今回のように「単につまらない」からです。
面白かった二次創作など数えるくらいしか知りません。
本作も二次創作はつまらないという傍証がまたひとつ増えただけでした。
全然関係ないけど近所のケーキ屋でロールケーキ3種類セットを「カルテット」って名前で売ってるんだがどうなのよ。
こえでおしごと3
☆☆☆☆☆
近所の本屋で売ってたのでとりあえず3巻買う→あとがきで限定版の存在を知る→遠出して限定版買ってきた(・∀・)
ヒャッホウエロエロだぜぇ!
…なんかその、直接そういうのは違うんだ。
もっとこう論撃的な感じじゃないと。
というわけで前半はちょっと影の薄かった文花の話。
後半はコミケに行ってしまう話。
撮影禁止とか言いつつオンステージとかどう考えてもアウトだろ。
限定版のおまけは微妙。
台詞がなんかこう直接的すぎて、もうちょっとどうにかならんかったんだろうか。
一個気になったこと。
高校で官能小説書き始めた、柑奈が小学生のときから書いていた、中2のときに2歳。
纏めると高3、柑奈が6歳で小1のときに書き始めた、ってことか?
やたらぎりぎりだな。
そんな奴ァいねえ!!15
☆☆☆☆
最近アフタヌーンコミックス見ないんだが滅びたのかな、と思っていたんだが久しぶりに発見した。
本作は登場人物を考え無しに増やしすぎでどうなんだろうなと常々感じていた。
そのわりに高橋とか設定詰め込みすぎて初期と性格が変わりすぎてるし、逆に能見とか設定が局所的すぎて今回出番ないし。
登場人物の中で今日子が一番好きだったんだが結局何もないまま終わってしまって残念。
論理少女2
☆☆☆☆
致命的矛盾は発見できなかった。残念。
ただ3桁の素数を暗記してるような変人共であれば4桁*3桁のかけ算くらい話をしてる間に暗算できそうなものなんだがとか、根本的にテストに加点という設定はどうなんだとかは気になった。
しかしこんなややこしい問題良く考えつくな。
ムダヅモ無き改革3
☆☆☆☆
麻雀やってねえ。
世界の指導者達がナチスドイツの英雄達と麻雀でバトルするお話。
何故と言われても困る。
相変わらず意味もなく無駄に熱い、勢いだけで何もかもを押し通してしまうという色々と困った漫画。
しかし、スコルツェニーやルーデルについては史実が漫画よりも奇だったりするから困る。
6日目までのチュートリアルにはapp.ymlの書き方が見つからなかったので簡単に解説。
config_handler.ymlまでやり始めるとよくわからないのでとりあえずはパス。
まずapp.ymlが何かっていうと、自作プログラムでありがちなconfig.phpとかconst.phpみたいなものです。
動的に変更されることはあまりないけど、サイト全体で共有しておきたい設定なんかを格納しておきます。
YAML形式で書くことで、PHPで書くより素人にもわかりやすく、XMLみたいにタグがいっぱいでめんどくさいといったこともない簡単な設定ファイルにすることができます。
app.ymlを置ける場所は複数あります。
config/app.yml
apps/frontend/config/app.yml
前者がプロジェクト全体から呼び出せるもので、後者はアプリケーション単位の定義となります。
使用する際には両者がマージされ、両者で同じ名前がある場合は範囲の狭いアプリケーション単位のほうが優先されます。
後者が置けるのだから当然apps/frontend/modules/***/config/app.ymlでモジュール単位の設定もできるだろうと思ったら何故かできませんでした。
次にYAMLの書き方。
6日目のチュートリアルでは下記のようになっています。
1
2
|
all:
active_days: 30
|
トップレベルにはall、prod、test、devの4種類を置くことができます。
allは全環境、prodは本番環境、devは開発環境でのみ設定される内容になります。
testはあんまり使わないのでまあいいや。
要素は半角スペース2つでインデントしたあとに記入します。
:のあとに半角スペースを最低1個置いたあと値を記入すると、それが要素の値となります。
上記の場合はactive_daysの値が30ということになります。
複数のトップレベルに同じ要素を記入した場合、範囲が狭い方が優先されます。
従って最終的な書き方は以下のようになります。
1
2
3
4
5
6
7
|
all:
hoge: hoge_all
prod:
name: 本番環境
dev:
name: 開発環境
hoge: hoge_dev
|
sfConfig::get()で実際に値を取得することができます。
app.ymlから内容を取得する場合は、引数のプレフィックスに必ず'app_'が付きます。
開発環境で実行した場合、
sfConfig::get('app_hoge') → hoge_dev
sfConfig::get('app_name') → 開発環境
本番環境で実行した場合、
sfConfig::get('app_hoge') → hoge_all
sfConfig::get('app_name') → 本番環境
というふうになります。
基本的には、allに全ての設定を記入し、prodで本番時のみ参照したいデータを記述するということになるでしょう。
さて、YAMLを階層構造にすることができます。
半角スペースを2つ増やすたびに一段階深くなります。
カテゴリ毎に情報を纏めたたりする場合に便利なのですが……ここにひとつ落とし穴があります。
1
2
3
4
5
6
7
8
|
all:
data: data_zero
one:
data: data_one
two:
data: data_two
three:
data: data_three
|
このようなYAMLがあるとして、階層構造のデータを取得するときは'_'で区切ることで次の階層に進むことができます。
'data_zero'を取得したいならsfConfig::get('app_data')です。
'data_one'を取得したい場合はsfConfig::get('app_one_data')となります。
ここまではいいのですが、では次の'data_two'を取得したければsfConfig::get('app_one_two_data')でいいかと思えば駄目です。
3段階目以降は何故か直接sfConfig::get()することができません。
sfConfig::get('app_one_two')とすることで、それ以下の階層が連想配列で入ってきます。
'data_three'は、$data_two=sfConfig::get('app_one_two');$data_three=$data_two['three']['data'];とする必要があります。
当初これに気がつかずに「値が取得できねー」とさんざっぱら悩みました。
リファレンスに微塵も書かれてないというのはどういうことなのだろうな。
Symfonyの記事一覧
まずDoctrineの検索条件から。
Doctrine::getTable('JobeetJob')->createQuery('a')->execute();
とするとjobeet_jobテーブルの内容が全て取得されます。
getTableでテーブル名を指定し、createQueryでエイリアス名を指定します。
このエイリアス名は作成されるSQLにも出力を扱う際にも全く関係が無く、Doctrine内でカラムやテーブルを扱う際にのみ利用されます。
最後にexecuteで作成したクエリを実行し、取得します。
今回はWHERE句やLIMIT句等を使用していないため、jobeet_jobテーブルの内容が全件そのまま取得されます。
それでは今からクエリを書き換えていきますが、チュートリアルは丸ごと全部書き換えてしまっているのでいきなりハードルが高くてわかりにくいです。
とりあえずさっきのクエリにひとつ条件を書き足してみます。
Doctrine::getTable('JobeetJob')->createQuery('a')->where('a.id = ?',1)->execute();
whereは文字通りWHERE句を指定します。
第一引数に比較する内容、第二引数に具体的な値を記述します。
上記の場合'?'の部分が第二引数の1に相当します。
where('JobeetJob.id = ?',1)と書くとおかしな結果になります。
createQuery('a')で、以後JobeetJobを'a'として扱うと決めたので、その後最初のjobeet_jobテーブルを扱う際は'a'と書く必要があるのです。
上記の結果として、jobeet_job.id=1のカラムだけが取得されることになります。
今回は1ですが、ここにユーザ入力値を突っ込むことで自動的にSQLインジェクション対策になります。
固定値の場合であればプリペアドステートメントにする必要もないので、where('a.id = 1')と書くこともできます。
上記をチュートリアル形式に書き換えてみます。
$q = Doctrine_Query::create()->from('JobeetJob a')->where('a.id = ?',1);
$this->jobeet_job_list = $q->execute();
最初に呼び出すクラスがDoctrineクラスからDoctrine_Queryに変わっていますが、実行できることはあまり変わりません。
というか違いがよくわからん。
Doctrine_Query::fromではDoctrine::getTableとcreateQueryが行っていたテーブル名とエイリアス名の指定を一気に行います。
以後はwhereやexecuteを全く同じように書くことができます。
当然ですが$qを介さず、
$this->jobeet_job_list =Doctrine_Query::create()->from('JobeetJob j')->where('j.id = ?',1)->execute();
と一行で書くこともできます。
結局Doctrine_Query::create()とDoctrine::getTable()って何が違うんだ。
ようやくチュートリアルに追いついた。
30日以内に投稿された求人だけ拾ってみましょう。
apps/frontend/modules/job/actions/actions.class.php
1
2
3
4
|
$q = Doctrine_Query::create()
->from('JobeetJob j')
->where('j.created_at > ?', date('Y-m-d H:i:s', time() - 86400 * 30));
$this->jobeet_job_list = $q->execute();
|
単にwhereメソッドが変わっただけですね。
idのときは=だけでしたが、不等号や関数を記述することもできます。
しかし、30日以内とかなら
$q->where('j.created_at > NOW()-INTERVAL 30 DAY ');
のほうが早い気が。
実際に実行されたSQLはブラウザ右上のドラム缶みたいなマークで確認することができますので、見てみるとよいでしょう。
次にモデルを変更してみます。
前slugifyとかを書いたJobeetJob.class.phpです。
lib/model/doctrine/JobeetJob.class.php
1
2
3
4
5
6
7
|
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);
}
|
モデルクラスのsaveメソッドをオーバーライドすればセーブ時の動作を変化させることができるそうです。
そういった流れというか挙動は一体何処を調べればわかるのでしょうか?
あと、よくわからんのだが、シリアライズってこんなところで使う言葉なのか?
$this->isNew()は一度も保存されていないときにtrueを返すと思われます。
Doctrineの各モデルクラスはsfDoctrineRecordを継承しており、sfDoctrineRecord::isNew()に実体があります。
>(boolean) isNew ()
>Function require by symfony >= 1.2 admin generators
どういう意味だ。
こういうのの存在ってチュートリアルが無ければ気付きようが無い気がするんだがどうしてるんだろう?
全体としては、現在のオブジェクトが、一度もセーブしたことが無く、expires_atが定義されていない場合、expires_atを指定して親のsaveメソッドを呼ぶということになります。
2回目以降のセーブ、もしくは既にexpires_atが入っている場合はそのまま親のsaveメソッドが呼ばれます。
せっかくexpires_atを登録できるようにしたので、呼び出す場合もexpires_atを利用するようにしましょう。
apps/frontend/modules/job/actions/actions.class.php
1
2
3
4
|
$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が現在より先のものだけ拾ってくるようになります。
これもやっぱり
$q->where('j.expires_at > NOW()');
のほうが早いんですがね。
このDQL変更によって、expires_atがNULLであるレコードは拾えなくなってしまいます。
新たなデータを例によってフィクスチャを利用して登録してみます。
しかし正直、どうもこの機能が便利とはとても思えないんですが。
データベースをいじって最初に投入したデータを変更していた場合、このチュートリアル通りにするとそこらへんの変更が全部消え去ってしまいます。
data/fixtures/jobs.ymlに、該当のYAMLのexpired_job:を追加します。
そしてコマンドを実行。
>php symfony doctrine:data-load
今回追加するこのexpired_job:にはexpires_atが指定されていないため、本来ならconfig/doctrine/schema.ymlの
JobeetJob:expires_at: { type: timestamp, notnull: true }
条件に引っかかってしまいます。
ところが上でJobeetJob.class.phpでsaveメソッドをオーバーライドしているため、expires_atに何も入れない場合、自動的にexpires_atを設定した上でセーブしてくれます。
expired_job.expires_atは、JobeetJob::save()メソッドに従いcreated_atの30日後に設定された上で保存されていました。
ついでに元あったデータは、直接DBをいじくって変更していたものがあったりした場合でも全削除されます。
このコマンドが効くのは開発用データベースだけではあるのですが、それでもテスト用データがたくさん詰まった状態なんかでうっかりコマンドを打ってしまうと大惨事。
現在のデータベースの内容をYAMLに書き出すコマンドは無いのか?
カスタムコンフィギュレーションは単にapp.ymlに設定を分離するというだけの話です。
apps/frontend/config/app.ymlにチュートリアルをコピペ。
この後コントローラからでもモデルからでもビューからでも、
sfConfig::get('app_active_days')
でapp.ymlに書かれている値を引っ張ってくることができるようになります。
JobeetJob::saveメソッドの該当部分を以下のように書き換えます。
$this->setExpiresAt(date('Y-m-d H:i:s', $now + 86400 * sfConfig::get('app_active_days')));
やっぱり掲載から60日は表示されるようにしたい、といったときに直接ロジックを書き換えることなく、設定ファイルを書き換えるだけで済むようになります。
最後にリファクタリング。
これまでDoctrine_Query::where('j.expires_at > NOW()')とかのロジックをコントローラに書いていました。
expires_atみたいなデータベースに直接関連する内容はコントローラに書かずに、全てモデルに押しやってしまいます。
コントローラを書き換えます。
apps/frontend/modules/job/actions/actions.class.php
1
2
3
|
public function executeIndex(sfWebRequest $request){
$this->jobeet_job_list = Doctrine::getTable('JobeetJob')->getActiveJobs();
}
|
JobeetJobTable::getActiveJobs()を書いたからには作らないといけません。
lib/model/doctrine/JobeetJobTable.class.php
1
2
3
4
5
|
public function getActiveJobs(){
$q = $this->createQuery('j')
->where('j.expires_at > ?', date('Y-m-d H:i:s', time()));
return $q->execute();
}
|
まあ見ての通り、これまでコントローラに書いてあった処理をJobeetJobTableモデルに持っていっただけです。
$thisになっていますが、別に
return Doctrine_Query::create()->from('JobeetJob j')
->where('j.expires_at > ?', date('Y-m-d H:i:s', time()))->execute();
とか書いても動きます。
コントローラでDoctrine::getTable('JobeetJob')を実行した場合、内部ではJobeetJobTableクラスが呼ばれています。
これまでのようにコントローラに検索ロジックを書いていた場合、複数の箇所で使用したくなったらその都度同じ処理を書かねばなりませんでした。
JobeetJobTableクラスにメソッドを記述することで、全てのプロジェクトからこのメソッドを使用できるようになります。
Symfonyの記事一覧
胸焼けというか腹が痛くて吐き気がして気分悪くて死にそう。
こえでおしごと2
☆☆☆☆☆
ヒャッホウエロエロだぜぇ!
まあ実際学校に広まったら人生終了でそれこそエロゲ的展開と思うんだが名前が一文字違いって酷いだろ。
しかし初回限定版なんてあったのか。
くぅ、知るのが遅かったぜ。残念。
>今はそんなことほとんどないから安心してね
いやあ目の前にその被害者っぽい人がいるわけですが。
デュラララ!!2
☆☆☆☆
誰か一人がついうっかり口を滑らしちゃったら終了、的な関係をもって「計画通り☆」とかどうなの。
正臣は黄巾賊だって隠してないんだから黄巾賊に潜り込んでる罪歌の配下なりダラーズの知り合いなりが3人一緒なところを見てしまったら終わりじゃないか。
相変わらず伏線や視点や筆力が凄いので一気に読んでしまうと全然気にならないのですが、後から思い返してみるとちょっとどうなんだろうという部分が今回は一部見受けられました。
史上最強の弟子ケンイチ36
☆☆☆☆
いいかげん梁山泊側の師匠ばっかり強すぎるだろ、と思ったらようやくその上を行くっぽい敵が現れた。
しかし胸はあんなに大きくなくてよかろう。
武術には多分邪魔。
論理少女1
☆☆☆
ありとあらゆる勝負事を禅問答で決めちゃうよというちょっとどうなの高校に編入してしまった一般人の受難物語。
出題のレベルは非常に高く、自力で解けたのはルービックキューブとBRAINくらいでした。
つうか高校生レベルであんな問題解けねーよ。
細かいことを言うと、buyには名詞としての用法も存在するのであの問題は成立しています。
あと、どう考えても出題者になったときは簡単な問題しか出さないのが正解なのですが、そういった根本的なルールの問題点はスルーすることが必要みたいです。
論理を標榜している割にそういう細かい点がしっかりしていないのが気になりました。
にょたいかっ1
☆☆☆
私のTSものの原点といえばおれがあいつであいつがおれでなわけです。
確かハードカバーだったと思うんだが小学校の課題図書でした。
あとがきで「もっとエロくしたかったが自重した」とか書いてあったのを覚えている。
フリーダムな時代だったなあ、と思ってググってみたら古典的名作扱いされてるのな。なんか違うだろそれ。
本作もその系譜の一作ですが、この手の本では珍しいことに最後までやっちゃいます。
どっかのレビューでそこらへんが絶賛されていたので買ってみたのだが微妙だった。
主人公が真性の非モテおっさんとかだったらまだしも、言うほど悪くないビジュアルだったのであんまり女体化のインパクトがなあ。
アブソリュート ブレイジング インフィニティ
オンデマンドで売ってたのでなんとなく購入してしまったのだが説明書がないので何をやればいいのかさっぱりわからない。
いきなり出来ることが多く、かつ効果が見えにくく後戻りできないという要素が多すぎてどうしたものやら。
前回の続き。
ここまではリンクの書き方とrouting.ymlで完結していました。
ここまでは単にURLとルーティングをマッチングさせているだけなのでまあわからないこともないです。
この後突然ルートクラスという単語が登場し、全然意味がわからなくなってしまいます。
個人的には、下記のように一カ所を変更したいと思う度に至る所を修正しなければならない作りが正しいとはとても思えないんですがどうなんですかね。
まずsfRequestRoute。
こちらは通常ルーティングで使われるはずのsfRouteに、リクエストメソッドによる分岐機能を追加したものになります。
同じURLにリクエストが来た場合でも、<a>や<form method="GET">で飛んできた場合はsf_method:[get]に合致し、<form method="POST">で飛んできた場合はsf_method:[post]に合致する、というふうに分岐を行うことができます。
routing.ymlのjob_show_user:url:を変更。
1
2
3
4
5
6
7
|
job_show_user:
url: /job/:company/:location/:id/:position
class: sfRequestRoute
param: { module: job, action: show }
requirements:
id: \d+
sf_method: [get]
|
class: sfRequestRouteとrequirements:sf_method: [get]が増えました。
これで
http://symfony.localhost/frontend_dev.php/job/sensio-labs/paris-france/1/web-developer
というURLに対し、リクエストメソッドがGETである場合のみにjob_show_userにマッチするようになります。
<form method="POST">で来た場合はルートが見つからないためエラーになります。
こちらは比較的わかりやすい。
次にsfDoctrineRoute。
routing.ymlのjob_show_user:url:をsfRequestRouteからsfDoctrineRouteに変更。
1
2
3
4
5
6
7
8
|
job_show_user:
url: /job/:company/:location/:id/:position
class: sfDoctrineRoute
options: { model: JobeetJob, type: object }
param: { module: job, action: show }
requirements:
id: \d+
sf_method: [get]
|
apps/frontend/modules/job/templates/indexSuccess.phpを変更。
url_for(array('sf_route' => 'job_show_user', 'sf_subject' => $job))
class:sfDoctrineRouteでルーティングにsfDoctrineRouteを指定し、model:JobeetJobでルートに関係するモデルクラスを指定し、type:objectでこのルートに関係するオブジェクトを定義します。
何を言ってるんだこいつは?
全く全然さっぱりちっとも意味がわかりませんが、何故かリンクは表示されました。
/frontend_dev.php/job/Sensio+Labs/Paris%2C+France/1/Web+Developer
というようなURLになります。
なんで?
どこがどう繋がってこんなことになってるの?
さらにカスタマイズします。
URLに+とか%2Cといった文字が出てくるのはあまりよろしくないのでURLをslugifyします。slugifyって何よ?
lib/model/doctrine/JobeetJob.class.phpにgetSlug系メソッドを追加。
// lib/model/doctrine/JobeetJob.class.php
1
2
3
4
5
6
7
8
9
|
public function getCompanySlug(){
return Jobeet::slugify($this->getCompany());
}
public function getPositionSlug(){
return Jobeet::slugify($this->getPosition());
}
public function getLocationSlug(){
return Jobeet::slugify($this->getLocation());
}
|
lib/Jobeet.class.phpを作成してチュートリアルからJobeetクラスをコピペ。
// lib/Jobeet.class.php
1
2
3
4
5
6
7
8
9
|
class Jobeet{
static public function slugify($text){
// 文字ではないもしくは数値ではないものすべてを-に置き換える
$text = preg_replace('/\W+/', '-', $text);
// トリムして小文字に変換する
$text = strtolower(trim($text, '-'));
return $text;
}
}
|
routing.ymlのjob_show_user:url:を変更。
url: /job/:company_slug/:location_slug/:id/:position_slug
jobActions::executeShowを変更。
$this->job = $this->getRoute()->getObject();
http://symfony.localhost/frontend_dev.php/job/sensio-labs/paris-france/1/web-developer
リンクが正しくなりました。めでたしめでたし。
JobeetJob::getCompanySlugは何処からどうやって呼ばれてるんだ?
lib/Jobeet.class.phpはいったい何処で此処に置くことを決定しているんだ?
getRouteって何?getObjectって何?
全然わかりません。
なんでリンクするだけのためにこんな馬鹿みたいなことをしないといけないのでしょうか。
あと日本語は文字化けします。
ルートコレクションという機能もあります。
基本的なフォームの閲覧から作成、編集までのひとまとまりのルーティングを纏めてキャッチしてくれます。
apps/frontend/config/routing.ymlに以下を追加します。
1
2
3
|
job:
class: sfDoctrineRouteCollection
options: { model: JobeetJob }
|
なんか色々なルートを纏めて作成できるそうです。
皆さんわかりましたか?
私にはさっぱりわかりません。
最後にデフォルトルートを削除します。
デフォルトルートを削除した場合、定義したルート以外には辿り着けなくなります。
変なURLによるアクセスを簡単に排除することが出来ます。
逆に、今後モジュールを追加したとしても、routing.ymlを編集しない限りアクセスできなくなります。
Symfonyの記事一覧
他山の石他山の石。
対岸の火事?
2009/12/08
セブンネットショッピングオープン
http://www.7netshopping.jp/all/
2009/12/09
一部商品で価格誤表記等の問題。
http://www.itmedia.co.jp/news/articles/0912/09/news054.html
2009/12/10
フリーダイヤルと言っていたカスタマー番号が実はフリーダイヤルではなかったことが発覚。
ということらしいんですが、そもそも連絡先が見あたらないんですが何処に?
2009/12/13
個人情報流出。
http://www.kajisoku.net/1/archives/eid251.html
URLを入力するとパスワード入力をパスして履歴を見ることができる仕組みというのは実のところよくある話です。
ただ、普通はランダムなURLを作成するなりハッシュ関数を通すなりして、第三者からはほぼわからないURLを作成することが一般的。
セブンの場合は注文番号を単に置換しているだけだったので、他者の注文番号を推測できれば簡単に閲覧を行うことができた。
2009/12/14
XSS脆弱性が発覚。
http://www.kajisoku.net/1/archives/eid261.html
簡単に言うと、うまいことリンクを作れば偽のパスワード入力画面などを作成することができ、ログイン情報を盗むことができるという脆弱性である。
偽といっても本物と全く同じ画面にすることができる(ただしURLが緑色になるのは真似できない)ので、URLをよく見てチェックしないと気付くことができません。
XSSが発覚した時点で一旦サイトを閉鎖するのが正しい対応なのだが、セブンは気にせずに付け焼き刃な対応で営業を続ける。
2009/12/15
セブンアンドワイ時代の個人情報がGoogle検索に引っかかっていた。
http://www.kajisoku.net/1/archives/eid272.html
2009/12/17
ディレクトリトラバーサルによりソースコード流出。
http://www.kajisoku.net/1/archives/eid284.html
Subversion配下のファイルが外部から閲覧できるようになっていた。
# Copyright(C) 2001 by e-Shopping! Books CORP.
# Copyright(C) 2001 by e-Commerce Technology CORP.
esBooksとはなんとも懐かしい名前だ。
そんなにセキュリティが重要視されていなかった時代の遺物を使い続けていたので今になってこんなことになっているのではなかろうか。
2009/12/18
セブンネットショッピング側がXSS対策を発表。
http://www.itmedia.co.jp/news/articles/0912/18/news100.html
>セブン&アイグループが12月8日にオープンしたECサイト「セブンネットショッピング」のソースコードが流出したとネットで騒ぎになっている。同サイトの広報担当者によると、流出したのは「検証で使っていたデモ用コードで、本番用のものではない」。個人情報流出の可能性もないとしている。
>同サイトについては14日ごろから、XSS(クロスサイトスクリプティング)脆弱性も指摘されていた。「個人情報を扱うページではXSS脆弱性はなく、個人情報の流出はないが、そうでないページに脆弱性があった」とし、15日までに対策を取ったという。
>また12月14日ごろまで、セブンネットショッピングの前身・セブンアンドワイのユーザーの注文情報3件がGoogleなどで検索すると表示できる状態になっていた。閲覧できたのは「公開ブックマークなどにURLを登録していた3件」の注文情報の一部で、この問題にもすでに対処しているという。
勿論全部嘘。
どう見てもはてブのURLではない。
http://www.kajisoku-f.com/dd/img09/img2015_2.jpg
2009/12/18
XSS対策できていなかった。
http://www.kajisoku.net/1/archives/eid293.html
即日新たなXSS脆弱性を指摘される。
またセッションハイジャックも可能では無かろうか、との指摘もある。
このあたりでひとまず沈静化しましたが、結局抜本的対策は取られなかったので他にどんな穴が眠っていることやら。
たとえばとりあえず誰も指摘していないみたいなんだけど、
お問い合わせフォームが一切のバリデートもエスケープもされていないという酷い代物。
しかもセッション使わず確認画面からhiddenで飛ばしているという今時のフォームでは絶対に有り得ない作り方。
俺がこんなもの作ろうものなら始末書ものだわ。
ちなみにDoda配下にある採用フォームはしっかりと対策がされていました。
また、ソースを見ると、<input type="hidden">が地獄のように並んでいます。
渡す意味が全く無いであろうタイトルやコメントから、リンク先URLすらhiddenで渡している始末。
こんな出鱈目な出力を行っている時点で技術力がないのが丸わかり。
こんなので今日もTVCMを行っているのだから恐れ入る。
http://www.atmarkit.co.jp/fsecurity/column/kawaguchi/013.html
>痛くもない腹を探られる
これが最大の問題点だと思うんですけどね。
今回のように、実際は腹が痛かったということもありますし。
XSS程度の単純な攻撃すら防げていない
↓
他にも脆弱性があるかもしれない
↓
ディレクトリトラバーサル、ソースコード流出など次々に発覚
上記問い合わせフォームについて指摘し、2ちゃんねるに書き込んだところ、翌日には見事に対策されていました。
対策(笑)
この程度一時間で直せ。