Pages

ラベル CakePHP3 の投稿を表示しています。 すべての投稿を表示
ラベル CakePHP3 の投稿を表示しています。 すべての投稿を表示

2019年3月26日火曜日

PHPソースからフロー図の自動作成 Visustin以外で

以前 Visustin というツールを利用してPHPのソースからフロー図を作成しようとしただけど、何かしっくり来ない。。

そして別のツールが無いかなと探していたらこんなブラウザアプリを発見した。

code2flow
https://code2flow.com/


無料プランもあるみたい。

早速トップページ画面右上の GET STARTED から試してみた。



自分は今、メインで CakePHP3 を利用しているので、サンプルソースに ブックマークチュートリアル を利用した。

ブックマークチュートリアル
https://book.cakephp.org/3.0/ja/tutorials-and-examples/bookmarks/intro.html

Gut Hub
https://github.com/cakephp/bookmarker-tutorial



早速ソースをコピペしてみるとこんな感じに。
ん~ さすがに見やすい! とはならないけど、コメントもオレンジで表示されてて便利だ。
ソースやフロー図のところをマウスオーバーすると対となる箇所にガイドが付いて解りやすい。

ソースレビューなどを行う時にこれは便利な気がする。

なかなかこまめに使う場面が自分には無いけども、良いものを見つけたかな。

2018年11月21日水曜日

[CakePHP3] 管理画面とそれ意外でCSRF用のCookieName(クッキー名)を変更したい

/src/Application.php のデフォルト指定をまず削除。
本当は設定を更新したかったが、方法が見つからず。。

$middlewareQueue->get(3) からできるかなと思ったけど無理だよね。。
てか名前も指定できるようになってたらいいのに。。
->add した瞬間にこれ実行してるのかな。。

まーとりあえず以下の方法で対応しました。

public function middleware($middlewareQueue)
{
    $middlewareQueue
        // Catch any exceptions in the lower layers,
        // and make an error page/response
        ->add(ErrorHandlerMiddleware::class)

        // Handle plugin/theme assets like CakePHP normally does.
        ->add(AssetMiddleware::class)

        // Add routing middleware.
        // Routes collection cache enabled by default, to disable route caching
        // pass null as cacheConfig, example: `new RoutingMiddleware($this)`
        // you might want to disable this cache in case your routing is extremely simple
        ->add(new RoutingMiddleware($this, '_cake_routes_'))

        // Add csrf middleware.
        ->add(new CsrfProtectionMiddleware([
            'cookieName' => 'csrf',
            'secure' => true,
            'httpOnly' => true,
        ]));

    return $middlewareQueue;
}

上記がデフォルトの内容となりますが、これから

// Add csrf middleware.
->add(new CsrfProtectionMiddleware([
    'cookieName' => 'csrf',
    'secure' => true,
    'httpOnly' => true,
]));

の部分を削除します。

public function middleware($middlewareQueue)
{
    $middlewareQueue
        // Catch any exceptions in the lower layers,
        // and make an error page/response
        ->add(ErrorHandlerMiddleware::class)

        // Handle plugin/theme assets like CakePHP normally does.
        ->add(AssetMiddleware::class)

        // Add routing middleware.
        // Routes collection cache enabled by default, to disable route caching
        // pass null as cacheConfig, example: `new RoutingMiddleware($this)`
        // you might want to disable this cache in case your routing is extremely simple
        ->add(new RoutingMiddleware($this, '_cake_routes_'));

    return $middlewareQueue;
}

するとこうなる。

/src/Application.php で分岐させても良いけど、Pluginのための分岐なので、
分岐はPlugin側でやる事に。
/plugins/Manage/config/bootstrap.php

ちなみに /plugins/Manage は管理画面用に作成したプラグイン。

plugins/Manage/config/bootstrap.php を更新。

以下の分岐を作成していたので、その中で

use Cake\Event\EventManager;
use Cake\Http\Middleware\CsrfProtectionMiddleware;

$cookieName = 'csrf';
if (!empty($_SERVER['REQUEST_URI']) && preg_match('/^\/manage*/', $_SERVER['REQUEST_URI'])) {
    $cookieName = 'mcsrf';
}

EventManager::instance()->on(
    'Server.buildMiddleware',
    function ($event, \Cake\Http\MiddlewareQueue $middlewareQueue) use ($cookieName) {
        $middlewareQueue->add(new CsrfProtectionMiddleware([
            'cookieName' => $cookieName,
            'secure' => true,
            'httpOnly' => true,
        ]));
    });

のように更新すればOK。

で、確認してたらどうも csrf、mcsrf の両方が登録されてしまう。

何故なんだろうとハマっていたら DebugBar のAjax?で呼んでいる様子。
なので、 DEBUG false にして確認したら無事 /manage 以下では mcsrf だけが利用されており、 /manage と それ以外 で別のクッキー値を参照するようになりましたとさ。

2018年11月20日火曜日

[CakePHP3] セッション(Session)のモデル(テーブル)を通常と特定ディレクトリで分ける方法

SessionのストレージをDatabaseとしている事として以下をメモしています。

管理画面(/manage/*) と その他(/manage/*以外) でセッションデータを保存するDBテーブルを分ける方法。

管理画面(/manage/*) はプラグイン(Plugin)機能を利用して作成。

config/bootstrap.php からプラグインを呼び出す。

Plugin::load('Manage', ['bootstrap' => true, 'routes' => true]);

この時に 'bootstrap' => true を忘れないように。

plugins/Manage/config/bootstrap.php ファイルを作成し、中身は

<?php
use Cake\Core\Configure;

if (!empty($_SERVER['REQUEST_URI']) && preg_match('/^\/manage*/', $_SERVER['REQUEST_URI'])) {
    Configure::write('Session.handler.model','ManageSessions');
    Configure::write('Session.cookiePath','/manage/');
}

DBテーブルは分けるので
デフォルトの sessions と manage_sessions を用意する事を想定する。

CREATE TABLE `sessions` (
  `id` char(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  `created` datetime DEFAULT CURRENT_TIMESTAMP, -- optional, requires MySQL 5.6.5+
  `modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- optional, requires MySQL 5.6.5+
  `data` blob DEFAULT NULL, -- for PostgreSQL use bytea instead of blob
  `expires` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `manage_sessions` (
  `id` char(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  `created` datetime DEFAULT CURRENT_TIMESTAMP, -- optional, requires MySQL 5.6.5+
  `modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- optional, requires MySQL 5.6.5+
  `data` blob DEFAULT NULL, -- for PostgreSQL use bytea instead of blob
  `expires` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2018年11月7日水曜日

[CakePHP3] オリジナルのログイン認証を作成

ちょっとややこしい要件が発生した。

ログインIDが1つでユニークではなく、店舗コード+社員コード という感じで2つのカラムでユニークとなる内容でした。

DBテーブルはこんな感じとする。

table: users
columns: id, shop_id, code, password

table: shops
columns: id, shop_id, code

そして、リクエスト値はこんな感じとする。

code, shop_code, password


やりたいSQLとしては

SELECT *
FROM users
INNER JOIN shops ON users.shop_id = shops.id
WHERE users.code = :code AND shops.code = :shop_code;

で取得してからパスワードチェックするというイメージ。

$this->loadComponent('Auth', [
    'loginAction' => [
        'controller' => 'Users',
        'action' => 'login',
    ],
    'authenticate' => [
        'Form' => [
            'userModel' => 'Users',
            'fields' => [
                'username' => 'username',
                'password' => 'password',
            ],
            'contain' => 'Shops', // 3.1 で非推奨
            'scope' => [], // 3.1 で非推奨
            'finder' => 'login',
        ],
    ],
    'authError' => 'ログインをして、ご利用ください。',
    'loginRedirect' => [
        'controller' => 'Users',
        'action' => 'index'
    ],
    'logoutRedirect' => [
        'controller' => 'Users',
        'action' => 'login'
    ]
]);

って感じでやりたいけど、shop_id を渡せない。。
scope や finder 使ってWHEREに固定した条件を追加する事はできるけど username を追加みたいな事ができない。。

で仕方ないので、オリジナルの Authenticate クラスを作成する事にする。

/src/Auth/UserAuthenticate.php

を作成。

中身は

getTableLocator()->get('Users');

        $user = $userTable->query()
            ->contain(['Shops'])
            ->where([
                'Users.code' => $request->getData('code', ''),
                'Shops.code' => $request->getData('shop_code', ''),
            ])
            ->first();

        $password = $request->getData('password', '');
        if ($this->passwordHasher()->check($password, $user->password)) {
            return $user->toArray();
        }

        return false;
    }
}

作成したら、

$this->loadComponent('Auth', [
    'loginAction' => [
        'controller' => 'Users',
        'action' => 'login',
    ],
    'authenticate' => [
        'Form' => [
            'userModel' => 'Users',
            'fields' => [
                'username' => 'username',
                'password' => 'password',
            ],
            'contain' => 'Shops', // 3.1 で非推奨
            'scope' => [], // 3.1 で非推奨
            'finder' => 'login',
        ],
    ],
    'authError' => 'ログインをして、ご利用ください。',
    'loginRedirect' => [
        'controller' => 'Users',
        'action' => 'index'
    ],
    'logoutRedirect' => [
        'controller' => 'Users',
        'action' => 'login'
    ]
]);

の記述を

$this->loadComponent('Auth', [
    'loginAction' => [
        'controller' => 'Users',
        'action' => 'login',
    ],
    'authenticate' => [
        'User' => [],
    ],
    'authError' => 'ログインをして、ご利用ください。',
    'loginRedirect' => [
        'controller' => 'Users',
        'action' => 'index'
    ],
    'logoutRedirect' => [
        'controller' => 'Users',
        'action' => 'login'
    ]
]);

と修正して完了。

2018年11月6日火曜日

[CakePHP3] パスワードのバリデート (入力 値チェック)

基本的には bake で追加されるメソッド名でのバリデート形式の指定を行うが、正規表現を2回利用したい場合。

$validator
      ->minLength('password', 8, __('6文字以上を入力してください。'))
      ->maxLength('password', 50, __('20文字以下を入力してください。'))
      ->alphaNumeric('password', 'alphaNumeric', __('半角英数字のみ入力可能です。'))
     ->regex('password', '/[a-zA-Z]+/', __('半角英字を1文字以上利用してください。'))
     ->regex('password', '/[0-9]+/', __('半角数字を1文字以上利用してください。'))
      ->requirePresence('password', 'create')
      ->notEmpty('password');

こんな場合は上書きされてしまって、半角英字チェックは動かないので、
こういう場合のみ add で名前を変えて指定するようにして重複を回避する。

$validator
      ->minLength('password', 8, __('6文字以上を入力してください。'))
      ->maxLength('password', 50, __('20文字以下を入力してください。'))
      ->alphaNumeric('password', 'alphaNumeric', __('半角英数字のみ入力可能です。'))
      ->add('password', [
          'inAlpha' => [
              'rule' => ['custom', '/[a-zA-Z]+/'],
              'message' => __('半角英字を1文字以上利用してください。'),
          ],
          'inNumber' => [
              'rule' => ['custom', '/[0-9]+/'],
              'message' => __('半角数字を1文字以上利用してください。'),
          ],
      ])
      ->requirePresence('password', 'create')
      ->notEmpty('password');

  $validator
      ->notEmpty('password_conf')
      ->sameAs('password_conf', 'password', __('異なるパスワードが入力されています。'))
      ->requirePresence('password', 'create')
      ->notEmpty('password');

最初からこの記述を基本にすれば良いと思っていたけどやっぱり bake を基本にした方が効率が良い場面が多いので、
必要な場合のみ利用するようにしました。

regex メソッドの中を見ると解るけど rule としては custom という落とし穴に注意。

[CakePHP3] function _setPassword() が思ったタイミングで動いてくれない

$entity = $this->Samples->get($id);
$entity->password = '12345678';
$this->save($entity);

$entity である

  class Sample extends Entity



  protected function _setPassword($password)

があれば、実行されるのかと思ったいたけどダメでした。。

  $entity->set('password',  '12345678');

でもダメ。。

で、色々試すとここで実行されました。

  $this->Samples->patchEntity($entity, ['password' => '12345678'])

何故この場合だけにしているかは謎ですが、とにかく値を更新する時は必ずこれを利用するようにした方が、
バグが減りそうというメモでした。

2018年11月2日金曜日

[CakePHP3] 文字列型のDBカラムに配列を保存する方法

こんな便利な機能があったとは。。
もっと早く気づきたかった。。

CakePHP3 には文字列カラムに配列をJSON形式などに変換して保存し、取り出し時にまた配列に戻してくれる方法がありました。

_initializeSchema( Cake\Database\Schema\TableSchema $schema )
https://api.cakephp.org/3.6/class-Cake.ORM.Table.html#__initializeSchema

使い方

1.保存したいテーブルの Model にメソッドを追加します。

protected function _initializeSchema(\Cake\Database\Schema\TableSchema $schema)
{
    $schema->setColumnType('categories', 'json');
    return $schema;
}

ここでは カラム名:categories にMultiCheckboxなどで配列で得られる値を JSON形式 で保存する事を想定してみました。

使い方は以上です。

簡単ですね。
今まで知らずに自作してた。。

2018年6月11日月曜日

CakePHP3 + PhpStorm + Compass の設定

最新の CakePHP3 では

 composer install

すると

 src/Sass
 config.rb

が作成される様子なので、このに準ずる事とする。

無ければ以下で作成する

composer.json があるプロジェクトディレクトリ内に移動して初期設定のコマンドを実行。

$ compass create --sass-dir "src/Sass" --css-dir "webroot/css" --javascripts-dir "webroot/js" --images-dir "webroot/img"

src/Sass/screen.scss
 src/Sass/print.scss
 src/Sass/ie.scss
 webroot/css/ie.css
 webroot/css/print.css
 webroot/css/screen.css

が作成されるのが嫌って人は --bare オプションを追加して。

$ compass create --bare --sass-dir "src/Sass" --css-dir "webroot/css" --javascripts-dir "webroot/js" --images-dir "webroot/img"

 src/Sass
 config.rb

が作成されるはず。(他は元からあるはず。)

config.rb の中身はテキストなので確認する。

後でやっぱり

 src/Sass/screen.scss
 src/Sass/print.scss
 src/Sass/ie.scss
 webroot/css/ie.css
 webroot/css/print.css
 webroot/css/screen.css

を作成したいって人は。

$ compass init

を実行すればOK。

--bare の場合はまだ作成されないけど、コンパイルが実行されると

.sass-cache

が作成されるはずなのでこれは .gitignore に追加してバージョン管理されないように。


ここかまでが無ければの人の下ごしらえ。
で、ここから phpstorm の設定方法。

1.phpstorm の設定ウィンドウを開く

 File > Settings (Ctrl + Alt + s)


2.Compass の設定画面に移動する。

 Languages & Frameworks > Sytlesheets > Compass

 この設定はコンパイルの自動化だけなら必要無し。
 恐らく何か便利機能のため。
 1つしかできないので管理用画面と会員用画面みたいな複数で config.rb を分けたい時は多分どうしようもない。。


3.Enable Compass support にチェックを入れる。

自動的に Compass executable file: と Config Path: は設定されるはず。

 Compass executable file: compass コマンドのパス
 Config Path: config.rb のパス


4.File Watchers の設定画面に移動する。

 Tools > File Watchers


5.設定を追加する。

 右に緑の十字アイコンの Add(追加)ボタン をクリックして Compass SCSS を選択。


6.以下の内容を設定

デフォルトである程度適切に設定されてるはずだが、 Arguments: だけ変更。

 File type: SCSS Style Sheet
 Scope: Project Files
 Program: compass or compass.bat のパス
 更新 Arguments: compile
 Output paths to refresh: 空
 Working directory: config.rb を置いているディレクトリパス

※ 動作確認したい場合は Show console: Always に変更して確認でもOK。


以上!

2018年5月20日日曜日

Cakephp3 でPOST値をGET値で上書き

POST値を更新するなんて場面そう無いだろうけど、一応メモ。

以前は
$this->request->data = $this->request->params;

みたいな書き方でいけてたはずだけど、今はNGみたい。

マニュアルにも無いし、そんな場面作るなよって意味だろうけど以下ならできた。

$params = $this->getRequest()->getQueryParams();
$request = $this->getRequest()->withParsedBody($params);
$this->setRequest($request);


2018/11/06 追記

https://book.cakephp.org/3.0/ja/core-libraries/form.html
このページに「値はリクエストメソッドが GET の時にのみ定義されるべきで、 さもないと正しくないまたは保存されていない直前の POST データを上書きしてしまいます。」
と記載があるので、

  $this->request->withData('name', 'John Doe');
  
を利用するのが良いのかもしれません。。
今は get*** と修正されていっているので、

  $this->getRequest()->withData('name', 'John Doe');

かな。

2018年1月15日月曜日

Cakephp3 CallbackStream を利用したCSVダウンロード

CakePHP3 にいつからか(3.0からあったっけ?) CallbackStream ってのがあったのでサンプル作成してみました。

https://book.cakephp.org/3.0/en/controllers/request-response.html

use Cake\Http\CallbackStream;

public function foo()
{
    $items = $this->items->find()->limit(10);

    $this->response->type(['csv' => 'text/csv']);
    $this->response = $this->response->withType('csv');
    $this->response = $this->response->withDownload('foo.txt');

    $stream = new CallbackStream(function () use ($items) {
        foreach ($items as $item) {
            echo $item->id . ',' . $item->name . "\n";
        }
    });
    $this->response = $this->response->withBody($stream);
    return $this->response;
}

メモリー利用方法までは確認できていませんが、そこが問題無ければフレームワークに用意されているのであれば積極的に利用した方がよさそうですね。
ちなみにヘッダー出力は $this->response->withHeader() を利用しなさいって事になってるようです。

2016年6月28日火曜日

CakePHP3 フォーム利用せずに hasMany 指定のテーブルに追加して save する

何か意味不明なタイトルになりましたが。。

今回シェル内でhasManyに指定されたTagsにデータを追加する事をsave一つでやりたかったんです。

何かしっくりこない気がまだしていますが、とりあえずこんな感じかな。。
$article = $articles->get(1, ['contain' => 'Tags']);

$data = $article->ToArray();
$data['tags'][] = [
  'name' => 'Tag Name',
];

$articles->patchEntity($article, $data, ['associated' => 'Tags']);
$articles->save($article);

2016年5月25日水曜日

CakePHP3 文字数チェックで改行コードがWindowsの時は2文字扱いになる

簡単に予想するからに

環境別改行コード

Windows CR+LF
Macintosh CR
Linux LF

って感じで Windows だけ2バイトになってるからだろう。。
って事で Validation.php を確認すると

public static function maxLength($check, $max)
{
    return mb_strlen($check) <= $max;
}
やっぱり。。 って事で AppController.php(デフォルトでは無い?) の initialize() に
array_walk_recursive($this->request->data, function(&$item, $key){
    // 改行コードをLFに統一
    $item = preg_replace("/\r\n|\r|\n/", "\n", $item);
});
を追加して今回は対応。

2016年1月8日金曜日

CakePHP2 & CakePHP3 の alphaNumeric は日本語も通過する

CakePHP2 & CakePHP3 の alphaNumeric が日本語も通過しちゃうので、内容を確認したメモ


ソース

public static function alphaNumeric($check) {
  if (is_array($check)) {
    extract(self::_defaults($check));
  }

  if (empty($check) && $check != '0') {
    return false;
  }
  return self::_check($check, '/^[\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]+$/Du');
}

パターン

http://php.net/manual/ja/regexp.reference.unicode.php

\p{Ll} | 小文字アルファベット
\p{Lm} | 擬似文字
\p{Lo} | 大文字小文字の区別や修飾文字を持たないその他の文字
\p{Lt} | タイトル文字
\p{Lu} | 大文字アルファベット
\p{Nd} | 多言語における10進数字

パターン修飾子

D (PCRE_DOLLAR_ENDONLY)

この修飾子を設定すると、パターン内のドルメタ文字は、検索対象文字列の 終わりにのみマッチします。
この修飾子を設定しない場合、ドル記号は、 検索対象文字列の最後の文字が改行文字であれば、その直前にもマッチします。
この修飾子は、m を設定している場合に無視されます。
Perl には、この修飾子に等価なものはありません。

u (PCRE_UTF8)

この修正子は、Perl 非互換な PCRE の機能を有効にします。
パターンと対象文字列は、 UTF-8 として処理されます。
この修正子は、UNIX では PHP 4.1.0 以降、Win32 では PHP 4.2.3 以降で 使用可能です。
また、PHP 4.3.5 以降では、パターンの UTF-8 としての妥当性も確認されます。
な対象文字列を preg_* 関数に渡しても、何もマッチしません。
無効なパターンを渡すと、E_WARNING レベルのエラーが発生します。
5オクテットおよび6オクテットの UTF-8 シーケンスは、PHP 5.3.4 以降 (PCRE 7.3 2007-08-28 以降) では無効とみなされます。
以前のバージョンでは、これらも UTF-8 として有効だとみなされていました。

※ 上記説明解りにくい人はこのサイトの説明見たら解るかも?
http://pentan.info/php/reg/use.html

処理内容

つまり上記文字のどれかが1回以上利用された文字だけで構成されたものかをチェックする

Followers