情弱エンジニアのなかのblog

一人前のエンジニアになる為のブログです

CakePHPでComponentの実装

使いまわせて便利なComponent!

現在CakePHPでアプリを制作していまして、Componentを実装したので手順等を記載致します。

商品の紹介記事を投稿するアプリで記事データ(articles)には商品データ(products)がひもづいていて、その商品データには商品を売っている企業のデータ(companies)が紐づいています。 企業ごとの記事の数を企業の一覧表示画面で確認するという機能を制作致しました。

1.Componentの作成

まずはapp/Controller/ComponentにComponentファイルを作成します。

class CustomComponent extends Component {


public function get_article_count($table_name,$field_name,$this_list_count) {

    //記事データを取得できるようにする
    $article_data = ClassRegistry::init('Article');

    //検索用に文字列を繋げる
    $table_field = $table_name . "." . $field_name;

    //記事データと記事データに付随する記事数を取得したいフィールド情報を取得する
    $article_list = $article_data->find('all', array('fields' => array($table_field)));

    //記事ごとの目的のフィードの値を取得する
    for($i = 0; $i < count($article_list); $i++) {
        $field_id_list[$i] = $article_list[$i][$table_name][$field_name];
    }

    //記事数を代入する
    $article_count = count($field_id_list);

    //記事数を取得したいフィールドの数分配列を用意する
    for($i = 1; $i <= $this_list_count; $i++) {
        $field_each_article_count[$i] = 0;
    }

    //記事数を取得したいフィールドごとの記事の数を配列に代入する
    for($i = 0; $i < $article_count; $i++) {
        for($j = 1; $j <= $this_list_count; $j++) {

            if($field_id_list[$i] == $j) {
                //フィールドごとの検索をして記事データがあったら
                //記事の数を加算していく
                $field_each_article_count[$j]++;
                continue;
            }
        }
    }

    return $field_each_article_count;
}

}

これを作っていれば複数のControllerでフィールドごとの記事の数を取得できます。

2.Controllerでデータを渡す

Controllerからデータを渡して、記事の数を取得します。 今回はサイトごとの記事の数を取得します。

public function index() {
    
    $table_name = "Product";
    $field_name = "company_id";
    //サイトの数を取得する
    $site_count = count($this->Company->find('list'));
    //各サイトごとの記事数を取得する
    $company_each_article_count = $this->Custom->get_article_count($table_name,$field_name,$site_count);

    $this->set('company_each_article_count',$company_each_article_count);
    $this->set('companies', $this->Paginator->paginate());
}

この形式の処理をControllerに書くことで様々なフィールドごとの記事数を取得できます。

3.indexで表示する

最後にindexでサイト名の横に記事数を表示します。

<?php foreach ($companies as $id => $company): ?>
//サイト名を一覧する処理
<td>
    <?php
    $id++;
    echo $company_each_article_count[$id];
    ?>
</td>

これでComponentにフィールドごとに記事数を取得する処理を作ることが出来ました。

もっと効率的に実装する方法もあるかと思うのですがとりあえずはここまでで 出来たらまたアップします。

それでは

サーバーを借りてアップロードするまでの流れ(LOLIPOP編)

制作したアプリを公開しましょう!

現在制作中のアプリをサーバーを借りてアップしたのですが アップするまでに色々と苦労したので手順などを記載致します。

ちなみにアプリはCakePHPで制作していまして、DBの接続などでつまずくこともあったので 同じような状況の方がいたら参考にしていただければ幸いです。

1.サーバーを借りる

まずはサーバーを借りるところから始まります。 今回私はロリポップ!というサービスのレンタルサーバーを使いました。

ロリポップ!レンタルサーバー |今だけ初期費用無料キャンペーン中!

プランは エコノミー、ライト、スタンダード、エンタープライズ、の4種類があり 料金や使える機能、容量に差があります。 個人利用ならスタンダードまでで十分だと思います。

とりあえずお手軽なライトプランを申し込みました。 契約自体は時間がかからず、契約後10分とたたずにサーバーを使えるようになりました。

早速FTPPuttyを繋いで、利用しようと思ったらSSHで繋げることが出来ませんでした。

原因は単純でSSHを使うにはスタンダードプランにする必要があるとのことでした。 もう契約しちゃった後なのでどうしようかと思ったのですが、契約後のプランの変更が出来たので スタンダードプランに変更しました。

2.CakePHPをアップする

FTPを使ってアプリをまるまるサーバーにアップしました。 この状態でアプリのURLをおすと

f:id:nonaka-katuma-hal:20170718191314j:plain

CakePHP is NOT able to connect to the database.

Database connection "Mysql" is missing, or could not be created.
SQLSTATE[HY000] [2002] No such file or directory

などと「CakePHPはDBに接続していない」という意味のエラーが出ると思うので まずはDBの準備をしましょう。

ロリポップの管理画面からDBを製作する画面があるのでそちらからDBを作ります。 管理画面からサーバー名、アカウント名、パスワードなど必要な情報を確認できるので App/Config/database.phpの public $default の中身を編集してください。

これでDBをつなぐ事が出来たかと思います。 DB接続に関して以下のサイトを参考に致しました。

CakePHP のデータベースの作成と接続設定 - PHP入門 - Webkaru

3.Migrationsを使う

アプリにMigrationsを実装しておくとDBをいちいち作らなくて済むので便利です。 Migrationsを使うのでPuttyを開き、 「./Console/cake Migrations.migration status」を実行してMigrationsを使えるかどうか確認しようとすると

Error: Plugin Migrations could not be found.

と出てしまいました。

解決方法としましては、まずapp/Plugin に「Migration」と「Schema」フォルダが入っているか、中にDBのデータを記載したファイルがあるか確認します。

次にBootstrapを使っている場合はapp/Config/bootstrap.php

CakePlugin::load(array('TwitterBootstrap' , 'Migrations'));

を記入しているか確認します。 これらをしてもう一度./Console/cake Migrations.migration statusをするとMigrationsを使うことができると思います。

まずはこれでCakePHPのアプリを起動出来るかと思います。

またサーバー周りで何かあれば記載致します。 それでは

phpでランダムな処理があるアプリの注意点

乱数生成!

アプリでランダムな数値を扱う処理を作ったのですが、なにやら偏りがあるということが発覚したので その対処等を記載致します。

1.rand()は偏る?

アプリでランダムな処理をする時にshuffle()を使っていたのですが、 どうやらshuffle()では内部でrand()を使っているようです。

ただこのrand()が問題でして、多少の偏りがあるようです。

2.mt_rand()を使う!

mt_rand()はrand()よりも高速かつ乱数生成において優秀との情報を見つけたので こちらを使っていきます。 普通にmt_rand()を使うか、どうしてもshuffle()を使わなきゃいけない場合は 以下のコードを参考にしてください。

function mt_shuffle($array) {

        $cnt = count($array);

        //渡される配列の添え字は$cntより1少ない数値~0なので$cnt -1をする
        for($i = $cnt -1; $i > 0; $i--) {

            $j = mt_rand(0 , $i);
            if($i !== $j) {
                //配列の中身を入れ替える
                list($array[$i], $array[$j]) = [$array[$j], $array[$i]];
            }
        }
}

このような形でランダムな処理を実装できます。 が、これでも偏りが全く出ないわけではないのでそこは注意してください。

今回の記事を書くにあたり以下のサイトを参考に致しました。

PHP関数 - 乱数を生成 - mt_rand() - PHP入門のカルマ

それでは

CakePHPでSQLを使いデータを出力する

以前データをカテゴリーごとに出力するという記事を投稿したのですが findではなくSQLでデータを持ってくる処理を実装したので記載致します。

以前の記事 nonaka-katuma-hal.hatenablog.com

1データの取得とModelへの引き渡し

こちらに関してはControllerでViewからPOSTされたデータを受け取る所まで前回の記事と同じなのでそちらを参考にしていただければと思います。

Controllerでcheckboxのデータを受け取るときに以下のように記載します。

//選択されたidを配列で取得します。
$category_id = $this->request->data['Article']['category'];

//こちらは前回と同じ要領でデータの受け渡しと記事データの取得をします
$selected_articles = $this->Article->get_selected_articles($category_id);
//こちらも前回と同じ要領でViewに記事データを渡します
$this->set('selected_articles',$selected_articles );

2.SQLでデータを抽出する

それではModel側でSQLを使いデータを出力していきます

public function get_selected_articles($category_id) {
    //渡されたカテゴリーidをカンマ区切りの文字列に変換する
    $category_id_str = implode("," , $category_id);

    //選択された条件で記事データを抽出する
    $sql = "SELECT *
    FROM articles
    //選択されたカテゴリーidを含んだ記事データだけを抽出する
    WHERE articles.category_id IN ($category_id_str)";

    //抽出したデータを$dataに代入する
    $data = $this->query($sql);
    return $data;
}

以上がSQLを使ったデータの取得方法になります あまり前回の記事とやっていることが変わらないかと思いますが。 このようにSQLを使うことで複雑な条件でデータの取得ができるようになります。

そのあたりはまた別の記事で紹介したいと思います。

それでは

CakePHPで記事データをカテゴリーごとに出力する方法

1.checkboxでカテゴリーをユーザーに選択させる機能の実装

現在CakePHPで記事を投稿するアプリを制作していまして ある特定の検索条件ごとに記述を出力する方法を記載させて頂きます。

記事に付随するCategoryというデータが検索条件になっています。

2.Controllerでカテゴリー情報をセットする

Controller側から検索条件となるデータをViewに渡します。

Categoriesテーブルにカテゴリ名(name)があるとします。

$this->set('category_name', $this->Category->find('list', array('fields' => array('name'))));

これでカテゴリーのカテゴリ名をViewに渡せます。

3.Viewの設定

それでは受け取ったカテゴリーのデータを使い、検索条件を選択するチェックボックスを作っていきます。

<?php
    echo $this->Form->input('category', array(
    //複数項目をチェックさせるにはmultipleを使います
    'multiple' => 'checkbox',
    //Controller側から受け取ったカテゴリーの名前を使う
    'options' => $category_name,
    ));
?>

3.Controllerでデータを受け取る受け渡す

POSTされたデータをControllerで受け取り、Modelへ渡します。 今回紹介する機能ではチェックボックスを使っていながら、 選択された一件のデータしか受け渡すことが出来ません。

複数検索はまた別の記事で紹介いたします!

//Viewで選択されたデータを一件だけ受け取ります
$category_id = $this->request->data['Article']['category'][0];

//Modelのget_categoryにほしいカテゴリーidを渡し、記事データを受け取る
$selected_articles = $this->Article->get_category($category_id);

4.Modelで記事データを返す

Model側で受け取ったカテゴリーidに該当する記事だけをControllerに返します。

public function get_category($category_id){
    //受け取ったカテゴリーidでDBのcategory_idを検索します。
    $data = $this->find('all' , array('conditions' => array('Article.category_id' => $category_id)));

    return $data;
}

5.ビューで結果を出力する

Modelから受け取ったデータをControllerからViewに渡します。

$this->set('selected_articles',$selected_articles);

最後にViewで受け取った記事のデータを出力して終了です。

<?php foreach ($selected_articles as $article): ?>
//以下記事のデータを出力する処理

という事で今回はカテゴリーごとの出力を記載致しました。 もっと細かい検索条件や複数検索など行いたいのですが、それはまた別の記事で

それでは

CakePHPのBehaviorを自作する方法

Modelを拡張させる方法

現在cakePHPでアプリを作っていてBehaviorの自作について理解したので手順等記載していきます。 Behaviorを自作するときの基本の基本になります。

1.なぜBehaviorを作るのか

まずCakePHPアプリ制作において、なぜBehaviorを使うのかというところですが 例えばModelに新しい機能を作ったとして、複数のModelでも使うことになり 利用するModelでいちいち同じ処理を書くのは手間になります。

そこでBehaviorというものを使って共通する処理を一つの場所に書いて それをModelで使いまわそうというものになります。

2.実装の仕方

Behaviorを作ることになったら app/Model/Behaviorに追加していきます。 今回はごくごく簡単な処理を確認していきます。

class TestBehavior extends ModelBehavior {

    public function test() {
        //返り値を「5」にしておきます
        return 5;
    }
}

続いて利用したいModelを編集していきます。

//利用したいBehaviorを指定します
public $actsAs = array('Test');

Modelに記載するのはこれだけになります!

2.処理をを出力する

続いてその処理をControllerからViewに渡してみます。

ControllerでBehaviorのメソッドを呼び出します。

$data = $this->Model->test();
$this->set("data",$data);

最後にViewで表示してみます。

echo $data;

とすると返り値に指定した5が出力されます。

といったここまでがBehaviorの実装の流れになります。 Behaviorでいい感じの処理を作ったら再度記事をあげます。

それでは

CakePHP ログイン認証チェック

CakePHPのAuthComponentでログイン認証がうまくいかない時は?

CakePHPで記事を投稿するアプリを作っていてログイン状態の保持の為にAuthComponentを利用していたのですが ログインページでユーザ名とパスワードは合っているのにログインができないということが起こったので解決法、手順等を記載していきます。

1. なぜか認証されない

まずログイン機能を実装するために「CakePHP ログイン Auth」などで検索すると 以下のような文が出てくると思います。

if($this->Auth->login()){
    //ログイン成功したときの処理
}else{
    //ログイン失敗したときの処理
}

私もこれを実装したわけですがユーザー名とパスワードは合っているのに「ログイン成功」しませんでした。

2.テーブル名、フィールド名は正しいのか?

では原因は何か探していきます。

まずユーザーテーブルとフィールド名が正しく用意できているかを確認してください。 参考書などを読んでいるとどうやら テーブル名は「Users」 フィールド名は「username」と「password」でないといけないようです。

テーブル作った後に知りました。 ModelのvalidateやViewのaddなどなど色々変えていかなくてはいけません。

ちなみにフィールドの「password」ですが、varcharで最低でも40文字以上は保管できるようにしてください。 「password」にはハッシュにより暗号化された文字列が入るので保管できる量が少ないと正しく値を保持できなくなります。

3.ハッシュ化はされているか?

こちらも重要なのですがAuthComponentで認証するにはパスワードがハッシュ化されている必要があります。 なのでハッシュ化の確認をしてください。

phpMyAdminのUserテーブルを確認してpasswordに入力した値がそのまま入っていたら パスワードをハッシュ化する必要があります。

手順ですが、新しくユーザーを追加するときにpasswordをハッシュ化させるのでUsersControllerのaddに

$this->request->data['User']['password'] = $this->Auth->password($this->request->data['User']['password']);

を追記します。ただし

$this->User->create();

createの上に書きましょう。

さてこれで新たにユーザーを作ってからphpMyAdminを見るとパスワードがハッシュ化されているはずです。

ハッシュ化されていれば、ログインをすると成功します!

それでは