RedMineが使えるサーバー探し

プライベートな開発でもチケット管理ができる物が欲しいと嫁が言っていたので、仕事で使っているRedMineが入る鯖を探しました。
無論チケットを発行するのは嫁や友人で、実装するのが私。プライベートでも進捗管理されるという…なんて素敵。

要件は以下の通り。

  • 月額 1,000円以下
  • ストレージ 20G程度
  • RedMineが動く
  • 出来ればSVN鯖も建てられる

2時間程調べた結果

さくらインターネット
http://www.sakura.ne.jp/
スタンダードコース月額
500円

さくらVPS
http://vps.sakura.ad.jp/
さくらのVPS 512コース
980円

IIJ
http://www.iij.ad.jp/GIO/
IIJ GIOホスティングパッケージサービス ベーシック
4,000円

ゴヤ
http://www.kagoya.jp/cloud/vps/
ゴヤクラウドVPS
980円

heteml
http://heteml.jp/service/charge/
1,500円

Heroku
http://devcenter.heroku.com/articles/s3
EC2借りないといけないから・・・
3,000円

なかなか見つかりません。
さくらVPSがやっぱり良いのかなーと考えていて・・・
最後に見つけたのが


ファーストサーバー
http://server-cowboy.jp/?bid=gtop
サーバーカウボーイ
416円

やっすー!どこかで聞いた数字だけども、これでいいんじゃないの?

インストールできる物の中を見てみるとたくさんある事ある事。

ブログ・CMS

コミュニティ

Eコマース

アンケート

  • Lime Survey

掲示

フォトギャラリー

  • Zenphoto

Wiki

バグ管理

  • Mantis

プロジェクト管理

テンプレート

RedMineみっけ。
phpBBみっけ。
他にもOpenPneがあるということはSymfonyも入るってこと。
これで416円は安すぎやしませんか?

で、早速作ってみました。
http://n416.server-cowboy.net/bts/
作成まで10分。

まずは無料で使いたおして見る事にします。

symfony 1.4オブジェクトルート

要件:オブジェクトルートを使って、部屋の一覧を表示するアプリケーションを作成する

プロジェクトのディレクトリを作成します。
以下のコマンドをxampshell上で流します。

cd c:\
mkdir object_routeing
cd object_routeing

プロジェクトを作成します。
以下のコマンドをxampshell上で流します。

symfony generate:project objectrouteing

DBを作成します。
C:\object_routeing\config\databases.ymlを確認後、以下のコマンドをxampshell上で流します。

symfony doctine:build-db

phpMyAdmin等でDBが作成されていることを確認します。

http://localhost/phpmyadmin/


frontendアプリケーションを作成します。
以下のコマンドをxampshell上で流します。

symfony generate::app frontend

Apacehの設定を修正します。
C:\xampp\apache\conf\httpd.confを任意のエディタで修正してください。

			Listen 80
			<VirtualHost *:80>
			    DocumentRoot "C:\object_routeing/web"
			    DirectoryIndex index.php

			    ErrorLog  logs/error_log_object_routeing
			    CustomLog logs/access_log_object_routeing common

			    <Directory "C:\object_routeing/web">
			        AllowOverride All
			        Allow from All
			    </Directory>

			    Alias /sf   "C:\xampp\php\PEAR\data\symfony\web\sf"
			    Alias /m/sf "C:\xampp\php\PEAR\data\symfony\web\sf"
			    <Directory "C:\xampp\php\PEAR\data\symfony\web\sf">
			        AllowOverride All
			        Allow from All
			    </Directory>
			</VirtualHost>
 http://localhost/にアクセスして確認します。


スキーマを変更します
C:\object_routeing\config\doctrine\schema.yml

Room:
  actAs:
    Timestampable:
  columns:
    name:            {  type: string(255) }
    prefecture_code: {  type: integer(2) }
    line_code:       {  type: string(4) }
  indexes:
    myindex:
      fields: [prefecture_code]
  relations:
    Prefecture:      {  class : Prefecture, local: prefecture_code, foreign: prefecture_code, type: one }

Prefecture:
  actAs:
    Timestampable:
  columns:
    prefecture_code: {  type: integer(2), primary: true }
    us_name:         {  type: string(32) }
    name:            {  type: string(4) }

SQLを作成します。
以下のコマンドをxampshell上で流します。

symfony doctrine:build --sql

SQLを確認します。
C:\object_routeing\data\sql\schema.sqlをエディタで開き確認してください。

CREATE TABLE prefecture (prefecture_code SMALLINT, us_name VARCHAR(32), name VARCHAR(4), created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, PRIMARY KEY(prefecture_code)) ENGINE = INNODB;
CREATE TABLE room (id BIGINT AUTO_INCREMENT, name VARCHAR(255), prefecture_code SMALLINT, line_code VARCHAR(4), created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, INDEX myindex_idx (prefecture_code), PRIMARY KEY(id)) ENGINE = INNODB;
ALTER TABLE room ADD CONSTRAINT room_prefecture_code_prefecture_prefecture_code FOREIGN KEY (prefecture_code) REFERENCES prefecture(prefecture_code);

テーブルを作成します。
以下のコマンドをxampshell上で流します。

symfony doctrine:insert-sql

マスターデータを作成します。fixtures.ymlを編集します。
C:\object_routeing\data\fixturesを任意のエディタで、追記してください。

Prefecture:
  東京:
    prefecture_code: 9
    us_name: tokyo
    name: 東京
  大阪:
    prefecture_code: 27
    us_name: osaka
    name: 大阪

Room:
  bukken1:
    Prefecture: 東京
    name:       東京の部屋1

  bukken2:
    Prefecture: 東京
    name:       東京の部屋2

  bukken3:
    Prefecture: 大阪
    name:       大阪の部屋1

  bukken4:
    Prefecture: 大阪
    name:       大阪の部屋2

fixtures.ymlの内容を、テーブルに入れます
以下のコマンドをxampshell上で流します。

symfony doctrine:data-load

phpMyAdmin等でDBの中にデータが挿入されていることを確認します。
http://localhost/phpmyadmin/
※もしも文字化けを引き起こしている場合は、fixtures.ymlあるいは、データーベースがutf-8ではない可能性があります。
fixtures.ymlがutf-8で無い場合はutf-8で保存し、データーベースがutf-8で無い場合は
ALTER TABLE `prefecture`
DEFAULT CHARACTER SET=utf8;
といったようなSQLを通すか、一度データーベースを削除し、
my.iniの[mysqld]の項目以下にdefault-character-set=utf8という記載を入れ、再度作り直してください。

モジュールを作成します。
以下のコマンドをxampshell上で流します。

symfony generate:module frontend search


モデルを作成します。使うことになるクエリを記載します。
C:\object_routeing\lib\model\doctrine\RoomTable.class.php

<?php
class RoomTable extends Doctrine_Table
{
  public static function getInstance()
  {
    return Doctrine_Core::getTable('Room');
  }
  public static function querySearchRooms()
  {
    $result = self::getInstance()->createQuery('r')
      ->leftJoin('r.Prefecture p');
    return $result;
  }
}

indexのアクションを作成します。今回はindexを、一覧にします。
C:\object_routeing\apps\frontend\modules\search\actions\actions.class.php

<?php
class searchActions extends sfActions
{
  public function executeIndex(sfWebRequest $request)
  {
    $p = $request->getParameter('p', 1);
    $pager = new sfDoctrinePager('Room', 10);
    $pager->setQuery(RoomTable::querySearchRooms());
    $pager->setPage($p);
    $pager->init();

    $this->pager = $pager;
  }

  public function executeShow(sfWebRequest $request)
  {
    $this->room = $this->getRoute()->getObject();
  }
}

テンプレートを作成します。今回はページャーインターフェイスを省いています。
C:\object_routeing\apps\frontend\modules\search\templates\indexSuccess.php

<?php if ($count = $pager->getNbResults()): ?>
<?php echo $count ?> 件の物件がみつかりました。
<ul>
<?php foreach ($pager->getResults() as $room): ?>
  <li><?php echo $room->getName() ?>
<?php endforeach; ?>
</ul>
<?php else: ?>
物件はありません
<?php endif; ?>

http://localhost/search/indexにアクセスして確認します。


続いて一件表示用にアクションとテンプレートを追加しましょう
searchにShowアクションを追加します。
C:\object_routeing\apps\frontend\modules\search\actions\actions.class.phpを開いて以下のメソッドを追加します。

  public function executeShow(sfWebRequest $request)
  {
    $DQL = RoomTable::querySearchRooms();
    $id  = $request->getParameter("id");
    $DQL->addWhere('id=?', $id);
    $this->room = $DQL->fetchOne();
  }

テンプレートを作成します。
C:\object_routeing\apps\frontend\modules\search\templates\showSuccess.php

<h2><?php echo $room->getName() ?></h2>

<ul>
  <li>物件名:<?php echo $room->getName() ?></li>
  <li>都道府県:<?php echo $room->getPrefecture()->getName() ?></li>
</ul>

http://localhost/frontend_dev.php/search/show/id/1にアクセスして確認します。

下準備はここまでになります。
本当は一覧のところにある

  • getName() ?>をそこにモジュール、アクション名でlink_toでリンクにして、関連付ければそれはそれで1つの完成を見ます。
    ですが、この実装方法だと、渡すパラメーターが変わる度actionソースの変更を行うことになり、あまり望ましい状態とは言えません。
    今回はオブジェクトルートという方法でもっと優雅に解決をしましょう。
    テンプレート側は

       <li><?php echo link_to($room->getName(), 'room_show', $room) ?>

    と記載しておきましょう。
    room_showというルーティングをオブジェクトルートで対応したいと思います。

    homepage:
      url:   /
      param: { module: default, action: index }
    
    room_show:
      url:     /room/:prefecture_us_name/:id
      class:   sfDoctrineRoute
      options: { model: Room, type: object, method: fetchOneByIdAndPrefectureUsName }
      param:   { module: search, action: Show }
      requirements:
        id: \d+
        sf_method: [get]
    
    default_index:
      url:   /:module
      param: { action: index }

    room_showルートは、URLにすると
    /localhost/room/tokyo/1
    といった雰囲気になります。
    urlの中で書いたprefecture_us_nameやidは、下のクラスのアクセサとして機能します。idであればgetIdにmprefecture_us_nameはgetPrefectureUsNameに対応します。
    getIdは存在するので値が入りますが、getPrefectureUsNameは、こちらで作る必要があります。こちらは後で作成しましょう。どこに作成するのかというのは次で説明します。
    optionsのmodelにはroomと記載しましたが、これはclassでsfDoctrineRuteと指定しているので、Doctrineのモデルを利用するという事を表しています。
    つまり、先のgetPrefectureUsNameは、 Room.class.phpに記載するという事です。
    methodは、fetchOneByIdAndPrefectureUsNameと指定していますが、これはどのメソッドを実行してコレクションを取り出すのかを指しています。
    よって、Roomのコレクションオブジェクトである、RoomTable.class.phpに書きます。
    では実際に
    Room.class.phpとRoomTable.class.phpにメソッドを実装しましょう。

    C:\object_routeing\lib\model\doctrine\Room.class.phpを任意のエディタで開いて編集します。

    class Room extends BaseRoom
    {
      public function getPrefectureUsName()
      {
        return $this->getPrefecture()->getUsName();
      }
    }

    C:\object_routeing\lib\model\doctrine\RoomTable.class.phpを任意のエディタで開いて以下のメソッドを追加します。

      public function fetchOneByIdAndPrefectureUsName($params)
      {
        return self::getInstance()->createQuery('r')
          ->leftJoin('r.Prefecture p')
          ->where('r.id = ?', $params['id'])
          ->andWhere('p.us_name = ?', $params['prefecture_us_name'])
          ->fetchOne();
      }

    さらにアクション側もこのルーティングに任せる修正を加えます。executeShowメソッドを変更します。
    C:\object_routeing\apps\frontend\modules\search\actions\actions.class.phpを開いて以下のメソッドを追加します。

      public function executeShow(sfWebRequest $request)
      {
        $this->room = $this->getRoute()->getObject();
      }

    シンプルなコードになりました。
    これで、room_showルートは完成しています。
    実際にアクセスをしてみましょう。 

    http://localhost/frontend_dev.php/room/tokyo/1
    http://localhost/frontend_dev.php/room/osaka/3

    はアクセスできますが

    http://localhost/frontend_dev.php/room/osaka/1

    はアクセスできず404が表示されているでしょうか?