気ままなタンス*プログラミングなどのノートブック

プログラミングやRPGツクール、DTM等について、学んだことや備忘録をアウトプットとして残し、情報を必要としている誰かにとって「かゆいところに手が届く」ブログとなることを願いながら記事を書いています。

【intra-mart】横配置・縦配置ノードに展開されたノードIDから、展開元のノードIDを取得する方法

こんにちは。

今回は、intra-martに関する記事です。

縦配置・横配置ノード

横配置・縦配置ノードには、複数の処理対象者を設定することができます。 (横配置の場合は、必ず設定順に処理が流れ、縦配置の場合は設定した対象者が全て処理を完了した時点で次の処理に流れるということが実現できます。)

つまり、稟議書のワークフロー等、複数の承認者が必要な場合には必須となってくるノードかと思います。

ノードとステータスの紐づけの要件があったら

例えば、業務ワークフローにおいて、各ノードが終了する度に、ステータスを更新するような要件が発生したとします。

  • 0: 未申請(申請前)
  • 1: 申請OK(申請終了)
  • 2: 複数承認OK(横配置終了)
  • 3: 同期承認OK(縦配置終了)
  • 4: 申請業務終了(案件終了)

その場合、ルート定義で設定したノードIDを元に、LogicDesigner内で分岐して、 対象のノードに到達した時点でDBにステータス更新を行えば良い・・・という感じに思ってしまうのですが・・・

横配置・縦配置それぞれが実際にワークフローとして動作する際には、 ルート定義で設定したノードIDではなく、動的に割り当てられたIDがセットされる仕様になっています。

そこで今回は、下記の機能を利用し、動的に割り当てられたノードIDから、ルート定義で設定したノードIDを取得する方法についてまとめます。

利用する機能

  • IM-BIS
  • IM-FormaDesigner
  • IM-LogicDesigner
    • ユーザ定義(スクリプト)
  • IM-Workflow
    • 到達処理
  • データソース定義

IM-BIS定義を作成し、ルート定義の設定とフォーム設定を行う

ルート定義

横配置、縦配置の順にノードを配置し、それぞれのノードIDは「yoko_node」「tate_node」に設定しました。

f:id:rinne_grid2_1:20180520084741p:plain

f:id:rinne_grid2_1:20180520084800p:plain

フォーム設定

IM-BISからIM-FormaDesignerを開き、簡易的なフォームを配置します。

f:id:rinne_grid2_1:20180520085154p:plain

フォーム共有で横配置・縦配置それぞれに共有し、「定義の反映」実行

共有

  • フォームを共有

f:id:rinne_grid2_1:20180520085450p:plain

貼り付け

  • 横配置・縦配置にそれぞれフォームを貼付

f:id:rinne_grid2_1:20180520085638p:plain

定義の反映

  • 忘れてはいけない、定義の反映を実行

f:id:rinne_grid2_1:20180520085834p:plain

LogicDesignerのフロー定義を作成

入出力設定

  • 到達処理で、動的に割り当てられたノードIDを取得するため、暗黙的なパラメータをセットします
    • 入力:システム案件IDとノードIDを設定
    • 出力:メール送信可否フラグを設定(mailSendFlagはboolean)

f:id:rinne_grid2_1:20180520094556p:plain

到達処理で割当可能なパラメータについては、下記のintra-martのリンクをご参照ください。

5.4. 到達処理 — IM-Workflow プログラミングガイド   第16版 2016-08-01   intra-mart Accel Platform

定数設定

  • 到達処理では、必ずメール送信可否フラグを返却する必要があるため、今回は定数でtrueを設定しておきます

f:id:rinne_grid2_1:20180520090943p:plain

ユーザ定義(JavaScript定義)を作成します

f:id:rinne_grid2_1:20180520091156p:plain

パラメータの設定

  • 到達処理で受け取ったパラメータをJavaScript定義に渡すため、入力パラメータにシステム案件IDとノードIDを設定します

f:id:rinne_grid2_1:20180520092551p:plain

スクリプトの作成

  • JavaScriptを記述します
    • ロケールコードとシステム案件IDを利用し、未完了案件ノードを取得
    • 未完了案件ノードから、ノード設定情報を取得
    • 取得できた場合、ノード設定情報から、展開元のノードIDを取得
function run(input) {

    var systemMatterId = input.systemMatterId;
    var nodeId = input.nodeId;
    
    var actvMatterNode = new ActvMatterNode("ja", systemMatterId);
    var config = actvMatterNode.getExecNodeConfig(nodeId);
    if(config.resultFlag && config.data !== undefined) {
      var originalNodeId = config.data.expansionOriginalNodeId[0];
      Debug.console("展開ノードID:" + nodeId + " 展開元ノードID:" +originalNodeId);
    }

    return {};
}

上記LogicDesigner定義でデータソース定義を作成

f:id:rinne_grid2_1:20180520093139p:plain

f:id:rinne_grid2_1:20180520093251p:plain

f:id:rinne_grid2_1:20180520093347p:plain

コンテンツ定義で、到達処理を設定

f:id:rinne_grid2_1:20180520093522p:plain

f:id:rinne_grid2_1:20180520093727p:plain

サンプルデータの青柳さんでログインして、申請を実施

(ロール:BIS担当者を付与していないと、申請時に権限エラーで落ちるので、注意しましょう)

f:id:rinne_grid2_1:20180520100757p:plain

f:id:rinne_grid2_1:20180520100909p:plain

横配置ノードに到達し、到達処理が動く

  • ノードIDが取得できたことが確認 (e Builderのコンソールより)

f:id:rinne_grid2_1:20180520101200p:plain

これで、現在ノードIDの展開元が何なのかを知ることができました。

ただし、「動的承認ノード」でも到達処理が動いてしまうため、動的承認ノードが存在する場合は、 ルート定義のIDを用いて、別途分岐を行う必要があります。 (動的承認ノードの場合は、ルート定義のノードIDがそのまま渡されるため、分岐に利用できます)

function run(input) {

    var systemMatterId = input.systemMatterId;
    var nodeId = input.nodeId;
        var dynamicNodeList = ["douteki_node1", "douteki_node2"];

        // 到達処理で渡されたノードIDが動的承認ノードのものではない場合、横配置・縦配置として展開元IDを取得
        if(dynamicNodeList.indexOf(nodeId) < 0) {
        var actvMatterNode = new ActvMatterNode("ja", systemMatterId);
        var config = actvMatterNode.getExecNodeConfig(nodeId);
        if(config.resultFlag && config.data !== undefined) {
          var originalNodeId = config.data.expansionOriginalNodeId[0];
          Debug.console("展開ノードID:" + nodeId + " 展開元ノードID:" +originalNodeId);
        }
        } else {
            // 動的承認ノードの処理
        }
    return {};
}

以上で終わります。 皆様も素敵なintra-martライフを。

【2018年5月最新】【体験レポート】[審査に落ち続けてる人必見]Google Adsenceの審査に7回目でようやく通過したので、対応内容を時系列で詳しく書きます

こんばんは。長かったゴールデンウィークはあっという間に過ぎ去ってしまいました!

GW最終日の昨日、ようやくGoogle Adsence審査を通過することができました。

そして、申請回数はタイトル通り・・・計7回です!

申請前にGoogle Adsenceについて調査していた際、 「一発OKだった」や、「ほんの数時間で通過連絡が来た」という形で書いてあったブログが多く出てきたので、 「なんとか大丈夫だろう」と、少々甘く考えていたのですが、現実は厳しかったです。

そして何より、1回目の申請で通過する方々は、凄すぎる・・・!

「何で自分のブログは駄目なのだろうか」と、計6回も落ち込んだのですが、最終的には審査通過し、通過メールと一緒にやってきたポップコーンの彼(黄色背景の画像)と対面することができました。

f:id:rinne_grid2_1:20180508010549p:plain

(通過メールには、上記画像がgif形式で貼付されているので、gifアニメで動いている姿を見てびっくりしました)

さて、今日の記事の本題です。 Google Adsenceに関して、6回も不承認連絡が来てしまい、とてもスマートとは言えず、非常に泥臭い感じがありますが・・・ 1回目の申請から通過まで間で、必死に試行錯誤を繰り返した内容を記事としてまとめました。

僕のように、なかなか審査通過ができない方や、今後Google Adsenceの申し込みを実施される方のご参考になりましたら幸いです。

申請前のページ状態

f:id:rinne_grid2_1:20180508010234p:plain

1回目

記事に関する情報

記事数
550記事
各記事の文字数
50文字~1500文字の記事が混在

対応内容

  • はてなブログ無料会員から、有料会員(はてなブログPro)に変更(独自ドメインを設定するため)
  • 独自ドメインの設定
  • 問い合わせページ、プライバシーポリシーページの作成
  • ブログ紹介文の変更(対応後イメージ中の[1])
  • サイドメニューへの追加
    • プロフィールメニュー(対応後イメージ中の[2])
    • サイドメニューに問い合わせページとプライバシーポリシーページへのリンク(対応後イメージ中の[3])

対応後のイメージ

f:id:rinne_grid2_1:20180508010256p:plain

申請の時系列

  • 2018/03/27(火) YOUTUBEの審査基準(動画10,000再生)を満たしていないにもかかわらず、パートナープログラム経由でアドセンス申請し、広告設定。この時、いくつかの記事にアドセンスのコードを貼り付ける(そして翌日には貼付したこと自体を忘れる)
  • 2018/03/28(水) パートナープログラム経由でのアドセンスアカウントをキャンセルするため、キャンセルリクエスト送信
  • 2018/03/29(木) 改めて、通常の方法でアドセンス申請を実施
  • 2018/04/04(水) 結果連絡がなかなか来ないため、対応内容を振り返る。パートナープログラム経由で設定した広告のコードをいくつかの記事に貼付していたことに気づき、急ぎ削除。その直後、アドセンス不承認通知がメールで送られてくる

不承認メール内容(僕の場合は1回目~6回目まで全て同じ内容でした)

  • Googleからのメールを引用

Google のポリシーに準拠していないサイト: お客様のサイトは、Google AdSense のプログラム ポリシーに準拠していないか、ウェブマスター向けの品質に関するガイドラインに準拠していないため、現時点では AdSense のお申し込みを承認できませんのでご了承ください。Google の目標は、質が高く有用なコンテンツと価値あるユーザーに関連性の高い広告を配信できるサイトを広告主様に提供することです。お客様のサイトは、現在この基準を満たしていないと判断いたしました。

次のヒントを参考に、サイトでのユーザーの利便性を高め、AdSense の要件を満たすようにしてください。

• Google 広告を掲載するサイトでは、ユーザーに価値ある情報を提供することが重要です。サイトに掲載するコンテンツは、ユーザーが真っ先にアクセスしたくなるような、独自性と関連性の高いものにしてください。

• 自動生成されたページや独自のコンテンツがほとんどないページには、広告を掲載しないでください。

• サイトは操作しやすい構成にして、ユーザーの利便性を高めることが重要で、クリック操作でページを移動して、探している情報を簡単に見つけられるようにする必要があります。

2回目

不承認メール内容と、「Google Adsenceのプログラムポリシー」や「ウェブマスター向けの品質に関するガイドライン」を確認しつつ、メールにあった 「サイトは操作しやすい構成に」や「探している情報を簡単に見つけられるように」というヒントを元に、対応方針を決めました。

  • ナビゲーションメニューをPC版に追加する

記事に関する情報(1回目と同様)

記事数
550記事
各記事の文字数
50文字~1500文字の記事が混在

対応内容

  • ブログ記事のカテゴリの見直しを実施(カテゴリの付与されていない記事が多くあった)
  • 画面上部にPC用のナビゲーションメニューを追加し、ファーストビューでカテゴリごとの記事にアクセスできるようにした(対応後イメージ中の[1])

対応後のイメージ

f:id:rinne_grid2_1:20180508010314p:plain

申請の時系列

  • 2018/04/05(木) アドセンス再申請
  • 2018/04/06(金) アドセンス不承認

3回目

スマホ版については、記事の検索ボックスがページ上にあるため、ナビゲーションメニューは不要だろうと勝手に考えてしまっていたのですが・・・

ウェブマスター向けの品質に関するガイドライン」を改めて確認し

パソコン、タブレット、スマートフォンを含む、あらゆる種類やサイズの端末向けにサイトをデザインします。

という記載を読み・・・スマホ版のナビゲーションメニュー追加を決意しました。

記事に関する情報(1回目と同様)

記事数
550記事
各記事の文字数
50文字~1500文字の記事が混在

対応内容

  • Google Search Consoleとの連携を実施し、sitemap.xmlを登録
  • スマホ版のナビゲーションメニューを作成(対応イメージ中の[1])
    • Bootstrap(CSSフレームワーク)を利用(CDNからcss、jsを読み込み)
  • 記事の投稿
  • カテゴリ表示数の削減(1ページに100種類ものカテゴリを表示していた)

対応イメージ

  • スマホ版:対応前

f:id:rinne_grid2_1:20180508010521p:plain

  • スマホ版:対応後

f:id:rinne_grid2_1:20180508010530p:plain

申請の時系列

  • 2018/04/07(土)アドセンス再申請
  • 2018/04/11(水)アドセンス不承認

4回目

プライバシーポリシー等の必要ページを準備しつつ PC、スマホともにナビゲーションメニューの追加を行ったにも関わらず 不承認通知が来たため、ようやくここに来て記事内容の 見直しを行うことにしました。

記事に関する情報

記事数
300記事
各記事の文字数
大半が500文字以上

対応内容

  • 内容の薄い記事(500文字未満)や、プログラムソースが記事の大半を占めており、自分なりの解説やソースコメントを記載していない記事を「非公開」設定に変更

申請の時系列

  • 2018/04/12(木) アドセンス再申請
  • 2018/04/13(金) アドセンス不承認

5回目

さらに記事の見直しを行いました。

記事に関する情報

記事数
100記事
各記事の文字数
大半が600文字以上

対応内容

  • 内容の薄い記事(600文字未満)のものを「非公開」設定に変更
  • 過去記事(2011年頃)の記事で、体裁の整っていないものを「非公開」に設定(ex. 画像リンク切れ、レイアウト崩れ等)
  • 白背景+白文字等、クリック反転しないと視認できない文字の削除(自作ゲームの攻略記事に、行き詰まった人向けに、隠しテキストでクリア後のマップ用パスワードを記載していたのですが、念のため削除しました)

申請の時系列

  • 2018/04/14(土)アドセンス再申請
  • 2018/04/27(日)アドセンス不承認

6回目

ここまで来ると、わりと心が折れそうだったのですが・・・ 改善点を必死に探して申請しました

記事に関する情報

記事数
50記事
各記事の文字数
大半が800文字以上

対応内容

  • 読書メモ等の記事において、「引用」のみがメインとなっているものを「非公開」に設定
  • プロフィールアイコンを変更(RPGツクール用に作成されたドット絵を利用していたため。)

申請の時系列

  • 2018/04/28(月) アドセンス再申請
  • 2018/04/29(火) アドセンス不承認

7回目

記事に関する情報

記事数
25記事
各記事の文字数
800文字以上

対応内容

  • 記事のタイトルを改善([中級者向け]や、「便利」など)
  • クリックを誘導するかのように、誤解を与える文言が存在する記事があったため、削除(ex. 導入手順については下記のURLをクリックし・・・等)

申請の時系列

  • 2018/05/01(火) アドセンス再申請
  • 2018/05/06(日) アドセンス承認

まとめ

最終的に審査OKとなった理由は、断定はできませんが 下記のような対応が功を奏したのではないかと考えています。

  • プライバシー・ポリシーページ、問い合わせページの設置
  • PC、スマホへのナビゲーションメニューの追加
  • 記事タイトルの改善
  • URLクリックに関する文言の削除
  • 内容の薄い記事、極端に文字数の少ない記事の排除
  • レイアウト崩れ、画像リンク切れへの対応
  • カテゴリ表示数の削減

不承認続きで、心が折れかけましたが、諦めずに改善を続けて申請して本当に良かったと思います。

【備忘録】マイクロフレームワーク「Slim」とEloquentを使って、某小規模企画サイトのバックエンドを作ったので実装の流れを備忘録として残す

こんばんは。 GW後半が始まりましたが、皆様はいかがお過ごしでしょうか。

僕はというと、タイトル通り、某企画サイトのバックエンド側を実装しておりました。 (仕事ではなく、プライベートでの趣味の一環です。とある方から依頼を受けました。)

本番環境の制約

依頼元の本番環境のサーバーがレンタルサーバーということもあり、 利用できる言語はPHPのみで、その中でも利用可能なWebフレームワークが限られていました。

特にサーバーの制約がなければ、比較的慣れている「Django」を 利用していたと思うので、普段使わないものを使うきっかけができて、逆に良かったと思います。

今回の要件

  • ユーザの識別
    • ただしメールアドレス等の個人情報は収集しない
  • 識別したユーザごとの状態管理、ユーザ自身の操作による状態更新

要件の実現方法

  • Twitter OAuth認証の導入
  • ユーザの状態をDBテーブルで管理する
  • ユーザの状態更新のために、然るべきAPIを準備し、HTTPリクエストによって処理を実行する

今回利用した各フレームワーク、ライブラリのバージョン

  • Slim 3.1
  • illuminate/database(Eloquent ORM) 5.5
  • Twig 2.3

開発環境

  • 仮想環境のUbuntu 14.04 - gistで提供されていたVagrantfile及びprovisionシェル

Twitter OAuth認証によるユーザ識別

実装を始める前には、Twitter認証のやり方が全くわからなかったのですが、こちらの記事を参照して なんとか実装することができました。

syncer.jp

大変わかりやすい記事で、本当に助かりました。 誠にありがとうございました。

Slimの導入

ディレクトリ構成の検討

Slimを利用するにあたって、ディレクトリ構成を決める必要があります。 自分で適当に決めることもできるようです。

個人的なアプリ開発ならば適当に決めたものを利用するのですが、 一応「依頼を受けての開発」なので、素直にテンプレート(Slim-Skeleton)を利用することにしました。

github.com

Slim Skeletonプロジェクトの作成

Composerを利用することで、すばやくSlimアプリの作成ができます

$ composer create-project slim/slim-skeleton [appname]

作成後のディレクトリ構成

こんな感じのプロジェクトが作成されます。 public配下のindex.phpには各種設定ファイルの読み込みに関する処理が記述されています。

appname
├── composer.json
├── composer.lock
├── CONTRIBUTING.md
├── docker-compose.yml
├── .gitignore
├── logs/
│ └── README.md
├── phpunit.xml
├── public/
│ ├── .htaccess
│ └── index.php
├── README.md
├── src/
│ ├── dependencies.php
│ ├── middleware.php
│ ├── routes.php
│ └── settings.php
├── templates/
│ └── index.phtml
└── tests/
└── Functional/
├── BaseTestCase.php
└── HomepageTest.php

データベース接続

「Slim DB」で検索してみると、PDO(PHP Data Objects)を利用した データベース接続の解説記事が出てきました。 しかし、「DBテーブルごとに、紐づくモデルクラスを作り、SQLを書く必要がありそうで面倒くさそう」的なことや 「DB接続に利用するコネクションオブジェクトの破棄等をうっかりと忘れてしまいそう」といったことを 考えてしまい、もっと別の方法はないか探すことにしました。

すると、公式ドキュメントに「Eloquent」という文字があったのです!
Using Eloquent with Slim - Slim Framework

Eloquent ORM(オブジェクト関係マッパー)は人気のPHPフレームワーク「Laravel」のORMとして提供されている アクティブレコードによるデータ操作の実装です。

(なお、Eloquentという言葉に反応した理由は下記の記事にもあるとおり、最近Laravelに入門していたからです。) www.rinsymbol.net

フレームワーク「Laravel」を使っていなくても、 データ操作部分に関係するモジュールを導入すれば、Slimでも使えるようになるとのことでした。

Eloquent ORMの導入(と、Twigの導入)

下記のURL先の記述従い、必要な設定を追記していきます。
Using Eloquent with Slim - Slim Framework

  • Eloquentモジュールの導入

リンク先は5.1になっていますが、僕は5.5が使いたかったので、5.5を指定しました。 (5.5を指定した理由ですが、LaravelのLTS(Long Term Support)が5.5らしく、とりあえずそれに合わせといた方が良いのでは、と短絡的に考えたためです)

$ composer require illuminate/database=5.5.*
  • 必要に応じて、twigモジュールの導入

今回、ビュー側のテンプレートエンジンはtwigを利用したかったので、僕はここでtwigも導入しました。

$ composer require twig/twig
  • src/settings.phpファイルへのデータベース設定の追記

settings.phpファイルには、ログやレンダラに関する設定が連想配列で記載されています。 この中に、データベースに関する設定を追加します。('db'から始まる行~timezoneまで)

<?php
'settings' => [
    'displayErrorDetails' => true, // set to false in production
    'addContentLengthHeader' => false, // Allow the web server to send the content-length header
    'determinateRouteBeforeAppMiddleware' => false,
    // Renderer settings
    'renderer' => [
        'template_path' => __DIR__ . '/../templates/',
    ],
    // ここから↓
    'db' => [
        'driver' => 'mysql',
        'host' => 'localhost',
        'database' => 'database_name',
        'username' => 'user_name',
        'password' => 'password',
        'charset' => 'utf8',
        'collation' => 'utf8_unicode_ci',
         'prefix' => '',
        'timezone' => '-09:00'
    ], // ここまで↑
// Monolog settings
    'logger' => [
        'name' => 'appname',
        'path' => isset($_ENV['docker']) ? 'php://stdout' : __DIR__ . '/../logs/app.log',
        'level' => \Monolog\Logger::DEBUG,
    ],
  • src/dependencies.phpファイルにサービスファクトリ情報を追記する
<?php
// DIC configuration

$container = $app->getContainer();

... 中略

// ここから↓
$container['db'] = function($container) {
    $capsule = new \Illuminate\Database\Capsule\Manager;
    $capsule->addConnection($container['settings']['db']);

    $capsule->setAsGlobal();
    $capsule->bootEloquent();

    return $capsule;
};

//コントローラから、$this->container['logger']->debug()といった形で呼び出すため、インスタンス情報を取得する
$container->get('logger');
$container->get('db');

// twigモジュールを入れている場合
$container->get('view');
// ここまで↑

DBテーブルとモデルクラスの作成

DBテーブルを作成し、Eloquentの機能を利用するための、モデルクラスを作成します。

今回は、「users」テーブルと対応するモデルクラス「User」を作成しました。

  • userテーブル
create table users (
  id int(5) auto_increment primary key,
  token varchar(100) not null,
  hash_value varchar(100) not null,
  created_at datetime,
  updated_at datetime
)
  • Userクラス src/models/user.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class User extends Model {
    protected $table = 'users';
    protected $dates = ['created_at', 'updated_at'];

    public function getAttribute($key) {
        return parent::getAttribute(\snake_case($key));
    }
    public function setAttribute($key, $value){
        return parent::setAttribute(\snake_case($key), $value);
    }
}

モデルの作成については下記の記事を参考にしました。 ピンポイントで求めていた情報でした!ありがとうございました!

taka512.hatenablog.com

コントローラ作成

リクエストを処理するため、コントローラを作成します。

配置するディレクトリはどこでも良いのですが、僕はsrc/app/controllersディレクトリを作成し、 hoge_controller.phpファイルを作成しました。

  • src/app/controllers/hoge_controller.php
<?php
namespace App\Controllers;
use Psr\Container\ContainerInterface;
use Illuminate\Database\Eloquent\ModelNotFoundException;

require __DIR__ . "/../models/user.php";

class HogeController {
    protected $container;

    public function __construct(ContainerInterface $container) {
        $this->container = $container;
    }

    public function index($request, $response, $args) {
        $users = User::all();
        return $this->container['view']->render($response,
        'index.twig',
        ['users' => $users]);
    }
}

Controllerクラスを作成し、コンストラクタでコンテナ(dependencies.phpで設定したオブジェクト)を 注入(依存性注入:DI?)している感じがしますが、詳細については調べきれていません。

ルーティングの設定がまだなのですが・・・indexにアクセスした場合 ユーザの変数をindex.twigに渡すようにしています。

僕の場合、コントローラでUserモデルクラスをわざわざ読み込んでいるのですが、 おそらく、下記の記事のようにindex.phpで読み込む形式が良いのだと思います。(ブログ投稿にあたって、改めて調べていた時にこの記述を見つけました。Eloquent\Modelを継承したモデルクラスにも適用できるかどうかは試せていません。。) qiita.com

ルーティングの設定

Slim Skeletonのプロジェクトテンプレートを作成した時から存在するsrc/app/routes.phpに設定を追記し、 /indexでアクセスした時に、HogeControllerのindexメソッドが呼ばれるようにマッピングを行います

<?php

use \App\Controllers\HogeController;
$app->get('/index', HogeController::class . ':index');

HogeController::class . ":index"という書き方で、 .(ドット)と,(カンマ)を間違えているのではないかと疑問に思ったのですが、 こちらは.(ドット)であっています。

これで、/indexでアクセスした時、HogeControllerのindexメソッドが呼ばれるようになります。

ビューの設定

今度は、src配下ではなく、appname配下のtemplatesフォルダにtwigファイルを作成します (今まではすべてsrc配下のファイルを更新していました)

  • appname/templates/index.twigファイルを作成する
<!DOCTYPE html>
<html lang="ja">
<head>
<title>index</title>
</head>
<body>
{% for user in users %}
  user.id - user.token
{% endfor %}
</body>
</html>

上記の例では、/indexにアクセスすると、userのid列、token列が表示されます twigで利用できるタグやフィルタについては公式ドキュメントをご参照ください。

Documentation - Twig - The flexible, fast, and secure PHP template engine

静的ファイルの配置

JavaScriptやCSSといった静的ファイルは、src/public配下に配置すると良さそうです。

ちなみに僕は、開発時にはapache2を利用していたのですが、 下記のような構成にしていました。

Apache2の設定 /etc/apache2/sites-enabled/000-default.conf

<VirtualHost *:80>
  DocumentRoot "/var/www"
  ServerName app.dev
  ServerAlias app.dev
  <Directory "/var/www">
    AllowOverride All
    Require all granted
  </Directory>
</VirtualHost>

したがって、開発サーバーでWebアプリに接続する際には下記のような形となっていました。 http://localhost:8000/appname/public/index
(ポートフォワーディングで、ゲスト80番をホストの8000番に設定していることが前提となります)

最後に、その他感想

普段利用しないスキルセットで対応する機会がもらえたり、 TwitterのOAuth等、そもそも知見のないことに挑戦できて、すごく充実した日々でした。

【雑記】会議の段取り力が不足していることに危機感を覚えた話

簡単な仕事、難しい仕事

おそらく誰しも、ゴール地点や方針が決まっているものに対して仕事に取り組む場合、 さほど困難はなく最後までやりとげることが可能だろうと思う。

ただ、その逆で、雲をつかむような状態・・・とは言いすぎだが、 方針が定まっていない状態であったらどうだろうか。

仮にコンサルティングや管理職・経営者の立場であれば、それが日常茶飯事である可能性が高く、別段苦手意識は存在しないかもしれない。

しかし、僕の現在の立場であるエンジニアとしてシステムに関する仕事をしていく中で、 その「状態」に出くわし、苦手意識の表出と同時にスキル不足を実感してしまった。

具体的に、どんなスキルが足りなかったのだろうか? 一言で表すとするならば、段取り力だろう。

段取り力不足

こちらの役割として伝えたいことは、資料を用いて(下手なりに)説明することができた。しかし、資料説明後に沈黙が続いてしまった。

質問はないか、課題事項は何か、次回までのタスクは何か。 打ち合わせの本題ともいえる議論に繋げるための司会進行が全く機能していなかったのである。

打合せ結果としては問題なく、現状の整理と次回までのタスクを共有することができ、建設的な議論ができた。 しかしそれは、上司のフォローがあったからであるとともに、社内の人物が相手だったためである。 自分一人では到底その場を乗り切ることはできなかったと感じる。 また、社外のユーザ相手であれば、おそらく苦言を呈されていることだろう。

打ち合わせの目的は乗り切ることか?

・・・僕自身の場当たり的な思考を示すため、あえて「乗り切る」という言葉を使った。

つまりはこういうことである。 「未確定な状態からいち早く抜け出すためには、今回の打ち合わせで絶対に説明を終えなければならない」と焦り、その超短期的なタスクに気を取られすぎ、打ち合わせを乗り切ることに躍起になり、視野が狭くなってしまったのだ。

打ち合わせの目的は、資料内容を全て説明することではなく、 新たなシステムを導入する上で、各々の認識を一致させることである。

説明がわかりづらい等の指摘は誰からも出てこない。 しかし、それは打ち合わせメンバーが優しい眼差しで見守ってくれている(もしくは無関心)からであり、 振り返れば振り返るほどと言葉に詰まったり、曖昧な表現が発生したりと、至らない点が多くあったことを痛感する。

ゴール地点が決まっている場合であれば焦りは少なく、一時しのぎ的な対応にもならなかった可能性はある。 しかし段取り力が不足しているという事実にも気づくことができなかったかもしれない。

失敗を経験し、認識し、今後に活かすにはどうすべきか

この失敗を経て、このままではまずいと危機感を覚え、行動を起こすために計画を練った。

なんだ、そんな当たり前なことか、と思うかもしれないが、 打ち合わせの冒頭で、説明したい内容とその議題にかける目安時間を予め1枚のスライドで伝えることにしたのだ。

打ち合わせ中にアドリブで段取りができないなら、事前に準備するに越したことはない。

失敗を経験する前は、"説明するタスクを終わらせること"ばかりに意識が向いていたが、 相手に説明内容が伝わることに意識が向くようになった。

・・・さて、その結果はどうだったのだろうか?

実は、今日の打ち合わせが練りに練った計画を実行するタイミングだった。

時間配分が多少前後したり、言葉に詰まったりということはあったものの、打ち合わせの目的である「認識の一致」を達成することができた

なお・・・認識が一致したことで、システム化する対象が変わったのは、また別の話である。

【RPGツクールMV】これで解決!スマホブラウザでInvalidStateErrorが発生した場合の対応記録

概要

RPGツクールMVで、ウェブブラウザ向けにデプロイメントを実施し、 iPhoneやiPad等のiOS系端末でゲームをプレイしていると下記のようなエラーが発生しました。

Error
InvalidStateError (DOM Exception 11): The object is in an invalid state.

原因

ゲームイベントで指定されている BGMやSEのm4a形式ファイルが存在しないためです。

対策

m4a形式ファイルをインポートする必要があります。

気を付けた方が良いこと

PC版のブラウザだと、開発者コンソールに「ファイルが見つからない」エラー出力があり、ゲームの継続が可能です。 しかしスマホ・タブレットの場合はゲーム自体が停止してしまいます。

個人的な話なのですが、外部サイト様の音楽素材を利用する際、 oggファイルだけをインポートし、m4aファイルの方が漏れてしまうことがたまにあるので、 それぞれのファイルをセットでインポートすることを忘れないようにしないといけませんね。

m4aファイルを配置しても、上記エラーが発生した場合

  • 下記の端末で、現象を確認
    • iPod touch(MKH02J/A iOS 10.1.1)のSafari、Chrome
    • iPad mini2(ME280J/A iOS 10.0.2)のSafari、Chrome

推測と対策

  • m4aファイルのサイズが大きすぎる可能性があります。
    • 2種類のm4aファイル(1.9MB、2.4MB)がそれぞれ再生されませんでした
    • ビットレート変更ツール等を利用し、音質を下げ、1.1MB程度に圧縮したところ、問題なく再生できました
    • ※ブラウザキャッシュを削除しないと、キャッシュからBGMを読んでしまい、いつまでたっても反映されないようです

公開バージョンで、ファイル系エラーを起こさないために

本事象に出くわしたことで、公開時に留意しておくべき点がいくつかわかりました

  • ゲーム停止は絶対に避ける(ゲーム進行できないより、音楽がならない方がマシ)
  • m4aファイルサイズを意識する

ゲーム停止を避けるために

とても素敵なプラグインがありました。

それでは、皆様も楽しいツクールライフを!

【雑記】一つのことに集中できるのが理想だがそうもいかないため、タスク・時間管理を頑張る話

至極真っ当な結論になったけど、自分の悩みの中で、なんとなく腑に落ちたことがあったので書く。

僕は会社では主にJavaを書いている。最近はSQLチューニングの知識が求められている。 また社内システムではあるが、要件定義や基本設計などのスキルも求められ、 他部門との調整・交渉力も必要になってきている。

一方、家では趣味でフロントエンドの勉強をしていたり、ゲームを作ったりと、 最近の流行り(?)に置いていかれないように勉強するようにしている。

  • 仕事の学習
  • 仕事のプログラミング
  • 趣味の学習
  • 趣味のプログラミング
  • 趣味の制作

主に趣味の学習系において、思うように自分の力になっていない気がして、もどかしい。

なぜだろう?

ポイントは、自身の情報受信時の衝動性と、タスクへの強制力。

情報受信時の衝動性

趣味のプログラミングでは、Webアプリを主として学習を行っている。
しかし、周囲の情報に流されやすく、一つの技術を突き詰める前に、別の技術に目移りする傾向にある。 (仕事に関していえば、目移りしたとしても、取り組むべき業務が終わないという結末がわかりきっており、強制的に完了させるため問題にはならない。  さらに完成させた事実があるので、少しは自分の力になっている)

例えば、2017年11月頃は、GoLangに夢中だった。 書籍をある程度読み終えたところで、APIの実装を模索しようとしているとき・・・ 何も成し得ることなく、Angularの学習を始めた。

理由としては、以下のとおりだと考えている

  • 自分自身のGoLang学習のゴール地点や成果物が定まっていなかった
  • 別部署の業務で「Angular」(フロントエンド)の話が湧いて出てきた

少なからず、趣味と仕事で同じ技術、言語を突き詰めて書きたいという想いを持っており、 今思うと非常に浅はかな考えではあるものの、こう考えたのだ。

「Angularの勉強を始めれば、技術要員としてアサインされて、家でも仕事でも同じ技術が使えるかもしれない」と。

結果として、「非公認オブザーバー」のような形で、Angularに関する意見交換を行うことはできたものの、 自部門の業務が手一杯であるのと、部門間の見えない壁により、浅はかな理想像は砕け散った。

さて、その後はどうなったのだろうか?

2018年1月、ゲームフェスの存在と、その締め切りが数日後に迫っていることを知ると・・・ なんと、急に(衝動的に)ゲームが作りたくなったのだ!

間に合わないことは目に見えてはいるものの、ゲームの全体的な流れは確定でき、OPまで実装した。 (その間のAngularの勉強状況は、ご想像におまかせする)

この状態について、よくわからない造語をすると、情報過敏というものになるのだろうか。 様々なことに手を出してしまい、一つのことを突き詰めてこなすことができていないということを認識した。

今後の生き方

では、僕は今後どうすべきなのだろうか?

情報の受信をトリガーとして、衝動的な欲求(学習欲、創作欲)が発生し、別のことが中断されてしまう弱点はあるものの その事象自体がモチベーションにつながることは間違いない。 (衝動的な欲求が発生した時の集中力は凄まじいものがある気がする)

したがって、それを弱点ではなくチャンスに変換し、価値あるものとすることが、自分自身の行うべき課題であると言える。

やはり、一番適切なのはこれしかないだろう。 基本中の基本である、正確な時間管理、タスク管理が必要だ。

  • 時間を決めてタスクをこなす(何時間もぶっ続けでやらないし、毎日最低限の時間を作る)
  • 定期的に復習する

そう、そもそも趣味でやることについては、期限がない。 そうすると当然のことながら、時間を意識するはずもない。

インプットもアウトプットも期限を決めて、計画的に実行していく必要がある。

【RPGツクールMV】メニューから回想モードの呼び出しを行えるようにする補助プラグインを公開しました

RecollectionMode.js(回想プラグイン)の補助として、メニューから回想モードを呼び出すための 補助プラグインを作りました。

現状の回想プラグインだけだと、タイトル画面からの移動しかできませんが、 本プラグインを追加することで、下記利用イメージのように、メニューに回想モードに移動するためのコマンドが表示されるようになります

利用イメージ

f:id:rinne_grid2_1:20180421212859j:plain

f:id:rinne_grid2_1:20180421213324j:plain

使い方

RecollectionMode.jsの導入

まず、RecollectionMode.jsがすでに適用済みである必要があります。
適用方法については、下記プラグイン配置場所(Github)をご確認ください。

https://github.com/rinne-grid/tkoolmv_plugin_RecollectionMode

補助プラグインのダウンロード

下記のURLより、補助プラグイン(RecollectionMode_back_to_menu_and_title_patch.js)をダウンロードし、 ツクールプロジェクト/js/plugins/に配置します。

https://raw.githubusercontent.com/rinne-grid/tkoolmv_plugin_RecollectionMode/master/RecollectionMode/js/plugins/RecollectionMode_back_to_menu_and_title_patch.js

プラグインの適用

プラグイン管理から、補助プラグインの状態をONにします f:id:rinne_grid2_1:20180421213444j:plain

プラグインパラメータの設定(任意)

各パラメータの設定を行います。

パラメータ 説明 デフォルト
コマンド追加位置 1~8の数字を指定します。
1: アイテムの下に追加
2: スキルの下に追加
3: 装備の下に追加
4: ステータスの下に追加
5: 並び替えの下に追加
6: オプションの下に追加
7:セーブの下に追加
8:ゲーム終了の下に追加
5: 並び替えの下に追加
「回想」コマンドの名称」 回想モードに移動するためのコマンド名称として表示される文字です。 回想モード
「戻る」コマンドの名称 回想モードからメニューに戻るためのコマンド名称として表示される文字です。 メニューに戻る
回想コマンドを表示する条件スイッチID 回想コマンドが表示される条件スイッチIDを指定します。
このスイッチがONになった場合に回想コマンドをメニューに追加します。
IDに0を指定した場合は常に回想コマンドが表示されます
0

2018/04/02 0:10追記 問題解消について

下記取り消し線で書かれている問題は解消されました。

問題点・注意事項 1点、よろしくない問題があります。 【問題の概要】 メニューから移動した回想モードで、回想を観てから、メニューに戻ってきた時、 背景(マップキャラクター等)が消えてしまう。 回想モードに移動する前 回想モードに移動し、回想を観てから、メニューに戻ってきた時 一度メニューを閉じてしまえば、元に戻るのですが、 メニュー経由での回想を観るたびに、背景表示が消えるという問題が残っている状況です。

2018/04/03 0:43追記 v1.0.3を公開

新たに発覚した問題点への対応を行いました。

  • メニューから回想メニューに移動した際の問題
    • BGSが再生されたままになる問題を修正
    • 画面の効果(天気、色調変更、フラッシュ、シェイク等)が実行されたままになる、あるいは回想からマップに戻ってきた時に効果が再開されない問題を修正

もし使ってみたいという方がいらっしゃいましたら、ご利用をご検討いただけると幸いです。