俺のラズパイ3で.NET Core 3.0 preview が動くまで

俺のラズパイ2で.NET Core 2.0が動くまで | Moonmile Solutions Blog

前回、ラズパイで .NET Core 2.0 が動いたのだが、セルフビルドができない(後から見れば .NET Core 2.1 で ARM32 対応になっているのだけど)と思ってみたものの、.NET Core 3 ならば大丈夫、という訳でメモ的に。

現時点の v3.0.0-preview8 を使うと Raspberry Pi 3 上で .NET Core でビルドができます。今まで、ラズパイ上でできなくても PC でクロスビルドしてから転送すればよかったので、動くと言えば動くのですが、やっぱり dotnet build ができないと「手軽」とは言えませんよね。手軽じゃなかったら、nodejs を使うか、python を使うか、PHP で Laravel か React を使ったほうが便利なわけですが、ひとまず、.NET Core 3 になるとビルドができます。

ダウンロードしてインストールする

Download .NET Core 3.0 (Linux, macOS, and Windows) https://dotnet.microsoft.com/download/dotnet-core/3.0

ここから SDK の Linux の「ARM32」をダウンロードします。

ラズパイへ WinSCP などで転送できたら、tar で展開します。



mkdir -p $HOME/dotnet && tar zxf dotnet-sdk-3.0.100-preview8-013656-linux-arm.tar.gz -C $HOME/dotnet export DOTNET_ROOT=$HOME/dotnet export PATH=$PATH:$HOME/dotnet

export のところは、~/.bashrc に書いて、常にパスを通しておきます。

dotnet new mvc する

ラズパイで .net core を使って HTTP サーバーを動かすパターン



$ dotnet new mvc -n web

すると動きます。.NET Core な読み込みにひどく時間がかかる(1分以上?)のですが、まあ、立ち上がれば何とか。


blazor する

.NET Core 3 の目玉機能である Blazor も動きます。Blazor は WebAssembly 上に .NET Core を乗って動かす SPA です。


laravel する

ついでに、PHP を入れて laravel してみます。


Laravel と Vue.js の相性は良いので、これも nodejs を使って SPA を作れます。

wpf や winforms してみるが駄目

もうひとつの .NET Core 3 の目玉機能として、Linux の環境で WPF や WinForms が動くという(噂?)があります。


一応、テンプレートはあるしプロジェクトは作れるのですが、Windows 上でしか動きません。これだと、Windows 上で .NET Framework を使う場合と同じじゃないか?と思うかもしれませんが、どうなんでしょう?

まあ、.NET Core と .NET Framework は、来年の秋頃に統合されて .NET 5 になるそうなので。

Introducing .NET 5 | .NET Blog

というわけで、まだラズパイ4は手に入れていないので、ラズパイ3での動作確認を。

カテゴリー: 開発, NET Core | 俺のラズパイ3で.NET Core 3.0 preview が動くまで はコメントを受け付けていません

Windows Update のない世界 Linux へ(サーバーだけ)移行しよう

うちの仕事の環境は、基本 Windows で揃えているのですが、一番の難点は Windows Update です。別口で Windows Server を使えばいいのでしょうが、これは私にはオーバースペックだし、そもそも別口でサーバーを揃える位ならば、Linux を使える環境を用意したほうがよかろう、ってのが主旨で、

サブ開発環境を Ubuntu 18.04 で準備する | Moonmile Solutions Blog

去年の夏に Linux 環境を整えました。仕事柄 Visual Studio が必須なのと、仮想環境がワンセットになっていないと辛い(Windows 7 とか、別の Linux 環境とか)ので、メモリを増加させて相乗りをさえていた訳ですが、月1に訪れる「Windows Updateによる再起動」が辛い。

会社で使う業務PCならば、帰宅時に電源を落とすというのが普通なのでしょうが、

  • 機械学習させているときに、数日間廻しっぱなしにする必要がある
  • バックグラウンドで仮想環境(VMWare)が常に起動している
  • バックグラウンドで Docker が常に起動している

ということになると、不意にやってくる Windows Update による再起動は結構な痛手です。暫くはタイミングを見て手動で再起動(VMWareも手作業でスリープさせる)というのを繰り返していたのですが、去年の春あたりから「勝手に再起動」が頻発してしまって、あまりに耐えかねて Linux マシンを用意しました、ってのが去年の夏です。

構成などは、さきのブログを見て貰うということで、1年間 Linux サーバーとして動作させた感想をメモしておきます。

なんといっても再起動しないのが良い

目的が「再起動させない」なので、Linux マシンは24時間運用です。電力的にどうか?という話しもありますが、私の場合は仕事場が自宅なので、利用時間はこれで良いのです。
単純な NAS や自宅 WEB サーバーならば、Raspberry Pi でも良いのですが(あるいは Orange Pi などの互換機でもOK.),仮想環境として VMWare を動かすのが前提だってので、通常の PC を用意します。メモリはそれなりに潤沢に置いて、CPU はそこそこ、直接画面を見ることはないのでグラフィック関係はオンボードのものを利用しています。

Linux サーバーには普段はモニタはつけていなくて、VNC で接続します。実は VNC での画面更新は非常に遅いので、VSCode などを使った編集が難しいのがデメリットなのですが、そこは Telnet でつなげて vi なり emacs を使います。

業務上、常に VMWare 上で Windows 7 が動作しています。裏で見えているのが保守しているシステム(旧顧客環境のため Windows 7 が稼働している)、手前にあるのが VSCode ですね。絵文字もカラーで綺麗にでます。ただし、VNC 経由だとキーボードの反応とかが極めて遅いので、基本は、

  • telnet で vi を使ってコードを書く
  • WinSCP を使ってファイル転送
  • samba を使って Windows とファイル共有

ってことをやります。VMWare 上で動いている Windows 7 は、そのままリモートデスクトップで接続ができるので、VNC を介在せずに、業務 Windows 10 – Windows 7 間で動かせるので、通常の PC のように動かせて便利です。

Linux マシンは不意に再起動しないので、仮想環境がいきなり倒れたり、機械学習用のプロセスが倒れてたりすることはありません。まあ、実際のとこれは適度に apt upgrade するのですが、面倒?なのもあって、ここ半年ぐらいはそのままです。旧来の意味でのサーバーとしての利用ですね。

インストールするソフトウェアは最小限に

この Linux サーバーは外部に公開している訳ではないので、セキュリティリスクが低いのですが、一応、インストールするソフトウェアは最小限にしています。
最小限とはいっても、適当に動かしたいものを入れるので、

  • mysql
  • PHP
  • apache
  • java
  • dotnet

あたりが入っています。あまりたくさん入れると、再構築が面倒なのと、バージョン違いのあれこれで悩むことになるので、少な目にしておきます。
実際の業務サーバーになると、メールなりFWなりの設定が出てくるわけですが、社内(個人内)で使うものなので、ややこしい設定は入っていません。その分、再構築は楽なのです。

逆に、なんらかのテストをしたいときは、VMWare か Docker に押し込めます。VMWare にしても本格的な運用ではなく、実験的に使うことが多いので既存のものをクローンして使うだけで済みます。HDD の容量を見て、適当なタイミングで仮想環境は消してしまいます。

Ubuntu なので snap が使えるのですが、あまり使っていません。仕事柄ソフトウェアのバージョン違い、OS のバージョン違いを確認することが多いので、snap を使うよりも、VMWare で用意してしまったほうが楽なのです。

なお、仮想環境は HDD の容量を気にしてあれこれと消したりするよりも、2T HDD を買ってしまって、さっくりと増量してしまったほうがベターです。メモリも仮想環境用に 32GB まで増量させています。本当は 64GB に上げたかったのですが、結構な値段になってしまったのでランクを落としました。それでも大丈夫なのは、

  • 業務用 PC で Windows 10 で 32GB
  • Linux で Ubuntu 18.04 で 32GB

に分けられたからですね。業務用 PC のほうでは仮想環境を動かすことがなくなった(実際は、アズールレーンが BlueStacksで動いていますがw)ので、32GB でも十分になりました。

クラウド環境でもいいのでは?

自宅 Linux を作るよりクラウド環境を要したほうがいいのではないか?と思うこともあります。実際、VPS を借りたとしても、年間2万円弱でそれなりの Linux サーバーを借りられます。手元の Linux サーバーは 13万円 + 電気代がかかっているわけで、実は外部で借りたほうがランニングコストは安くなるでしょう。おそらく、VMWare 等を使って仮想環境を作らないのであれば、クラウド上に Linux や Windows を入れてしまったほうが手軽です。問題は、回線スピードなのですが、今となっては光回線が普通になってきているので、Windows 10 からリモートデスクトップで繋げても、そこそこの画面更新スピードで動くでしょう。

なので、最近となっては全面的にクラウドに持って行ってもよいのですが、クラウド環境を使う最大のデメリットは、

  • ビックデータや画像データなどの大量のデータを送り出すのに時間が掛かること

です。光回線の場合、下りのスピードは早いのですが、上りのスピードが遅いことが多々あります。なので、検索のためのデータとか、何か加工するためのデータをクラウド上にアップロードするときにかなりの時間が掛かります。転送量の価格もあるのですが、時間が掛かるのが大問題です。

手もとの Linux サーバーだと、HDD を USB でつなげるとか、社内ネットワーク越しにコピーするとかで相当にスピードでコピーができます。一番早いのは、micro SD カードか外付けの SSD にコピーして USB 3.0 経由ですね。
なので、業務的にデータの秘匿性が高くはなくても、オンプレミスで Linux サーバーを運用させるメリットはそれなりにあるわけです。まあ、オンプレミスの場合には、SE 役が必須なわけですが…ひとまず、自宅 Linux の場合は自分な訳だし。

カテゴリー: 開発 | Windows Update のない世界 Linux へ(サーバーだけ)移行しよう はコメントを受け付けていません

クソコードとマイナス生産性の関係

定期的に発生する「クソコード」発言問題だけど、新人の未熟なコードよりも(自称)熟練者のクソコードのほうが害が大きいので、さりげなく「クソコード」というようにしている。例えば、

  • こんなクソみたいなコードを残してしまうと、未来に禍根を残すし
  • 使う身になってみれば、クソみたいなAPIを公開されても誰も使わないし
  • こんなクソみたいなUIは誰も使わなくなるから、削ったほうがコストが安くなりますよ

とかとか。以前は汚物用語は使わないようにしていたのだけど、なぜかインパクトが薄く(自称)熟練者には届かないようなので、時に散弾銃のように混ぜるようにしている。ちなみに、さりげなく「クソ」を混ぜると、当たらない人には当たらずにスルーされる。本人だけには分かるようなので、顔が少し赤くなるので判別がつく。
また、「クソコード」を言うときは、本人に対して言うのではなく、

  • ディスプレイのコードを示しながら、「ここのクソコードなんですけどね」とか
  • 印刷したコードにアンダーラインっぽい「削除ライン」を引きながら「ここのコードなんですけどね」

とやる。コードを書いた本人が悪いのではなく、悪いコードを修正しない当人が悪いという示唆である。プログラマは職人的な能力が問われるので、日々進化する。だから、数日前に書いたコードは、自分が未熟だったゆえに(ものを知らなかったゆえに)書かれたコードであれるのだから、数日後にものを知ったあとで直せばいいのだ。
だから、直せばOK. 直さないのは何かが「クソ」なのだから。

何を参考書にする?

コードレビューに関しては、この2冊があれば十分である。

「ソフトウェア インスペクション」は、IBMで育ったレビュー方式で、厳密にレビューの仕方が規定されている。ざっくりと集まって、ざっくりと(自称)熟練者が新人に対して評価を下すのではなく、レビューを行う品質管理担当がいて厳密に手順を決めておく。きっちりと行うには、IBM ぐらい大きな組織でないと難しいのだが、レビューする日を別に定めて、レビューを記録しながらやると同じような効果が得られる。
最大のポイントは、レビューをするものが事前にレビューをしておくことだ。レビューをするために長い時間取られるが、それだけの効果があるし、なによりも面と向かって言うのではなく、あらかじめ紙面に書いておくことで感情的にならずに済む。また、面と向かったときには「何を基準にして駄目なのか」を明確に言えるようにしておく必要がある。

「ピアレビュー」は、インスペクションよりも緩く、一般的なレビューよりも丁寧なものを示している。コードを前にしてピンポイントで進んでいくのではなく、頭から最後まで通してコードを読み進め、レビューをしていく。このローラー作戦を遂行することで、コードの品質が「標準的」になるのが、ピアレビューの特徴である。コードが標準的になると、他の人から読みやすくなるし、保守もしやすくなる。改修コードを入れるときにも、既にあるコードになじむように「補修」あるいは「補完」することによって、全体の質を同じに揃えることができる。

マイナス生産者を排除するためレビューを行え

一般的に、コードレビューは

  • コードの品質を上げる
  • 新人が作ったコードの間違いを直す

という目的で行うことが多いのだが、最近の新人プログラマはそれなりにプログラムコードを勉強しているし、30年前とは違ってプログラム言語も豊富になってきたので、極端に不味いコードが出てくることは少なくなっている…と思う。会社に属さなくなって10年以上経つので、ほんとうのところはどうなのか分からないのだが(いやいや、頭でっかちな新人っぽい人はちらほら見るので、それなりの「ぴよぴよコード」はあるとおもうのだけど C#のぴよぴよコードをなんとか使える形にするための2つの方法 | Moonmile Solutions Blog)。
と、その前に(自称)熟練プログラマが陥りがちな「クソコード」のほうが有害である。どんなところが有害かというと、

  • 「クソコード」を書いた人にしか分からない、クソ理論でコーディングされて、誰も理解できない。
  • 「クソコード」を書くために、クソみたいなライブラリを付随されていて保守性が悪い
  • 業務システムの場合、テスト費用、保守費用が別途かかってくるので、それを増大させるのは全て「クソコード」である。

という点があげられる。個人開発をしていたり、ずっとその会社でライブラリを作り続けていたりするのならばいいのだが、ある程度の大き目の業務システムになると、複数名が関わらざるを得ない。なので、良かれと思ってコードを改修していていても、それは「クソコード」になり得るし、実際に「クソコード」となってしまう。
よって、クソコードは「レガシーコード」よりも低位にある。逆に、先に書いた通り、クソコードは直せばふつうのコードになる。だから、「直さないクソ」な人がクソコードを生み出す≒マイナス生産者である、という話になるのだ。

「ピープルウェア」にある通り、マイナス生産者は清く排除するのがよろしい。あるいは隔離だ。その方法いくつか逆引きにも書いたし、どこかで検索すれば出てくるだろう。

自称熟練者のクソコードの例

自分の過去のコードを見れば、いくらでもクソコードが出てくる。なので、私の書いたクソコードを紹介しておこう。



static int hxx_number = 0; ... int HXX_get_NUMBER() { return hxx_number ; }

その昔、UnixにC++がなくてC言語しか使えなかったとき(まだg++はなかった)に、オブジェクト指向風にメソッドぽいものを作ったクソコード。呼び出し側は、HXX_get_NUMBER 関数で値を取るわけだが、いやいや面倒くさいし、中身が何か分からない。中規模程度であれば、hxx_number を直接参照したほうが可読性がよいだろう。
可読性がわるくなるので、コードを引き継いだ人が迷惑するクソコードだ。



struct DATA data ; for ( int i=0; i<2; i++ ) { ... data.array[i] = 0 ; }

構造体のデータを0で初期化する例だが、for文で2つまでか使わないのは無駄だ。arrayが可変ならば必要かもしれないけど、そもそも「2」を即値で置いているのが駄目だろう。素直に、array[0] = 0; array[1] = 0 ; で十分だし、C言語だから memset( &data, 0, sizeof(data)) と memset を使ってゼロリセットするほうがよい。



$h = $this->params['data']['Reservation']['CompletionTime']['hour']; $m = $this->params['data']['Reservation']['CompletionTime']['min']; if ( $this->params['data']['Reservation']['CompletionTime']['meridian'] == 'pm' ) { $h += 12 ; if ( $h == 24 ) { $h = 12; } } $CompletionTime = "$h:$m"; $h = $this->params['data']['Reservation']['StartTime']['hour']; $m = $this->params['data']['Reservation']['StartTime']['min']; if ( $this->params['data']['Reservation']['StartTime']['meridian'] == 'pm' ) { $h += 12 ; if ( $h == 24 ) { $h = 12; } } // 入庫時刻の補正 if ( $h == 12 && $m == 1 ) { $h = 0; } $StartTime = "$h:$m"; $CustomerBilling = $this->params['data']['Reservation']['CustomerBilling']; $y = $this->params['data']['Reservation']['AcceptanceDate']['year']; $m = $this->params['data']['Reservation']['AcceptanceDate']['month']; $d = $this->params['data']['Reservation']['AcceptanceDate']['day']; $AcceptanceDate = "$y-$m-$d"; $Remarks = $this->params['data']['Reservation']['Remarks']; $Scene = $this->params['data']['Reservation']['Scene'];

これは、PHPをよく知らないときに作ったコードなのだが、日時変換が駄目過ぎて泣けてくる。いわゆる「自称熟練者」がプログラム言語の流儀をよく調べずに、ひとつだけの金づちでなんとかしようとした悪い例である。WEB APIのGET呼び出しなので、今ならばJSON形式のGMT表記で一発で変換できるパターンである。実際、ここは結合テストをすり抜けてしまって運用時にバグが発覚したものである。つまり、クソコードが被害を出してしまった好例?だ。

個人開発や少人数開発であれば、どんなトリッキーなコードを書いてもいいけど(自分がわかるならば)、小規模であっても製品にする場合や複数名でコードを弄るときには、できるだけ「平坦なコード」を書くのがよい。それはコードの標準化とか、共通ライブラリの利用とかいう上辺の話だけではなくて、コードの質を揃えて資産化するという意味でだ。本格的にはMDAとかもうちょっと模索が必要なところだけど。

カテゴリー: 開発 | クソコードとマイナス生産性の関係 はコメントを受け付けていません

ASP.NET MVC から Redmine+MySQL を操作する

以前 独自にポートフォワード作成し、ラズパイの MySQL へ LINQ で接続する で SSH ポートフォワードを使って MySQL に接続したのだが、これをもうちょっと活用すると、.NET Core で作成した ASP.NET MVC から Redmine の MySQL を閲覧できるようになる。

主な手順

  1. Redmine テーブルの EF を用意する
  2. ASP.NET MVC のひな形を .NET Core で作る
  3. ASP.NET MVC に EF をコピーする
  4. ASP.NET MVC のスキャフォード機能を使って、Controller と 各種の View を自動生成する。
  5. 動作時は、SSH ポートフォワードで、実運用の Redmine + MySQL に接続させる

ASP.NET MVC で利用する EF は直接 MySQL から作成してもよいのだけど、何かと面倒なので手作業で作るか、あらかじめ SQL Server で作ったものを用意する。

EF のモデルクラスを作成する

Visual Studio から EF を作成するほうが楽なので、”ADO.NET Entity Data Model” を追加して、SQL Server 上に作成した Redmine のテーブルから、モデルクラスを作成する。

必要なのは、RedmineModel.tt 配下にある *.cs のクラスで、たとえばチケットを管理している issues クラスはこんな感じになる。




public partial class issues { public int id { get; set; } public int tracker_id { get; set; } public int project_id { get; set; } public string subject { get; set; } public string description { get; set; } public Nullable<System.DateTime> due_date { get; set; } public Nullable<int> category_id { get; set; } public int status_id { get; set; } public Nullable<int> assigned_to_id { get; set; } public int priority_id { get; set; } public Nullable<int> fixed_version_id { get; set; } public int author_id { get; set; } public int lock_version { get; set; } public Nullable<System.DateTime> created_on { get; set; } public Nullable<System.DateTime> updated_on { get; set; } public Nullable<System.DateTime> start_date { get; set; } public int done_ratio { get; set; } public Nullable<double> estimated_hours { get; set; } public Nullable<int> parent_id { get; set; } public Nullable<int> root_id { get; set; } public Nullable<int> lft { get; set; } public Nullable<int> rgt { get; set; } public bool is_private { get; set; } public Nullable<System.DateTime> closed_on { get; set; } }

ASP.NET MVC Core のひな形を作る

コマンドラインから、以下のように作成する。




dotnet new mvc -n redmine.web

VSCode を使ってもよいのだけど、スキャフォード機能を使う場合、Visual Studio 2019 を使ったほうがよいので、*.sln ファイルも作っておく。

EF のモデルクラスをコピーする

先のプロジェクトで作成したモデルクラスを ASP.NET MVC の Models フォルダーにコピーする。

注意しないといけないのは、Redmine では tinyint(1) をフラグ代わりに使っているので、これを bool 型で扱うか? int 型で扱うかという問題がある。本来はチェックボックスにしたいので bool 型にしたいところなのだが、ひとまず int 型にしておく。




public partial class issues { public int id { get; set; } public int tracker_id { get; set; } public int project_id { get; set; } public string subject { get; set; } public string description { get; set; } public Nullable<System.DateTime> due_date { get; set; } public Nullable<int> category_id { get; set; } public int status_id { get; set; } public Nullable<int> assigned_to_id { get; set; } public int priority_id { get; set; } public Nullable<int> fixed_version_id { get; set; } public int author_id { get; set; } public int lock_version { get; set; } public Nullable<System.DateTime> created_on { get; set; } public Nullable<System.DateTime> updated_on { get; set; } public Nullable<System.DateTime> start_date { get; set; } public int done_ratio { get; set; } public Nullable<double> estimated_hours { get; set; } public Nullable<int> parent_id { get; set; } public Nullable<int> root_id { get; set; } public Nullable<int> lft { get; set; } public Nullable<int> rgt { get; set; } public int is_private { get; set; } public Nullable<System.DateTime> closed_on { get; set; } }

データベースの接続情報やデータアクセスを LINQ で使えるようにするため、DbContext を継承した RedmineContext クラスを作る。




namespace redmine.web.Data { public class RedmineContext : DbContext { public RedmineContext(DbContextOptions<RedmineContext> options) : base(options) { } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); } } }

NuGetで MySql.Data.EntityFrameworkCore を入れた後、
Startup.cs を開いて、MySQL への接続文字列を ConfigureServices メソッド内に書く。




public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddDbContext<redmine.web.Data.RedmineContext>(options => options.UseMySQL(@"server=localhost;user id=redmine;password=redmine;database=redmine;port=3306;sslmode=None"); }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); }

スキャフォード機能で Controller と View を作る

ひとまずローカルの MySQLに接続して確認する

Visual Studio からデバッグ実行すると、デバッグ用のコマンドラインが表示されて、localhost:5000 へ接続できるようになる。

https://localhost:5001/Issues のようにアクセスすると、MySQL の中身が表示される。Issue の一覧になるので、すべてのプロジェクトの Issue がまとめて表示されてしまうが、これは独自にルーティングを使ってプロジェクト内のみ表示させればよい。

SSH ポートフォワードを使う

options.UseMySQL で記述した接続先を SSH ポートフォワード用に変更すればよいのだが、ひとまず SSH で繋がるかどうかを MySQL Workbench で接続してみる

Connection Method を “Standard TCP/IP over SSH” に変更して SSH 経由で接続できることを確認しておく。

その後で、Ubuntu on Windows で、localhost の 190000番を openccpm.com 内部にある MySQL(3306) に通すようにする。




ssh -L 19000:localhost:3306 masuda@openccpm.com

Setup.cs の該当箇所を書き換える




services.AddDbContext<redmine.web.Data.RedmineContext>(options => { options.UseMySQL(@"server=localhost;user id=redmine;password=redmine;database=redmine;port=19000;sslmode=None"); });

Visual Studio 2019 からデバッグ実行できれば ok.

カテゴリー: 開発 | ASP.NET MVC から Redmine+MySQL を操作する はコメントを受け付けていません

京都アニメーションの被害者に哀悼を込めて

実は、アニメ制作の進行とソフトウェア開発のマネジメントの話 | Moonmile Solutions Blog の追記をしようと思った矢先に、京都アニメーションの放火事件があり、心理的に続きが書きづらくなってしまった。ので、1時間ほど哀悼を込めて綴っておこう。

プログラミングのバックグラウンド映像にアニメを流している位に「アニメファン」ではあるのだが、実は京アニ「だけ」に思い入れがある訳ではなく、どちらかといればそれ以前のアートランドあたりからスタートしている「アニメマニア」であったりする。

先に書いた通り、アニメーション制作の進行係にはプロジェクト管理として興味を持っていて、その周辺の技術(動画とか作画とか諸々)はセルアニメの頃の知識で止まっていたりするので、実は「なつぞら」あたりのほうがよく知っていたりする。知識としてだけだが。

「なつぞら」と「SHIROBAKO」の進行を比べたり、三鷹のジブリ美術館に行ってみたり、ディズニーの絵コンテを買ってみたりするのは、趣味というのもあるけれど、絵を描くことと、プログラムを書くことは「手を動かす」という意味で同じジャンルにあるだろう(あるとしたい)という気持ちがある。まあ、大学の頃に漫研に居たというのもあるし、なんとなく絵を描き続けられなくてプログラムに逃げてしまった?という経緯もあって、いまだにちょっとだけ「憧れ」がある。とはいえ、50歳も過ぎれば、憧れは憧れのままにしておく手法もあり、どこに時間を費やすかといえば、プログラム&文章に費やしたりしてしまう。技術書をちまちまと書きおろしているのはそのためでもある。

いまさら、アニメーションが人々の心に影響が…という話でもないと思うのだが、「けいおん!」がアニメマニアではない高校生に大きな影響を及ぼしたのは間違いないらしい。間違いないらしいというのは、実は「けいおん!」はリアルタイムで見てはいなくて、時期的に言えば就職して暫くテレビを見てなかった頃だったので、後からレンタル屋さんで借りてきてみている。私の高校時代といえば、「うる星やつら」とか「ガンダム」とか「みゆき」とかが午後7時台に放映されたいた頃なので、アニメを見るのは子供でもありつつ高校生でもありつつ、ちょっとマニアックな大学生でもあった時代だった。「おたく」がテレビに認知されつつも、宮崎事件があった頃でもある。そこから、だいぶん下ったところに「けいおん!」があって、放映後にしばらくたった時期ではあるけれど「けいおん!」が放映されてときの同時代性というのものは、当時の高校生にとって非常に大きかったことを伺い知れる。

いわゆる、いじめが陰湿化してしまった後に、学校への不登校とクラスになじめない自分とのはざまの中で、疑似的にではあっても「けいおん!」が高校生活を体験させてくれたというブログ記事がちらほらみられるのが興味深い。それと同時に「けいおん!」を見て軽音を始めた高校生もいて、このアニメが影響を与えた層というものは、従来のアニメファンだけではなくく、一般的に理解されるであろう(おそらく当時の親の層からも理解されたかもしれない)層までも「アニメ」を広げたという感じがしている。おそらく、かつて「金八先生」をクラスで話題にしても恥ずかしくなかったぐらいに、「けいおん!」をクラスの友達と話してもハズか少なかった位にも浸透したのではないだろうか?という気がしている。

同じ現象は、ジブリアニメでも起きているわけで「天空の城ラピュタ」を誰もが知っているように、「ジョジョの不思議な冒険」を知っているように、そこの登用人物や科白を使っても恥ずかしくはない程度に「アニメ」は浸透したのだと思う。

実は京アニ自体に特別な思い入れはないのだけど、あらためて「響け!ユーフォニアム」や「たまこまーけっと」を見返すと、節に復活を願う。当然のことながら、「日常」と「らき☆すた」は、作業用BGMの定番である。

ちなみに、仕事中にアニメをBGMで流しておくのは、

  • タイマー替わり
  • 過集中を避ける

の2つの意味合いがある。タイマーのほうは、艦これでもよいのだが、TVアニメの場合24,5分単位で終わるので時間が測りやすい。よって、映画ではなくてTVアニメの各話を流し続ける。

あと、プログラミング自体に集中しすぎると最終的には効率が悪い。職業柄、大量なサンプルコードを書くことが多いので適度な休憩が必要になる。その休憩のタイミングをTVアニメの各話に挟めるというメリットがある。また、過度に集中するよりも、ある程度気を散らしたほうが疲れないというのもある。このあたり、ラジオが良い人もあれば、音楽が良い人もいる。あるいは、静かなほうが効率が良い場合もある。それぞれだ。

カテゴリー: 雑談 | 京都アニメーションの被害者に哀悼を込めて はコメントを受け付けていません

チケット駆動のチケット数を概算見積もりする

うまいチケット駆動でのプロジェクト管理を見たのは、20年ほど前のアジャイル協議会のTLだったと思う。ライントレーサーのロボットを作るのに、チケット駆動とPSP(パーソナル・ソフトウェア・プロセス)を組み合わせて、Excelで進捗チェックをするひとりプロジェクトだ。

複数名のチケット駆動というと、壁に付箋を貼るか、チケット駆動のツール(Redmineとか、Backlogとか)を使うことが多いのだろうが、何もツールの全ての機能を使わなくてもよい。というか、全ての機能を使おうとすると何かしらうまくいかないことが多い。最大公約数で機能を詰め込んであるのでオーバースペックなのだ。

で、

チケット駆動の最大の難点なのは、最終的にプロジェクトがいつ終わるのか?が見えないことだ。スクラムのバックログ方式も同じだけど、アジャイル方式で開発を行うとどうも終わりが見えなくて困る。当然のことながら終わりが見えないからアジャイル方式を使うのだけど、じゃあ、いつ終わるのか?という概算的な時間見積もり≒納期はどのあたりに定めればよいのだろうか?

プロジェクトが終わった時点から見れば、チケット駆動であれスクラムのバックログであれ、消化したチケット数/タスク数は決定的である。当たり前だ。プロジェクトは完成して終わったのだから、目の前にできたものに費やした時間は「記録さえしてあれば」計算できることになる。

なので、実はチケット駆動でも概算的な規模見積もりは可能であったりする。

ソフトウェア見積り 人月の暗黙知を解き明かす
https://www.amazon.co.jp/dp/B00KR96M6K

FP とかも含めて、いろいろと試してきたけれど、私の場合マコネル氏のこの本に同意する。マコネル氏はこの前後に計量的にタスクの時間見積もりをするツールを売っている(今はなくなっている…はず)けど、細かなタスクあるいはチケットを確率的に求めなくても、もっと概算で十分だろう、ってところに今の私は落ち着いている。年初あたりに確率統計を使って計算する試みをしてみたのだが、正確なシミュレーションをしたとしても途中のタスクの増減の影響が大きいのでプロジェクトを進める間は、その正確さは重要ではなくなってくる。大規模プロジェクトの計画段階(予算と期間を決めるという意味で)は重要になっているのだが、高々数百万のプロジェクト、期間が4か月程度のものに最初の正確さを求めても意味がないことが多い。それよりも、プロジェクトを運営する上での「やりくり=本当の意味でのマネジメント」を考えたほうが安全にプロジェクトを終わらせることが可能である。

期間を概算する

会社にいた頃は、機能を洗い出して機能ごとに時間単位あるいは日単位で見積もりを出して積み上げていたものだが、今はやっていない。超概算見積もりで人月を計算した後に、Excelで機能に割り振る、あるいはチケットに割り振ってしまう。

正確に言えば、

  • 超概算見積もりで、人月で算出する
  • 顧客の懐具合で、予算を算出する
  • 機能の数で、人月で算出する

と3つの人月を出す。大体において、派遣なり契約社員なり社員なりに支払うお金は「月単位」になることが多いので、週単位で出してもあまり意味はない。細かい単位でずれを修正しても仕方がないし、そもそもスポンサー≒お客の懐具合に予算を合わせないといけない。

ならば、月単位の単価をベースにして、超概算してしまうのがよい。細かいところはプロジェクトを進めながら合わせていく。

チケット数を概算する

期間と人数が決まれば、おのずと動いている時間が決まってくる。当然だが、20日/月、160h/月で考える。22日で計算すると祝日や別な割り込みの分を勘定できないし、そもそも計算が面倒くさい。2日≒10%程度の余裕が最初からあるほうがよい。

1日で消化するチケットする数は3つ程度と決めておく。期間が長ければ1日1チケットでもよい。しかし、1週間1チケットみたいなのは駄目。スクラムのスプリント期間に近くなってしまうし、1つのチケットが1日よりも長くなる場合は、チケットを分割するか、複数のチケットに同じタイトルで割り当てたほうが良い。いつまでも進行中のまま終わらないからだ。

チケットの作業時間の粒をできるだけ揃えておくのがよい。そうするとバーンダウンチャートで先行きが見えやすくなる。

プロジェクトの開発期間と人数が分かれば、全体のチケット数が概算できる。

既知の開発をチケットに割り振る

プロジェクトの開始時点(規模見積もりをしている時点)で、ある程度の既知の開発チケットがあるはずだ。そうじゃないと、どうやって規模/金額見積もりをしたんだ?という話になる。大まかな機能と、おおまかな時間(1日なのか3日なのか)を適当に決めて、チケットに割り当てる。

見積もったチケット数をすべて書き込む必要はない。いや、むしろ書き込んではいけない。プロジェクト開始時のチケット数はあとから増える可能性が大なので、空白のチケットを残しておく。これは保険だ。あるいは、未知なるチケットだ。

あるいは、チケットが全て埋まってしまう状態になったら、それはそもそもが全体のチケット数が足りない → プロジェクト期間が短すぎる、ことを意味している。先の超概算に戻って、再確認する。

残チケットを消化する

プロジェクトが進む中で、さまざまな要望が増えていくる。チケットを消化しつつも、チケットが増えてしまうのがチケット駆動の難点ではあるが、あらかじめ全体のチケット数が分かっていれば≒概算してあれば、プロジェクトが進んだとして大幅にチケット数が増えることはないだろう(それでも予想を超えて増えてしまうのが常なんだけど)。

私の場合、チケットの消化記録は、Excel か Redmine を使っている。先に書いた通り、Redmine を全ての機能を使うと多機能すぎてオーバースペックになる。なので、

  • チケットは新規/進行中/終了しか使わない。進捗率は使わない
  • 作業時間はいらない
  • ガントチャートも使わない
  • 優先度も使わない
  • チケットはあらかじめ概算時点で大量に置いておく。順々に消化する。

という非常にシンプルな使い方をする。

ひとりプロジェクトだと Excel ベースで十分なのだけど、2名以上だとファイル競合がでるので Redmine を使う。最初に大量のチケットを投入する必要があるので、現在ツールを作って投入中。ClosedXML を使って、Excel へのマージを作っている。これ、Redmine の CSV のインポート機能でいいんじゃないだろうか?と気づいたのだがどうだろう。あとで試してみる。

追記

CSV からインポートすると常に新規になるので、二重にチケットができてしまう。更新版を自作しないと駄目。初期チケットの大量投入は CSV を使うと便利。

カテゴリー: プロジェクト管理 | チケット駆動のチケット数を概算見積もりする はコメントを受け付けていません

アニメ制作の進行とソフトウェア開発のマネジメントの話

結論から言えば、非常に似ている…というのは10年以上前から思っているのだけど、アニメ制作会社の「進行」が具体的にどんな仕事をこなしているのかわからなかったのだが、TVアニメ「SHIROBAKO」公式サイト を見るとよくわかる。というか、ソフトウェア開発のマネジメント(主にアジャイル開発のほう)で、どのようなプランニングと調節/折衝を行えばよいのかが、よく描かれている。

プランニング

何を作るのかが決まると、概略のスケジュールを立てる。スケジュールを立てるには、あらかじめ必要なタスクが必要になる。

image

ソフトウェア開発は複雑怪奇なのでプランニングがうまくいかないという話を昔からよく聞くが、実は、大まかな単位(要件定義、設計、実装、試験などの古来のウォーターフォール開発の工程)と関わる人はあまり変わらない。

だから、超概算見積もり的には、ざっくりと開発者の人数と人月を出してしまって、超概算のスケジュールを立てる。最初のプランにあうかどうかは分からないし、実際には合わない。しかし、プロジェクトが進めば「確実性が増す」ので、その都度プランを変えていく。

どちらのせよ、最初にタスクの総量の概算を出す必要がある。

変更に対応する

それぞれのメンバの思惑があって、ステークホルダー(利害関係者)があるので、横槍なり変更がはいる。

image

過度な変更を排除するのもマネジメント手法のひとつ(スクラムマスターの役割とか)だけど、やむ得ない変更の場合は、現在の進行を変える必要がでてくる。

image

現場の進行全体を止める必要はなく、進められる部分は勧めてしまう。いわゆるタスクを並行に動かして依存関係をチェックする。

大抵のマネージャはガントチャートにそれを求めてしまうが、変更しにくいガントチャートのソフトを使うことによって、現場の進行を変更できないというドツボにはまってしまう。なので、ガントチャートは過信せず、単なるチケットの羅列でもよい場合が多い。

残件を確認する

全てのタスクをこなしたら終了になるとすれば、すべてのタスクがどれほどあるのか?を知っておかないといけない。

image

トラブルが発生したら、タスクが増える。何かを整理したらタスクが減る。プロジェクト開始時に未知だった部分も、プロジェクトが進むと既知になってくる。タスクを抽出して、消化したタスクを消していけば、残りのタスクが分かる。

そうすると、あとどれだけのタスクをこなせばプロジェクトが終わるのかが判明する。

なので、マネージャはあとどれだけ仕事をこなしたら、プロジェクトが終わるのか?をバーンダウンチャートなどを使いながら常に頭に入れておく必要がある。

合意形成

ソフトウェア開発において、顧客との合意形成(場合によってはメンバに対しても)することは、営業の範疇なのかマネジメントの範疇なのかが問われるけど、単純な契約書だけでは済まないものが、合意形成である。

image

理不尽な要求ははねのけるのだけど、どうしても受け入れざるを得ない要求変更というものがある。そこは、プロジェクト予算とか保険とかマージンとかで賄うものなのだけど(あるいは、工数単位で請求するとか)、納期が変わらないというパターンが一番面倒である。

これも、日本のソフトウェア開発の事情と、欧米の開発事情、中国や東南アジアでの開発事情もあるので、それぞれの文化における「合意形成」のラインを引いておく必要がある。こちらの常識はあちらでは通じないし、同時にあちらの常識をこちらに押し通してくる場合もある。

合意形成の失敗によって、被害をこうむりそうな場合、いくつかの対処のパターンがある。そう、対処が数パターンあることを覚えておくとよい。

  • 違約金を払ってでも撤退する
  • 自腹で、予算を変えても、変更を飲み込む
  • 予算を変更せずに、できればスケジュール(納期)を変更する。
  • 納期は変えずに、追加費用を請求する
  • 未作成の部分と追加要求を交換する
  • 変更要求を、納期後に飲み込む
  • 変更要求を、受け付けない。

どのパターンを取ってもよいのだが、いちばんやってはいけないのは「変更要求をのみ込んで、予算も納期も変えない。機能も変えない」というパターンだ。これだと、いくらでも変更を突っ込まれてしまう。何かを詰め込めば、何がはみ出るというパターンに必ずしておく。

ひとまず、ざっと箇条書き的に。全話見たら、また追記しましょうか。

カテゴリー: プロジェクト管理 | アニメ制作の進行とソフトウェア開発のマネジメントの話 はコメントを受け付けていません

少しシビアにプロジェクトの金額見積もりをしてみよう

「シビアに」というのは、社員ではなくても、派遣/契約/準契約などなど、なんでもいいんだが「社員以外」の人がプロジェクトに入って開発を行う場合を考える。

例えば、

  • マネージャ1名
  • 派遣3名
  • 開発期間が3か月

というパターンを考えてみる。一見、プロジェクトの費用的には、マネージャと派遣社員の4名だけが関わっているように見えるが、実は、

  • 契約を取ってきた営業
  • 派遣している派遣会社

がある。他にも会社の間接部門だとか設備費用とか諸々があるけど、ひとまず人としてかかわってくる2名(派遣会社は組織だけど)も加える。

こうなると、プロジェクトの予算を、マネージャ、派遣3名、営業、派遣会社の6名で分け合うことになる。

割合を決める

営業費用、マネジメント費用、派遣会社の斡旋費用を割合で決定する。

  • 営業費用 = プロジェクト費用の20%
  • マネジメント = プロジェクト費用の10%
  • 斡旋料 = 派遣単価の20%

この割合が、だいたい現実的なところ。大きなプロジェクトであればマネージャがべったりつくこともできるけど、5人以下の小規模のプロジェクトの場合はフルという訳にはいかない。ので、費用的に10%程度にしてある。

営業費用、歩合制も含めて20%と考えてみる。営業の場合、足で稼がないと駄目な場合もあれば引き合いの場合もあって色々なので、実際は結構かかっている。

仮の予算を決める

最初に、概算予算を決める。

3か月のプロジェクトなので、

  • 派遣社員の単価を50万円として、3×3人月で 450万円
  • 営業費用が2割として、概算で90万円
  • マネジメントが1割として、概算で50万円

合計で600万円程度ではないか?と初期予算を決める。

image

諸々計算すると、630万円になるので、ちょっとたりない。なので、調節する。

image

概算を、650万円にすると、合計が似た感じになるので、この予算でいけることがわかる。

対外的には、派遣社員の3名しか見えないので、お客に示す単価は 72万円/月程度になる。

割合を見る

総金額の取り分を円グラフにしてみると、こんな感じになる。

image

総取り分の半分位を、営業職、マネージャ、派遣会社が取っていることになる。この割合が妥当かどうかは、各自が判断することだが、少なくともアニメ業界よりもマシだけど、それに近い感じになっていることが分かる。

派遣Aのみに外注する

ためしに、3人月のプロジェクトを派遣Aのひとりだけに外注してみる。

image

トータルだと、220万円ぐらいでいけることがわかる。

image

同じように円グラフにすると、割合はこんな感じになる。派遣Aの手取りが、妥当になるように調節していくと、人月単位での総予算が計算できる(あまり、手取りが低いと、家賃と税金で精いっぱいになってしまうので、生活苦になりITの勉強ができない)。これを、規模見積もりの概算と比較していくと、予算が妥当かどうかがわかる。

ツール

Excel で作った「超概算金額見積もりツール」はこちら

https://1drv.ms/x/s!AmXmBbuizQkXgpt507RUtRIS3BY9yw

カテゴリー: プロジェクト管理 | 少しシビアにプロジェクトの金額見積もりをしてみよう はコメントを受け付けていません

VB6用ステップカウンタを使い、移行見積もりする

実にベタなツールではあるけれど、VB6 用のステップカウンタを作ったので github moonmile/vb6check: VB6移行の規模見積もりツール に公開しておきます。

使い方

使い方は簡単で、.vbp ファイルか、フォルダを指定して VB6プロジェクトのファイル(.bas, *.frm)を拾ってきてステップカウントします。

こんな風に、全コード/コードのみ/空行抜きで取得できるので、Excel に出力してから、フィルターを掛けたり sum を使ってカウントすれば ok です。

  • 全コードは、単なるファイルの行数
  • コードのみは、*.frm の先頭にあるデザイン部分を抜いた行数
  • 空白抜きは、所謂コメントや空行を抜いたときの行数

これ自体で何ができるという訳ではないけれど、つまりは VB6 から C# へ移行するときのコード調査として使います。

使いどころ

ここからが本格的な使い方で、本来の目的は VB6 から C# へ移行するときに「どの程度の規模が発生するのか?」の目安にする。

現システムが VB6 を使っていると、いよいよ Windows 7 の期限切れが発生してきて、Windows 10 に OS を移行しないといけない。しかし、既存の VB6 の業務ツールがあって簡単には移行できない。予算が潤沢にあれば、えいっと業務改善も含めて Windows 10 へ移行&クラウド利用、などに変えてしまう方法もあるのだが、なんらかの理由があってそれができない。

消極的な理由としては「予算がない」のだろうけど、実は、ある程度予算があったとしても「積極的に移行しない」理由というのが存在する。

  • 現状のシステムが、会社の業務に最適化されていてシステム自体を変更する必要がない

というパターンだ。IT システムの場合、現在の AI やらクラウドやらで、最新技術を使ったほうがよいような気がしていたのでが、実はそうでもないことが分かってきた。業務をするときの「慣れ」ではないか?という理由を自分はつけて来たが、もっと良い例を最近思いついた。

明治や大正時代の鋳物で作られたような工場の機械が、現在でも丁寧に使われていると同じ理由ではないだろうか?

IT 自体はどんどん進化するし、モノの工作機械もどんどん進化する。けれども、作られるもの自体は必ずしも変化するとは限らない(伝統工芸とか昔から変わらない製品とか)。だから、昔のよいものを引き続き作る場合には、昔と同じ方法を使う必要があるし、プラス進化したものも必要になる。だから、古い工場で鋳物の工作機械が動いていて、最新のWEBやスマホで受注ができたり、工程をAIで管理/観測できたりする。そういう新旧混在した場合もある。

なので、まるごと最新にする必要はないし、場合によっては古いものをそのまま使っても良い。RPA もそのひとつの解答だろう。

無料RPAで「ソリティア」に挑んでみた 業務自動化でゲームも自動化できるか (1/2) – ITmedia NEWS https://www.itmedia.co.jp/news/articles/1903/03/news033.html

そんな訳で、RPA とは別に、手作業で VB6 から C#/WPF/MVVM に移行するパターンをいくつか実践している。

VB6 から C#/WPF への移行パターン

VB6 で作成した業務システムの移行先として、WEB アプリがあるのだけど、WEB に移行できない&デスクトップアプリである必要があるパターンがある。

  • 他のデスクトップアプリと連携している
  • 印刷ものが多い
  • 複数の画面を同時に開いて作業することが多い
  • キーレスポンスが早いことが求められる

最近は、JavaScript/Node.js/React のパターンで、画面のレスポンスは早くなったのだが、問題は「VB6 時代のベタな UI をブラウザ上で実現するのが難しい」ってこともある。UI を変えない、Look & feel を変えない場合は、ベタなデスクトップアプリのほうが忠実に作れたりする。

これは以前書いたかもしれないが、業務 VB6 にはいくつかの要素があって、

  • データベース接続(ODBC, RDO, ADO)がある。
  • 帳票出力(クリスタルレポート、スプレッドシート)がある
  • データグリッドを使ったリスト表示がある
  • 3Dコントロールのボタンがついている
  • エンターキーによる項目移動が実装されている
  • 数値や文字制限などが独自実装されている

このあたりを、WPF/MVVM で実装する。

  • データベースは EF/LINQ を使う
  • 帳票出力は Excel 出力に切り替える、あるいはクリスタルレポート
  • グリッド表示は DataGrid を使い、データバインドする
  • 3Dコントロールは、通常のボタンに戻す
  • エンターキー移動は、独自ライブラリを作った
  • 数値、文字制限は、コンバーターで実装可能

というパターンに切り替える。このあたり、VB6 のときはデータベース接続やテキストボックス内の操作でコードが肥大していたが、C# の MVVM パターンを使うことにより、1/4 から 1/3 程度のコード量に減らすことができる。

このあたりの話は後日。

カテゴリー: 開発 | VB6用ステップカウンタを使い、移行見積もりする はコメントを受け付けていません

Laravel で wordpress を読み込む(自前MVC)

ひとまず、Laravel に慣れるために MVC パターンを直接扱ってみる。先に InfyOm を使って自動作成してみたのだが、エラーが頻発してよくわからん。ので、そういうときは手作業でやってみるに限る。

目的としては、

  • wordpress のような既存のデータベースを読み込む
  • マスター管理のような CRUD ベースのページを自動作成する
  • 場合によっては、CRUD な Web API も自動生成する。
  • 場合によっては、クライアントから呼び出すコード(C#, nodejsとか)も生成してしまう。

まずは、xampp で wordpress をインストールして、mysql 部分だけアクセス可能にしておく。

composer でインストール&動作確認

適当なフォルダーを作成して、Laravel をインストール


composer create-project --prefer-dist laravel/laravel blog

データベースの設定

/.env を修正。wordpress のデータベースを読み込ませる。


DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=wordpress DB_USERNAME=wordpress DB_PASSWORD=wordpress

リスト表示を作成

いわゆるトップにアクセスして、一覧を表示させる。http://localhost:8000/WpPost/ にアクセスする。wordpress の記事は wp_posts にあるのだが、これを Model クラスでは WpPost として使う。

Model の作成

Model クラスを作る。対象のテーブル名を記述していないが、この手の MVC パターンの命名規約に

  • テーブル名は複数形、モデルクラスは単数形
  • モデルクラスは、キャメルパターンが多い
  • テーブル名はスネークパターンが多い
  • 「_」が単語の区切りになる。

ということで、自動的に WpPost から wp_posts を推測させている。wordpress の場合プレフィックスは「wp_」とは限らないので、このあたりは明示的にテーブル名を指定したほうがよいかも。


php artisan make:model Models/WpPost

/app/Models/wp_posts.php が生成される


<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class WpPost extends Model { // }

Model クラスの中身が空っぽで不安になるが、これで ORM されている。ただし、条件があって、

  • プライマリーキーは「id」の int 型である。
  • 作成日時に「create_on」を使う。
  • 更新日時に「update_on」を使う。

が必要になる。これを満たすテーブルの場合はそのまま使えるし、新規にテーブルを作るときはこの規約に合わせるほうが便利(テーブルの複数形もあわせて)。

ただ、今回は既存の wordpress のテーブルを使うので、この条件が少し違っている。これは後で修正することになる。

Controller の作成

コントローラーは、モデル名 + 「Controller」となる。


php artisan make:controller WpPostController

/app/Http/Controllers/WpPostController.php が生成される


<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class WpPostController extends Controller { // }

これも中身が空っぽで不安になるのだが、ここは手作業で作ることになる。別途、自動生成することもできるのだけど、今回は手動で作ってみる。

リスト表示する index アクションだけ追加する。


<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Models\WpPost; class WpPostController extends Controller { public function index() { $items = WpPost::all(); return view('WpPost.index', ['items' => $items]); } }

データベースから全件拾ってくる all() を使うのと、ビューに渡すために items で参照できるようにしておく。

  • ASP.NET の EF のように、ent.WpPost.ToList() な感じで取得
  • ViewBag( “items” ) = items のようなもの

View を作成する

/resources/views/layouts/app.blade.php を作成する。いわゆる
全ページから参照するテンプレートで、@extends(‘layouts.app’) で参照できるようにする。


<!DOCTYPE html> <html> <head> <title> @yield('title') </title> </head> <body> <h1>@yield('title')</h1> <div class="content-wrapper"> @yield('content') </div> </body> </html>

各ページから入れ込む部分は「@yield()」で書いておく。各ページからは「@section()」で参照する。本来は CSS とか JS とかの読み込みが書いてるのだが、今回はシンプルに書いておく。

一覧表を表示するページはこっち。/resources/views/WpPost/index.blade.php を新規作成する。


@extends('layouts.app') @section('title','WpPost.index') @section('content') <table> <thead> <td>ID</td> <td>post_title</td> <td>post_date</td> </thead> <tbody> @foreach( $items as $item ) <tr> <td>{{ $item->ID }}</td> <td>{{ $item->post_title }}</td> <td>{{ $item->post_date }}</td> </tr> @endforeach </tbody> </table> @endsection

レイアウトのファイルを「@extends(‘layouts.app’)」で参照している。あと、コントローラーから渡された、$items を使ってリスト表示する。

ルート情報を追加

/routes/web.php に追加する


Route::get('WpPost', 'WpPostController@index');

ブラウザから呼び出すと、どこが呼び出されるかを記述する。「http://localhost:8000/WpPost」とすると、WpPostController クラスの index メソッドが呼び出される。

ブラウザで表示する.

簡易 HTTP サーバーを動かして、呼び出してみる。


php artisan serve

作成順序

MVC パターンを使うと、書き込むコードや設定があちこち散るのが大変なのだけど、手順を作っておくと便利だったりする。

  1. モデルクラスを作る /app/Models/wp_posts.php
  2. コントローラーを作る /app/Http/Controllers/WpPostController.php
  3. アクションを作る WpPostController::index
  4. ビューを作る /resources/views/WpPost/index.blade.php
  5. ルート情報を作る /routes/web.php

Laravel の場合は、Model -> Controller -> View -> ルート情報(web.php) の順で作っていくとわかりやすい。

詳細表示を作成

id を指定して詳細表示をするページを作る。http://localhost:8000/WpPost/1 のように ID を指定してページを開く。

Controller の作成

/app/Http/Controllers/WpPostController.php を編集


<?php class WpPostController extends Controller { ... public function show($id) { $item = WpPost::find($id); return view('WpPost.show', ['item' => $item]); } }

プライマリ―キーの id での検索は find() を使う。

View を作成する

/resources/views/WpPost/show.blade.php を新規作成する。


@extends('layouts.app') @section('title','WpPost.show') @section('content') <table> <tr> <td>ID</td> <td> {{ $item->ID }}</td> </tr> <tr> <td>post_title</td> <td> {{ $item->post_title }}</td> </tr> <tr> <td>post_date</td> <td> {{ $item->post_date }}</td> </tr> </table> @endsection

ルート情報を追加

/routes/web.php に追加する


Route::get('WpPost/{id}', 'WpPostController@show');

ブラウザで表示する.

http://localhost:8000/WpPost/1

MVC パターンの関係はこうなっている。

Create ページを追加

新規作成のページを作る。

  • 新規入力をする create
  • データを登録する store

に分かれる

Model を修正

/app/Models/wp_posts.php を修正

  • ID が自動インクリメントなので除外する
  • create_at, update_at を除外する
  • デフォルト値を補う

class WpPost extends Model { // IDは自動インクリメント protected $guarded = array('ID'); // 作成日、更新日はなし const CREATED_AT = null; const UPDATED_AT = null; public function __construct() { // insert 時にデフォルト値を補っておく $this->post_excerpt = ''; $this->to_ping = ''; $this->pinged = ''; $this->post_content_filtered = ''; } }

Controller を追加

/app/Http/Controllers/WpPostController.php を編集


class WpPostController extends Controller { ... /// 新規作成(入力) public function create() { $item = new WpPost(); return view('WpPost.create', ['item' => $item]); } /// 新規入力(登録) public function store( Request $request ) { $item = new WpPost(); $form = $request->all(); unset($form['_token']); $item->fill($form); // 現在時刻を入れておく $item->post_date = date('Y-m-d H:i:s'); $item->save(); return redirect('/WpPost'); } }

View を作成する

/resources/views/WpPost/create.blade.php を新規作成する。


@extends('layouts.app') @section('title','WpPost.create') @section('content') <form action="/WpPost/create" method="post" > {{ csrf_field() }} <table> <tr> <td>post_title</td> <td><input type="text" name="post_title" value="{{ $item->post_title }}" /></td> </tr> <tr> <td>post_content</td> <td><input type="text" name="post_content" value="{{ $item->post_content }}" /></td> </tr> </table> <input type="submit" /> </form> @endsection

ルート情報を追加

/routes/web.php に追加する

  • create と store は、show の前に入れる

Route::get('WpPost', 'WpPostController@index'); Route::get('WpPost/create', 'WpPostController@create'); Route::post('WpPost/create', 'WpPostController@store'); Route::get('WpPost/{id}', 'WpPostController@show');

ブラウザで表示する.

http://localhost:8000/WpPost/create

エラーがなければ、一覧へ戻る

編集画面を作る

  • 更新情報を入力する画面
  • 更新処理をする

の2つのアクションの分かれる

Model の修正

/app/Models/wp_posts.php を修正

  • プライマリ―キーが 「id」以外の時は $primaryKey で設定

class WpPost extends Model { // IDは自動インクリメントなので除外 public $incrementing = true ; protected $guarded = array('ID'); // プライマリーキー名を変更 protected $primaryKey = 'ID'; // 作成日、更新日はなし const CREATED_AT = null; const UPDATED_AT = null; public function __construct() { // insert 時にデフォルト値を補っておく $this->post_excerpt = ''; $this->to_ping = ''; $this->pinged = ''; $this->post_content_filtered = ''; } }

Controller を作る

/app/Http/Controllers/WpPostController.php を編集


class WpPostController extends Controller { ... /// 更新(入力) public function edit($id) { $item = WpPost::find($id); return view('WpPost.edit', ['item' => $item]); } /// 更新(コミット) public function update(Request $request) { $item = new WpPost(); $form = $request->all(); $id = $form['ID']; unset($form['_token']); $item = WpPost::find($id); $item->fill($form); // 更新日時を入れておく $item->post_modified = date('Y-m-d H:i:s'); $item->save(); // 一覧へ戻る return redirect('/WpPost'); } }

View を作成する

/resources/views/WpPost/edit.blade.php を新規作成する。


@extends('layouts.app') @section('title','WpPost.edit') @section('content') <form action="/WpPost/edit" method="post" > {{ csrf_field() }} <input type="hidden" name="ID" value="{{ $item->ID }}" /> <table> <tr> <td>post_title</td> <td><input type="text" name="post_title" value="{{ $item->post_title }}" /></td> </tr> <tr> <td>post_content</td> <td><input type="text" name="post_content" value="{{ $item->post_content }}" /></td> </tr> </table> <input type="submit" /> </form> @endsection

ルート情報を追加

/routes/web.php に追加する

  • create と update は、show の前に入れる
  • update のほうに、id が含められないので、form から取得する

Route::get('WpPost/edit/{id}', 'WpPostController@edit'); Route::post('WpPost/edit', 'WpPostController@update'); Route::get('WpPost/{id}', 'WpPostController@show');

ブラウザで表示する.

http://localhost:8000/WpPost/create

エラーがなければ、一覧へ戻る

アイテムを削除する

  • 更新画面に「削除」ボタンを追加する
  • 削除処理をする

の2つのアクションの分かれる

Controller を作る

/app/Http/Controllers/WpPostController.php を編集


class WpPostController extends Controller { ... /// 削除 public function destroy( Request $request ) { $form = $request->all(); $id = $form['ID']; WpPost::find($id)->delete(); // 一覧へ戻る return redirect('/WpPost'); } }

View を編集する

/resources/views/WpPost/edit.blade.php に追記する

下のほうに「削除」ボタンを付ける。


@extends('layouts.app') @section('title','WpPost.edit') @section('content') <form action="/WpPost/edit" method="post" > {{ csrf_field() }} <input type="hidden" name="ID" value="{{ $item->ID }}" /> <table> <tr> <td>post_title</td> <td><input type="text" name="post_title" value="{{ $item->post_title }}" /></td> </tr> <tr> <td>post_content</td> <td><input type="text" name="post_content" value="{{ $item->post_content }}" /></td> </tr> </table> <input type="submit" /> </form> <script> function check(){ return window.confirm('削除してよろしいですか?'); } </script> <form action="/WpPost/delete" method="post" onSubmit="return check()" > {{ csrf_field() }} <input type="hidden" name="ID" value="{{ $item->ID }}" /> <input type="submit" value="削除" /> </form> @endsection

ルート情報を追加

/routes/web.php に追加する

  • destroy は、show の前に入れる
  • destroy に、id が含められないので、form から取得する

Route::post('WpPost/delete', 'WpPostController@destroy'); Route::get('WpPost/{id}', 'WpPostController@show');

ブラウザで表示する.

http://localhost:8000/WpPost/edit/10

「削除」ボタンを押したときに問い合わせのダイアログが出る。

カテゴリー: 開発, PHP | Laravel で wordpress を読み込む(自前MVC) はコメントを受け付けていません