ECサイトをリニューアルオープン
symfonyの開発実例って大規模なものが多いとか言われたりしますが、小さなECサイトの開発をsymfonyをベースで作ったのでちょこっと紹介。
愛犬のための犬グッズ専門サイト アットペット
アットペット
元々個人的に関わっているサイトなのですが、7年前に古い自前PHPフレームワークで動いていたものをそろそろどうにかするかということで慣れたsymfonyで機能を追加しつつ作り直しました。
オンラインショッピングはもちろんのこと、ポイントでお買い物したり、買った商品に愛犬の写真と共にレビューを投稿することもできます。ポイント還元率が高いので大容量のドッグフードをお探しの方など是非利用してみてください。
なぜsymfonyを選択したか?
PHPでECサイトをさくっと作るとなると、EC-CUBEで構築するという選択肢もありましたが次の理由でEC-CUBEを使わないことにしました。
カスタマイズする部分が結構多いのでEC-CUBEだと余計に工数が掛かりそう
買い物のフローがEC-CUBEと異なっていたり、会員機能にもペット情報が紐づいていたりと、改修するのにどのファイルを触ればよいか調べたり、その修正が他の機能に影響を与えないかテストを書いたりすることを考えると今回はEC-CUBEを使わないほうがベターと判断
今後がっつり機能追加していきたいので拡張性重視
会員機能やコミュニティー機能を追加していくのに、ベースが奇麗なほうが良いと判断。無理に組み込んで拡張したEC-CUBEを更に拡張は大変だろうなぁと。
正直EC-CUBEをカスタマイズしたこともありますが、かなり昔のことですし精通しているわけではないので現在はこれらの理由は当てはまらないかもしれません。
で、PHPといえばフレームワーク天国ですがsymfonyを選んだ理由は次の理由
自分がsymfonyに慣れ親しんでいるから
symfony1が出る前から触ってるぐらいですから、愛着もあります。なんでもそうですが慣れれば便利です。そして結構重要な要素です。
CMSとしてWordpressを利用しsymfonyと連携
オーナーがページを増やしたり、記事を書いたりするのはすべてWordpressにお任せしました。そして、Wordpressで表示する画面のヘッダーのphpでsymfonyのインスタンスを作成し「ログイン」状態でログインボタンの表示を着替えたりできるように連動させました。つまり、Wordpressのテンプレートからsymfonyのlink_toヘルパー関数などを呼んだり、Userインスタンスを利用できりるようにしたということですね。これが簡単にできるのは便利でした。
via:http://www.exgear.jp/blog/2010/10/wordpress-with-symfony/
プラグインとことん活用
あるものは利用させていただく方針。個人的にPEARで管理はあまりしたくない派なので公式サイトやOpenpearのSubversion、Githubからgit cloneでプロジェクトに持ってきてます。
そして自分で作ったプラグインも含め以下のプラグインを利用
- sfDoctrineGuardPlugin
- 言わずと知れたDoctrineで会員管理するためのプラグイン。これをベースに会員登録、ログイン、パスワード再発行などの実装を行いました。また、追加の会員属性(ふりがななど)を保持する必要があるので、そういう情報を含め全て別テーブルにデータを持つようにし、sfDoctrineGuardPluginでは基本情報のみ持つようにしました。スッキリ。
- via:symfony 1.x legacy website
- sfFormExtraPlugin
- jpFormExtraPlugin
- sfFormExtraPluginとはsfFormの標準Widgetにさらに便利なwidgetを追加するプラグインです。日付をカレンダーで洗濯したりするアレなどが含まれます。入れておいて損はないです。そして、このwidget類にさらに日本文化特有のwidget(日本語カレンダー、郵便番号から住所検索)を追加したプラグインを用意し自前のwidgetはこのプラグインで管理するようにしました。今度別の案件があってもそのまま利用できますしね。
- via:GitHub - brtriver/jpFormExtraPlugin: the extra symfony form widgets/validators for Japanese
- jpSimpleMailPlugin
- JISでメール送信するためのプラグインです。インストールするだけで文字化けせずに日本語でメールが送信できます。
- via:http://develop.ddo.jp/new-tech/php/framework/symfony/plugin/jpsimplemailplugin
- sfSimplePagePlugin
- ちょっとしたページを追加したいときにアクションを用意しなくてもページを作成できるプラグインです。サイトマップの各ページで利用し、アクションクラスを用意せずに作りました。
- via:symfony 1.x legacy website
- sfThumbnailPlugin
- 画像のサイズを変更したりするライブラリですね。投稿写真のリサイズ処理に利用しています。
- via:symfony 1.x legacy website
Google AnalyticsのAPIを利用したアクセスランキング表示
Google AnalyticsのAPIを利用すればアクセス数ランキングのためのデータを集計したりすることも簡単です。本当はタスクとしと書きたかったのですが、とりあえずコンポーネントとして切り出して1日に1回だけAPIにアクセスし以後はその結果をキャッシュしたものを表示するようにしました。モジュールのconfig/cache.ymlに以下のように書けば特定のコンポーネントだけキャッシュできるsymfonyの標準の機能そのままです。
via:symfony 1.x legacy website
_accessRanking: enabled: true
ジェネレータは自前のひな形を用意
symfonyのデフォルトのgenerate:moduleタスクではコンポーネントクラスを作ってくれなかったりするので、自前のスケルトンを用意しそれをひな形にするようにしました。また、そのひな形にはアクションクラスでよく使うメソッドや使い方をactions.class.sample.phpとして書き出すようにしておいたのでオンラインでググる必要も最小限に。
via: http://www.exgear.jp/blog/2010/04/symfony%E3%81%A7%E3%81%AE%E9%96%8B%E7%99%BA%E3%82%92%E6%A5%BD%E3%81%AB%E3%81%99%E3%82%8B3%E3%81%A4%E3%81%AE%E6%96%B9%E6%B3%95/
actions.class.sample.php
<?php class sampleActions extends sfActions { public function preExecute() { // code here } /** * Executes index action * * @param sfRequest $request A request object */ public function executeIndex(sfWebRequest $request) { // default return sfView::SUCCESS; // not return template but render text return $this->renderText('message here'); } private function __sampleCode() { // for debug $this->logMessage($message, 'debug'); // emerg, alert, crit, err, warning, notice, info, and debug // get Rouging object $object = $this->getRoute()->getObject(); // with form $this->form = new HogeForm($object); $this->form->bind($request->getParameter('hoge')); if ($this->form->isValid()) { // code here $name = $this->form->getValue('name'); } $widget = $this->form['name']->getWidget(); // use Request Parameter $name = $request->getParameter('name', 'default_value'); $all_parameters = $request->getParameterHolder()->getAll(); $condition = $request->isMethod('post'); // if request method is 'post', return true. // set template $this->setTemplate('new'); // render 'newSuccess.php' instead of 'indexSuccess.php' // use session $this->getUser()->setAttribute('name', 'value'); $condition = $this->getUser()->hasAttriute('error'); $name = $this->getUser()->getAttribute('name', 'default_value'); $condition = $this->getUser()->isAuthenticated(); // use flash $this->getUser()->setFlash('error', 'error is occured!'); $condition = $this->getUser()->hasFlash('error'); $name = $this->getUser()->getFlash('error'); // forward $this->forward('module', 'action'); $this->forwardIf($condition, 'module', 'action'); // if $condition is true, forwarding to 'module/action' $this->forward404If($condition, 'message'); // if $condition is true, forwarding to the 404 page // redirect $this->redirect('@routing_name'); $this->redirect('module/action'); $this->redirectIf($condition, '@routing_name'); $this->redirect404If($condition, '@routing_name'); // call Doctrine $result = Doctrine_Core::getTable('ModelName')->find('id'); $q = Doctrine_Query::create()->from('ModelName'); // access to Global Parameters $request->getMethod() // $_SERVER['REQUEST_METHOD'] $request->getUri() // $_SERVER['REQUEST_URI'] $request->getReferer() // $_SERVER['HTTP_REFERER'] $request->getHost() // $_SERVER['HTTP_HOST'] $request->getLanguages() // $_SERVER['HTTP_ACCEPT_LANGUAGE'] $request->getCharsets() // $_SERVER['HTTP_ACCEPT_CHARSET'] $request->isXmlHttpRequest() // $_SERVER['X_REQUESTED_WITH'] == 'XMLHttpRequest' $request->getHttpHeader() // $_SERVER $request->getCookie() // $_COOKIE $request->isSecure() // $_SERVER['HTTPS'] $request->getFiles() // $_FILES $request->getGetParameter() // $_GET $request->getPostParameter() // $_POST $request->getUrlParameter() // $_SERVER['PATH_INFO'] $request->getRemoteAddress() // $_SERVER['REMOTE_ADDR'] // add a new parameter to RequestParameter $request->addRequestParameters(array('new_key' => 'new_value')); } }
気軽なデプロイ
小規模なプロジェクトなのでデプロイはsymfony標準コマンドの project:deploy をそのまま利用。このコマンドはrsyncでファイルを転送してくれます。ある意味PHP関係ないですがこんなコマンドが用意されているところがsymfonyっぽい。
sfFormはきつい
symfonyを触っているというよりsfFormに苦しんだ開発でした。sfFormとはsymfonyに用意されているフォーム処理用のサブフレームワークのようなものです。ある程度場数をこなせばその流用で早く開発ができるのですが、そこにたどり着くまでがかなり大変です。今回もこの部分で苦しみました。
doctrine:build-formsでフォームクラスは生成されますが、このフォームクラスは直接使わずにextendsしたものを必ず用意するようにしました。同じ会員テーブルでも、会員登録時と管理画面の編集時でフォームが異なることなんてよくありますから。
UIはJQueryで
JavaScriptはJQueryを使いました。これも使い慣れているのが一番の理由。カレンダーとかそういうウィジット関連はsymfonyのプラグインが用意されているのでそれを利用。でも、表示の日本語化とかそのあたりは自前で拡張。