セカイモンの裏側

★毎週木曜日更新★ 海外ショッピングサイト『セカイモン』のブログです。私たちスタッフの仕事風景や日々の出来事など、     “セカイモンの舞台裏”とも言える日常を綴っていきます。

ModelをLibraryとして使う

典型的なMVCは好きではないです。

ViewとControllerを分けるのはわかるし納得ができる。

あと、古いフレームワークでDBとのクエリをModelでまとめるのも納得できる。

しかし、今時DBとの接続がフレームワークが設定されていので、あえてModelでSQLの記述と接続を記述しなくてもいいかと思います。

古いフレームワークの場合はmysql_connect()を記述してSQLも書いてとなって色々Model上で記述が多くなってくるかと思います。

しかし、最近では細かい記述はしなくてもよく

$results = DB::select( DB::raw("SELECT * FROM some_table WHERE some_col = :somevariable"), array(
   'somevariable' => $someVariable,
 ));

これでコントローラからもSQLが記述できるのでModelで記述する理由もだんだんとなくなってきているかと考えられると思います。

 

そうなってくるとMVCじゃなくてVCだけでもほぼいいんじゃないか?っていうのが考えです。

VCになると結局さらにファットコントローラの思想に行きついてしまいます。

 

とはいえ、じゃぁModelは不要って事になるので使わなくてもいいかってなるけどもライブラリとして使用することでいいじゃないかっていうのが今回のコンセプトです。

 

ライブラリとして使用するつもりが1つのコントローラからのみのアクセスになるってことよくあったり、一回きりの関数の呼び出しが他のコントローラからもModelを呼び出すってこともよくあります。

であれば初めから何がライブラリで何がModelに入れるべき処理っていうのを分けなくてもいいかと思いました。

モダンなフレームワークでのMVCについて懐疑的な意見のページを参照します。

togetter.com

ここに記載されてる事とほぼ同意です。一般的にはMVCにするときれいになるとかロジックはModelに書くべき、ロジックをModelにかけないエンジニアはまだまだ初級レベルみたいな概念がまかり通っているが、MVC自体にメリットがなければVCでもいいのではというのが個人的な意見です。

言いたいことを代替して言ってくれてるからすっきりします。

「LLのフレームワークはコードを整理してるだけ」という意見をModelとControllerの分け方を整理してるだけとくみとって解釈すると、もしコントローラで2000行ぐらいになるファイルになるのであればそれを分割してModelに分けていってもいいかと思います。

変更箇所が多そうな部分を切り出せばバージョン管理上、運用しやすくなるメリットはあるので。

共通ロジックはゴッドオブジェクトとsingle action per controllerの時にまたこの話になると思います。

 

 

 

POSTは全てAjaxで

<form>タグでpostすることを始めに覚える事かと思いますが、システムを作るうえでJavascriptからPOSTした方がいい理由がたくさんあります。

なので今では何も迷わずにPOSTをAjaxで行っています。

ではいつもの通りメリット・デメリットを記載していきましょう。

 

色々他の記事も参考にしようとしたけども、formタグとの比較でのメリットデメリットに対しての記事がなかったので参照なしに書き綴っていきます。

 

メリット

・サーバーに負担がかからない

 まぁ、これはサーバーが多少は負荷がやわらぐかもしれないけども、作り方次第ってのも大きいのと、Ajaxにしたからといって劇的に変わるわけではないと思いますが、一般的にこれが一番初めに想起させるメリットみたいです。

・バリデーションがJSで書ける

 バリデーションをJS上で書くことによってユーザーへのレスポンスをフルリロードしなくても、メッセージをエラーとしてユーザーに見せる事ができるので、ユーザーにとっても、サーバーにとっても都合がいいかと思います。中にはサーバーサイドでしか確認がとれないバリデーションとかもあると思いますが(ユーザーIDがユニークかどうか)、大部分はそうではないので大きなメリットかと思います。

・POST後の表示を考えなくてもいい

 formでPOSTするとPOST後に成功したエラーになったにかかわらずそれなりのHTMLを書いて表現しないといけないです。でも、JSからではエラー内容、通常のレスポンスもJSONで返すだけで済むのでPOSt後のデザインを考える必要もないです。

それに伴ってコントローラ名を**add,**edit, **delみたいに表示はJSON, 他はHTML表示というように分かりやすく分ける事も可能になってきます。

・formタグの位置を気にしなくてもいい?

 JSでは.POST時にどこのタグから取得するかを指定できるので、HTML上どこにタグ、データを置こうが関係なくPOSTできるようになります。hiddenタグでやればformでもできるのでメリットは薄いですが。。

 

デメリット

・コールバックを知らないといけない。

 POSTした後何かしらのサーバーのレスポンスをもとにアクションを起こしたい場合はコールバック上でアクションの関数を実行しないといけないので、慣れてない人には1ステップの障害になるかもしれません。でもコールバックは覚えないとWebエンジニアって言えないくらいですね。

・どこからの実行なのかがわかりづらい

 とんでもないJSファイルからPOSTを実行しているっていうケースもあるので、どこからどのように実行するかのルール決めをしないといけない。

・バリデーションをJS,サーバーサイド両方で記載しないといけない。

 基本はバリデーションはJSで行って、サーバーサイドではサーバでしかチェックすることができないバリデーションのみでOKかと思います。例えば、通常のユーザーの操作以外の悪意があるようなPOSTに対して「あなたは悪意があるPOSTです」ってわざわざ言わなくてもいいので適当にレスポンスは丸めておけばいいかと思います。

 

ずっとWebをやっててJqueryのPOSTでやることになんら違和感もなくやってて、それで運用も問題ないので、POSTは全てAjaxでいいという結論です。

つばめグリルでランチ会

品川駅のつばめグリルに行ってきました。

オフィスから10分ぐらいでつばめ風ハンバーグを4つ パン2つ ご飯2つでした。

 

はじめ銀紙で包まれていたので手で開けてい

 

いのか、フォークで開けていいのかわかららなかったけど、フォークであけました。

ボリュームは多かったです。

f:id:sekaimon-staff:20170609170226j:plain

f:id:sekaimon-staff:20170609170248j:plain

f:id:sekaimon-staff:20170609170305j:plain

これってスイートじゃなくてトマトでした。

 

ORMは無用の長物

無用の用と書こうとしたが、無用の用って役にたたなさそうで、役に立つっていう意味みたいなので、今回の意味とは違うようです。

kotowaza-allguide.com

本題のORMがなぜ不要かというと、ORMのメリットとデメリットを記載しました。

 

メリット

1、SQLがわからなくてもDBからデータを引っ張ってこれる

2、SQLインジェクションからORMで防げる

3、DBに関係なく同じコードで利用できる

4、バリデーションをモデルを書く事で構成が楽になる

 

デメリット

1、各フレームワークによって記述が違う

2、じゃぁこれって一体なんのSQLの実行??っていうのが直感でわからず、

DB::getQueryLogで確認しないと実際のSQLがわかりずらいって事がよくある

3、複雑なクエリに対応していない。またはいちいち調査しないといけない。

 

メリット1のSQLがわからなくてもDBからデータを引っ張ってこれるっていうメリットをよく聞くけど、そもそもプログラマーやっててSQLかけないなんてありえないというかプログラマーとして失格なんで メリットはほぼなし

 

メリット2ORMで記述している限りSQLインジェクションは防げます。

ただ、rawクエリでもplaceholderで防ぐ事ができます。 以下に例

$results = DB::select( DB::raw("SELECT * FROM some_table WHERE some_col = :somevariable"), array(
   'somevariable' => $someVariable,
 ));

なのでこれもメリットなし。

 

メリット3はDBがpostgresだろうがmysqlだろうが関係なくコードを書く事ができるという事ですが、基本的にSQLほぼDB一緒なんでこれもメリット薄 という事で

 

メリット4は過去にORMを崇拝してた事があった一番の要因です。Laravelでいうとardentというのを入れればtableを定義したmodelにバリデーションも追加できるので直感的にどのバリデーションが必要かどうかが簡単に記載できる 以下例

class Article extends Ardent {
//class Article extends Ardent {
/**
* The database table used by the model.
*
* @var string
*/
  protected $table = 't_article';
  public $timestamps = false;
  public static $rules = [
    'title' => 'required|between:1,500|',
    'body' => 'required|between:1,1000000|',
    'img' => 'between:1,1000000|',
  ];

}

コントローラで

DB::table('m_article')->insert(
array(
'name' => $_POST['title']
,'draft_name' => $_POST['title']
,'category_id' => $_POST['category_id']
,'created_at' => date("Y-m-d H:i:s")
,'updated_at' => date("Y-m-d H:i:s")
,'order_num' => $_POST['order_num']
,'draft_order_num' => $_POST['order_num']
,'full_txt' => $_POST['free_txt']
,'draft_full_txt' => $_POST['free_txt']
,'id' => $article_id
,'public' => 0
)
);

と書いても、もし文字列以上のバリデーションに引っかかるとexceptionに吐かれる。

自動でmodelで記載してるのでいちいちコントローラでバリデーションのコードを書かなくてすむ。なので、ORMはやっぱり必要と思っていました。

しかし

運用していくうちに割とあしかせになってる事がおおかった。「あれインサートできない??なぜ??あ、Modelのバリデーションで引っかかってた」という事が多かった。

あと、insertする部分を全てAJAXでpostするってなった場合はjavascriptでバリデーションするので結局JS部分でも記述が必要になった。

という2つの理由で結局使わない方がいいというのが今の判断です。

API専用のWebサービスとかであればいいかもしれないですが、バリデーションも全てモデルで結局補えないのでコントローラで書くって事に決めた方がわかりやすいかと思います。バリデーションをコントローラでモデルどっちでまとめるかっていうのも迷ってた時がありました。

detail.chiebukuro.yahoo.co.jp

今となってはFat Controllerの思想なので迷わずコントローラですね。

参考までにcodezineさんのメリット・デメリットです。

http://codezine.jp/article/detail/5858

メリット

メリット1と一緒でSQLを最小限にしたところでそれがメリットにはならない。

  • PDT(PHP Development Tools)やPHPEclipseなどの統合開発環境でメソッドの補完を行うことができるので、スペルミスやAPIを調べる手間が省ける

?? スペルミスはテーブル名とかのミスが多くてそれを防げるのであればいいけどそれは防げないかと。スペルミスがあっても実行してすぐにわかればいいかと。

  • 可搬性が上がる。データベース固有の振る舞いを吸収するので、別のデータベースに移行する際にビジネスロジックの変更を少なくできる

メリット3と一緒ですね。

デメリット

  • スキーマを変更するたびにORMクラスの再構築が必要になる場合がある
  • ORM自体を実行するオーバーヘッドが増える

 

symfonyとかであれば再構築しないときれいにORMクラスが作成されないとかはあったと覚えてます。kohanaとかは更新でORMはクラスを構築できるかと思います。

オーバーヘッドはコードが増えればそうなるかと思います。ないに越した事がないとなるとやっぱりFat Controllerが好きな理由です。

やっぱりSQLが直感で見れないのはわかりにくいわ〜。

NetworkManagerではまった

ディスプレイ画面付きのCentOSをインストールするとデフォルトで付いてくるらしい。

 

固定IPアドレス降っているにも関わらず書き換わってしまう。

resolve.confも書き換わってしまって????の状態が続いた。

 

調べてみたらNetworkManagerという事が判明

固定IPにしたければすぐに停止すべし

13.3.2. Network Manager の無効化 を参考

 

service NetworkManager stop

で一時的に停止して試しながら、うまくいけば

chkconfig NetworkManager off

で再起動後も停止した方がよさそう。

あと業務でよくある特異なネットワークの設定が必要でした。

 

 

f:id:sekaimon-staff:20170526091558p:plain

 

普段はこんな感じで1つのデフォルトルートだけで問題ないのでが今回の場合は

オフィスとデータセンターのルーティングが必要との事でした。

デフォルトルートだけの場合

[root@localhost ~]# netstat -rn

Kernel IP routing table

Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface

192.168.181.0   0.0.0.0         255.255.255.0   U         0 0          0 eth0

169.254.0.0     0.0.0.0         255.255.0.0     U         0 0          0 eth0

0.0.0.0         192.168.181.210 0.0.0.0         UG        0 0          0 eth0

ルーティングが2つある場合

Kernel IP routing table

Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface

192.168.181.0   0.0.0.0         255.255.255.0   U         0 0          0 eth0

192.168.9.0     192.168.181.1   255.255.255.0   UG        0 0          0 eth0

169.254.0.0     0.0.0.0         255.255.0.0     U         0 0          0 eth0

0.0.0.0         192.168.181.210 0.0.0.0         UG        0 0          0 eth0

これが必要なのでその設定方法は?

vi /etc/sysconfig/network-scripts/route-eth0

このパスに新しいファイルを作成して

GATEWAY0=192.168.181.1

NETMASK0=255.255.255.0

ADDRESS0=192.168.9.0

と設定して再起動すればOK

ここが今回つまったところ

他設定が必要なファイルをまとめると

/etc/sysconfig/network-scripts/route-eth0

/etc/sysconfig/network-scripts/ifcfg-eth0

/etc/resolv.conf

/etc/sysconfig/network

で設定は終わり。

 

MACでOracle にアクセスした時のtips

1.マックのターミナルでの話

oracleが文字化けで激しいのでというか未だにSJISなのでターミナルから入った場合、

文字列が炸裂します。

 

いつも忘れるのでここでメモ

export NLS_LANG=Japanese_Japan.AL32UTF8

マックは関係ないかも

 

2.他のテーブルを参照して更新

oracleでは update ** from ** where ** でもできなかったんで調べてみたら違うSQLのようです。

update (
select
A.ITEM_AMOUNT_DLLR A_ITEM_AMOUNT_DLLR, B.ITEM_AMOUNT_DLLR B_ITEM_AMOUNT_DLLR
from h_mypage_history A INNER join h_sales_dt_left B
on A.SALES_ID = B.SALES_ID
) set
A_ITEM_AMOUNT_DLLR = B_ITEM_AMOUNT_DLLR

 

これで違うテーブルから参照して更新できます。

「テーブル移動」「テーブル複数」って検索してもなかなか出てこなかったんでメモ

 

MySQLからORACLEにデータを移行するまでの道のり

ORACLEがライセンスの問題で複数サーバーを立てられない。

なのでバックアップサーバーとかはなかなか立てられない。。

という事でバックアップにMySQLサーバーに使っています。

あと分析用とかもMySQLを使わざる終えないのでOracle > MySQL

のデータ移行をしないといけないという悲しい状況です。

 

今回はそのバックアップを取っていたデータから新しいテーブルを作成してそれを本番のOracleDBに移行するっという作業でしたがいろいろ詰まったところがあったんでメモ

 

エンコードを合わせないといけない

バックアップの方はUTF8なので問題ないですが、オラクルの方はふる~~いのSJISでした。しかもエンコードを調べるSQLでの結果もわかりにくい。

SELECT VALUE FROM NLS_DATABASE_PARAMETERS
WHERE PARAMETER='NLS_CHARACTERSET';

「JA16SJIS」

見たこともないエンコードが。。なのではじめはSJISなのかEUCなのかさえわからなかったのですが、このコードでさらにぐぐると SJIS?っていう事がわかる。

www.atmarkit.co.jp

 

なのでmysqlでエクスポートしたテーブルをUTF8>SJISに変換しないといけない作業。

mysql> select * from h_mypage_history INTO OUTFILE "/tmp/h_mypage_history.tsv" FIELDS TERMINATED BY '\t';

,(カンマ)よりもタブの方がごみデータがすくないと重いTSVでエクスポート

nkf -s /tmp/h_mypage_history.tsv > /tmp/h_mypage_history_sjis.tsv

nkfSJISに変換

・NULLの取り扱いが違う

MySQLでは\Nっていう風に記載されているのに対し、オラクルはそういう記述の仕方ではないらしい。

なのでNULLをデータなしに変換。

sed -e 's/\\N//g' /tmp/h_mypage_history_tab_sjis.tsv > /tmp/h_mypage_history_tab_sjis2.tsv 

 

・インポートが難しい

[~]$ sqlldr ユーザー/パスワード control=mypage.ctl direct=true

というコマンドを叩く。そのまえにmypage.ctl

というコントロールファイルを作成しないといけない。

その内容は

LOAD DATA
INFILE '/tmp/h_mypage_history_sjis2.tsv'
INTO TABLE h_mypage_history
APPEND
FIELDS TERMINATED BY X'9' trailing nullcols
(
USER_ID

)

タブテキストの場合はFIELDS TERMINATED BY X'9'

としないといけないみたいです。

あと失敗すると、mypage.ctlファイルを作成する場合は

mypage.logていうログが生成されるのでそこからエラーなどを見てデバッグするみたいで直接のコマンドからエラーが見れないっていう遠まわしな。。。

 

なんだかんだ3人日ぐらいかかってデータ移行ができました。

参考URL

http://blog.livedoor.jp/ncad_webdb/archives/51384321.html

http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/server.102/B19211-01/ldr_control_file.html

http://www.shift-the-oracle.com/config/multibyte-characterset.html

http://www.oracle.co.jp/forum/thread.jspa?threadID=8008600

http://www.oracle.co.jp/forum/thread.jspa?threadID=35001617

http://www.searchman.info/tips/1150.html

http://vertex.air-nifty.com/blog/2008/10/oraclecsv-fe87.html