A Day In The Boy's Life -49ページ目

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

開発が進んでいくと様々な機能ができてくるわけですが、中には似通ったモジュールが出来上がってきたりするので共通化していきたいという考えになってきます。

CakePHPでも、そのような共通のライブラリを作ることができます(前回同様、環境はCakePHP2系を前提としています)。



Controllerから呼び出す共通コンポーネントの作り方


コントローラーから呼び出す共通ライブラリはコンポーネントと呼ばれ、様々なアクションやコントローラーファイルから利用することができます。

コンポーネントの配置場所は、Controllerディレクトリ内にあるComponentディレクトリです。


$ ls Controller/
AppController.php  Component  HogeController.php  PagesController.php


この中に、共通コンポーネント用のファイル、CommonComponent.phpを作ってみます。


<?php

class CommonComponent extends Component {

    public function getString() {
        return "Hello";
    }
}


ファイル名とクラス名は一致しておけばなんでもかまいません。

次に、このコンポーネントをコントローラー側から呼び出してみます。


<?php

App::uses('AppController', 'Controller');

class PagesController extends AppController {

    public $name = 'Pages';
    public $components = array('Common');

    public function display() {
        $str = $this->Common->getString();
    }
}


コンポーネントを使いたい場合は、メンバ変数$componentsにコンポーネント名を配列で指定します。

後は、$thisに続きコンポーネント名とメソッド名を呼び出せば利用可能です。



Viewで利用できる共通ヘルパーの作り方


コンポーネントは、コントローラー側で利用できる共通ライブラリでしたが、ビュー側でもそのような共通のライブラリ機能を利用したいというケースがあります。

ビュー側で利用する共通ライブラリは、ヘルパーと呼ばれテンプレートファイルの中で呼び出しが可能です。


ヘルパーファイルの配置場所は、Viewディレクトリの中にあるHelperディレクトリとなります。


$ ls View/
Elements  Emails  Errors  Helper  Layouts  Pages  Scaffolds

この中に共通のヘルパー用ファイル、CommonHelper.phpを作ってみます。

<?php

App::uses('AppHelper', 'View/Helper');

class CommonHelper extends AppHelper {

    function getPath()
    {
        return "/path/to/image/";
    }
}


こちらも、ファイル名とクラス名は一致させておき、AppHelperのサブクラスとして定義します。

これを、ビュー側から呼び出してみます。

ただ、いきなりビュー側を編集する前にコントローラファイルでコンポーネント同様に、利用するヘルパーを呼び出しておく必要があります。


<?php

App::uses('AppController', 'Controller');

class PagesController extends AppController {

    public $name = 'Pages';
    public $helpers = array('Common');
    public $components = array('Common');

    public function display() {
        $str = $this->Common->getString();
    }
}


前述のコントローラに、メンバ変数の$helpersが加わっています。

ここに利用するヘルパーを配列で指定します。

準備が整ったら、ビュー側で使うヘルパーのメソッドを呼び出します。


<div>
<?php echo $this->Common->getPath(); ?>
</div>


書く場所は、コントローラーとビューとで違いますが、呼び出し方はどちらも同じですね。



ヘルパーからコンポーネントを呼び出す


何れも、共通のライブラリとして機能するわけですが、コンポーネントはシステム処理に必要な機能を定義し、ヘルパーはテンプレート利用する機能と使い方のすみわけが行われたりするものの、お互いで利用したい機能がかぶるということはよくあったりもします。


そういった場合、ヘルパーからコンポーネント内に定義したメソッドを呼び出すことができるので、処理が重複するといったことを避けることができます。


<?php

App::uses('AppHelper', 'View/Helper');

class CommonHelper extends AppHelper {

    function getPath()
    {
        App::import('Component', 'Common');
        $this->cmm = new CommonComponent(new ComponentCollection());
        $str = $this->cmm->getString();
        return "/path/to/images/" . $str;
    }
}


上記の例では、先ほど作成したCommonComponentのgetStringメソッドを呼び出しています。


綺麗に処理をまとめていけば、コードもすっきりかけ開発も効率的になるのではないでしょうか。





前回、CakePHPのControllerとViewの使い方 を説明していきましたが、今回はMVC最後となるモデル編です。

CakePHPのModelでは、DBとの関係を定義しデータの操作の振る舞いを隠蔽してくれる役割があります。



テーブルとModelの関係


今回対象とするテーブル(dogs)の構造は下記のようになっているとします(DBはPostgreSQLを使っています)。

テーブル名は、Controller名と同様に複数形で定義しておきます。


cakephp=# \d dogs
  Column  |            Type             |                     Modifiers                   
----------+-----------------------------+---------------------------------------------------
 id       | integer                     | not null default nextval('dogs_id_seq'::regclass)
 name     | character varying(50)       |
 created  | timestamp without time zone | default now()
 modified | timestamp without time zone | default now()

idはPrimary Keyにして採番はdogs_id_seqというシーケンスに任せるようにしています。

これに対して、下記のCakePHPのModelファイル(Dog.php)を用意します。


<?php

class Dog extends AppModel {
    public $name = 'Dog';
}


ファイル名やクラス名は、テーブル名とは違って単数形とし、クラスはAppModelのサブクラスとして定義します。

メンバ変数$nameはモデル名を定義します。

最も基本的なモデルを利用するために必要なものはこれだけです。


最後に、ControllerからModelを呼び出してみます。

前回作成しているPagesControllerを少し改良してみます。


<?php

App::uses('AppController', 'Controller');

class PagesController extends AppController {

    public $name = 'Pages';
    public $uses = array('Dog');

    public function display() {
        $this->set('dogs_data', $this->Dog->find('all'));
    }
}


コメントは取り払ったのですっきりしていますが、前回からの変更点はメンバ変数の$usesに利用するモデル名を配列で追加し、アクションdisplayの中でfindメソッド(データの参照)を利用してデータを取得し、View側にセットしています。


もし、dogテーブルにデータが投入されていれば、Viewを下記のように書くことで上記のモデルから取得したデータを出力することができます。

<?php foreach($dogs_data as $dog): ?>
<li><?php echo $dog['Dog']['name']; ?></li>
<?php endforeach; ?>
</ul>

画面の出力イメージは下記のようになります。

デバッグモードが有効になっていれば、下部に実際に実行されているSQLが表示されるので、それを元にデバッグしてみるのが効率的かもしれません。



A Day In The Boy&#39;s Life-CakePHPのモデル



Modelを使った複雑な問合せ


このようにModelを通すことで、開発者がわざわざSQLを書かなくてもデータ操作が行えるようになるわけですが、条件をつけたり複数のテーブルからデータを取得するといったことも可能です。


    public function display() {
        $options = array('fields'    => array('Dog.name', 'Dog.id'),
                         'conditions' => array('Dog.id' => 1));
        $this->set('dogs_data', $this->Dog->find('all', $options));
    }

上記の例では、nameとidカラムだけを対象にidが1のデータを取得するという条件付のデータ抽出を行っています。

検索条件のオプションは配列で指定しておき、findメソッドの第2引数にセットしていたオプションデータを指定します。

A Day In The Boy&#39;s Life-CakePHPのモデル2


デバッグで出力しているSQLも指定した条件に合わせて変化しているのがわかります。

他にもLIKE分を使ったあいまい検索を行うようなSQLもモデルを通して行うことができます。


    public function display() {
        $options = array('fields'     => array('Dog.name', 'Dog.id'),
                         'conditions' => array('OR' =>
                                            array(0 => array('Dog.name like' => '%dog%'),
                                                  1 => array('Dog.name like' => '%inu%'))),
                         'order'      => 'Dog.name');
        $this->set('dogs_data', $this->Dog->find('all', $options));
    }

モデルの使い方の基本は、決まったキーに配列でデータを詰め込んでいくだけです。

上記のfieldsはカラム名を、orderはデータの取り出し順を指定しています。



Modelを使って複数のテーブルからのデータ取得


もちろん、これまで解説してきたような単一のテーブルからのデータ取得だけでなく、複数のテーブルを外部キーと連結させて一度にデータを取得するといったこともできます。


例えば、先ほどまで利用していたDogsテーブルに加えて、犬の性格を表すデータを格納するcharactersテーブルがあったとします。

Charactersテーブルの「陽気」な犬に該当するデータをDogsテーブルから取得したいといった要件にあわせるため、Charactersテーブル用のモデルファイルを用意します。


<?php

class Character extends AppModel {
    public $name = 'Character';
    public $belongsTo = array('Dog' =>
                           array('className' => 'Dog',
                                 'foreignKey' => 'id')
                     );
}

最大の違いは、$belogsTo変数に、関連するテーブルや外部キーを定義しておくことです。

この定義により、Dogsテーブルのidカラムを利用してテーブルの結合を行います。

Controller側も上記にあわせて利用するモデルや条件を変更してみましょう。


<?php
App::uses('AppController', 'Controller');

class PagesController extends AppController {

    public $name = 'Pages';
    public $uses = array('Dog', 'Character');

    public function display() {
        $options = array('fields'     => array('Dog.name', 'Dog.id'),
                         'conditions' => array('Character.name' => '陽気'),
                         'order'      => 'Dog.name');
        $this->set('dogs_data', $this->Character->find('all', $options));
    }
}


利用するモデルが増えているので、$usesに追加しておきます。

条件はCharactersテーブルから指定するので変更し、find(検索対象)もDogからCharacterに変更しています。


これだけで、後は勝手にDogsテーブルと結合して、DogsテーブルにあるIDとか名前を取得してくれます。


A Day In The Boy&#39;s Life-CakePHPのモデル3


このようにモデルを使ってもある程度複雑な処理を簡単に記述することができます。

ただ、あまりに複雑な条件になってくるとモデルの定義や問合せ方法が煩雑になり、SQLではわかっているのに・・・、という状況に陥るかもしれません。

そんな場合は、素直にSQLを直接書いたほうがよいかもしれません。


 $dogs = $this->Dog->query("select id from dogs order by id desc");


Modelを通したデータの投入、更新


データの参照だけでなく、データの投入や更新もModelを通して簡単に行えます。
例えば、新たにデータを挿入したい(INSERT)場合は、下記のように書くだけです。


    public function display() {
        $data = array('Dog' => array('name' => 'Dachshund'));
        $this->Dog->save($data);
    }


元々、Dogsテーブルのidカラムはシーケンスから採番するようにしているので、特に値を指定する必要がありません。

nameカラムに投入したいデータを指定し、後はDogモデルのsaveメソッドに渡すだけでデータが投入されます。


更新も同じsaveメソッドを使って行えます。


    public function display() {
        $data = array('Dog' => array('name' => 'Bull Dog'));
        $this->Dog->id = 2;
        $this->Dog->save($data);
    }


saveの前にidを指定していますが、これが更新対象となります。

もし、このIDが存在しない場合はデータが投入されます。

他にも特定の条件にマッチする行を一気に更新したい場合は、updateAllを利用することできます。


    public function display() {
        $condition = array('id >' => 2);
        $this->Dog->updateAll(array('name' => "'Poodle'"), $condition);
    }

条件の書き方が少し特殊にはなりますね。

また、更新するカラムとその値を配列で指定しますが、文字列は’’で括る必要があります。


SELECT "Dog"."id" AS "Dog__id" FROM "public"."dogs" AS "Dog" WHERE "id" > 2


と、ここまで簡単な紹介をしてきましたが、CakePHPのModelでできることはかなり多くあるため、マニュアル もあわせて確認してください。





私の仕事はプログラマだったりプロマネの仕事だったりで、どちらかというと開発側の人間に入るのでデザインのことはよくわかっていませんが、システム開発においてデザインの重要性というのは上がってきていて、体制としてデザイナーさんが参画するというのは珍しくなくなってきています。


それで、サイトのデザインを決めていく際に、まずはプロダクトオーナー側がサイトの構成やイメージカラーやらなんとなくのテーマを提示し、それをデザイナーさんがサンプルを作って抽象的なイメージを具体化していったりするのですが、結構その抽象的なイメージの中でもデザイナーさんはしっかりとしたデザインの意思を込めてきていて関心することが多々あります。

ただ、その意思がプロダクトオーナーの鶴の一声で全然違ったものに置き換わっていくさまもよく見ていて、非常に残念な気分になることがあります。



デザインに込められた意思


というのは、デザイン1つにも結構なんでこういう形にしたとか色合いにしたかというところの意味が込められています。

ただ、格好よければいいというわけではないわけですね。

この辺は企業ロゴとか製品のイメージキャラとかをみてもわかります。


The Meaning Behind the Sony Vaio Logo @ imjustcreative

A Day In The Boy&#39;s Life-VAIOのロゴの由来

VAIOのロゴの由来を解説したページですが、VAでアナログの波を表しIOでデジタル(1と0にもじってる)を表しているそうで、アナログとデジタルの融合を意味しているようです。

これを、「俺、日本人だからカタカナがいい」とか言われたらそんな意味もなくなってしまいますよね。

まぁ、それはそれで無理やり意味をつけたデザインにすることもできたりしますし、中にはやりすぎなロゴを作ってしまって、ネットでダサいとか俺が作ったほうがいいとか批判されたりするのもありますけど、それでもやはりデザイン自体に意味というのはしっかりと込められていたりするわけです。


もちろんデザイナーさんはプロダクトオーナー側の頭のイメージを具体化して書き起こすことが仕事なので、一方的な自分の頭の仲にあるものを押し付けるものではないかと思います。
ただ、色合いをもう少しどうとか、アルファベットで書いているものをカタカナにしてくれとか、個人的な好みでデザインが書き換わっていくのを見たりすると、出来上がったそのデザインにはいったい何の意味が込められているのだろうという疑問が出てくるわけです。

単なる見栄えだけを気にしたものができてしまったんだなと。
そうなってくると当初込められた意思というのが全く無くて、ただの図のようなものが仕上がって反映されていくわけですね。

これは、傍から見ていて結構残念な気分になってきます。



個人の思い入れで変わるサイトの使い勝手


私も昔は際とデザインと勝手あんまり気にしてなくて、よくあるプログラマが勝手に押し付けたデザインでシステムを作っていたりしました。

本物のデザインというものに触れない状況では、それでも「自分的に格好いい!」とか痛い考えではあったんですが、デザイナーさんがちゃんとデザインしたり、色んなサイトでいいものを見ると過去に自分が作ったものに恥ずかしい気持ちにもなったりします。


こういうのは、プロダクトオーナーさんにも当てはまっていて、個人的な考えでメニューの位置とかテーマカラーとかが決まってくるわけです。

ブログなんかによくある、3カラムや2カラムの構成やグローバルナビゲーションの位置を上に持ってくるか、左に持ってくるかなんて事が決まってくるわけです。

それぞれデザインの良し悪しや使い分けというのはありますし、テーマカラーにしてもインパクトのある配色にするにしても、色んな色を詰め込みすぎると統一感がなくなってきたり、目が痛いサイトにもなってくるわけで、暖色系の色は情熱や躍動感を伝えるとか、若くて女の子向けならピンクとか好みの色があるとかそういった知識ことを取り入れるわけでもないわけです。


もちろんこういったことを私も最初からわかっていたわけではないので、デザイナーさんのノウハウとかを聞いて学んだことではあるのですが、こういったのはトレンドもありますし、サイトの統計データからのそういうのも活かす手段というのも生まれています。

ただ、そういったことを知っている人に言われるならまだしも、「メニューって普通上にあるでしょ?」とか「私は赤が好きだから赤でいいんじゃない?」とか、一定の権限がある人が言ってしまったら反論できないような状況も生まれてそのまま突っ走って決まってしまうわけですよ。


昔の私がそうであったように、デザインを単なるテンプレートとしか思ってないような節があって、ロゴのくだりで書いたようなサイトデザインとしての意味がまった失われるような場面に出くわしたりしてこれまた残念な気分にもなってきます。



まとめ


デザインというのは、ユーザーの第一印象を左右する重要な要素です。

まずは、全体感を見て自分の好みとの乖離を確かめたりするものですが、それがちゃんと意味があるものならまだしも、中の人の一言で決まったのであれば、非情にもったいないと思うわけです。

今やシステムの内部構造や提供するサービスというのは似通っていたりするのも多いのですが、そのユーザーを獲得を左右する要素の一つがデザインになってくるのだと思いますし、その重要性を語られるのも多くなってきています。


当然、プロダクトオーナーの話に限らず、プログラマから見たデザインというのもさもありなんですけどね。