CakePHP3でisAuthorizedファンクションを通らないときの対処

CakePHP3での話です。

コントローラに記述するisAuthorizedファンクションってありますよね。

アクセス制御などを行うファンクションです。

ここを上手く処理が通ってくれないことがありました。

処理が止まるとかではなく、全く処理が通過する気配がなかったんです。

そういうときの対処をお伝えします。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

beforeFilterファンクションでloginアクションが入っていないか確認する

これを確認してください。

正直これだけです。

beforeFilterファンクションはisAuthorizedファンクションとは逆で、全部処理を通すよ、というファンクションです。

なので関連があると言って良いでしょう。

公式ドキュメントを見るとbeforeFilterファンクションのところにこう書いてあります。

>allow のリストに "login" アクションを追加しないでください。
>そうすると AuthComponent の正常な機能に問題が発生します。

シンプルな認証と認可のアプリケーション - 3.9

これは認証と認可をするAuthComponentが上手く動作しなくなるということです。

自分はよくわからずlogin画面は認可されていないユーザーでもみんなアクセスしたい…と思って足していました。

するとisAuthorizedに処理が通らなくなってしまっていました。

気をつけたいです。

終わりに

以上となります。

地味な点ですが、セキュリティに大きな影響を及ぼすので気をつけたいです。

ありがとうございました。

Webサービスの利用規約とプライバシーポリシーの作り方

Webサービス利用規約、プライバシーポリシーの作り方を説明します。

正直言って、この部分はかなりめんどくさいですよね。

ですが書いておかないと運用し始めた後に大変なことになると思います。

しっかり書いておきましょう。

まずこのQiitaがとても参考になりました。

Webサービス個人開発するなら知りたい利用規約とプライバシーポリシーの作り方 - Qiita

それでは見ていきましょう。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

まず利用規約って何?

具体的には、サービス内容・利用料・サービス利用にあたっての禁止事項・トラブル発生時の解決方法などです。

事業者・ユーザーそれぞれが守らなければいけないルールが定められています。

ところでプライバシポリシーって何?

プライバシーポリシーとは、事業者が個人情報も含めたプライバシーに関する情報の取扱方針を定めた文書のことをいいます。

Webサービスなどは、以上の2つはしっかり書いてあげる必要があります。

実際の書き方

1から書くのは大変です。

雛形があるので、それを参考にしましょう。

汎用的な利用規約の雛形(ひな型) | Webサイトの利用規約(無料テンプレート・商用利用可)

プライバシーポリシーの雛形(ひな型) | Webサイトの利用規約(無料テンプレート・商用利用可)

そして雛形だけでなく、自分のWebサービスに似た実際の利用規約・プライバシーポリシーを探して参考にしていきます。

こうすることで自分のWebサービスの場合は?というようなイメージが掴みやすくなります。

自分は完全無料のサービスなので、そのようなサービスを探して参考にしました。

文言を変えるところがわかってくるので、そこを自分のサービスの場合の文言にしていきます。

終わりに

とはいえめんどくさいと思います。

ざっくり両方書き上げるのに3時間くらいかかりました。

良いアドバイスかどうかはわかりませんが、ここはたぶんコピペでいいだろ、という割り切りも必要かもしれません。

雛形や参考Qiitaはとても良いので、見てみてください。

ありがとうございました。

画像名に日本語が入っていると画像が上手く表示されない

画像名に日本語が入っていると画像が上手く表示されないということがありました。

CakePHP関係なく、WordPressなどでもそうらしいです。

最初は「PCで上げた画像がスマホで表示されない」と悩んでいたんですが、それは厳密には違いました。

PCのスクリーンショットの画像は「スクリーンショット+[日時].jpeg」みたいな名前になります。

これがスマホでは画像名に日本語(「スクリーンショット」というカタカナ)が入っているので表示できないということでした。

当時めちゃくちゃ焦りましたが、解決できたので書いていきます。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

画像名に日本語を使わないでIDなどを使うようにする

結論として「画像名に日本語を使わないでIDなどを使うようにする」ようにしました。

例えば命名規則が単純に「日時+[画像名].拡張子」だと日本語が混じる可能性があります。

なので「日時+[画像名].拡張子」みたいにしてしまいます。

自分の実際のコードは以下です。

// POST送信時の処理
        if($this->request->isPost()){
            // 画像ファイル名称の先頭に時間をつけて/webroot/imgに移動させる
            $upload_file = $this->request->getData('name');
            $filePath_pc = WWW_ROOT.'img/'.date("YmdHis").$this->Auth->user('id').'.'.pathinfo($upload_file['name'])['extension']; // 注目する行
            $filePath_sp = WWW_ROOT_SP.'img/'.date("YmdHis").$this->Auth->user('id').'.'.pathinfo($upload_file['name'])['extension']; // 注目する行
            if(is_uploaded_file($upload_file['tmp_name']) && copy($upload_file['tmp_name'],$filePath_pc) && move_uploaded_file($upload_file['tmp_name'],$filePath_sp)){
                // photoにフォームの送信内容を反映
                $data = [
                    'name' => date("YmdHis").$this->Auth->user('id').'.'.pathinfo($upload_file['name'])['extension'], // 注目する行
                    'user_id' => $this->request->getData('user_id'),
                ];
                $photo = $this->Photos->newEntity($data);
                // photoに保存する
                if($this->Photos->save($photo)){
               (中略)

コードの一部なので、見辛いかもしれません。

まず前提として、PC画像とスマホ画像でパスを分けています。

「注目する行」と書いたところを見ていただきたいです。

この3つの行で画像を命名しています。

一番分かりやすいところで最後のDBに格納する名前の値の部分ですが、以下になっています。

'name' => date("YmdHis").$this->Auth->user('id').'.'.pathinfo($upload_file['name'])['extension'], // 注目する行

まずdate("YmdHis")(日時)、次に$this->Auth->user('id')(ユーザーID)、最後に拡張子です。

※pathinfoファンクションは拡張子などが取れるファンクションです。

これで命名に日本語を避けています。

ユーザーID以外にもいろんな方法があると思うので、好きな方法を取ってください。

終わりに

以上となります。

とにかく画像名は日本語を避けたほうが無難かと思います。

画像が命のサービスなので、そこは気をつけました。

他にも良い方法があったら教えていただきたいです。

ありがとうございました。

CakePHP3でファイルアップロードボタン(ファイルを選択)の文字がスマホ版だと大きくならない

ファイルアップロードボタン(ファイルを選択)の文字、ありますよね。

これがなぜかスマホ版だとCSSで指定してfont-sizeを変えても大きくならないことがありました。

ブラウザ依存だからですかね。

そこで強引にではありますが解決したので、その方法を記します。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

ファイルアップロードボタン(ファイルを選択)を新たに生成する

結論から言うと「ファイルアップロードボタン(ファイルを選択)を新たに作ってしまう」ということです。

もともと以下のようになっています。

<?= $this->Form->create($photo,['type'=>'file']) ?>
        <fieldset>
            <p class="title">〜画像を選択〜</p>
            <?php
                echo $this->Form->file('name',['class'=>'file']);
            ?>
        </fieldset>
        <?= $this->Form->button(__('写真を登録する!'),['class'=>'button']) ?>
        <?= $this->Form->end() ?>

これだと普通のファイルアップロードボタンです。

PC画面だとこれでフォントサイズを変えられていいんですが、スマホ画面だと変わらなかったです。

そこで以下のようにします。

<?= $this->Form->create($photo,['type'=>'file']) ?>
        <fieldset>
            <p class="title">〜画像を選択〜</p>
            <?php
                echo '<label>'.'<div class="choice">画像選択ボタン</div>'.$this->Form->file('name',['class'=>'file']).'</label>'; // この行を変更
            ?>
        </fieldset>
        <?= $this->Form->button(__('写真を登録する!'),['class'=>'button']) ?>
        <?= $this->Form->end() ?>

「この行を変更」とした部分を見てください。

元々は「$this->Form->file('name',['class'=>'file'])」だけでした。

それを

<div class="choice">画像選択ボタン</div>'.$this->Form->file('name',['class'=>'file'])

とした上で、さらに無理やりlabelタグで囲っていますね。

labelタグで囲むことで、label部分(「画像選択ボタン」を含む部分)をクリックすることで、実はファイル選択ダイアログが表示できるのです。

この考え方は、以下のサイトが参考になりました。

input type='file'のデザインを変更する

CSSを調整する

仕上げにCSSを調整します。

元々の「ファイルを選択」みたいなボタンはdisplay: none;で非表示にします。

あとは「画像選択ボタン」を自由に調整します。

自分は以下のようなCSSにしました。

.main_text .image_add .choice{
    width: 500px;
    height: 100px;
    font-size: 60px;
    margin: 100px 0;
    background-color: #000000;
    color: #FFFFFF;
    border-radius: 16px;
    text-align: center;
    line-height: 90px;
}
.main_text .image_add .file{
    display: none;
}

ちょっと長いですが、まず要点は.file(元々のボタン)をdisplay: none;にして、.choice(作ったボタン)を目立たせているということです。

ちなみにスマホ版での表示です。

参考にしてみてください。

終わりに

以上となります。

意外とハマった場面でした。

スマホ画面にもこだわりたいという方は、お試しください。

ありがとうございました。

CakePHP3でバリデーションを発動させる3つの方法

CakePHP3でバリデーションを発動させたいとき、ありますよね。

なぜか発動してくれないときとか、もどかしいです。

バリデーションを発動させる3つの方法について説明します。

※目次のタイトルではわかりやすく「$this->Users」としていますが、「$table」と読み替えて問題ありません。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

①$this->Users->newEntity()

まず1つ目はnewEntity()メソッドです。

これはエンティティの作成時に使うメソッドですね。

Usersテーブルがidカラム(auto_increment)、nameカラム、roleカラムの構成で、かつPOSTでデータが送られている場合、以下のようなかんじで使用します。

$data = [
                    'name' => $this->request->getData('name'),
                    'role' => $this->request->getData('role'),
                ];
$user = $this->Users->newEntity($data);

これで「newEntity($data)」のところでそれぞれのカラムの値をバリデーションしてくれます。

②$this->Users->patchEntity()

patchEntity()メソッドの時も発動されます。

patchEntity()はエンティティの更新です。

patchEntity()の実用場面については、たくさん文献があるので調べてみてください。

③$this->Users->validate()

最後はvalidate()メソッドです。

これは自分で明示的にバリデーションしたい、というときに使えるメソッドです。

なぜかバリデーションされないな…というときに強引にバリデーションさせたい、という場面で使用したりしました。

以下は実用場面です。

$contacts = new ContactsForm();
$contacts->validate($this->request->getData())

2行目で「$this->request->getData()」の値をバリデーションしてます。

終わりに

以上となります。

バリデーションはユーザーの使いやすさやセキュリティの面で必須な機能です。

いろいろな方法があるので、適したものを使ってみてください。

ありがとうございました。

MAMP環境で作ったサイトをPC以外の端末から見たい

MAMP環境を使ってます。

開発段階でサイトをPC以外の端末から覗きたい時ってありますよね。

自分はスマホで見られるように設定しています。

その設定を見ていきます。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

PCとスマホを同じWi-Fiに設定する

まず同じWi-Fiに設定してください。

このWi-FiIPアドレスApachehttpd.confの設定で許可していく流れです。

macOSでのIPアドレスの確認の仕方

念のためIPアドレスの確認方法です。

画面上のメニューバーのリンゴマークから「システム環境設定」>「ネットワーク」と表示すると、上の方にIPアドレスが書いてあります。

「●●●.●●●.●.●●」のような数字の羅列です。

httpd.confの設定

Applications/MAMP/conf/apacheディレクトリの中のhttpd.confファイルをエディターで開きましょう。

中にいろいろな設定があります。

以下のように設定しましょう。

#Listen 12.34.56.78:80 // 既存の行
Listen 8888 // 既存の行
Listen ●●●.●●●.●.●●:8888 // 追加する行

「●●●.●●●.●.●●」は自身のIPアドレスに置き換えてください。

MAMPを再起動する

Apacheの設定を反映させるため、MAMPを再起動してください。

起動しましたら、タブレット端末やスマホWebブラウザで「●●●.●●●.●.●●:8888」でローカル開発環境にアクセスすることができるようになります。

MAMPのアプリケーションのページですと「●●●.●●●.●.●●:8888/MAMP/」でアクセスできます。

これでWi-Fiに接続していればスマホでも何でもサイトを見ることができます。

試してみてください。

注意点

たまにスマホのキャッシュが残っていると、うまく接続できないです。

キャッシュを消してアクセスすると、すんなり入れるかと思います。

終わりに

以上となります。

割と簡単な設定でPC以外の端末での閲覧を許可できます。

デザインを見たりする際に必要になるので、使ってみてください。

ありがとうございました。

CakePHP3でモデルのないフォームからメール送信したい

CakePHP3でメール送信したい時ってあると思います。

通常はコントローラにメール送信処理を書きます。

ですが前回の続きの内容となるので、モデルのないフォームにメール送信処理を書いて送る方法を今回は見ていきます。

お問い合わせフォームでサンクスメールを相手に送ったり、お問い合わせ内容の載ったメールを自分に送りたかったりしますよね。

そこで今回はメール送信の方法を見ていきます。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

大前提

メール送信のプログラムを書く前に、設定が必要です。

自分の場合はMAMP環境でGmailから送る方法を調べました。

以下の記事が神のようにわかりやすいです。

【2021年版】PostfixからGmail経由でメールを送信する方法

結構な設定なので時間がかかりますが、MAMPの方は必須です。

他のレンタルサーバーの場合などは自分は分かりかねますので、各自で設定をお願い致します。

それではコードを見ていきましょう。

app.phpの設定を見る

app.phpにあるEmailTransportは以下のようになっていると思います。

'EmailTransport' => [
        'default' => [
            'className' => MailTransport::class,
            /*
             * The keys host, port, timeout, username, password, client and tls
             * are used in SMTP transports
             */
            'host' => 'localhost',
            'port' => 25,
            'timeout' => 30,
            /*
             * It is recommended to set these options through your environment or app_local.php
             */
            //'username' => null,
            //'password' => null,
            'client' => null,
            'tls' => false,
            'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null),
        ],
    ],

恐らくデフォルトでこうなっています。

もしも「className」が「smtp」になっていると別途設定が必要なので、気をつけてください。

以下は「className」が「Mail」や「MailTransport::class」となっている体で話します。

それでは見ていきましょう。

モデルのないフォームの_executeファンクションにメール送信処理を記述

モデルのないフォームは以下のようになります。

(中略)
use Cake\Mailer\Email;
(中略)
protected function _execute(array $data)
    {
        // メールを送信する
        $info_email = new Email('default');
        $info_email
            ->setFrom(['example@gmail.com'=>'事務局'])
            ->setTo('example@gmail.com')
            ->setSubject('ご意見・ご要望がありました。')
            ->send('お客様のお名前:'.$data['name'].' 様'."\n\n".'メールアドレス:'."\n".$data['email']."\n\n".'内容:'."\n".$data['body']);


        $thank_email = new Email('default');
        $thank_email
            ->setFrom(['example@gmail.com'=>'事務局'])
            ->setTo($data['email'])
            ->setSubject('ご意見・ご要望をありがとうございます!')
            ->send($data['name'].' 様'."\n\n".
                    '貴重なご意見・ご要望をいただきまして、誠にありがとうございます!'."\n".
                    'サービス改善のヒントとして役立てるほか、必要があればお客様に折り返しメールを差し上げることもございます。'."\n\n".
                    'お客様のお名前:'.$data['name']."\n\n".'メールアドレス:'."\n".$data['email']."\n\n".'内容:'."\n".$data['body']);

        return true;
    }

このようになっています。

まずinfo_emailというのは自分から自分宛てのメールです。

自分の場合はユーザーからのお問い合わせの内容を書いています。

そしてthank_emailは自分からお問い合わせユーザーに向けたメールです。

本文には「ご意見いただき、ありがとうございます」みたいなことが書いてあります。

構造ですが、一番上で「use Cake\Mailer\Email;」しています。

これでEmailクラスが使えるようになります。

そして_executeファンクションでは、そのEmailクラスを使って「new Email('default');」という記述があります。

まず「$info_email = new Email('default');」みたいなかんじで$info_emailに代入すると、メールのインスタンスができます。

そこからはメソッドチェーンで送信元(setFrom)、宛先(setTo)、題名(setSubject)など設定していきます。

重要なのがメールの本文(sendの引数)です。

あらかじめ$dataの中にname(お問い合わせユーザーの名前)、email(ユーザーのメールアドレス)、body(お問い合わせ内容)が入っています。

なのでこれを使ってメールの本文を作っています。

上記のものは実際のコードなので参考にしてみてください。

これでバリデーションに通れば送信できます。

終わりに

以上となります。

なかなかモデルのないフォームの記事でメール送信の方法が書いていなかったので苦労しました。

記事として残したいと強く思っていた部分です。

この記事は以下のページも参考にしながら書きました。

CakePHP3のメール送信の処理・テンプレート使用・添付ファイル送信も解説 | エス技研

このページには通常のコントローラでメール送信処理する方法が書いてあります。

ありがとうございました。