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

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

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を見るとパスワードがハッシュ化されているはずです。

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

それでは

Cakephp でjQuery を使ってみる

マウスオーバーした時に枠線を表示する

CakePHPで記事を投稿するサイトを作っているのですが、マウスオーバーで反応があるものを作りたいと思い 簡単ではありますがjQueryを使ってマウスオーバーで記事に枠線をつけたので手順等記載していきます。

1. jsファイルを作る

まずはjsファイルを作るのですが、CakePHPではjsファイルは基本的にwebrootの中のjsフォルダの中に入れていきます。 (jQueryのライブラリーも入れておいてください)

cssファイルや画像ファイルも入れる場所があります。

さて重要な中身ですが、記事をマウスオーバーするとその記事の周りにグレーの枠線が出るようにしました。

$(function(){

  $(".article_chunk").mouseover(function(){
      //記事にマウスが重なっている間は枠線を出す
      $(this).css("outline","5px solid #e6e6e6");
  });

});

これで枠線はつくのですがこれだと枠線が出っぱなしで消えなくなってしまうので

$(function(){

  $(".article_chunk").mouseover(function(){
      //記事にマウスが重なっている間は枠線を出す
      $(this).css("outline","5px solid #e6e6e6");
  }).mouseout(function(){
      //記事からマウスが離れたら枠線を消す
      $(this).css("outline","");
  });

});

こうすることで、マウスが離れると枠線が消えます。

$(this).css("outline","");

mouseoverで指定したcssの値を"“にすれば解決できます。

また「border」を使うとレイアウトが崩れてしまったので「outline」を使いました。

2. 記事一覧のビュー

記事一覧側はまずjQueryを読み込みます

    <?php echo $this->Html->script('jquery-3.2.0.min.js'); ?>
    <?php echo $this->Html->script('jsファイル名.js'); ?>

これでwebroot/js内のjsファイルを読み込みます。

続いて記事のひとまとめでclassを指定すればオーケーです。

<div class="article_chunk">

//記事を表示する処理

</div>

以上になります。 それでは

CakePHP Paginatorでページ分けと表示順を指定

記事の表示件数と表示する順番を指定する

CakePHPで記事を投稿するアプリを作っていて、表示するページを分けたいと思いPaginatorでページ分けをしたので手順等を記載します。

1.ページを分ける

まずはページごとに分けていきます。 Cntrollerを編集していきます。

表示件数を5件、表示順は記事の降順で表示する

class ArticlesController extends AppController {

public $paginate = array(
    //表示件数を5件に指定する
    'limit' => 5,
    //表示順番をArticleのidの降順
    'order' => array('Article.id' => 'desc')
);

これでページを分けて表示できます。

2.ページ遷移をさせる

続いてビュー側でページ遷移の選択肢を作ります。

・前のページ ・次のページ ・ページ数を選ぶ ・最初のページ ・最後のページ

これらを実装します

<?php
    //最初のページへ
    echo $this->Paginator->first('最初のページへ' , array());
    //一つ前のページへ
    echo $this->Paginator->prev('戻る', array(), null, array('class' => 'prev disabled'));
    //ページ数を選択する
    echo $this->Paginator->numbers(array('separator' => ''));
    //一つ次のページへ
    echo $this->Paginator->next('進む', array(), null, array('class' => 'next disabled'));
    //最後のページへ
    echo $this->Paginator->last('最後のページへ', array());
?>

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

これでページ遷移が出来るのですが、一つ注意点があります。

3.デフォルトを参照していないか

私が引っかかったところなのですが、Controllerで使うPaginatorがlib/Cake/Controller/Component にある「PaginatorComponent.php」を参照していていくらlimitを指定しても結果が反映されないという状況に陥りました。

まず原因としてはControllerで$componentsでPaginatorを指定していることになります。

public $components = array(
    'Paginator',
    'Session', 
    'Flash'
    );

解決策としては簡単で'Paginator'を削除すればオーケーです。

それでは

サブクエリの使い方

サブクエリでのWHEREについて

今回はサブクエリの使い方に関して記載していきます。

1.WHERE

まずはサブクエリであるデータを検索して全体の平均以上もしくは以下の場合出力したいとき

例題 ・とあるショッピングサイトには会員がいて買い物をするごとにポイントがつきます。各会員が保有しているポイントの平均値以上のポイントを持っている顧客を抽出したい。

customersテーブル

+------+-----+
| name |point|
+------+-----+
| 上田 | 15  |
| 田口 | 25  |
| 赤西 | 20  |
| 中丸 | 45  |
| 田中 | 10  |
| 亀梨 | 35  |
+------+-----+
SELECT
    name , point
FROM
    customers
WHERE
    point >= (
    SELECT
        avg(point)
    FROM
        customers
);


田口 | 25 
中丸 | 45
亀梨 | 35

保有ポイントの平均値以上のポイントを持っているメンバーのみ表示されます。

ちなみにサブクエリを使わずに無理矢理WHERE句だけで書こうとした場合

SELECT
    name , point
FROM
    customers
WHERE
    point >= avg(point)

これはうまくいきません

今回は以上になります。

CakePHP 画像のサイズを決めて出力する

アップロードした画像を任意のサイズにして表示する

画像のアップロードが出来たらその画像を丁度いいサイズにして出したいでよね ということでやり方をアップします。

前提として「webroot」に「upimg」というフォルダを作っているものとします。

今回は私は記事一覧を表示するところで画像の表示を行いました。 なのでforeachの中だと考えてください。

まず画像のファイルパスを取得していきます。

$article_id = $article['Article']['id'];
$id = "/img/" . $article_id . ".jpg" ;

// コピー元画像の指定
$path = WWW_ROOT . "upimg/" . $article_id  . ".jpg";
//出力先のファイル
$file = WWW_ROOT . "img/" . $article_id  . ".jpg";

続いて画像サイズの取得や表示するサイズ設定をしていきます。

// ファイル名から、画像インスタンスを生成
$in = imagecreatefromjpeg($path);
//元画像サイズ取得
$size = GetImageSize($path);
$width = 200;
$height = 200;
//サイズを指定した背景画像を生成
$out = ImageCreateTrueColor($width, $height);
//サイズ変更・コピー
ImageCopyResampled($out, $in, 0, 0, 0, 0, $width, $height, $size[0], $size[1]);

最後に出力と不要なデータの削除を行います。

//画像保存
imagejpeg($out, $file ,100);
echo $this->Html->image($id, array('alt' => 'baz'));

ImageDestroy($in);
ImageDestroy($out);

これで画像のサイズを指定して出力することができます。

それでは

CakePHP画像のアップロード

ファイル形式の指定とフォルダを移動して保存するまで

CakePHPでのアプリ制作で画像のアップロードを実装したので手順等を記載致します。

1.addでファイルをアップロード

まず最初にやることは当然addでファイルのアップロードを出来るようにすることです。 こちらはaddに少し記述するだけで済みます。

<?php
echo $this->Form->input('image', array(
    'label' => false,
    'type' => 'file','multiple'));
?>

これでaddを確認するとファイルのアップロードが出来るようになっていると思います。

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

2.アップロードファイルの情報確認方法

では早速アップロードしたファイルを扱っていく、、、前にですね アップロードしたファイルの情報を見る方法をご紹介します。

今後ファイルパスやファイル名を確認するのに必須になってくるものなので使い方を覚えましょう! と言っても確認方法は簡単です。

addからファイルをアップロードした後にControllerで

//中身をみたい変数などあれば確認できます。
debug();
//処理をここで止めます
return;

こう記述することで記述した場所で処理を止めることが出来ます。 これを書いて実行しまして右上のケーキのマークを押します。

するとビロビロと出てくるので「Request」をおすとアップロードしたファイルの情報を見ることができます。

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

「image」の中身を見てもらうと

「name」がアップロードしたファイルのファイル名になります。

「type」がファイルの拡張子になります。

「tmp_name」がアップロードしたファイルの一時的なパスになります。 この一時的なパスはファイルを識別するために与えられるものでして、 コードでファイル自体を扱いたいときはこのパスを指定することになるので覚えておいてください。

3.受け取ったファイルを保存する、パスを指定する

続いてControllerでDBにファイルの保存して、特定のフォルダに画像ファイルを保存していきます。

1.ファイルの保存先を指定

2.ファイルのパスを保存

3.ファイルの名前を取得

4.ファイルの保存

//保存先のパスを保存、WWW_ROOT はwebrootを示します。
$path = WWW_ROOT . "upimg/";

//アップロードしたファイルの一時的なパスを取得します
$img = $this->request->data('Article.image.tmp_name');

//アップロードしたファイル名を取得します
$name = $this->request->data('Article.image.name');

//現在ある記事のidの最大値を取得します
$box = $this->Article->find('first', array("fields" => "MAX(Article.id) as max_id"));
$id = $box[0]['max_id'];

//今回追加する記事番号にします
$id++;

$uploadfile = $path .  "$id.jpg";

//画像のフォルダとファイル名を指定して保存します
if(!move_uploaded_file($img,$uploadfile)) {
    $this->Flash->error(__('画像ファイル保存できませんでした'));
    return $this->redirect(array('action' => 'index'));
}

//DB保存用にファイル名を保存します
$this->request->data['Article']['image'] = $name;

こちらまでがファイル自体の保存になります この場合画像は「webroot/upimg」というフォルダに保存されます。

続いてDBへの保存がこちらになります。

if ($this->Article->save($this->request->data)) {
    $this->Flash->success(__('記事の追加が出来ました!'));

    return $this->redirect(array('action' => 'index'));

} else {
        $this->Flash->error(__('記事の追加でエラー'));
}

これで保存は完了です。

4.拡張子をチェックして画像以外をはじく

続いてはアップロードされたファイルが本当に画像ファイルかチェックしていきます。 悪意のあるファイルでもアップされたら大変ですからね!

書き方は「jpeg」「jpg」「gif」「png」以外のファイルがアップロードされたら別のビューに移動して はじくというものになります。

//アップロードしたファイルの拡張子を取得します
$extension = $this->request->data('Article.image.type');

//アップロードを許可するファイルの拡張子を代入します
$check_array = array(1 => 'image/jpeg', 2 => 'image/jpg', 3 => 'image/gif', 4 => 'image/png');

//アップロードされたファイルが画像ファイルかどうかチェックします
if(!array_search($extension , $check_array)) {
    $this->Flash->error(__('画像ファイルを入れて下さい'));
    return $this->redirect(array('action' => 'index'));
}

これで画像ファイルだけがアップロードされます。 なぜ配列が 1 => からなのか気になる方はこちらのサイトを見てみてください。

【array_search関数】は戻り値に注意!! | HANGOUT - ハングアウトエンジニアブログ

5.GitHubにアップするときの注意

さて機能としてはこれで完成なのですがGitHubなどバージョン管理ツールにアプリをアップするときは画像を保存しているファイルをアップしないように注意しましょう。 と言っても方法は簡単でgitignoreに「app/webroot/upimg」と記載すればオーケーです。

さてこれで一通りの画像アップロードが出来ます。 もっと効率的な方法など見つけたらまたアップします。それでは

SQLのビューとサブクエリについて

検索を楽に行うために

1.ビューの概念説明

2.ビューの作り方

3.サブクエリの概念説明

4.サブクエリの作り方

1.ビューの概念説明

まずビューとは概念的にはテーブルと同じようなものなのですが、違いは実際のデータを保存しているかどうかになります。

通常のデータ保存方法をおさらいすると、 INSERT文を使ってレコードを追加するとそのデータは記憶装置等に保存されます。 そしてそのデータをSELECT文などでデータを検索します。

一方ビューの場合、データを記憶装置に保存しません。ビューが保存しているのは「SELECT文」そのものになります。 ビューは参照されるたびに一時的に仮想のテーブルを作ります。

メリットは二つあります。 1.記憶容量を大きく圧迫しない 2.SELECT文をいちいち書かなくてよい

ビューが含むデータは元のテーブルと連動して自動的に最新の状態に更新されます。 ビューを実行するとはつまり「そのSELECT文を実行する」ということになります。

  • よく使うSELECT文はビューにして使いまわしましょう

2.ビューの作り方

ビューを作るには「 CREATE VIEW 文 」を使います 使い方は以下のようになります。

CREATE VIEW ビュー名 (<ビューの列名1>,<ビューの列名2>, ...)
AS
<SELECT文>

ASキーワードの後にはSELECT文を使います。 SELECT文の列とビューの列は並び順で一致し、SELECT分の二番目の列はビューの二番目の列になります。 ビューの列名は、ビューの後ろのリストで定義します。

  • 重要なポイントは一度書いたSELECT文を何度も書かなくてよいという点になります。

さらにビューを元にさらにビューを作ることもできます。これを「多段ビュー」といいます。 ただし多くのビューを重ねることはパフォーマンスを下げるので推奨されません。

またビュー定義でORDER BY句は使えません。

まとめるとビューとはデータそのものを保存するのではなく、データを取り出すSELECT文だけを保存することで ユーザーの利便性を高めるものです。

3.サブクエリの概念説明

サブクエリとは一言でいうと「使い捨てのビュー」です。ビューとは異なり、SELECT文の実行終了後に消去されます。 ビューのように記憶装置に保存されることはなくSELECT文の実行終了後には消えてなくなります。

サブクエリ(subquery)とは「下位の(sub)」の「問合せ(query)」という意味です。 SELECT文が入れ子構造になっていてまずFROM句の中のSELECT文が実行され、そのあとに外側のSELECT文が実行される、という順番になります。

4.サブクエリの作り方

ではそのサブクエリの作り方の例を説明します。

SELECT <ビューの列名1> ,<ビューの列名2>, ...
FROM (SELET <フィールド名1> , COUNT(*) AS <ビューの列名2>
FROM <テーブル名>
GROUP BY <フィールド名1> ) AS <ビュー名>

といった形になります。順番としては 1. まず枠のFROM()の中のSELECT文が実行されます 2. 1の結果に対して外側のSELECT文が実行されます。

サブクエリの階層数には原則制限がないので、サブクエリの中のFROM句にさらにサブクエリを使って、その中のFROM句にサブクエリを使って、その中のFROM句にサブクエリを、、、 といくらでも入れ子を作ることが可能です。 ただしサブクエリの階層が深くなればなるほどSQL文が読みにくくなりますし、パフォーマンスにも悪影響を与えるため階層を深くしすぎることは避けましょう。

以上で簡単なビューとサブクエリの説明を終わりたいと思います。 また改めて更新したいと思います。