docker-compose up -dでエラー(network ネットワーク名 declared as external, but could not be found)

dockerをアップデート後に、以前は動いていたプロジェクトをdocker-composeでupしようとしたらエラーがでました。

dp-local:docker dp$ docker-compose up -d
network ネットワーク名 declared as external, but could not be found

「could not be found」・・あれ、ネットワークってアップデートで消えるっけ?

network *** declared as external, but could not be foundエラーの対策

$ docker network create ネットワーク名
94e22c6998b77e55944db7c4f41b6acf48efa366bf4abe224ff574510

でネットワークを再作成すればOK

フォームプラグインのContact form 7にキントーンとの連携などできないか、独自のカスタムができるのか調べてみる

/wp-json/contact-form-7/v1/contact-forms/{postId}/feedback

フォームの送信先はAjaxでここにアクセスしてた。
ここで作成されたカスタムエンドポイント
plugins/contact-form-7/includes/rest-api.php

register_rest_route( $namespace,
		'/contact-forms/(?P<id>\d+)/feedback',
		array(
			array(
				'methods' => WP_REST_Server::CREATABLE,
				'callback' => 'wpcf7_rest_create_feedback',
				'permission_callback' => '__return_true',
			),
		)
	);
function wpcf7_rest_create_feedback( WP_REST_Request $request ) {
	$url_params = $request->get_url_params();

	$item = null;

	if ( ! empty( $url_params['id'] ) ) {
		$item = wpcf7_contact_form( $url_params['id'] );
	}

	if ( ! $item ) {
		return new WP_Error( 'wpcf7_not_found',
			__( "The requested contact form was not found.", 'contact-form-7' ),
			array( 'status' => 404 )
		);
	}

	$result = $item->submit();

	$unit_tag = $request->get_param( '_wpcf7_unit_tag' );

	$response = array(
		'into' => '#' . wpcf7_sanitize_unit_tag( $unit_tag ),
		'status' => $result['status'],
		'message' => $result['message'],
		'posted_data_hash' => $result['posted_data_hash'],
	);

wpcf7_contact_formで取得したインスタンスのsubmitを実行してるので中身をみる

contact-form.php

public function submit( $args = '' ) {
		$args = wp_parse_args( $args, array(
			'skip_mail' =>
				( $this->in_demo_mode()
				|| $this->is_true( 'skip_mail' )
				|| ! empty( $this->skip_mail ) ),
		) );

		if ( $this->is_true( 'subscribers_only' )
		and ! current_user_can( 'wpcf7_submit', $this->id() ) ) {
			$result = array(
				'contact_form_id' => $this->id(),
				'status' => 'error',
				'message' => __(
					"This contact form is available only for logged in users.",
					'contact-form-7'
				),
			);

			return $result;
		}

		$submission = WPCF7_Submission::get_instance( $this, array(
			'skip_mail' => $args['skip_mail'],
		) );

		$result = array(
			'contact_form_id' => $this->id(),
			'status' => $submission->get_status(),
			'message' => $submission->get_response(),
			'demo_mode' => $this->in_demo_mode(),
		);

		if ( $submission->is( 'validation_failed' ) ) {
			$result['invalid_fields'] = $submission->get_invalid_fields();
		}

		switch ( $submission->get_status() ) {
			case 'init':
			case 'validation_failed':
			case 'acceptance_missing':
			case 'spam':
				$result['posted_data_hash'] = '';
				break;
			default:
				$result['posted_data_hash'] = $submission->get_posted_data_hash();
				break;
		}

		do_action( 'wpcf7_submit', $this, $result );

		return $result;
	}

ahamoに契約変更ができない(明細内訳表示設定(WORLD WING):「下4桁を除き表示」から「全桁表示(すべて表示)」への設定変更)

ahamoにdocomoのギガホから変更しようと思ったら、「料金明細サービス」の設定状態が原因で手続きができませんでした。

「現在のご契約状態ではahamoへのプラン変更ができません。」

「料金明細サービス 明細内訳表示設定(WORLD WING) 下4桁を除き表示■料金明細サービス明細内訳表示設定(WORLD WING):「下4桁を除き表示」から「全桁表示(すべて表示)」への設定変更をお願いします」

そんな設定した覚えがなく、WORLD WINGも加入はしてるけどってレベルなので調べました。

現在のご契約状態ではahamoへのプラン変更ができません。

以下の項目をご確認いただき、各サービスの申込/廃止/設定変更等のお手続きを実施いただいた後、ahamoへのプラン変更をお申込みください。

サービスの申込/廃止/変更については、My docomo、ドコモインフォメーションセンター(局番なし151)、お近くのドコモショップにてお手続きが可能です。

※dアカウントの発行、dポイントクラブ入会、オンライン発行dポイントカード発行/登録がお済みでない方は、お手続きをお願いします。

※ahamoへのプラン変更時の「事前に必要なお手続き」について詳しくはコチラ

料金明細サービス 明細内訳表示設定(WORLD WING) 下4桁を除き表示

■料金明細サービス

明細内訳表示設定(WORLD WING):「下4桁を除き表示」から「全桁表示(すべて表示)」への設定変更をお願いします

WORLD WINGの下4桁を除き表示なんて設定したっけ?というくらい忘れてる項目でどこから変えるかもわからない。。

確かにDocomoの契約変更から見ると「WORLD WING」は契約中で料金明細の表示区分は下4桁を除き表示ってなってるけど、お手続き内容に「国際ローミングサービス( WORDL WING)を解約する」しかない。。

どこから設定変更するんだろう。

ahamoだと「WORLD WING Wi-Fi」が使えないこととローミングできる国が少ないのは知ってるけど、明細の表示とかどうでもよくない?

151で聞いたらWORLDWING明細は未契約だった

151のWORDLWING窓口でmydocomoを見ながら教えていただいたんですが、WORDLWINGは契約中だけど、WORDLWING料金明細サービスは未契約でした。

ちなみに下4桁を除き表示はデフォルト設定らしく、ローミング中に電話した相手の電話番号を明細にどう表示するか?の設定らしいです。

ahamoの窓口に電話で調べてもらった

自分の場合はWORDLWING料金明細サービスは未契約なので、WORDLWINGの設定変更とかじゃなく、ahamo側の問題ということで電話を回してもらえました。

料金明細サービスを申し込む必要がある

自分の場合、料金明細サービスに申し込んでいなかったので、エラーだったのですが、「明細内訳表示設定(WORLD WING):「下4桁を除き表示」から「全桁表示(すべて表示)」への設定変更をお願いします」という表示になってしまっているとのことでした。

申込はmydocomoから行えて、申し込み後すぐに適用されます。

申込後、設定を見てみると契約中となって、表示区分も全桁表示になっていました。

下4桁表示がデフォルトって言われてたけど。。未契約状態の表示のことなのかな。

WORDL WINGのエラーが表示されなくなった

これでahamoでエラーがでなくなりました。

Redash(ver9)のDocker-compose upでエラー – Cannot install -r requirements_all_ds.txt

Redashのソースコードを修正する必要がでたのでredash(v9)をdocker-composeで起動した際にbuildでエラーが発生したので修正

rsion is compatible with other requirements. This could take a while.
INFO: pip is looking at multiple versions of atsd-client to determine which version is compatible with other requirements. This could take a while.
ERROR: Cannot install -r requirements_all_ds.txt (line 12), -r requirements_all_ds.txt (line 21) and -r requirements_all_ds.txt (line 34) because these package versions have conflicting dependencies.

The conflict is caused by:
    atsd-client 3.0.5 depends on python-dateutil
    azure-kusto-data 0.0.35 depends on python-dateutil>=2.8.0
    dql 0.5.26 depends on python-dateutil<2.7.0

To fix this you could try to:
1. loosen the range of package versions you've specified
2. remove package versions to allow pip attempt to solve the dependency conflict

ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/user_guide/#fixing-conflicting-dependencies
ERROR: Service 'server' failed to build : The command '/bin/sh -c if [ "x$skip_ds_deps" = "x" ] ; then pip install -r requirements_all_ds.txt ; else echo "Skipping pip install -r requirements_all_ds.txt" ; fi' returned a non-zero code: 1
rsion is compatible with other requirements. This could take a while.
INFO: pip is looking at multiple versions of atsd-client to determine which version is compatible with other requirements. This could take a while.
ERROR: Cannot install -r requirements_all_ds.txt (line 20) and mysqlclient==1.3.14 because these package versions have conflicting dependencies.
ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/user_guide/#fixing-conflicting-dependencies

The conflict is caused by:
    The user requested mysqlclient==1.3.14
    memsql 3.0.0 depends on mysqlclient==1.3.13

To fix this you could try to:
1. loosen the range of package versions you've specified
2. remove package versions to allow pip attempt to solve the dependency conflict

ERROR: Service 'server' failed to build : The command '/bin/sh -c if [ "x$skip_ds_deps" = "x" ] ; then pip install -r requirements_all_ds.txt ; else echo "Skipping pip install -r requirements_all_ds.txt" ; fi' returned a non-zero code: 1
rc211v-linough:redash-9.0.0-beta yuta_takehira$ docker-com

requirements_all_ds.txtの2箇所をコマンとアウトして再実行すれば起動できました。

memsqlとdqlです。

google-api-python-client==1.7.11
gspread==3.1.0
impyla==0.16.0
influxdb==5.2.3
mysqlclient==1.3.14
oauth2client==4.1.3
pyhive==0.6.1
pymongo[tls,srv]==3.9.0
vertica-python==0.9.5
td-client==1.0.0
pymssql==2.1.4
#dql==0.5.26
dynamo3==0.4.10
boto3>=1.10.0,<1.11.0
botocore>=1.13,<1.14.0
sasl>=0.1.3
thrift>=0.8.0
thrift_sasl>=0.1.0
cassandra-driver==3.21.0
#memsql==3.0.0
atsd_client==3.0.5
simple_salesforce==0.74.3
PyAthena>=1.5.0
pymapd==0.19.0
qds-sdk>=1.9.6
ibm-db>=2.0.9
pydruid==0.5.7
requests_aws_sign==0.1.5
snowflake-connector-python==2.1.3
phoenixdb==0.7
# certifi is needed to support MongoDB and SSL:
certifi>=2019.9.11
pydgraph==2.0.2
azure-kusto-data==0.0.35
pyexasol==0.12.0
python-rapidjson==0.8.0
pyodbc==4.0.28

javascript(electron/nodejs)でカタカナ(濁点あり)の文字が不一致(ジ==ジがfalse)になる

ElectronでGoogleSpreadSheetやGoogleDriveからデータを取得して、文字列の一致判定で濁点が含まれる文字の場合、見た目は同じだけど別の文字として判定されてしまうケースが出てしまった。

見た目は一緒だけど不一致になる濁点カタカタ

ElectronからGoogle SpreadSheetに結果を出力すると、確かに見た目は一緒だけど、SpreadSheetの文字検索でも一致とはならないし、コピーしてVSCodeやSublimeでテキスト検索を試しても別の文字として判定される。

試しにjsで試すとこんな感じ

Welcome to Node.js v15.4.0.
Type ".help" for more information.
> let a = 'ジ'
undefined
> let b = 'ジ'
undefined
> a == b
false

濁点文字の文字コード

調べてみると、UTFとSJISとかのい問題ではなく、UTFの中でも濁点ありには複数あって、濁点を含んだ文字か、濁点が追加された文字か?の違いだった。

escapeでコードを確認すると「し」の濁点ありでも、コードも長さも異なる。

> escape(a)
'%u30B8'
> escape(b)
'%u30B7%u3099'
> a.length
1
> b.length
2

String.normalize( NFC/NFD )

これはstring.normalizeで統一することができるようで、NFCとNFDを指定することで任意の型式に変換できる。

今回はSpreadSheetやDriveからの文字取得時にはNFCに統一する型式にして回避

> escape(a.normalize('NFC'))
'%u30B8'
> escape(b.normalize('NFC'))
'%u30B8'
> escape(a.normalize('NFD'))
'%u30B7%u3099'
> escape(b.normalize('NFD'))
'%u30B7%u3099'

Laravelのmigrateで「doctrine/dbal」をインストールしても「Class ‘Doctrine\DBAL\Driver\PDOMySql\Driver’ not found」が発生

rc211v-mbp:backend ros2$ php artisan migrate
Migrating: 2020_12_07_191937_add_provider_users_table

   Error 

  Class 'Doctrine\DBAL\Driver\PDOMySql\Driver' not found

  at vendor/laravel/framework/src/Illuminate/Database/MySqlConnection.php:89
     85▕      * @return \Doctrine\DBAL\Driver\PDOMySql\Driver
     86▕      */
     87▕     protected function getDoctrineDriver()
     88▕     {
  ➜  89▕         return new DoctrineDriver;
     90▕     }
     91▕ }
     92▕ 

      +8 vendor frames 
  9   database/migrations/2020_12_07_191937_add_oauth_users_table.php:22
      Illuminate\Support\Facades\Facade::__callStatic("table")

      +22 vendor frames 
  32  artisan:37
      Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))

タイトルの通り、doctrine/dbalをインストールしても「Doctrine\DBAL\Driver\PDOMySql\Driver」エラーが発生して、「Doctrine\DBAL\Driver\PDOMySql\Driver」で検索すると「composer require doctrine/dbal」が必要という記事がよく見つかります。

「composer require doctrine/dbal」は成功しています。

Using version ^3.0 for doctrine/dbal
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Package fzaninotto/faker is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: facade/ignition
Discovered Package: fideloper/proxy
Discovered Package: fruitcake/laravel-cors
Discovered Package: laravel/socialite
Discovered Package: laravel/tinker
Discovered Package: laravel/ui
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Discovered Package: rcrowe/twigbridge
Package manifest generated successfully.
78 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

ですがファイル「Doctrine\DBAL\Driver\PDOMySql\Driver」はvendorにないので、どうやらバージョンが不一致のようです。

バージョン指定なしでインストールして「3.0」が入っていましたが、Githubで2系の情報をみると、「Doctrine\DBAL\Driver\PDOMySql\Driver」があるので、Laravelのマイグレーションでは2系をインストールする必要があります。

composer require "doctrine/dbal:2.*"
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 3 installs, 0 updates, 0 removals
  - Installing doctrine/event-manager (1.1.1): Loading from cache
  - Installing doctrine/cache (1.10.2): Loading from cache
  - Installing doctrine/dbal (2.12.1): Downloading (100%)         
doctrine/cache suggests installing alcaeus/mongo-php-adapter (Required to use legacy MongoDB driver)

2系をインストール後にmigrateを実行すると成功!

$ php artisan migrate
Migrating: 2020_12_07_191937_add_provider_users_table
Migrated:  2020_12_07_191937_add_provider_users_table (230.26ms)

さくらレンタルサーバーでLINE BOT SDKがComposerでインストールできない( requires ext-sockets * ) sockets.soエラー

LINE BOTでFlexメッセージなどをテストする必要があり、最新版のline-bot-sdkを別ディレクトリにcomposerでインストールしようとしたところエラーとなりました。

エラー「requires ext-sockets」

以前はさくらのレンタルサーバーでもcomposer でLINE BOT SDKがインストールできましたが、下記のエラーでLine-bot-sdkのインストールに失敗。。

Using version ^4.5 for linecorp/line-bot-sdk
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Installation request for linecorp/line-bot-sdk ^4.5 -> satisfiable by linecorp/line-bot-sdk[4.5.0].
    - linecorp/line-bot-sdk 4.5.0 requires ext-sockets * -> the requested PHP extension sockets is missing from your system.

  To enable extensions, verify that they are enabled in your .ini files:
    - /usr/local/php/7.4/etc/php.ini
    - /usr/local/php/7.4/etc/conf.d/apcu.ini
    - /usr/local/php/7.4/etc/conf.d/mcrypt.ini
    - /usr/local/php/7.4/etc/conf.d/opcache.ini
  You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode.

Installation failed, deleting ./composer.json.

エラーはext-socketsがphpで有効になっていない為で、ext-socketsはLINE SDKのcomposer.jsonを見ると最近のアップデートでrequireにext-soketsが追加されていました。

  "require": {
    "php": ">=5.5",
    "ext-curl": "*",
    "ext-json": "*",
    "ext-sockets": "*"
  },

さくらのレンタルサーバーにphp ext-socketsをインストール

ext-socketsはさくらのレンタルサーバーのphp.iniではコメントアウトされています。

自分用のphp.iniにモジュールを追加すればいいかと思いましたが、そもそもさくらレンタルサーバーのPHPのエクステンションにはsockets.soは入っていません。

cat /usr/local/php/7.4/etc/php.ini | grep socket
; Default timeout for socket based streams (seconds)
; http://php.net/default-socket-timeout
default_socket_timeout = 60
;extension=sockets

$ ll /usr/local/php/7.4/lib/php/extensions/no-debug-non-zts-20190902/
total 2348
-rw-r--r--  1 root  wheel   124476 Aug  7 09:14 apcu.so
-rw-r--r--  1 root  wheel   595997 Aug  7 09:13 imagick.so
-rw-r--r--  1 root  wheel    61009 Aug  7 09:14 mcrypt.so
-rw-r--r--  1 root  wheel   312644 Aug  7 09:13 oauth.so
-rwxr-xr-x  1 root  wheel  1185945 Aug  7 09:12 opcache.so

php-configで確認するとさくらレンタルサーバーのPHP7.4.9ではSocketは指定されていないので、sockets.soを自前で準備する必要があります。

--configure-options [--prefix=/usr/local/php/7.4 --mandir=/usr/local/php/7.4/man --with-config-file-path=/usr/local/php/7.4/etc --with-config-file-scan-dir=/usr/local/php/7.4/etc/conf.d --with-apxs2=/usr/local/apache/bin/apxs --with-openssl --with-zlib --with-curl --enable-exif --enable-gd --with-jpeg --with-webp --with-freetype --with-gettext --with-gmp --with-iconv-dir=/usr/local --enable-intl --enable-mbstring --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-snmp --enable-soap --with-xsl --enable-opcache --with-zip --enable-ftp --with-pear OPENSSL_CFLAGS=-I/usr/include OPENSSL_LIBS=-L/usr/lib -lssl -lcrypto]

まずは、同じバージョンのPHPのソースを落として、解凍したphpのソースディレクトリ内のext内にあるsocketsディレクトリ でビルドを行い、sockets.soを自分のphp.iniで指定します。

$ php -v
PHP 7.4.9 (cli) (built: Aug  7 2020 09:12:44) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.9, Copyright (c), by Zend Technologies

$ wget https://github.com/php/php-src/archive/php-7.4.9.zip
解凍して

$cd ./php-src-php-7.4.9/ext/sockets/
$phpize
Configuring for:
PHP Api Version:         20190902
Zend Module Api No:      20190902
Zend Extension Api No:   320190902

$./configure --prefix=/home/○○/php_modules/
$ make

cp ./.libs/sockets.so /home/〇〇/php-src-php-7.4.9/ext/sockets/modules/sockets.so
cp ./.libs/sockets.lai /home/〇〇/php-src-php-7.4.9/ext/sockets/modules/sockets.la

さくらのレンタルサーバーに最新のLINE BOT SDKをインストール

出来上がった、sockets.soとsockets.laiを自分のphp.iniで指定して、aliasでphp.iniを読み込む様に変更してから、下記のコマンでインストールが成功

$  alias php='/usr/local/bin/php -c /home/〇〇/www/php.ini'
$ php ~/bin/composer.phar require linecorp/line-bot-sdk
PHP Warning:  PHP Startup: mailparse: Unable to initialize module
Module compiled with module API=20170718
PHP    compiled with module API=20190902
These options need to match
 in Unknown on line 0
Using version ^4.5 for linecorp/line-bot-sdk
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing linecorp/line-bot-sdk (4.5.0): Downloading (100%)
linecorp/line-bot-sdk suggests installing apigen/apigen (Install with roave/better-reflection:dev-master to generate docs)
linecorp/line-bot-sdk suggests installing roave/better-reflection (Required by apigen/apigen:dev-master)
Package oscarotero/psr7-middlewares is abandoned, you should avoid using it. Use middlewares/* instead.
Writing lock file
Generating autoload files

ec-cube4 タグ 検索 – ECCUBE4の商品検索で商品タグも条件に追加するANDじゃなくてORで – Customizer(Where| Query)だと出来ない?

EC-Cube4で商品検索でタグも検索条件に追加したいときどうすれば出来るのかいろいろ試してみました。

そもそも商品検索のクエリにdtb_product_tagが含まれていないので、WhereCustomizerではなくQueryCustomizerで下記のように作りました。

namespace Customize\Repository;

use Doctrine\ORM\QueryBuilder;
use Eccube\Doctrine\Query\QueryCustomizer;
use Eccube\Repository\QueryKey;

class TagSearchQueryCustomizer implements QueryCustomizer {

    protected $tag;

    public function __construct(TagRepository $tagRepository){
        $this->tag = $tagRepository;
    }

    public function getQueryKey() {
        return QueryKey::PRODUCT_SEARCH;
    }

    public function customize(QueryBuilder $builder, $params, $queryKey){
        if ($params['name']) {

            $builder->innerJoin("p.ProductTag", "pt");

            $tags = $this->tag->createQueryBuilder('t')
                    ->where("NORMALIZE( t.name ) LIKE NORMALIZE( :name )")
                    ->setParameter("name", '%' . $params['name'] . '%')
                    ->getQuery()->getResult();

            foreach ($tags as $tag) {
                $builder->orWhere("pt.Tag = :TagId");
                $builder->setParameter("TagId", $tag->getId());
            }
        }
    }
}

タグレポジトリからLIKE検索した結果のタグIDをキーに検索してくれるのですが、これだと下記のようなクエリになってしまい、表示属性や廃止属性を無視してしまいます。

FROM
    dtb_product d0_
    INNER JOIN
        dtb_product_class d1_
    ON  d0_.id = d1_.product_id
    AND d1_.discriminator_type IN('productclass')
    INNER JOIN
        dtb_product_tag d2_
    ON  d0_.id = d2_.product_id
    AND d2_.discriminator_type IN('producttag')
WHERE
    (
        (
            d0_.product_status_id = 1
        AND (
                CONVERT(d0_.name USING utf8) COLLATE utf8_unicode_ci LIKE CONVERT(? USING utf8) COLLATE utf8_unicode_ci
            OR  CONVERT(d0_.search_word USING utf8) COLLATE utf8_unicode_ci LIKE CONVERT(? USING utf8) COLLATE utf8_unicode_ci
            OR  EXISTS(
                    SELECT
                        d3_.id
                    FROM
                        dtb_product_class d3_
                    WHERE
                        (
                            d0_.id = d3_.product_id
                        AND CONVERT(d3_.product_code USING utf8) COLLATE utf8_unicode_ci LIKE CONVERT(? USING utf8) COLLATE utf8_unicode_ci
                        )
                    AND d3_.discriminator_type IN('productclass')
                )
            )
        AND d1_.visible = 1
        )
    OR  d2_.tag_id = ?
    )
AND d0_.discriminator_type IN('product')

search_wordやnameをみているグルーピングの中にORでタグ条件を入れたいのですが、QueryCustomizerだと入れ込めませんでした。。

次にWhereCustomizerならsearch_wordやnameの部分に入れ込めるか試してみました。

<?php
namespace Customize\Repository;

use Eccube\Doctrine\Query\WhereClause;
use Eccube\Doctrine\Query\WhereCustomizer;
use Eccube\Repository\QueryKey;
use Eccube\Repository\TagRepository;

class TagSearchWhereCustomizer extends WhereCustomizer {

    protected $tag;

    public function __construct(TagRepository $tagRepository) {
        $this->tag = $tagRepository;
    }

    public function getQueryKey() {
        return QueryKey::PRODUCT_SEARCH;
    }

    protected function createStatements($params, $queryKey) {

        $whereCluse = [];

        if ($params['name']) {

            $tags = $this->tag->createQueryBuilder('t')
                ->where("NORMALIZE( t.name ) LIKE NORMALIZE( :name )")
                ->setParameter("name", '%' . $params['name'] . '%')
                ->getQuery()->getResult();

            // foreach ($tags as $tag) {
            //  // $whereCluse[] = WhereClause::eq("pt.Tag", ':Tag', $tag->getId());
            // }
        }

        return [
            WhereClause::isNull('p.note'),
        ];

        // return $whereCluse;
    }

}

一旦テストとして、dtb_productのノート属性を検索条件にしてcreateStatementsの戻り値がクエリのどこに追加されるのか確認してみました。

 INNER JOIN
        dtb_product_tag d2_
    ON  d0_.id = d2_.product_id
    AND d2_.discriminator_type IN('producttag')
WHERE
    (
        d0_.product_status_id = 1
    AND (
            CONVERT(d0_.name USING utf8) COLLATE utf8_unicode_ci LIKE CONVERT(? USING utf8) COLLATE utf8_unicode_ci
        OR  CONVERT(d0_.search_word USING utf8) COLLATE utf8_unicode_ci LIKE CONVERT(? USING utf8) COLLATE utf8_unicode_ci
        OR  EXISTS(
                SELECT
                    d3_.id
                FROM
                    dtb_product_class d3_
                WHERE
                    (
                        d0_.id = d3_.product_id
                    AND CONVERT(d3_.product_code USING utf8) COLLATE utf8_unicode_ci LIKE CONVERT(? USING utf8) COLLATE utf8_unicode_ci
                    )
                AND d3_.discriminator_type IN('productclass')
            )
        )
    AND d1_.visible = 1
    AND d0_.note IS NULL

WhereCustomizerでも思った部分にはクエリは追加されませんでした。QueryCustomizerでinnerJoinして、WhereCustomizerで条件追加という作戦は失敗のようです。

ここに来てやっとProductRepositoryのソースをみてみると、getQueryBuilderBySearchDataの中でandWhereで追加したいグループが追加された後、customizeが呼ばれているので、自分が入れ込みたい場所にいれるにはProductRepositoryをいじる必要があるのかもしれない。。

    public function getQueryBuilderBySearchData($searchData)
    {
        $qb = $this->createQueryBuilder('p')
            ->andWhere('p.Status = 1');

        // category
        $categoryJoin = false;
        if (!empty($searchData['category_id']) && $searchData['category_id']) {
            $Categories = $searchData['category_id']->getSelfAndDescendants();
            if ($Categories) {
                $qb
                    ->innerJoin('p.ProductCategories', 'pct')
                    ->innerJoin('pct.Category', 'c')
                    ->andWhere($qb->expr()->in('pct.Category', ':Categories'))
                    ->setParameter('Categories', $Categories);
                $categoryJoin = true;
            }
        }

        // name
        if (isset($searchData['name']) && StringUtil::isNotBlank($searchData['name'])) {
            $keywords = preg_split('/[\s ]+/u', str_replace(['%', '_'], ['\\%', '\\_'], $searchData['name']), -1, PREG_SPLIT_NO_EMPTY);

            foreach ($keywords as $index => $keyword) {
                $key = sprintf('keyword%s', $index);
                $qb
                    ->andWhere(sprintf('NORMALIZE(p.name) LIKE NORMALIZE(:%s) OR 
                        NORMALIZE(p.search_word) LIKE NORMALIZE(:%s) OR 
                        EXISTS (SELECT wpc%d FROM \Eccube\Entity\ProductClass wpc%d WHERE p = wpc%d.Product AND NORMALIZE(wpc%d.code) LIKE NORMALIZE(:%s))',
                        $key, $key, $index, $index, $index, $index, $key))
                    ->setParameter($key, '%'.$keyword.'%');
            }
        }

並び替えなど。。。

 return $this->queries->customize(QueryKey::PRODUCT_SEARCH, $qb, $searchData);
    }

ECCUBEの作法というかいろいろわかってないけど、ProductRepository直接編集ってNGな気がするけど、どうするのが正しいんだろうか。。

もしProductRepositoryを修正していいならこんな感じでできたけど、proxyとかでやるのかな?

foreach ($keywords as $index => $keyword) {
                $key = sprintf('keyword%s', $index);
                $qb
                    ->andWhere(sprintf('NORMALIZE(p.name) LIKE NORMALIZE(:%s) OR 
                        NORMALIZE(p.search_word) LIKE NORMALIZE(:%s) OR 
                        EXISTS (SELECT wpc%d FROM \Eccube\Entity\ProductClass wpc%d WHERE p = wpc%d.Product AND NORMALIZE(wpc%d.code) LIKE NORMALIZE(:%s)) OR
                        EXISTS (
                            SELECT eept%d FROM \Eccube\Entity\ProductTag eept%d
                            WHERE p = eept%d.Product
                            AND eept%d.Tag IN (
                                SELECT eet%d FROM \Eccube\Entity\Tag eet%d
                                WHERE NORMALIZE( eet%d.name ) LIKE NORMALIZE( :%s )
                            )
                        )',
						$key, $key, $index, $index, $index, $index, $key, 
						$index, $index, $index, $index, $index, $index, $index, $key))
                    ->setParameter($key, '%'.$keyword.'%');

traitの例はカラム追加とかが多くて、メソッド書き換の方法を調べてみます。

iOS一部のユーザだけWKWebViewでエラー( NSErr.code 105 )が発生する – アクセスログは存在するがdidFailProvisionalNavigation

func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
        let nserr = error as NSError

NSLog("didFailProvisionalNavigation :\(nserr.code)")

iOSのアプリでWKWebViewを利用して表示しているページが一部のユーザだけ表示できず、didFailProvisionalNavigation Error 105が発生するので原因調査

自分の環境では発生しないので、一般ユーザから動作ログをレポートしてもらう機能をつけてから、アクセスログと突合させてみると、didFailProvisionalNavigationが発生した場合、アプリに同梱したHTMLを表示させているが、そのHTMLが表示された後、すぐにアクセス失敗したURLのパラメータ欠損みたいなURLに自動的に再度読み込まれる動きをしていることがわかりました。

didFailProvisionalNavigationで発生しているのは105で検索しても情報があまり見つからなかったが、下記のスレッドを発見しました。

https://bugs.chromium.org/p/chromium/issues/detail?id=831381

どうやらペアレントコントロールの影響のようで、Code 105で動きが今回の件と似ています、当初は広告ブロックも疑いましたが、SFSafariViewControllerではなく、wkWebviewなので除外していましたが、ペアレントコントロールはwkWebviewでも動作するようで、多分これです。

試しに再現するユーザー直接連絡してsafariでwkWebviewでエラーとなるページを開いてもらいましたが、アクセスできないとのことでした。

試した限りはコンテンツの内容ではなく、URLのそれっぽい文字列のようでしたので、別のURLを用意してリリースして対応

nodemailerでISO-2022-JPのメールを送信したい( Mailparserじゃなくて送信)

メールの受信プログラムのテストでマルチパートやUTFだけじゃなく、iso-2022-jpのメールも正しく受信できるか試したかったので、nodemailerで送信しようと思いましたが、どうやらnodemailerがiso-2022-jpに対応していない。。

解析するmailparserの例は見つかるけど、iso-2022-jpで送信するサンプルが見つからないので、試した結果、なんんとかiso-2022-jpで送信でき、Yahooメール、Gmailなど主要なサービスでも正しく表示されました。

iconvで変換したiso-2022-jpの文字列をsubjectなどに渡しても、「Content-Type: text/plain; charset=ISO-2022-JP」にはなりません、Content-TypeはUTF8で本文などがiso-2022-jpで文字化けします。

なのでrawを使って強制的にパラメータを指定します。

let message = {
    envelope: {
        from: "from@gmail.com",
        to: ["to@gmail.com"]
    },
    raw: `Content-Type: text/plain; charset=ISO-2022-JP
From: =?ISO-2022-JP?B?` + new Buffer(iconv.convert("nodemailerです").toString()).toString("base64") + `=?= <from@gmail.com>,
To: to@gmail.com
Subject: =?ISO-2022-JP?B?` + new Buffer(iconv.convert("ISO-2022-JPのメール").toString()).toString("base64") + `=?=
` + iconv.convert("nodemailerでISO-2022-JPのメールを送信したい( Mailparserじゃなくて送信)").toString()
};

smtpTransport.sendMail( message , function(error, r) {

これで送信すると、「Content-Type: text/plain; charset=ISO-2022-JP」で送信され、文字化けもなくクライアント側でもISO-2022-JPとして認識されて本文などが表示されます。