20200730のPHPに関する記事は13件です。

[PHP]西暦を和暦に変換する

何度か西暦を和暦に変換する処理を書く機会があったので、
また必要になった時に汎用的に使い回せるよう、関数を作成しました。

なお、日本では明治5年12月2日(西暦1872年12月31日)に旧暦(天保暦)を廃止し、その翌日からグレゴリオ暦に移行して明治6年(1873年)1月1日になったため、明治5年以前の日付には対応していません。
※明治5年以前の場合は西暦で返します。

/**
 * 西暦→和暦変換
 *
 * @param string $format 'K':元号
 *                       'k':元号略称
 *                       'Q':元号(英語表記)
 *                       'q':元号略称(英語表記)
 *                       'X':和暦年(前ゼロ表記)
 *                       'x':和暦年
 * @param string $time 変換対象となる日付(西暦)‎
 *
 * @return string $result 変換後の日付(和暦)‎
 */
function to_wareki($format, $time='now')
{
    // 元号一覧
    $era_list = [
        // 令和(2019年5月1日〜)
        [
            'jp' => '令和', 'jp_abbr' => '令',
            'en' => 'Reiwa', 'en_abbr' => 'R',
            'time' => '20190501'
        ],
        // 平成(1989年1月8日〜)
        [
            'jp' => '平成', 'jp_abbr' => '平',
            'en' => 'Heisei', 'en_abbr' => 'H',
            'time' => '19890108'
        ],
        // 昭和(1926年12月25日〜)
        [
            'jp' => '昭和', 'jp_abbr' => '昭',
            'en' => 'Showa', 'en_abbr' => 'S',
            'time' => '19261225'
        ],
        // 大正(1912年7月30日〜)
        [
            'jp' => '大正', 'jp_abbr' => '大',
            'en' => 'Taisho', 'en_abbr' => 'T',
            'time' => '19120730'
        ],
        // 明治(1873年1月1日〜)
        // ※明治5年以前は旧暦を使用していたため、明治6年以降から対応
        [
            'jp' => '明治', 'jp_abbr' => '明',
            'en' => 'Meiji', 'en_abbr' => 'M',
            'time' => '18730101'
        ],
    ];

    $dt = new DateTime($time);

    $format_K = '';
    $format_k = '';
    $format_Q = '';
    $format_q = '';
    $format_X = $dt->format('Y');
    $format_x = $dt->format('y');

    foreach ($era_list as $era) {
        $dt_era = new DateTime($era['time']);
        if ($dt->format('Ymd') >= $dt_era->format('Ymd')) {
            $format_K = $era['jp'];
            $format_k = $era['jp_abbr'];
            $format_Q = $era['en'];
            $format_q = $era['en_abbr'];
            $format_X = sprintf('%02d', $format_x = $dt->format('Y') - $dt_era->format('Y') + 1);
            break;
        }
    }

    $result = '';

    foreach (str_split($format) as $val) {
        // フォーマットが指定されていれば置換する
        if (isset(${"format_{$val}"})) {
            $result .= ${"format_{$val}"};
        } else {
            $result .= $dt->format($val);
        }
    }

    return $result;
}

作成した関数を使って、和暦に変換してみます。

echo to_wareki('KX年m月d日', '18730101').PHP_EOL;
echo to_wareki('kx/n/j', '1914/08/07').PHP_EOL;
echo to_wareki('Qx-m-d', '1947-05-16').PHP_EOL;
echo to_wareki('m/d/Y(qX) H:i:s', '2016-03-20 12:34:56').PHP_EOL;
echo to_wareki('Y(Kx)年n月j日');

出力結果は以下の通りです。

明治06年01月01日
大3/8/7
Showa22-05-16
03/20/2016(H28) 12:34:56
2020(令和2)年7月30日

和暦変換プログラム自体はとくに目新しさはないですが、色々なフォーマットに対応できるようにしてみました。
もしよかったらぜひ試してみてください。

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

[PHPStorm]Shelfで並行開発を楽に管理する

はじめに

PHPStormのバージョン管理のヘルパー機能である、Shelfの使い方メモです。

詳細は公式ドキュメントを参照してください。
Gitを使用して複数の機能を同時に処理する

PHPStormの使い方シリーズ

作業用フォルダを用意する

まず、今回作業を行うフォルダを用意します。
この記事では、ローカルリポジトリ上で作業を行います。

% cd ~/Desktop
% mkdir shelf
% cd shelf

ローカルリポジトリを作成します。

% git init
% ls -la

drwxr-xr-x@  3 mitsuoka-takahiro  staff   96  7 29 21:19 ./
drwx------@ 14 mitsuoka-takahiro  staff  448  7 29 21:16 ../
drwxr-xr-x   9 mitsuoka-takahiro  staff  288  7 29 21:19 .git/

.gitフォルダがあればOK。

PHPStormでGitツールウィンドウを表示する

View > Tool Windows > Gitから開くことができます。
ショートカットはデフォルトで⌘9に設定されています。

このような感じのウィンドウが開きます。

Shelfで並行開発を楽に管理する

Shelfはgit stashと似た機能で、ブランチをクリーンにすることができます。
変更リストとの親和性が高く、合わせて使うことで開発中のファイルをより便利に管理できます。

準備

新しいファイルを3つ追加し、ステージングします。
ステージングするのは、バージョン管理対象ファイルでないとLocal Changesに表示されなからです。

% echo hello > a1.txt
% echo hello > a2.txt
% echo hello > b2.txt
% git add .

Gitツールウィンドウはこのようになっていると思います。

さらに、変更リストadd feature Aa1.txta2.txtを入れておきます。

ファイルをShelfにしまう

Gitツールウィンドウでb2.txtを右クリックし、Shelf Changesを選択します。

コミットメッセージを求められるので、add feature Bと入力し、Shelve Changesをクリックします。

これでShelfにb2.txtをしまうことができました。
GitツールウィンドウにShelfタブが表示され、内容を確認することができます。

同時に、作業ブランチ上からb2.txtがなくなっていることが確認できます。

% git status

On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   a1.txt
        new file:   a2.txt

Shelfからファイルを取り出す

Shelfタブのadd feature Bを右クリック、Unshelve...を選択します。

ウィンドウが表示されるので、変更せずUnshelve Changesをクリックします。

Local Changesタブに変更リストadd feature Bが追加され、b2.txtの変更も適用されます。

変更リストをShelfにしまう

ファイルだけでなく、既に作成している変更リストをShelfにしまうこともできます。
対象の変更リストを右クリック、Shelve Changesを選択することで、Shelfに格納されます。

まとめ

Shelfを使うことで、変更リストと親和性の高いgit stashに似た機能を利用することができる。
Shelfにしまったファイルは、ワークツリーから削除されるので、作業ブランチをクリーンに保つことができる。

PHPStormの使い方シリーズ

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

VultrでKUSANAGI Runs on Dockerが動くまで(その2)

前回のおさらい

前回の記事『VultrでKUSANAGI Runs on Dockerが動くまで(その1)』ではVPC環境であるVultrにKUSANAGI RoDをインストールするところまで確認しました。
今回はWordpressを運用しやすくするため、Dockerコンテナの設定を少し変えていきたいと思います。

動作環境など

  • Ubuntu 20.04
  • docker 19.03.12
  • docker-compose 1.26.2
$ docker-compose ps
       Name                     Command               State         Ports       
--------------------------------------------------------------------------------
kusanagi01_certbot   certbot --version                Up                        
kusanagi01_config    docker-entrypoint.sh wp -- ...   Up                        
kusanagi01_db        docker-entrypoint.sh mysqld      Up                        
kusanagi01_ftp       /bin/sh -c /docker-entrypo ...   Up                        
kusanagi01_httpd     /docker-entrypoint.sh /usr ...   Up      8080/tcp, 8443/tcp
kusanagi01_php       /usr/local/bin/docker-entr ...   Up  

修正前のdocker-compose.yaml

プロビジョニング直後はこんな感じになっています。
【主な特徴】

  • それぞれのコンテナでDocumentRoot含む必要なファイル類を/home/kusanagiにマウント
  • kusanagiボリュームとdatabaseボリュームを永続化
docker-compose.yml
version: '3'

networks:
  default:
    driver: bridge
    driver_opts:
      com.docker.network.driver.mtu: 9000
  shared-network:
    external: true

services:
  httpd:
    container_name: kusanagi01_httpd
    image: primestrategy/kusanagi-nginx:1.17.9-r0
    restart: always
    env_file:
      - .kusanagi
      - .kusanagi.httpd
    volumes:
      - kusanagi:/home/kusanagi
      - kusanagi:/etc/letsencrypt
      - kusanagi:/var/www/html/.well-known
    ports:
      - "8080:8080"
      - "8443:8443"
    extra_hosts:
      - "domain.name:127.0.0.1"
    networks:
      - shared-network

  certbot:
    container_name: kusanagi01_certbot
    image: certbot/certbot:v1.6.0
    restart: always
    network_mode: "service:httpd"
    env_file:
      - .kusanagi.httpd
    volumes:
      - kusanagi:/var/www/html/.well-known
      - kusanagi:/etc/letsencrypt
    command:
      - --version

## CONFIG
  config:
    container_name: kusanagi01_config
    restart: always
    build:
        context: ./wpcli
    user: "1000:1001"
    env_file:
      - .kusanagi
      - .kusanagi.wp
      - .kusanagi.db
    volumes:
      - database:/var/run/mysqld
      - kusanagi:/home/kusanagi
    command: wp --version

  php:
    container_name: kusanagi01_php
    image: primestrategy/kusanagi-php:7.4.8-r0
    restart: always
    env_file:
      - .kusanagi
      - .kusanagi.php
      - .kusanagi.mail
    network_mode: "service:httpd"
    volumes:
      - database:/var/run/mysqld
      - kusanagi:/home/kusanagi

## MYSQL 
  db:
    container_name: kusanagi01_db
    image: mariadb:10.5.4-focal
    restart: always
    user: "999:999"
    env_file:
      - .kusanagi.mysql
    network_mode: "service:httpd"
    volumes:
      - database:/var/run/mysqld
      - database:/var/lib/mysql

## FTPD
  ftp:
    container_name: kusanagi01_ftp
    image: primestrategy/kusanagi-ftpd:1.0.2-r1
    restart: always
    network_mode: "service:httpd"
    env_file:
      - .kusanagi
      - .kusanagi.wp
    volumes:
      - kusanagi:/home/kusanagi


volumes:
  kusanagi:
  database:

今回の変更点

以下の点を変更します。

  1. nginxのログ出力をDockerの標準ログ出力に変更
  2. PHPのファイルアップロード設定を追加
  3. 不要なコンテナ(certbot)を停止

変更点1

nginxのログ出力をDockerの標準ログ出力に変更します。
nginxフォルダの中にnginx.confファイルを作成しhttpdコンテナのnginx.confを書き換えます。nginx.confの変更部分のみ記載しておきます。

$HOME/kusanagi01/nginx/nginx.conf
#error_log /var/log/nginx/error.log warn;
error_log /dev/stderr warn;

    #access_log /var/log/nginx/access.log  main;
    access_log /dev/stdout  main;

変更点2

デフォルトだと2MB以上のファイルアップロードができないのでPHPのファイルアップロード設定を追加します。
phpフォルダの中にuploads.iniファイルを作成しphpコンテナにuploads.iniを追加します。

$HOME/kusanagi01/php/uploads.ini
file_uploads = On
memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 600

以下のようなフォルダ構成になります。

/home
  └$USER
        ├https-portal ← リバプロサーバー(マルチサイト& Let’s Encrypt対応)
        │ └docker-compose.yml
        │ 
        ├kusanagi01 ← KUSANAGI Runs on Docker(WordPressサイト1つ目)
        │ ├nginx                ※追加
        │ │ └nginx.conf         ※追加
        │ ├php                  ※追加
        │ │ └ uploads.ini       ※追加
        │ └docker-compose.yml
        │
        └kusanagi02 ← KUSANAGI Runs on Docker(WordPressサイト2つ目)
          └docker-compose.yml

変更点3

certbotコンテナが実験中で使われていないので起動しないようにします。
こちらはdocker-compose.ymlファイルで該当箇所をコメントアウトしてしまえばOKです。

修正後のdocker-compose.yml(抜粋)

docker-compose.yml
  #certbot:
    #container_name: kusanagi01_certbot
    #image: certbot/certbot:v1.6.0
    #restart: always
    #network_mode: "service:httpd"
    #env_file:
      #- .kusanagi.httpd
    #volumes:
      #- kusanagi:/var/www/html/.well-known
      #- kusanagi:/etc/letsencrypt
    #command:
      #- --version

コンテナの再作成

構成を変更したのでコンテナを作り直します。エラー無く起動することを確認します。

$ docker-compose down
$ docker-compose up -d

動作確認

変更が反映されているか確認していきます。

nginxのログ出力先変更

以下のコマンドでDockerのログを表示させWebのアクセスログが出力されることを確認します。

$ docker logs kusanagi01_httpd -f

PHPのファイルアップロード設定

phpinfoファイルを作成しコンテナにコピーし動作確認します。コンテナにシェルアクセスして作成してもよいですけどkusanagi-dockerコマンドでコンテナへのファイルコピーができます。

$HOME/content/DocumentRoot/test.php
<?php
    phpinfo()
?>
$ cd $HOME/kusanagi01
$ kusanagi-docker config push
chown: unknown user/group kusanagi:www
ERROR: Failed.

ブラウザでtest.phpにアクセスしてuploads.iniで変更した部分が反映されていることを確認しておきましょう。確認が終わったらtest.phpの削除も忘れずに。
VultrでKUSANAGI Runs on Dockerが動くまで(その3)に続きます。

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

Laravel artisanコマンド 【備忘録】

今後使わなくなっても、思い出しやすくするために書きました。

Laravelで使うコマンド

基本形
ターミナル
$ php artisan コマンド
作成系
ターミナル
$ php artisan make:controller コントローラー名


$ php artisan make:migration マイグレーション名
マイグレーション実行
ターミナル
$ php artisan make:migrate
サーバーを起動
ターミナル
$ php artisan serve

もっと価値ある情報発信できるようにならねば。

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

raspberry pi 4にphpenvをインストール!!

とはいいつつ、基本的には公式サイトに掲載されている#installationを参考にすればOKです。前提パッケージが無い場合コンパイルが失敗するので主にそっちのメモの意味合いが強いです...

前提パッケージ

sudo apt install \
build-essential \
libxml2-dev \
libkrb5-dev \
libonig-dev \
libxslt1-dev \
libtidy-dev \
libzip-dev 

上記が無いとコンパイル中にコケるので入れておきましょう。上記を実行した上で失敗した場合はエラーログに必要なパッケージが書いてあるので、適宜追加して下さい。そして教えて下さい。追記致します。

phpenvのinstall

Github - phpenv/phpenv#installationを参考にして下さい(投げやり)

トラブルシューティング

  1. ダウンロードに失敗する場合
    • 自宅の回線速度が遅い場合、openssl云々という表示が出たりしてタイムアウト、失敗してしまいます。空いている時間帯にもう一度チャレンジしましょう。Stay Homeの影響で固定回線も混雑しているんでしょうか...。
  2. ダウンロードは出来たが、コンパイルに失敗する場合
    • コンパイルに必要なパッケージが足りないと思われます。ログを確認後、apt searchなどを駆使してそれっぽいのを入れてみましょう。

引用・出典・参考

  1. https://github.com/phpenv/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「 $i = $i++;」が使えないことを知った話

※自分用メモです

はじまり

事の始まりは、実行すると無限ループするfor文から。
内容としては
1.要素数「4」の配列に、乱数(0~9)を利用して値を格納する。
2.格納した内容はループを利用して出力する。
の簡単なものだ。
終了条件がおかしくなっているように思うのでそこを見てみた。この1行である。
for($i=0; $i<=3; $i=$i++)

全くわからなくてTwitter上でアドバイスを求めたところ、一人の先輩からこんなアドバイスをいただいた。
$i=$i++$i++でいいよ」

なぜ

上のアドバイスと一緒に、理由も書いてくれているページを教えてもらった。
なぜ i = i++; としてはいけないか
ここを読んで自分なりに考えた結果、以下のような解釈に至った。
(注:コメントで正確な話を教えていただきました!ありがとうございます!)

(「;」といういわゆる副作用完了点という存在を知った前提で)
一度に、
1. iへの代入をしようとした
2. i+1をしようとした
つまり、「iという変数を1つの副作用完了点と副作用完了点の間で2回書き換えようとしたこと」が原因だと解釈した。
つまり、PHPに限らずある副作用完了点から、その次の副作用完了点までの間で1つの変数を複数回書き換えるような動作は認められていないために、ほかの言語でこれ(\$i=$i++)をやってもアウトだと考えた。

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

PHPでPayPayAPIにリクエスト投げて成功するまで

PayPayAPIにリクエスト投げて成功するまでの記事です

PayPay for Developers

PayPay for Developersで登録してAPI_KEY、API_SECRET、MERCHANT_IDの取得が必要(登録方法は割愛)

sdkインストール

composer require paypayopa/php-sdk

成功したソース

<?php
namespace App\Helpers;

use PayPay\OpenPaymentAPI\Client;
use PayPay\OpenPaymentAPI\Models\CreateQrCodePayload;

class PayPay {
  protected Client $paypayClient;

  function __construct() {
    $paypayConfig = \Config::get('paypay');

    $this->paypayClient = new Client([
      'API_KEY' => $paypayConfig['API_KEY'],
      'API_SECRET' => $paypayConfig['API_SECRET'],
      'MERCHANT_ID' => $paypayConfig['MERCHANT_ID'],
    ], true); //Set True for Production Environment. By Default this is set False for Sandbox Environment.


    // Creating the payload to create a QR Code, additional parameters can be added basis the API Documentation
    $payload = new CreateQrCodePayload();
    $payload->setMerchantPaymentId("my_payment_id" . \time());
    $payload->setCodeType("ORDER_QR");
    $amount = [
        "amount" => 1,
        "currency" => "JPY"
    ];
    $payload->setAmount($amount);
    $payload->setRedirectType('WEB_LINK');
    $payload->setRedirectUrl('https://paypay.ne.jp/');
    $payload->setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1');

    //=================================================================
    // Calling the method to create a qr code
    //=================================================================
    $response = $this->paypayClient->code->createQRCode($payload);
    // 処理がうまくいってなかったら抜ける
    if($response['resultInfo']['code'] !== 'SUCCESS') {
      return;
    }

    // Collectionに変換しておく
    $QRCodeResponse = collect($response['data']);

    //=================================================================
    // Calling the method to get payment details
    //=================================================================
    $response = $this->paypayClient->payment->getPaymentDetails($QRCodeResponse['merchantPaymentId']);
    // 処理がうまくいってなかったら抜ける
    if($response['resultInfo']['code'] !== 'SUCCESS') {
      return;
    }

    // Collectionに変換しておく
    $QRCodeDetails = collect($response['data']);

    //=================================================================
    // Calling the method to cancel a Payment
    //=================================================================
    $response = $this->paypayClient->payment->cancelPayment($QRCodeResponse['merchantPaymentId']);
    // 処理がうまくいってなかったら抜ける
    if($response['resultInfo']['code'] !== 'REQUEST_ACCEPTED') {
      return;
    }

    \Log::info(print_r($QRCodeResponse, true));
    \Log::info(print_r($QRCodeDetails, true));
    \Log::info(print_r($response, true));
  }

  function __destruct() {
  }
}

logの内容

[2020-07-30 06:53:42] local.INFO: Illuminate\Support\Collection Object
(
    [items:protected] => Array
        (
            [codeId] => 04-PKkfM9UDxn13nFfl
            [url] => https://qr-stg.sandbox.paypay.ne.jp/28180104PKkfM9UDxn13nFfl
            [expiryDate] => 1596081556
            [merchantPaymentId] => my_payment_id_1596093507
            [amount] => Array
                (
                    [amount] => 1
                    [currency] => JPY
                )

            [codeType] => ORDER_QR
            [redirectUrl] => https://paypay.ne.jp/
            [redirectType] => WEB_LINK
            [isAuthorization] => 
            [deeplink] => paypay://payment?link_key=https%3A%2F%2Fqr-stg.sandbox.paypay.ne.jp%2F28180104PKkfM9UDxn13nFfl
        )

)

[2020-07-30 06:53:42] local.INFO: Illuminate\Support\Collection Object
(
    [items:protected] => Array
        (
            [status] => CREATED
            [acceptedAt] => 0
            [requestedAt] => 0
        )

)

[2020-07-30 06:53:42] local.INFO: Array
(
    [resultInfo] => Array
        (
            [code] => REQUEST_ACCEPTED
            [message] => Request accepted
            [codeId] => 08100001
        )

    [data] => 
    [transit] => Array
        (
            [0] => HTTP/2 202 
            [1] => date: Thu, 30 Jul 2020 06:53:42 GMT
            [2] => content-type: application/json
            [3] => x-request-id: OPA4CE2D1787C11436F95A9B13DFEDFE3D6
            [4] => x-content-type-options: nosniff
            [5] => x-xss-protection: 1; mode=block
            [6] => cache-control: no-cache, no-store, max-age=0, must-revalidate
            [7] => pragma: no-cache
            [8] => expires: 0
            [9] => x-frame-options: DENY
            [10] => x-rate-limited: 1
            [11] => 
            [12] => 
        )

)

つまづきポイント

コメント文のミス?

サンプルコードでは

use PayPay\OpenPaymentAPI\Client;

$client = new Client([
  'API_KEY' => 'YOUR_API_KEY',
  'API_SECRET'=>'YOUR_API_SECRET',
 'MERCHANT_ID'=>'YOUR_MERCHANT_ID'
],false); //Set True for Production Environment. By Default this is set False for Sandbox Environment.

//Set True for Production Environment. By Default this is set False for Sandbox Environment.
って書いてるけどtrueでステージング環境(開発用)になる

サンプルソースのミスその1(ペイロード?を作るとき)

サンプルコードでは

// Creating the payload to create a QR Code, additional parameters can be added basis the API Documentation
$payload =new PaypaySdkPayload();
$payload->set_merchant_payment_id("my_payment_id");
$payload->set_code_type("ORDER_QR");
$amount = [
    "amount" => 1,
    "currency" => "JPY"
];
$payload->set_amount($amount);
$payload->set_redirect_type('WEB_LINK');
$payload->set_redirect_url('https://paypay.ne.jp/');
$payload->set_user_agent('Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1');

// Calling the method to create a qr code
$response = $client->code->createQRCode($payload);
// Printing if the method call was SUCCESS
console.log($response['resultInfo']['code']);

こうなってるけど、PaypaySdkPayloadのようなクラスはない、そしてset_の関数もない

サンプルソースのミスその2(詳細を見るとき)

// Calling the method to get payment details
$response =  $client->code->getPaymentDetails('<merchantPaymentId>');
// Printing if the method call was SUCCESS, this does not mean the payment was a success
console.log($response['resultInfo']['code']);
// Printing if the transaction status for the code has COMPLETED/ AUTHORIZED
console.log($response['data']['status']);

$client->code->getPaymentDetails('<merchantPaymentId>');実際は$client->payment->getPaymentDetails('<merchantPaymentId>');

サンプルソースのミスその3(キャンセルするとき)

// Calling the method to cancel a Payment
$response =  $client->code->cancelPayment('merchantPaymentId');
// Printing if the method call was SUCCESS
console.log($response['resultInfo']['code']);

$client->code->getPaymentDetails('<merchantPaymentId>');実際は$client->payment->getPaymentDetails('<merchantPaymentId>');
および
// Printing if the method call was SUCCESSって書いてて、ああ上と一緒でSUCCESSなんだろうなぁって思うかもしれないけど、実際はREQUEST_ACCEPTEDが帰ってきている(ちゃんと読め私w

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

PHPでClassをrequireせずに使う

Laravelのrequest()とかがどうやって呼ばれてるのかを調べていくうちに辿り着いたのでメモがてら。

Classは簡単に読み込めるけど、Functionは一筋縄ではいかなさそう。

ついでにClassに好き勝手プロパティ増やせることも発見した。

autoloaderを実装

spl_autoload_registerを使う。Laravelはcomposerが上手いことやってくれてるっぽかった

bootstrap.php
<?
function regist() {
    spl_autoload_register(function() {
        require './Hoge.php';
        require './Piyo.php';
    });
}

使いたいクラス

適当に用意

Hoge.php
<?
class Hoge {
    public $hoge;
}
Piyo.php
<?
class Piyo {
    public $piyo;
}

autoloaderを呼んで使う

ついでに好き勝手にプロパティも生やす

test.php
<?
require './bootstrap.php';
// ここでクラスをautoloadする
regist();

// どこからも呼んでないけど
$h = new Hoge;
// 生やせる
$h->fuga = "aaa";
$h->hogepiyo = "bbb";

// 使える
$p = new Piyo;
$p->fuga = "aaa";
$p->hogepiyo = "bbb";

var_dump($h, $p);

/*
結果

class Hoge#2 (3) {
  public $hoge =>
  NULL
  public $fuga =>
  string(3) "aaa"
  public $hogepiyo =>
  string(3) "bbb"
}

class Piyo#3 (3) {
  public $piyo =>
  NULL
  public $fuga =>
  string(3) "aaa"
  public $hogepiyo =>
  string(3) "bbb"
}

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

Laravel 任意のディレクトリ直下にモデルファイルを作成する

目的

  • 実務でモデルファイルを作成する時にアプリ名ディレクトリ/appに作成することよりもアプリ名ディレクトリ/app/Models直下に作成することが多いため、方法をまとめる

実施環境

  • ハードウェア環境
項目 情報
OS macOS Catalina(10.15.5)
ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
プロセッサ 2 GHz クアッドコアIntel Core i5
メモリ 32 GB 3733 MHz LPDDR4
グラフィックス Intel Iris Plus Graphics 1536 MB
  • ソフトウェア環境
項目 情報 備考
PHP バージョン 7.4.3 Homwbrewを用いて導入
Laravel バージョン 7.0.8 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う
MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする

前提条件

  • 実施環境と同じLaravelローカル開発環境が構築されていること。

前提情報

  • 特になし

読後感

  • モデルファイルをアプリ名ディレクトリ/app直下の任意のディレクトリ内に作成することができる。

  1. アプリ名ディレクトリに移動して下記コマンドを実行する。

    $ php artisan make:model app直下のモデルファイルを作成したいディレクトリ名/モデルファイル名
    

具体例

  1. アプリ名ディレクトリ/app/Models直下にTestというモデルファイルを作成したい時はアプリ名ディレクトリで下記コマンドを実行する。(Modelsディレクトリが存在しない時は自動で作成されてその中にモデルファイルが格納される)

    $ php artisan make:model Models/Test
    

モデルファイルを移動してエラーが出た話

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

phpのフレームワークLaravelを触ってみた。(新規コントローラー作成編)

artisanの利用

ArtisanとはLaravelを構成しているコマンドラインインターフェイスの名前です。
ちなみに読み方は「アルチザン」。
使い方は、php artisan listで一覧を表示してくれます。

$ php artisan list
Laravel Framework 7.21.0

Usage:
  command [options] [arguments]

Options:
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
      --env[=ENV]       The environment the command should run under
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
                         ・
                         ・
                         ・
                         ・
                         ・
                         ・
                         ・
                         ・

※長いので、一部記載。

コントローラの作成

次のコマンドを実行します。

$ php artisan make:controller SampleAppController
Controller created successfully. ※これが表示されればちゃんと出来てます。
  • 作成されるディレクトリは、こちら
    (Laravelアプリディレクトリ)/app/Http/Controllers

ファイルの中身は、こんな感じです。

app/Http/ControllersSampleAppController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SampleAppController extends Controller
{
    //
}

下記の様にindex functionを追加します。

app/Http/ControllersSampleAppController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SampleAppController extends Controller
{
    //下記のメソッドを追加
    public function index() {
        return view('sampleapp');
    }
}

ルーティングの設定

(Laravelアプリディレクトリ)/routes/web.phpに下記のように追加します。

routes/web.php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/','SampleAppController@index');

ビュー作成

(Laravelアプリディレクトリ)/resources/views/にsampleapp.blade.phpを作成します。

resources/views/にsampleapp.blade.php
<html>
<head>
    <title>Sample App</title>
</head>

<body>
    Sample App Viewです。
</body>

</html>

動作確認

http://localhost:8000/にアクセスして画面が表示される事を確認します。

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

PHP 文字化けしないCSVファイル作成

はじめに

色々なところで紹介されているものだと思いますが、
私の環境ではどうしても上手くいかなかった。。。
こんなところで詰まっている場合ではないのに。。。

コピペに頼っているから悪い。一から自分で作るしかない!
思い立ち作ったらうまくいった~しかも簡単すぎ!!

なんだかんだできたので記事にしてみたいと思います!
※初投稿なのでお手柔らかに。

コード

「ファイル名」「データカラム」「データ本体」を渡すと決められたパス先にCSVを作成し、作成したCSVファイルのフルパスを返す関数。

  /**
   * CSVファイルの作成
   */
  public function createCSV($fileName, $csvColumn, $csvBodyData){

    //ファイルフルパス作成
    $filePath = 'ファイルパス'.$fileName;

    //CSVファイル作成
    $csvFile = fopen($filePath, 'w');

    //CSVを開いても文字化けしないように
    stream_filter_prepend($csvFile,'convert.iconv.utf-8/cp932');

    //カラムを書き込む
    $row = fputcsv($csvFile, $csvColumn);

    //データを書き込む
    foreach($csvBodyData as $csvBody){
      fputcsv($csvFile, (array)$csvBody);
    }

    //ファイルフルパスを返却
    return $filePath;
  }

なんでこんな簡単だったのに悩んだのか。。。

最後に

やはりコードのコピペはダメですね~
理解が大事でそれが一番の近道だと思い知りました。

もっと勉強せねば!!!

もっと良い方法あれば教えてください。
あと間違っていたらお願いします。

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

PHP Excelで文字化けしないCSVファイル作成

はじめに

色々なところで紹介されているものだと思いますが、
私の環境ではどうしても上手くいかなかった。。。
こんなところで詰まっている場合ではないのに。。。

コピペに頼っているから悪い。一から自分で作るしかない!
作ったらうまくいった~しかも簡単すぎ!!

なんだかんだできたので記事にしてみたいと思います!
※初投稿&PHP歴半年なのでお手柔らかに。

コード

「ファイル名」「データカラム」「データ本体」を渡すと決められたパス先にCSVを作成し、作成したCSVファイルのフルパスを返す関数。

      /**
       * CSVファイルの作成
       */
      public function createCSV($fileName, $csvColumn, $csvBodyData){

        //ファイルフルパス作成
        $filePath = 'ファイルパス'.$fileName;

        //CSVファイル作成
        $csvFile = fopen($filePath, 'w');

        //CSVを開いても文字化けしないように
        stream_filter_prepend($csvFile,'convert.iconv.utf-8/cp932');

        //カラムを書き込む
        fputcsv($csvFile, $csvColumn);

        //データを書き込む
        foreach($csvBodyData as $csvBody){
          fputcsv($csvFile, (array)$csvBody);
        }

        //ファイルフルパスを返却
        return $filePath;
      }

なんでこんな簡単だったのに悩んだのか。。。

最後に

やはりコードのコピペはダメですね~
理解が大事でそれが一番の近道だと思い知りました。

もっと勉強せねば!!!

もっと良い方法あれば教えてください。
あと間違っていたらお願いします。

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

LaravelでFacadeを作ってSlackに通知してみた(Laravel6.xバージョン)

概要

前回の記事から約1年経って、Laravelのバージョンも上がったし、私の知識も少しだけ増えたので書き直してみました。

環境

Laravel:6.18.31
PHP:7.4.6

SlackのWebhook URLを取得

SlackにメッセージをPOSTするためのWebhook URLを取得します。

Incoming Webhook を Slack に追加

アプリから取得する場合は、ワークスペースの「App」メニューから検索欄に“Incoming Webhook”と入力して検索してください。
ブラウザで取得する場合は、以下のURLにアクセスしてください。

https://slack.com/apps/A0F7XDUAZ--incoming-webhook-

  • Slack に追加 ボタンをクリック
  • 通知するチャンネルを選択し、Incoming Webhook インテグレーションの追加 ボタンをクリック
  • Webhook URLをコピーする

通知先のチャネルや投稿されるときの名前等は設定画面で登録しておくことができますが、通知するときに指定することもできます。

Incoming Webhook.png

取得したURLは.envファイルに記載しておきます。

.env
SLACK_URL=https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/xxxxxXXxxXxXXxxXXXXXxx

また、Slack 通知用の設定ファイルも用意しておきましょう。

app/config/slack.php
<?php

return [
    // Webhook URL
    'url' => env('SLACK_URL'),
];

Slack 通知の準備

Slack 通知については公式ドキュメントに記載がありますのでそちらも参考にしてください

Slack 通知 - Lavavel 6.x

Slack 通知チャンネルのインストール

composer require laravel/slack-notification-channel

通知クラスの作成

以下のコマンドで Slack 通知クラスを作成します。

php artisan make:notification SlackNotification

app/Notifications/SlackNotification.php というファイルが作成されますので、Slack 通知用に変更していきます。

  • via メソッドが返す配信チャンネルを slack に変更する
  • SlackMessage クラスのインスタンスを返す toSlack メソッドを追加する

SlackMessage クラスの content メソッドでテキストメッセージを送信することができますが、 attachment メソッドで活用することでより詳細な情報をSlackに送ることができます。

app/Notifications/SlackNotification.php
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Messages\SlackAttachment;

class SlackNotification extends Notification
{
    use Queueable;

    /**
     * 通知チャンネル情報
     *
     * @var array
     */
    protected $channel;

    /**
     * 通知メッセージ
     *
     * @var string
     */
    protected $message;

    /**
     * 添付情報
     *
     * @var array
     */
    protected $attachment;

    /**
     * 通知インスタンスの作成
     *
     * @return void
     */
    public function __construct($channel = null, $message = null, $attachment = null)
    {
        $this->channel = $channel;
        $this->message = $message;
        $this->attachment = $attachment;
    }

    /**
     * 通知の配信チャンネルの取得
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['slack'];
    }
    /**
     * Slack通知表現を返す
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\SlackMessage
     */
    public function toSlack($notifiable)
    {
        $message = (new SlackMessage)
            ->from($this->channel['username'], $this->channel['icon'])
            ->to($this->channel['channel'])
            ->content($this->message);

        if (!is_null($this->attachment) && is_array($this->attachment)) {
            $message->attachment(function ($attachment) {
                if (isset($this->attachment['title'])) {
                    $attachment->title($this->attachment['title']);
                }
                if (isset($this->attachment['content'])) {
                    $attachment->content($this->attachment['content']);
                }
                if (isset($this->attachment['field']) && is_array($this->attachment['field'])) {
                    foreach($this->attachment['field'] as $k => $v) {
                        $attachment->field($k, $v);
                    }
                }
            });
        }
        return $message;
    }
}

Slack通知ファサードの作成

設定ファイルにチャンネル情報を追加

app/config/slack.php
<?php

return [
    // Webhook URL
    'url' => env('SLACK_URL'),

    // チャンネル設定
    'default' => 'work',

    'channels' => [
        'work' => [
            'username' => '作業通知',
            'icon' => ':face_with_rolling_eyes:',
            'channel' => 'notice-work',
        ],
        'error' => [
            'username' => 'エラー通知',
            'icon' => ':scream:',
            'channel' => 'notice-error',
        ],
    ],
];

Slack通知サービスの作成

以下の処理を実行するためのサービスを作成します。

  • Notifiableトレイトを利用する
  • 通知先のURLを返す routeNotificationForSlack メソッドを追加する
  • 通知するチャンネルを指定する channel メソッドを追加する
  • send メソッドで Slack 通知クラスのインスタンスを引数として $this->notify() を呼び出す
app/Services/SlackService.php
<?php

namespace App\Services;

use Illuminate\Notifications\Notifiable;
use App\Notifications\SlackNotification;

class SlackService
{
    use Notifiable;

    /**
     * 通知チャンネル情報
     *
     * @var array
     */
    protected $channel = null;

    /**
     * 通知チャンネルを指定
     * 
     * @param array $channnel
     * @return this
     */
    public function channel($channel)
    {
        $this->channel = $channel;

        return $this;
    }

    /**
     * 通知処理
     *
     * @param string $message
     * @return void
     */
    public function send($message = null)
    {
        if (!isset($this->channel)) {
            $this->channel(config('slack.channels.' . config('slack.default')));
        }

        $this->notify(new SlackNotification($this->channel, $message));
    }

    /**
     * Slack通知用URLを指定する
     *
     * @return string
     */
    protected function routeNotificationForSlack()
    {
        return config('slack.url');
    }
}

ファサードクラスの作成

app/Facades/Slack.php
<?php
namespace App\Facades\Slack;

use Illuminate\Support\Facades\Facade;

class Slack extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'slack';
    }
}

サービスプロバイダの作成

以下のコマンドでサービスプロバイダを作成します。

php artisan make:provider SlackServiceProvider

app/Providers/SlackServiceProvider.php というファイルが作成されますので、register メソッドにキーとサービスをバインドする初期を記述します。

app/Providers/SlackServiceProvider.php
<?php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class SlackServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(
            'slack',
            'App\Services\SlackService'
        );
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}

サービスプロバイダとエイリアスを追記する

app/config/app.php
    'providers' => [
        (省略)
        App\Providers\SlackServiceProvider::class,
        (省略)
    ],
    'aliases' => [
        (省略)
        'Slack' => App\Facades\Slack::class,
        (省略)
    ],

通知処理

以下のように記述すればシステム内のどこからでも Slack への通知が可能です。

// メッセージを送信
\Slack::send('Hello World!');

// チャンネルを指定して送信する場合
\Slack::channel('error')->send('Error!!');

おまけ

Tinkerを使えば、画面やコンソールコマンドを用意しなくても動作が確認できます。

php artisan tinker
Psy Shell v0.10.4 (PHP 7.4.6 ― cli) by Justin Hileman
>>> \Slack::send('Hello World!');
=> null
>>> exit
Exit:  Goodbye

詳しくはこちらをご確認ください。

Tinker - Lavavel 6.x

サンプルソース

https://github.com/freeneer/slack-notice-test

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