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

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

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文が読みにくくなりますし、パフォーマンスにも悪影響を与えるため階層を深くしすぎることは避けましょう。

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

bakeにおけるControllerについて

必要な情報をうまく使うにはコントローラーを使いこなしていきましょう

CakePHPの制作を始めて、bakeの便利さに感動する毎日であります。

ただやはり万能というものではなく自分の理想通りに制作を進めたかったら決まった工程を踏まないといきません。

そこで今回は私がゴリゴリ使っているコントローラーのbakeの仕方を記載致します。

scaffoldを使用しない

まあそれがどうしたと思われるかもしれませんが、実際私が制作を進める上でscaffoldを使わないというだけで いい感じに制作が進められたのでご紹介したいと思います。

やり方ですが簡単でbakeでControllerを選択し「Would you like to use dynamic scaffolding? (y/n) 」scaffoldを使う? と聞かれたら「n」使わないです。と選択します。

続いて「Would you like to create some basic class methods (index(), add(), view(), edit())? (y/n) 」 基本的な4つのmethodを作る?と聞かれるので、「y」はい作ってください。にしましょう。

あとはすべてデフォルトで進めるだけで使い勝手のいいコントローラーを手に入れられます。

もしまた使いやすいもの見つけたらアップしていきます。

vagrantを使っていてGitHubにリポジトリを上げられない時の対処法

Gitは奥が深く一朝一夕では理解出来ませんね

現在vagrantを使って制作物をGitで管理しているのですが、Git add .やGit commit などはうまくいっても GitHubリポジトリにpush出来ずに苦労したので解決法などを記載致します。

Git pushしたときのエラーログごとに解決策を記載していきます。

1.ssh: Could not resolve hostname github.com: Temporary failure in name resolution

ネットワークの問題の可能性があるので再起動すると解決するかと思います。

2.error: The requested URL returned error: 403 Forbidden while accessing https:ka/ecitem_review.git/info/refs

こちらが出たときやリポジトリの名前が違うなどのエラーが出たときは言われた通りにリポジトリの名前を変えてあげましょう、ただ私自身そうだったのですがいろんなサイトを見るとそれぞれ書いてあることが異なりどれを参考にしていいのか分からなくなります。 実際私が以前自分で書いた記事を参考にしてもうまくいきませんでした。

解決法は一つではなく自分のエラーログをしっかり見て何が必要かただ盲目的にサイトを回遊するのではなくはっきりと目的を定めて原因を探ることが大切です。

さてといったところで自分の解決法を記載します。

  • まず自分で作ったGitHubのアカウントに入りCodeを見ます

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

そこにhttpsのURLかSSHを確認できるところがあると思うのでSSHの方をコピーします。

$ git remote set-url origin ここに先ほどのSSHを貼り付けます

これでもう一度git pushをしてみてください。

3.Bad owner or permissions on /home/vagrant/.ssh/config

公開鍵を設定し直してみてください。このサイトに詳しく書いてあります。

monsat.hatenablog.com

4.Permission denied (publickey)

cd ~/.ssh

.sshに移動するのでフォルダ内にある鍵やconfigの権限を変えてみてください。

パーミッションの変更の仕方は「chmod 数字三つ ファイル名」です。

権限は777として書き込み、読み込み、実行がすべてのユーザーで可能にして試してください、ただ本来はよくないので、pushが成功したら不必要なものは変えましょう。

パーミッションにはルールがあって全て権限を777にしても一時的には通ったとしてもその後うまくいきません 私が試して成功したものをエラーログごとに記載していきます。

Bad owner or permissions on /home/vagrant/.ssh/config

というエラーが出たら見てわかる通りconfigについて言われているので

chmod 020 config

とします。

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0777 for '/home/vagrant/.ssh/id_rsa' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
bad permissions: ignore key: /home/vagrant/.ssh/id_rsa
Permission denied (publickey).

このようなエラーが出た場合は、秘密鍵パーミッションがオープンすぎると言われているので

chmod 600 id_rsa

こうするのが正しいらしいです。

パーミッションにはそれぞれルールがあるりそれが影響していたようです。 パーミッションに関してはこちらの記事を参考にさせて頂きました。

SSHで接続しようとしたら秘密鍵のパーミッションでエラーが出た - yachibit.log

ちなみに他サイトやドットインストールを利用している場合ほぼほぼ「id_rsa.pub」が公開鍵です.pubはpublicのpubだと思われます。

これでうまくいかないときはGitHubに送るフォルダやファイルの権限も変えてください。

これらのエラーに対してのアプローチは以上になります。

この記事を書くにあたってかなりの時間を使ってエラーと向き合っていたのですが、正直言ってエラーの原因等が分かりにくくて苦労しました。ただ

CUIでのエラーは良く分からないからといって投げてしまってはなにも解決しません。

何が原因かを考えて試行錯誤していくことでいつか必ず答えが見つかるのでこれを読んでいて頭を抱えてるあなたも諦めずにやりきって下さい。

そして出来ればその過程を記録して解決法をまとめていつか同じように困っている人に教えてあげてください。 自分が助けられたら次は他の誰かを助けてあげるといいかと思います。