20200625のPHPに関する記事は10件です。

PHP 値渡し

値渡し

値をコピーして渡しているため、元の変数に影響を与えない。

関数

関数値渡し
<?php

$tar = 1;
function plusOne($tar){
    $tar++;
}

plusOne($tar);

var_dump($tar);  //int(1)

関数は引数を受け取ったあと、そのコピーを作って処理をする

foreach

基本

  • ループ回数
    • 要素の数と一致する。
    • 下記のループ回数=$valueの数
  • 値渡し
    • \$keyと$valueはループごとに値が変わる。
    • 値をコピーしているため、元の変数($arr)に影響を与えない
foreach基本
<?php

foreach ($arr as $key => $value) {
  //...code
}

値渡し

foreach値渡し
<?php

$arr = [
    0 => [
        'a' => 'あ',
        'b' => 'あ',
        'c' => 'あ',
    ],
    1 => [
        'd' => 'う',
        'e' => 'う',
        'f' => 'う',
    ],
];

foreach ($arr as $key => $value) {//2回ループ

    foreach ($value as $k => $v) {//6回ループ
        //$v = str_replace('あ','い',$v);
        //var_dump
        $value[$k] = str_replace('あ','い',$v);//※1 $vは'「あ」のまま'
        //var_dump
        $value[$k] = $v;//ここで元の配列を操作
        //var_dump
    }
    //var_dump
    $arr[$key] = $value;//ここで元の配列を操作
    //var_dump
}
  • var_dumpと書いてあるとこでvar_dumpしてみると理解が深まる
  • ※1:この記事上部の関数の値渡しをおさらい
    • 関数(ここではstr_replace)に値を渡して処理した後は、返り値に処理後の内容が反映される
  • ある変数に処理をし、その変数を上書くときは、左辺と右辺に記載する変数を同じにするとバグが防げる
    • × \$value[\$k] = str_replace('あ','い',\$v);
    • 〇 \$value[\$k] = str_replace('あ','い',\$value[$k]);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP 参照渡し

関数

関数参照渡し
<?php

$tar = 1;
function plusOne($tar){
    $tar++;
}

plusOne($tar);

var_dump($tar);  //int(1)

関数は引数を受け取ったあと、そのコピーを作って処理をする

foreach

foreachのループ回数は、要素の数と一致する。
下記のコードで言うとループ回数=$valueの数

foreach基本
<?php

foreach ($arr as $key => $value) {
  //...code
}
foreach参照渡し
<?php

$arr = [
    0 => [
        'a' => 'あ',
        'b' => 'あ',
        'c' => 'あ',
    ],
    1 => [
        'a' => 'い',
        'b' => 'い',
        'c' => 'い',
    ],
];

foreach ($arr as $key => $value) {//2回ループ

    foreach ($value as $k => $v) {//6回ループ
        $v = str_replace('あ','い',$v);
        // var_dump($value[$k],$v);exit; //※1
        $value[$k] = $v;
        // var_dump($value[$k],$v);exit; //※2
    }
    // var_dump($arr[$key],$value);exit; //※3
    $arr[$key] = $value;
    // var_dump($arr[$key],$value);exit; //※4
}

※1,※3:var_dumpの第一引数と第二引数の値が違う
※2,※4:var_dumpの第一引数と第二引数の値が同じ

foreachのasの後の\$key,$valueはループするたびに変わる&コピーを生成して処理をしている

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】DateTimeクラスで 2020/06/31 が通っちゃった!?

環境

OS:CentOS 7.7
PHP: 7.1.26

現象

DateTimeクラス のコンストラクタで、日にちが31までなら、
存在しない年月日でも、正常に受け付けるようです(ただし翌月に繰り上がる。)

PHP歴は長いのに知らなかった。じつは常識なのか?

$date = new DateTime( '2020-02-28' ); // OK
$date = new DateTime( '2020-02-29' ); // OK
$date = new DateTime( '2020-02-30' ); // OK
$date = new DateTime( '2020-02-31' ); // OK
$date = new DateTime( '2020-02-32' ); // Error!

$date = new DateTime( '2020-06-30' ); // OK
$date = new DateTime( '2020-06-31' ); // OK
$date = new DateTime( '2020-06-32' ); // Error!

たしかに マニュアル には こっそり下記の説明があります。

// 存在しない値は繰り上がります
$date = new DateTime('2000-02-30');
echo $date->format('Y-m-d H:i:sP') . "\n";
    
2000-03-01 00:00:00-05:00

対策

日付チェックには checkdate() を併用したほうがよさそうです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】Herokuでメール送れるようにする

問題

Laravelってユーザー認証とか用意してくれてありがたいよね
laravel new で作ったアプリにAuth入れてユーザー登録はうまくいった
ただ、パスワードリセット用のメールを送ろうとしたら、エラー500が出て何時間も詰まった
環境変数とか設定してないから、そりゃエラーは出るだろうけど、それだけじゃなかったので備忘録として記しておこう

前提

Laravel6です
なので、laravel/uiをインストールするところから始めます
初心者にとってはおまじないですが、以下を実行

// laravel/uiをインストール
% composer require laravel/ui

// Auth関連ファイル(基本的なスキャフォールド)を生成
% php artisan ui vue --auth

// フロントエンドに必要なパッケージをインストール&必要なファイルをコンパイル・ビルド
% npm install && npm run dev

このようにしてAuth機能を使えるようになっていることが前提です

解決法

解決法というかメール送れるようにする方法
今回は、GメールのSMTPを使ってメールを送れるようにしたいと思います
行うことは以下のリスト

  • Gメール側でアプリパスワードを生成
  • Herokuの環境変数設定
  • mail.phpの編集

では順にやっていきましょう

アプリパスワードの生成

Googleアカウントからセキュリティ→Googleへログインパネルのアプリパスワード
※2段階認証をオンにしないとアプリパスワードは出てこない
パスワード聞かれるので入力
デバイス名をその他でアプリ名(何でも良さそう?)を記入→生成
基本、生成したパスワードは覚えておく必要がないが、これをあとで環境変数に設定するのでメモしておく

Herokuの環境変数を設定

% heroku config:set MAIL_MAILER=smtp
% heroku config:set MAIL_HOST=smtp.gmail.com
% heroku config:set MAIL_PORT=587
% heroku config:set MAIL_USERNAME=自分のGmailアドレス
% heroku config:set MAIL_PASSWORD=アプリパスワード(16桁)
% heroku config:set MAIL_ENCRYPTION=tls 
% heroku config:set MAIL_FROM_ADDRESS=自分のGmailアドレス
% heroku config:set MAIL_FROM_NAME=アプリ名

TLSの代わりにSSLを使う場合は以下農用に設定してください

% heroku config:set MAIL_PORT=465
% heroku config:set MAIL_ENCRYPTION=ssl

ローカル環境でメール送信を行いたい場合は、これと同様の環境設定を.envファイルに書き込む

mail.phpの編集

デフォルトで設定してあるなら編集する必要はないですね
確認してください

config/mail.php
'default' => env('MAIL_MAILER', 'smtp')

smtpの設定は、さらに下の方でデフォルトで設定してあるので、そのまま使いましょう

おわり

3つのことを行うだけで、Heroku実環境でLaravelアプリからメールが送れるようになります
簡単ですね
もし、これでもエラーが出てしまった場合は、エラーをしっかり読んで調べましょう
エラー500などのページしか出てこない場合は、Herokuの環境変数にAPP_DEBUG=trueを設定してみましょう
エラーを確認した後は必ずAPP_DEBUG=falseに戻しておきましょう

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【php】Web API関連メモ

【php】Web API関連メモ

自分用のメモです。

namespace

namespace 名前空間名;

  • 名前空間を定義する。
  • 同一名のクラスや関数などが定義できるようになる。(衝突によるエラーや上書きを防ぐ)
  • 一番上に書く。(HTMLやファイル読み込みよりも上)

▼使い方
名前空間\呼び出したいもの

  • 名前空間\関数名()
  • 名前空間\変数
(method.php)同一ファイルに同じ関数を書
<?php
namespace name1;

function hello(){
  return 'こんにちは';
}

namespace name2;

function hello(){
  return 'Hello';
} 
?>
(exec.php)関数の呼び出し
<?php

//ファイル読み込み
require_once 'method.py';

//関数の呼出し
echo name1\hello();
echo name2\hello();

?>

//出力
こんにちはHello

use as

use 名前空間名 as エイリアス名;
名前空間を作成し、エイリアス化する
名前空間が長い場合に使う。

use AAA\BBB\CCC\DDD as name3;
 └ 名前空間「AAA\BBB\CCC\DDD」を「name3」と定義。

▼asがない場合
use AAA\BBB\CCC\DDD
 └ 名前空間は「DDD」になる

function 関数名(array 引数)

(型 引数, 型 引数,,,)は型宣言。配列を指定。
渡された引数の方が指定と違う場合にエラーを返す。
間違いの早期発見用。

function test(array $list, int $val)

//$list:配列
//$val:整数

アロー演算子(->)

クラス名->$プロパティ名

クラス名->$プロパティ名
指定したクラスのプロパティを取得。

クラス内で呼び出す場合は、$this->$プロパティ名

※アロー関数はJS

インスタンス名->関数名()

インスタンス名->関数名()
インスタンスの中で関数を呼び出す。
 └ インスタンスの入った変数。

require dirname(DIR). '絶対パス';

ファイルを読み込む。

//以下は同じ
require dirname(__DIR__) . '/path/ファイル名.php';
require dirname(__FILE__) . '/path/ファイル名.php';
require ( __DIR__ . '/path/ファイル名.php');
require dirname(__DIR__) . '/vendor/test.php';

この場合、後から追加したライブラリ(vendorが保存場所)のtest.phpというファイルを読み込んでいる。

https://book.cakephp.org/2/ja/controllers/request-response.html

$this->request->controller;
$this->request['controller'];
$this->request->params['controller'];

コメントアウト + @(アットマーク)

パラメータや変数の説明。

//@param [型] 変数名 説明
//@var [型] 説明
//@return [型] 説明

▼使用例

class Userdb
{
    private $name;
    private $db;

    /**
     * コンストラクタ
     *
     * @param Users $name
     * @param Database $db
     */
    public function __construct(Users $name, Database $db)
    {
        $this->name = $name;
        $this->db = $db;
    }

キー=>値

連想配列のキーと値の指定は「=>」

array (key1=>値1, key2=>値2, key3=>値3,,,)
キーの名前で値を呼び出す

array_shift

array_shift(配列名)
配列の一番最初の要素を抜き出す。
破壊的。実行後、元の配列からその要素が抜ける。

$lists=array("aaa","bbb","ccc");
$data1=array_shift($lists);

echo "0番目の要素:".$data1."。<br>";
print_r($lists);


////出力
//0番目の要素:aaa
//("bbb","ccc");

argc, argv

$argc: 引数の個数(Argument Count)

▼ファイル実行時に、引数を渡し、その引数の値をつかう

if ($argc === 1) {
    echo '引数は1つです', PHP_EOL;
    exit(1);
}

$argv: 引数の値 (Argument Value)

//入力値 aaa bbb ccc
$argv[0] //aaa
$argv[1] //bbb
$argv[2] //ccc

//変数に格納
$answers = $argv

PHP_EOL

実行環境のOSで改行を行う。

実行環境に合わせ、下記に変換される。
・windows:「\r\n(CRLF)」
・Linux系: 「\n(LF)」

echo "aaa".PHP_EOL."bbb".PHP_EOL."ccc";

////出力
//aaa
//bbb
//ccc

eixt()

処理の終了

exit(整数)
エラーのステータスコードを整数にする。
画面表示はされない。

json_decode関数

JSON形式のデータを変換する。

$a = json_decode(JSON形式のデータ)
 └ オブジェクトを返す
 └ {"obj1":値1,"obj2":値2}
 └ $a{obj1} → 値1

$a = json_decode(JSON形式のデータ, true)
 └ 連想配列に変換
 └ array(obj1=>値1, obj2=>値2)
 └ $a[obj1] → 値1

Pimple

  • DIコンテナの一つ
  • インスタンスの生成を一括実行するファイル。
  • DI用の設定データを保存しているcontainer.php
  • 「$変数名= インスタンス生成処理」が省略できる
  • 引数の数が大幅に減る
use Pimple\Container;
$container = new Container();

//または
$container = new Pimple\Container();

DI

DI:Dependency Injection

依存性のあるデータ(インスタンスの生成)などを変数で与える。

  • クラスとの依存性が低くなるため、動的に変更可能になる。
  • クラスをいじらず、注入しているデータ元をいじれる(メンテ簡略化)

guzzle http

外部APIの実行。

//guzzleのりーソスを生成し、requestメソッドで送信
new GuzzleHttp\Client([
        'handler' => $handler,
        'base_uri' => 'http://www.example.com',
    ]);
  • base_uri: アクセスするURLのベースとなるもの。(必須)
  • handler: リクエストヘッダーにつけるパラメータを指定。配列形式で複数指定可能。(オプション)

▼応答データの取得(文字列)

$response->getBody()

request

Client->request('メソッド', 'URL');
 └ メソッド:GET or POST

指定したURL(またはパス)からデータを取得する。

$response = Client->request('POST','/test', ['json' => データ]);

・Client= GuzzleHttp\Client
・/testファイルにjson形式でデータを渡し、その結果をPOSTで受け取り、$responseに格納.

Cake PHP

ruby on railsの思想をベースに作成したPHP用のフレームワーク。

▼Cake PHP

  • コマンドや規則が充実。
  • スピード感重視で作成できる。
  • 自由度が低い。

▼Lravel

  • 自由度が高い
  • 最新技術も導入しやすい

簡単なサイト作成ならCake PHP, がっつり組みたいならLaravel。

インストール

composerで行う
 └ 作業ディレクトリ毎にインストール

composer install

MVCモデル

Model(Routing)
View
Controller

Controller(処理の割り振り) -> Model(データ処理)-> View(表示)

ディレクトリ構成

PJディレクトリ

ディレクトリ 内容
cake CakePHPの中心部分となるライブラリファイル類がまとめられている
plugins プラグインのプログラムを組み込むためのもの
app Webアプリケーションを配置していくディレクトリ あらかじめ定められたルールでファイルを配置し開発していく
venders ライブラリ類を後から追加するときに利用されるところ
gitignore git用のファイル
htaccess Webサーバの動作に関する設定ファイル
index.php CakePHPのディレクトリのインデックスファイル このディレクトリにアクセスしたときにこのページが表示される
README 最初に読むドキュメント
appディレクトリの中身
ディレクトリ 内容
config CakePHPで利用される各種の設定情報を保管。Routingファイルもこの中。
libs ライブラリファイルを追加する場所
controllers プログラムの制御に関するソースコードを追加していく
locale 言語・地域情報に関するファイルを保管する
models データベースアクセス関係のソースコードを追加していく
plugins 機能追加のためのプラグインファイルを追加する
tests テストに関するフレームワークを追加する
tmp プログラム実行中に作成される一時ファイルの保管場所
vendors ライブラリ類を後から追加するときに利用されるところ
views ページレイアウトのためのファイルを追加していく
webroot CakePHPによるWebアプリケーションのルートになる ここにファイルを入れるとルートに配置された状態で呼び出される
.htaccess,index.php このディレクトリのWebサーバの設定情報とデフォルトページ
  • cakeフォルダは基本触らない
  • 編集や追加をするのは基本appフォルダ

Configuration

環境設定:config/bootstrap.phpから読み込まれる。

初期状態は「config/app.php」のみ存在。
自分で、「config/bootstrap.php」を追加し設定を書き込むと、 bootstrap.php側の設定が読み込まれるようになる。

インスタンスの生成や、関数の作成を行う

$product = new 名前空間\クラス名();

$product['server_db'] = function ($c) {
    return new 名前空間2\クラス名(dirname(__DIR__) . '/var/server_db.txt');
};

Routing

「config/routes.php」で設定する。

Router::connect('入力URL', ['controller' => 'コントローラー名', 'action' => 'アクション名(開くページ)']);

config/routes.php
Router::connect('/top', ['controller' => 'Home', 'action' => 'index']);

(ドメイン)/topが入力されたら、HomeContorllerのindexというアクションを実行する。

パラメータの取得と呼び出し

▼格納
:プレースホルダ名

▼呼び出し
['プレースホルダ名' => '\d+']

config/routes.php
Router::connect('/top/:top_id', ['controller' => 'Home', 'action' => 'index'], ['top_id' => '\d+']);

Request

  • デフォルトのリクエストを扱うクラス。
  • リクエストのデータの管理・操作を行うときに使用
  • 大本のデータはCake\Network\Request
  • 呼び出し:$this->request
  • requestの中にパラメータが入っている。
  • Controller、View、Cell、Helperからアクセス可能。

▼パラメータへのアクセス方法

//param or params
$this->request->params['controller']; // 複数形(params)
$this->request->param('controller'); // 単数形(param)

▼GETのパラメータ
$this->request->query
取得データは連想配列になっている。

/home?name=aaa&age=25

$this->request->query; # array('name' => 'aaa','age' => '25')

▼POSTの場合

$this->request->data('name.aaa');
$this->request->data['name'];

Controller

▼ファイルの場所
src/Controllers/コントローラー名Contoller.php

  • 各ControllerはAppController(src/Controllers/AppController.php)を継承

→冒頭に「namespace App\Controller;」がある。

saveメソッド

$this->Model->save("キー名", 値)

$this->db->save('name', 'tanaka');

▼保存したデータの取得
fetchを使う

$this->db->fetch('name');
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】migrationで複数テーブルの共通カラム定義を1か所に共通化する方法

はじめに

複数のテーブルに同じカラムを持たせるとき、
migrationでテーブル1つ1つに同じカラム定義を記述するのは面倒です。

例えば、よく使われるのは
created_at、created_by、updated_at、updated_by、deleted_at、deleted_byなどです。

普通にやると各テーブルごとに上記カラム定義を記述することになりますね。

    public function up()
    {
        Schema::create('foo_table', function (Blueprint $table) {
            $table->id();

            $table->timestamp('created_at')->nullable();
            $table->unsignedBigInteger('created_by')->nullable();
            $table->timestamp('updated_at')->nullable();
            $table->unsignedBigInteger('updated_by')->nullable();
            $table->timestamp('deleted_at')->nullable();
            $table->unsignedBigInteger('deleted_by')->nullable();
        });

        Schema::create('bar_table', function (Blueprint $table) {
            $table->id();

            $table->timestamp('created_at')->nullable();
            $table->unsignedBigInteger('created_by')->nullable();
            $table->timestamp('updated_at')->nullable();
            $table->unsignedBigInteger('updated_by')->nullable();
            $table->timestamp('deleted_at')->nullable();
            $table->unsignedBigInteger('deleted_by')->nullable();
        });

        Schema::create('baz_table', function (Blueprint $table) {
            $table->id();

            $table->timestamp('created_at')->nullable();
            $table->unsignedBigInteger('created_by')->nullable();
            $table->timestamp('updated_at')->nullable();
            $table->unsignedBigInteger('updated_by')->nullable();
            $table->timestamp('deleted_at')->nullable();
            $table->unsignedBigInteger('deleted_by')->nullable();
        });
    }

※Laravelは$table->timestamps();という機能を提供しており、created_atとupdated_atだけは共通で定義可能です

このように全テーブルに毎回共通カラム定義を羅列するのは面倒です。
共通コード1か所だけにそのカラム定義を記述し、
migrationではそれを利用するだけの形が望ましいです。
こんなイメージ。

    public function up()
    {
        Schema::create('foo_table', function (Blueprint $table) {
            $table->id();

            $table->systemColumns(); // 共通カラム定義
        });

        Schema::create('bar_table', function (Blueprint $table) {
            $table->id();

            $table->systemColumns(); // 共通カラム定義
        });

        Schema::create('baz_table', function (Blueprint $table) {
            $table->id();

            $table->systemColumns(); // 共通カラム定義
        });
    }

$table->systemColumns();を呼ぶだけで6つの共通カラムが定義できる状態です。

Laravelではmacroという機能を利用してBlueprintクラスを拡張することで
いい感じにできるので、今回はその方法を紹介します。

macroとは

macroとは、Laravelフレームワーク自体が提供しているクラスに
好きなメソッドを追加することができる機能です。

例えば、
Requestクラスにユーザーエージェントを判定するメソッドを追加してみたり、
Carbonクラスに特定のフォーマットに変換するメソッドを追加してみたり、
Laravelのクラスに自由にメソッドを追加できます。

macroの詳しい使い方や
macro利用可能なクラスなどについては
こちらの記事が非常にわかりやすく解説していますのでご参考に。

今回は、migrationで利用されている
Blueprintクラスにこのmacroで機能追加することによって
共通カラムのコード共通化を行います。

Blueprintにmacro追加

まずはBlueprintのmacroを定義するために
サービスプロバイダを新しく作ります。

makeコマンド実行。

php artisan make:provider BlueprintServiceProvider

すると下記のようにBlueprintServiceProviderのクラスが生成されます。

app/Providers/BlueprintServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\ServiceProvider;

class BlueprintServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

このbootメソッドに、macro定義を記述します。

app/Providers/BlueprintServiceProvider.php
    public function boot()
    {
        Blueprint::macro('systemColumns', function () {
            $this->timestamp('created_at')->nullable();
            $this->unsignedBigInteger('created_by')->nullable();
            $this->timestamp('updated_at')->nullable();
            $this->unsignedBigInteger('updated_by')->nullable();
            $this->timestamp('deleted_at')->nullable();
            $this->unsignedBigInteger('deleted_by')->nullable();
        });
    }

これでBlueprintクラスにsystemColumnsというメソッドが追加されました。

このサービスプロバイダを有効にするため、
config/app.phpに登録します。

config/app.php
'providers' => [
    // 中略
    App\Providers\BlueprintServiceProvider::class,
];

これでBulueprintへのmacro定義完了です。

migrationでmacroを利用

あとは、冒頭で見せたように、
migrationでこのmacroを呼び出すだけです。

    public function up()
    {
        Schema::create('foo_table', function (Blueprint $table) {
            $table->id();

            $table->systemColumns(); // 共通カラム定義
        });

        Schema::create('bar_table', function (Blueprint $table) {
            $table->id();

            $table->systemColumns(); // 共通カラム定義
        });

        Schema::create('baz_table', function (Blueprint $table) {
            $table->id();

            $table->systemColumns(); // 共通カラム定義
        });
    }

migrationでは$table->systemColumns();と書いているだけですが、
内部的には先ほど定義したmacroのsystemColumnsメソッドが呼び出されていて
created_at、created_by、updated_at、updated_by、deleted_at、deleted_by
の6つのカラムを定義してくれています。

参考

https://public-constructor.com/laravel-add-method-to-collection-or-facade/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【総集編】6月ふりかえり

はじめに

今月のふりかえりです。
月1で振り返り
今後は何を勉強するかを決めます。


6月 1週目

2020/06/03 【プログラミング】吾輩は関数である名前はまだない

2020/06/07 【SQL】最初に覚えるSQLの話


6月 2週目

2020/06/08 【データベース】正規化について知る

2020/06/10 【ExcelVBA】オブジェクト指向プログラミングをしよう

2020/06/14 【PHP】ワイ、客先でウェブサーバーをビルトするの巻


6月 3週目

2020/06/17 【AWS】用語を整理しながら学ぶAWS - part1

2020/06/21 【AWS】用語を整理しながら学ぶAWS - part2


今後

・DBスペシャリストになる為SQL頑張る。
・AWSをうまく扱えるようにガンガン活用してみる。
・Pythonでデータ分析をできるように数学を復習する。
・PHP案件を受ける為今後も引き続き勉強して発信する。

勉強と合わせてさらにおもしろい記事を投稿できたらと思います。


おわり


  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Fetch APIでPHPにデータを送信する方法

画面遷移をせず、ajaxでデータをPHPに渡す方法です。

下記は「ボタンをクリックした時にPHPにデータを渡す」処理についてのサンプルです。

Javascript

id="start"のボタンがあるとします。

index.html
<div class="Container">
   <button class="btn" id="start">start</button>
</div>

vanilla Javascriptでは、下記のように記述します。

main.js
start.addEventListener('click', () => {
        const postData = new FormData; // フォーム方式で送る場合
        postData.set('firstName', 'hoge'); // set()で格納する
        postData.set('lastName', 'fuga');

        const data = {
          method: 'POST',
          body: postData
        };

        fetch('test.php', data)
          .then((res) => res.text())
          .then(console.log);
    });

PHP

PHP側で、$_POST連想配列に値が保存されます。

test.php
<?php
echo $_POST['firstName']; // hoge

未対応の古いブラウザでもPolyfillを使用することで、従来のXMLHttpRequestを使った処理をfetchに置き換えることができます。

jQuery

ちなみにjQUeryにもajaxPOSTできる構文があります。

test.js
 $.post( "ajax/test.html", function( data ) {
  $( ".result" ).html( data );
 });

プロジェクトによってはこちらを使用する事もあるかもしれません。

参考

github/fetch
MDN/Fetch API
MDN/Promise
jQuery.post()

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】 空白文字で文字列を分割して検索用キーワードの配列を作る

これは何?

データベースの LIKE 検索用途とかでキーワードを抽出するやつ。
例えば

PHP Qiita Laravel

と入力したら,

PHP Qiita Laravel

として取り出す関数を作ります。

実装

実装したい関数の形を示し, 2 つの異なるアプローチでの実装例を記載します。

関数シグネチャ

$input に入力文字列を渡し, $limit にキーワード数制限を指定します。 -1 を指定した場合は無制限になり,これをデフォルトとします。

function extractKeywords(string $input, int $limit = -1): array

手法の比較

※ 「手法」の名称は一般的に提唱されているものではありません

手法 説明 よく使われる関数
空白分割法 空白文字で分割し,残った部分をキーワードとする。キーワード数制限を超過する場合,最後の1つの中に空白文字を含む残りのすべてが含まれる。 preg_split()
explode()
非空白抽出法 空白文字ではない部分だけを直接キーワードとして抽出する。キーワード数制限を超過する場合,単に超過分は無視される。 preg_match_all()
preg_replace_callback()

実装が簡単なのは空白分割法のほうですが,キーワード数制限超過時の挙動がそれぞれ異なるので注意してください。例えば a b c d e において $limit = 3 で抽出する場合,

  • 空白分割法の場合, a b c d e となる
  • 非空白抽出法の場合, a b c となる

という違いがあります。

空白分割法

半角スペースで分割

一番シンプルな実装。 explode() を使う手法もありますが, $limit の適用や空文字列の除外を考えると,こちらのほうが基本的に上位互換です。

function extractKeywords(string $input, int $limit = -1): array
{
    return preg_split('/ ++/', $input, $limit, PREG_SPLIT_NO_EMPTY);
}

あらゆる空白文字で分割

半角スペースの他に全角スペース,改行,タブ,ノーブレークスペースなどあらゆる空白系の制御文字を対象とする場合はこちら。 \p{Z} は ASCII 範囲にある制御文字の集合, \p{Cc} は Unicode 範囲にある制御文字の集合を表しています。またこれらを適用するためには u フラグが必須になります。

function extractKeywords(string $input, int $limit = -1): array
{
    return preg_split('/[\p{Z}\p{Cc}]++/u', $input, $limit, PREG_SPLIT_NO_EMPTY);
}

非空白抽出法

半角スペース以外を抽出

function extractKeywords(string $input, int $limit = -1): array
{
    $matches = [];
    preg_replace_callback(
        '/[^ ]++/',
        function (array $match) use (&$matches) {
            $matches[] = $match[0];
        },
        $input,
        $limit,
        $_,
        PREG_SET_ORDER
    );
    return $matches;
}

「なんで preg_match_all() じゃないんだ!?」

が素直な感想だと思います。 preg_replace_callback() で参照代入を使うみたいな変なことをやっているのは, $limit 適用に対応するためです。 preg_match_all() にはマッチング回数を制限する仕組みがありません。

あらゆる空白文字以外を抽出

function extractKeywords(string $input, int $limit = -1): array
{
    $matches = [];
    preg_replace_callback(
        '/[^\p{Z}\p{Cc}]++/u',
        function (array $match) use (&$matches) {
            $matches[] = $match[0];
        },
        $input,
        $limit,
        $_,
        PREG_SET_ORDER
    );
    return $matches;
}

パターンの部分を変えるだけで全体的な体裁は同じですね。

ダブルクオーテーションで括った部分は保持したまま,あらゆる空白文字以外の部分を抽出

こちらは,非空白抽出法でしか実装できません。

Laravel "Taylor Otwell" PHP と入力したら Laravel Taylor Otwell PHP として欲しい場合はこちら。 厳密にイコールではないですが,ある程度 Google 検索っぽい動きになります。

function extractKeywords(string $input, int $limit = -1): array
{
    $matches = [];
    preg_replace_callback(
        '/""(*SKIP)(*FAIL)|"([^"]++)"|([^"\p{Z}\p{Cc}]++)/u',
        function (array $match) use (&$matches) {
            $matches[] = $match[2] ?? $match[1];
        },
        $input,
        $limit,
        $_,
        PREG_SET_ORDER
    );
    return $matches;
}

ダブルクオーテーションで括られた空文字列は, PCRE 特有の機能である (*SKIP)(*FAIL) でマッチングを強制終了して $limit 適用の際に 1 キーワードとしてカウントしない,など少し気を利かせています。

補足

重複を削除する

もし重複したキーワードを削除する場合は, array_unique() + array_values() の処理を組み合わせてください。以下に使用率の高そうな,適用版の一部を記載しておきます。

あらゆる空白文字で分割し,重複を除外する
function extractKeywords(string $input, int $limit = -1): array
{
    return array_values(array_unique(preg_split('/[\p{Z}\p{Cc}]++/u', $input, $limit, PREG_SPLIT_NO_EMPTY)));
}
ダブルクオーテーションで括った部分は保持したまま,あらゆる空白文字以外の部分を抽出し,重複を除外する
function extractKeywords(string $input, int $limit = -1): array
{
    $matches = [];
    preg_replace_callback(
        '/""(*SKIP)(*FAIL)|"([^"]++)"|([^"\p{Z}\p{Cc}]++)/u',
        function (array $match) use (&$matches) {
            $matches[] = $match[2] ?? $match[1];
        },
        $input,
        $limit,
        $_,
        PREG_SET_ORDER
    );
    return array_values(array_unique($matches));
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】日本語で指定されたFakerから英語のFakerを使う

はじめに

モデルファクトリをつかってテスト用のデータを生成する際、氏名は日本語でFakerを使いたい、でもIDとかは英語で使いたい・・・ってなった時に、コンフィグで日本語設定をしつつ、使いたいときだけ英語のFakerを使う方法です。

標準言語設定

app.php
    /*
    |--------------------------------------------------------------------------
    | Faker Locale
    |--------------------------------------------------------------------------
    |
    | This locale will be used by the Faker PHP library when generating fake
    | data for your database seeds. For example, this will be used to get
    | localized telephone numbers, street address information and more.
    |
    */
    'faker_locale' => 'ja_JP',

この状態でFakerを使うとこんな感じなる。

Factory.php
$factory->defineAs(User::class, 'user', function (Faker $faker) {
    return [
        'login_id'     => $faker->unique()->firstName . '_' . $faker->unique()->lastName,
         //例)山口_真綾
        'firstname'    => $faker->firstName, 
         // 例)津田
        'lastname'     => $faker->lastName, 
          //例)裕樹
    ];
});


氏名はいいけどlogin_idは英語にしたい時

Factory.php
$factory->defineAs(User::class, 'user', function (Faker $faker) {
    $faker_en = \Faker\Factory::create('en_US');
    return [
        'login_id'     => $faker_en->unique()->firstName . '_' . $faker_en->unique()->lastName,
     //例)Brannon_Donnelly
        'firstname'    => $faker->firstName,
         // 例)津田
        'lastname'     => $faker->lastName,
          //例)裕樹
    ];
});

configはデフォルトの言語を設定してくれるだけなので、他言語を使うときはfaker オブジェクトをインスタンス化する際に引数に指定するだけでよかった。

調べてみて

Laravelにデフォルトでついている機能なだけあって、他にも簡単に設定ができて柔軟に対応できる機能が沢山あった。
ただLogin_idの値とfirstname,lastnameでの値が変わってくるのはどうしようもないのか・・・。
テストでのみ使うと割り切ればいいのかな。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む