gologiusの巣

プログラミングなどの技術メモです。誰かの役に立てるとうれしいです。

PHP+SQLiteで、UPDATE文を実行すると、エラーを吐かずになぜか0 がセットされる

総括

SQLiteにおいて、SQLの書き方によっては文法エラーにならずに、 意図しない値がDBにセットされる模様。

※見解が間違っていたらごめんなさいm( "m)

背景

下記のようなPHP関数を実行する。

実行結果としては、特定IDのレコードのカラム値がUPDATEされてほしい。

function update($id, $manual_label1, $manual_label2)
{
    var_dump($id, $manual_label1, $manual_label2);

    $pdo = new PDO(DB_DSN);
    $pdo->beginTransaction();

    $sql = "
        update TRAINING set 
                MANUAL_LABEL1 = :manual_label1 
            and MANUAL_LABEL2 = :manual_label2
        where ID = :id
    ";
    $stmt = $pdo->prepare($sql);
    $stmt->execute(
        array(
            ":manual_label1" => $manual_label1,
            ":manual_label2" => $manual_label2,
            ":id" => $id,
        )
    );

    $pdo->commit();
    return;
}

問題

  • 処理は成功する(エラーは出力されない)
  • 意図した値でUPDATEされない。 MANUAL_LABEL1 に 0 が入る。

原因

聡明な方ならお気づきだろうが、SQLが間違っている。

andではなく , でしたと。

typoなんだ許してくれ

 update TRAINING set 
                MANUAL_LABEL1 = :manual_label1 
            and MANUAL_LABEL2 = :manual_label2
        where ID = :id

 update TRAINING set 
                MANUAL_LABEL1 = :manual_label1 
               ,MANUAL_LABEL2 = :manual_label2
        where ID = :id

というよりちゃんとロールバックまでやるべきだが、 どうも例外吐いてすらいなさそう

    try {
        $pdo = new PDO(DB_DSN);
        $pdo->beginTransaction();

        $sql = "
            update TRAINING set 
                    MANUAL_LABEL1 = :manual_label1 
                   ,MANUAL_LABEL2 = :manual_label2
            where ID = :id
        ";
        $stmt = $pdo->prepare($sql);
        $stmt->execute(
            array(
                ":manual_label1" => $manual_label1,
                ":manual_label2" => $manual_label2,
                ":id" => $id,
            )
        );

        $pdo->commit();
    } catch (PDOException $e) {
        $pdo->rollBack();
    }

感想

SQL文法エラーで落ちてくれないですかね・・・

proxy環境下において、Python requests がグローバルIP制限に引っかったときのメモ

結論を先に書くと、「プロキシ用pacファイルまでちゃんと意識してコード書いてる?」

問題

プロキシ環境化において、Python requestsパッケージを用いて、APIを叩きたい

前提として、

  • API、管理画面はグローバルIP制限を施している
  • APIからレスポンスは返ってくるが、「許可されていないIPです」と出る
  • ブラウザ側から管理画面には普通にアクセスできる(=インフラ的な設定・申請は問題ないはず)
  • IP制限を外すと、PythonからAPIは叩ける

コード例

import requests

response = requests.get('APIのURL', proxies={
    'http' : 'http://example.com'
    'https' : 'http://example.com'
})

GIP制限外すとつながるので、コードにも問題はなさそう。

原因

OS側でpacファイルが指定されていた。 そのpacファイル内にて、アクセス先のサイトに応じて 使用するプロキシサーバーを振り分けていることが判明。

私のコードでは、通常利用するプロキシサーバーを指定しており、 そのプロキシサーバーのIPが可変だったため、 グローバルIPの制限にひっかかっていた。

対策

方法1 PACファイル内部を見て、アクセスしようとしているAPIのURLが どのプロキシサーバーを使うかを確認し、そのプロキシサーバーを記載する

方法2 pypacというパッケージがあるので、 プロキシサーバーではなく、 PACファイルを指定→pacファイル側でプロキシサーバーを指定するようにする

使い方は下記参照

PyPAC: Proxy auto-config for Python — PyPAC 0.16.3 documentation

PACファイル確認方法の一例

win11の例

コントロールパネル→インターネットオプションとかからでも確認できると思いますよ。

総括

プロキシと言われたときには、プロキシサーバーだけではなく、 その手前の.pacファイルまで意識しましょう。

終わり

WYSIWYGエディタをWEBページに埋め込めるライブラリ比較

背景

WEBページの管理画面でHTMLを直接書かせたくないので、 WYSIWYGエディタを埋め込みたいと思った。

WYSIWYGエディタの例(はてなブログの記事改廃画面)↓

javaScriptでいい感じのあるだろうと思ってググると案の定存在した。

調査比較

前提:

  • 無料
  • HTML+CSS+JSで埋め込める

三つほど見つかったので、触ってみた特徴を記載していく

一応簡単に実装もしたので、下記に配置しておく。

作成した比較サイト

WYSIWYGエディタライブラリ比較

ソース

gologius.github.io/test/test_WYSIWYG at master · gologius/gologius.github.io · GitHub

以下個人の感想。上記の比較サイトなども使って、自分にベストなものを選んでほしい。

trix.js

  • ○ 埋め込むだけなら一番楽。 jsも書かずに済むし、input への反映も楽。すでにinputタグがあるなら、既存コードにすぐ埋め込める
  • ○ undo redo ボタンがある
  • × 文字サイズとか色が設定できない
  • × 設定APIなどがないので、カスタマイズができない?

quill

  • ○ 機能豊富。文字の色なども変えられる。画像もbase64形式で埋め込める
  • ○ JSの設定変数で、表示するボタンなどを制御できる
  • × カスタマイズ方法が独特。 文字サイズを任意指定できないので改造しようと思ったら、CSSの改造まで必要。

POSTでHTMLを飛ばそうとすると、下記のように少し工夫が必要になる。 詳細は上記URLの quill_test.js 参照。

    var editor_input = document.getElementById(input_id);
    var editor_output = document.getElementById(output_id);
    
    var quill = new Quill(editor_input, baseOption);
    
    //inputにセットされている生HTMLを、変換してエディタに反映させる
    let initialContent = quill.clipboard.convert(editor_output.value);
    quill.setContents(initialContent);
    quill.root.innerHTML =  editor_output.value;

    //エディタ入力時に、input に生HTMLをセットする 
    quill.on('text-change', function(delta, oldDelta, source) {
        var editorHtml = editor_input.querySelector('.ql-editor').innerHTML;
        editor_output.value = editorHtml;
    });

Editor.js

  • ○ 表が挿入できる
  • × ブロックスタイル?と呼ばれる最新の見た目なので、使うのに慣れが必要かもしれない。通常のHTMLエディタを想定していると、現場の人に「なんじゃこれ」と言われるかも。
  • × ツールバー?を固定できない
  • × 行(ブロック)単位でしか、スタイル変更できない

感想

個人的なおすすめは quill

が、ベストだとは思わない*1ので、ほかにいいのがあれば教えて。

*1:quillは文字の大きさ変更がうまくいかない。HTMLも独自クラスで生成されるので、実際に反映しようとするとCSSの修正も必要になる。

stackoverflow.com

バニラJavaScriptで頑張ってファイルアップロード処理を書く話

JSのライブラリを利用せずに、モダンなファイルアップロード処理を書こうとすると 割と色々コーディングしないといけないということが分かったのでメモ。

ネットに転がっているのは、

  • D&DでUPする場合と、ファイルダイアログからUPする場合を共存させる考慮
  • ファイルを追加でUPする場合の考慮
  • 特定のファイルを削除する場合の考慮
  • UPしたファイルを一覧で見たい場合の考慮

などが漏れているサンプルが多かったので、参考になれば幸い。

前提

  • JQueryは使わない
  • ライブラリは使わない(いわゆるバニラJS)Vueとか使ったほうが本当はきれいなんだけどね。
  • IEなんて考慮しない
  • 以下では、ある程度JavaScriptを分かっている人向けの適当な解説しかしない(メモなので)

サンプルページ

ファイルUPすると、リストでファイルが表示される。

https://gologius.github.io/test/file_upload/upload.html

ソース

下記参照。

gologius.github.io/test/file_upload at master · gologius/gologius.github.io · GitHub

ポイント1

input タグを二つ用意する。 片方がファイルを仮UPする用で、チェックが通ったものを、もう一方のinputにセットする。

HTML(抜粋

 <form action="./upload.php" method="post" enctype="multipart/form-data">
        <div id="dd_area" class="drop_area_off">
            <div>
                ここにファイルをドラッグ&ドロップ
                or
                <input type="button" value="手動でファイル追加" onclick="clickUpload();" >
            </div>

            <input type="file" id="upload_files" name="upload_files[]">
            <input type="file" id="tmp_files" name="tmp_files" multiple>
        </div>

        <div>アップロードしたファイル↓</div>
        <table class="file_list">
            <tbody id="upload_files_list" >
            <!--実際の要素はJS側から挿入されるで-->
            </tbody>
        </table>    
    </form>

Javascript(抜粋

function upload_files(new_files) {
    
    //変数準備
    let sum_size = 0;
    let file_count = 0;
    let file_name_list = [];
    let work_transfer = new DataTransfer(); //通常の配列ではなく、datatransferでファイル管理する必要あり。

    //現時点でUPされているファイルを走査する → 配列に格納
    let now_files = document.getElementById("upload_files").files;
    for (var i = 0; i < now_files.length; i++) {
        var file = now_files[i];
        work_transfer.items.add(file);
        
        sum_size += file.size;
        file_count += 1;
        file_name_list.push(file.name);
    }

    //アップロード対象のファイル群を走査 → 配列に格納
    for (var i = 0; i < new_files.length; i++) {
        var file = new_files[i];
        work_transfer.items.add(file);
        
        sum_size += file.size;
        file_count += 1;
        file_name_list.push(file.name);
    }

    //対象ファイルの走査が完了して用済みのため、tmp_filesはリセットする
    let elem = document.getElementById("tmp_files");
    elem.files = new DataTransfer().files;

    //UPしてよいか判定
    let errflg = false;
    if (sum_size > MAX_FILE_SIZE_MB * 1048576){
        alert(`ファイルサイズの合計値は ${MAX_FILE_SIZE_MB}MB です`);
        errflg = true;
    }
    if (file_count > MAX_FILE_COUNT){
        alert(`ファイルをUPできる最大個数は ${MAX_FILE_COUNT}個 です`);
        errflg = true;
    }
    //重複を削除したSetを生成。ファイル名が重複していると、Setの要素数がファイル数よりも減るので、それを利用する
    let uniqueSet = new Set(file_name_list); 
    if (file_count != uniqueSet.size) {
        alert("同じファイル名が既に存在します");
        errflg = true;
    }
    
    //UPしても問題ないと判断できれば、実際にファイルアップロードする
    if (errflg == false) {
        document.getElementById("upload_files").files = work_transfer.files; 
    }

    //表示更新
    update_file_list_html();

    return;
}

なんでこんなことをしているのかというと、 inputタグからファイルダイアログを開くと、そこにセットされていたファイルが上書きされてしまう。 上書きされてしまうので、追加でのファイルUPができないのである。

仮UPすることで、ファイル全数のサイズや個数のチェックもできるので、 仮UPしたほうがいろいろ都合もよい。

ポイント2

inputタグが持つfiles要素は、インデックスを指定して参照はできるが、 「直接の加工はできない」

できない例

 let now_files = document.getElementById("upload_files").files;
 for (var i = 0; i < now_files.length; i++) {
        now_files[i]  =  tmp_file;// ←これはできない
 }

よって、datatransferを丸ごとfiles にセットしてあげる必要がある。

できる例

 let now_files = document.getElementById("upload_files").files;
 let work_transfer = new DataTransfer();
 for (var i = 0; i < now_files.length; i++) {
       work_transfer.items.add(tmp_file);
 }

document.getElementById("upload_files").files = work_transfer;

特定のファイルを削除する場合には、「特定のファイルを除いた」datatransferをセットすれば、実現できる。

補足

もっとよい方法があれば教えて。

自己紹介ページの改修をした

人生のポートフォリオでも残しとこうかなと思って、HPを少し充実させることにした。

自己紹介 | gologiusのページ

とりあえず

  • 自分がいいと思った名言メモ
  • デートパターンのメモ

を追加した。

進捗があれば随時追記する。

本当は会社でやってきたこととか書きたいんだけど、情報流出になるしなぁ・・・

婚活の所感(途中編)

もうすぐ30歳になるので、婚活なるものを初めてみた。

1か月やった個人的な感想などを書く。

ちなみにまだゴールはしてない。

※2023/08/15追記 別ルートで交際したので、結局入会した意味はなかった。

※2023/04/01追記 現在真剣交際してる。結局1か月半くらいしか相談所使ってない・・・

続きを読む

【アズレン】 14-4 ほぼオート周回用編成

アズールレーンの14-4(2023/01時点の最終ステージ)周回用編成がだいぶ安定したので、メモとして残す。

アズレンに興味がない人は、この記事見なくていいです。

続きを読む