nanisore oishisou

Webエンジニアあるまさんのゆるふわ奮闘記。

PHPでメール着弾時、メール本文をパースしDBへ格納する(2)〜メールパース編〜

2年前に、私はこんなことを言っていた。

次回は、PHPをキックしてメールをパースしてDBに格納するよっ♥
お楽しみにね★

その次回が、令和という元号が発表された節目の日になろうとは、私も読者も思っていなかっただろう。
びっくり!

この作業について、これからも聞かれると思うのでまとめておきます。

おさらい

とあるメールアドレス(chakudan@example.com)にメールを送信すると
Laravelでつくったstore_emailというコマンドが叩かれて、
メールの内容をDBに格納してくれるという、メール内容データDB取り込みの機構を構築する。

まずは、メールサーバを構築し、着弾用ユーザをつくり、
そのユーザにメールが届いた際に別のアドレスに転送できるようにサーバを設定するというところまでが
前回の作業だった。

arm4.hatenablog.com

今回は、その次のPHPをキックしてDBに格納するところまでをまとめる。

やること

  • Laravelでstore_emailコマンドを作成
  • /etc/aliasesでメール受信時にstore_emailが叩かれるよう設定
  • phpにmailparseモジュールを追加
  • php-mime-mail-parserをinstall
  • メールをパースしてDBに格納する

Laravelでstore_emailコマンドを作成

まずはメール送信時に、そのコマンドが本当に叩かれるかどうかというのを確かめたいので、 ログを吐き出すだけのコマンドを作成する。

.env

MAIL_DRIVER=sendmail

コマンドを作成

php artisan make:command StoreEmail

*古いバージョンのLaravelをお使いの方はmake:commandではなくmake:consoleになる。

app/Console/Commands/StoreEmail.php

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use \Log;

class StoreEmail extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:store_email';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'メールをDBに保存するコマンド';
    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        Log::info('メールを受信しました。');
    }
}

/etc/aliasesでメール受信時にstore_emailが叩かれるよう設定

chakudan@example.comにメールを送ったら、chakudanユーザのメールボックスにメールが届き、
さらにstore_emailコマンドがキックされるように、/etc/aliasesのchakudanユーザの箇所を以下のようにしておく

sudo vi /etc/aliases

/etc/aliases

chakudan: chakudan, "| /usr/bin/php /var/www/hogehoge/artisan command:store_email"

chakudai@example.comにメールを送信したら、ログに「メールを受信しました。」が記載されていたら メール送信&PHPキックが成功している。

phpにmailparseモジュールを追加

php-mime-mail-parserというライブラリを使ってメールのパースを行おうと思うが、 インストールするにはmailparseモジュールをphpに追加する必要がある。

cd /tmp
pecl download mailparse
tar xvzf mailparse-3.0.2.tgz
cd mailparse-3.0.2
phpize
./configure
sed -i 's/^\(#error .* the mbstring extension!\)/\/\/\1/' mailparse.c
make
make install
echo "extension=mailparse.so" > /etc/php.d/30-mailparse.ini
httpd -k graceful

mailparseのモジュールがちゃんと入ってるか確認する

php -i| grep mailparse

mailparse
mailparse support => enabled
mailparse.def_charset => us-ascii => us-ascii

php-mime-mail-parserをinstall

php-mime-mail-parserというライブラリをinstallする。

composer require php-mime-mail-parser/php-mime-mail-parser

メールをパースしてDBに格納する

app/Console/Commands/StoreEmail.php

<?php


use PhpMimeMailParser\Parser;
use App\Inquiry;

    public function handle()
    {
        Log::info('メールを受信しました。');

        $parser = new Parser();
        $parser->setStream(fopen('php://stdin', 'r'));

        $to = $parser->getHeader('to');
        $addressesTo = $parser->getAddresses('to');
        $from = $parser->getHeader('from');
        $addressesFrom = $parser->getAddresses('from');
        $subject = $parser->getHeader('subject');
        $body = $parser->getMessageBody('text');
        $text_arr = explode("\n", $body);

        // ↓こんな感じでデータが取得できる
        
        // $from = 'example@example.co.jp';
        // $subject = '◯◯システムお問い合わせ';
        // $text_arr = [
        //     "◯◯システムWebサイトからお問い合わせがありました。\n",
        //     "\n",
        //     "--\n",
        //     "お問い合わせ番号: 0000000203\n",
        //     "氏名(漢字): 山田 太郎\n",
        //     "氏名(カタカナ): ヤマダ タロウ\n",
        //     "性別: 男\n",
        //     "年齢: 32歳\n",
        //     "お電話番号: 03-1234-5678\n",
        //     "メールアドレス: hogehoge@example.com\n",
        //     "お問い合わせ内容:\n",
        //     "商品について質問があります。\n",
        //     "よろしくお願いします。\n",
        //     "--\n",
        //     "\n",
        //     "※このメールは◯◯システムのお問い合わせフォームから送信され\n",
        //     "ました\n",
        //     "\n",
        //     "\n",
        // ];

        Inquiry::create([
            'subject' => $subject,
            'body' => $body,
            'from' => $from,
        ]);
    }

詳しくはphp-mime-mail-parserのgithubページに使い方が書いてあるので、
そちらを参照してみてください。

github.com

あとがき

どうでしたでしょうか、二年越しの私のメールパース編。

メール送信してデバッグして、修正して送信してとかやるのが結構たいへんなので、 Laravel5.7.7以上ならメールパースのデバッグはLaravel Telescopeを使ったほうがいいのでは、と思います。

laravel.com

次のブログは令和元年になるかなーw

次の時代で会おうねー!!!!!!

今さら過ぎるけど、GoogleスライドとSlackは神

今までパワポの既存テンプレートを使っていたのですが
おしゃれなスライドテンプレートサイトを同僚から教えてもらいました。

https://www.slidescarnival.com/ja/

おしゃれなテンプレートばかり!!!

なにこれ、ど熱い!!!

しかもGoogleスライドのテンプレートとしても使えるそうな。

Googleスライドならそのまま公開できるし、なんだ、別にパワポなんて要らないんだ。

何でパワポ使っていたんだろう、私。
アホなのかなw

これが無料とか、すごいよGoogle様!

というわけで、今週の土曜日に開催されるLT会用にスライドを早速作ってみました!

いい感じ☆

タイトルは、「Laravelのバッチ処理終了をSlackでお知らせする」です。



もうこれで私は、裏で動いてるバッチいつ終わるかなーと気にしてたことも忘れて別の実装に没頭してしまうことも無くなるわけです。
そうSlackのスココココ!を待てばいいだけです。

むしろマシンを放置して家に帰っても、帰りの電車でスココココ!「やったーバッチ終わってるー」とかプチ安堵を得られるわけです。

時には
「あ、誰かからメッセージきたかなー♪
 うわ、バッチがコケてる。。。」

という謎のどっきりにも遭遇できたりね!

Slackって神ですね!

しかもこのサービスクラスをコピペすればどんな通知も好き放題カスタマイズできそうです。(まだやってないが)

土曜日はコードをお見せしながらデモをやりますよ!ますよ!

お楽しみにーーー☆

Laravelでgroup byしたら、"~ isn't in GROUP BY (~)"って怒られました

group byしたら、うちのMariaさんが
「selectで設定した全カラムをgroup byに入れなさいよ!言わせんな恥ずかしい!!」
という、ヒステリックなことを言ってきた。
あ、これ、どれ選べばいいのか分からないから勝手にマージして表示してやんぜ、というmysqlの優しさが失われているやつや。。
そんな設定した覚えないのに、なんで。。。

おいらの環境

config/database.php

strict = true

この環境で、ONLY_FULL_GROUP_BYがsqlのmodeとして追加されてしまうみたい。

これはLaravel側で設定してしまうようで、私のMariaDBではmodeとして追加はされてはいなかった。

f:id:arm4:20190111181301p:plain
ONLY_FULL_GROUP_BYは設定されてないYODA

以下にissueが上がっていた。

github.com

原因はstrict = trueのようだ。

strict = falseにすれば、確かにヒステリックなことは言わなくなりMariaさんは優しさを取り戻してくれる。

しかし、マージされてしまっていて特に使わないカラムを取得する意味もないし、
ヒステリックなMariaさんも、私は何気に嫌いでもない。

true時に適用されてしまう項目は以下のようなので、

  • ONLY_FULL_GROUP_BY
  • STRICT_TRANS_TABLES
  • NO_ZERO_IN_DATE
  • NO_ZERO_DATE
  • ERROR_FOR_DIVISION_BY_ZERO
  • NO_AUTO_CREATE_USER
  • NO_ENGINE_SUBSTITUTION

現状の差分的には以下が追加で設定されてしまうということだ。

  • ONLY_FULL_GROUP_BY
  • NO_ZERO_IN_DATE
  • NO_ZERO_DATE

うーん。

正直falseでもいいような気がするが、アプリ側でそんなん絶許!!してくれるというのは、考えようによってはいいのかもしれない。

一部のみを適用したい場合はissueにあるとおり、config/database.phpmysqlの設定にmodesを追加して、適用したいmodeのみを記載すればいい。

ONLY_FULL_GROUP_BYだけをオフりたい場合は、↓以下。
分かりやすくコメントアウトにしてあげました。(優しさ)

config/database.php

            'strict' => true,
            'modes'  => [
                // 'ONLY_FULL_GROUP_BY',
                'STRICT_TRANS_TABLES',
                'NO_ZERO_IN_DATE',
                'NO_ZERO_DATE',
                'ERROR_FOR_DIVISION_BY_ZERO',
                'NO_ENGINE_SUBSTITUTION',
            ],

ちなみにONLY_FULL_GROUP_BYをオフらなくても怒られなくするには、
以下の記事を参考にそれぞれのカラムでどの値を持ってくるか指定してやればいいようだ。

mashup.hatenablog.com

え、超めんどくさい。。。。
おとなしくオフしたい。

末永いお付き合いをするには、優しさも大事なときが来るかもしれないが
ひとまずヒステリックになったのは、きっと何か嫌な出来事があったからだと思うので
大人しく過敏になったLaravelさんとお付き合いを続けることにした。

まあでも、今回はテストで*で書いて気づいたけど、
ちゃんと書き込むときはマージされるカラムなんかselectしない気もする。

教えてくれて親切だけど、
怒られるとビックリして、それの原因突き止めるのに時間かかったりして
なんかちょっとしたトラップ設定のような気もするし
こんなんオフればいいやんって気もする。

ただ何度も言うが、
これをデフォルト設定にしたのには何か嫌な出来事があったからかもしれないので
このストイックさを優しく受け止めてあげる男っぷりを私たちは試されているのかもしれない。
そんなわけないか。

新年一発目のブログでしたーーー!!!!

Atomic DesignをVueコンポーネント設計に適用するために見るやつ

Vue Fes JapanのスライドでAtomic Designについて書いてあるスライドがあり、
コンポーネント設計であったもやもやが解決しそう!と思ってほんのり調べてみた。

コンポーネント設計にAtomic Designを採用したというスライド note.mu

▼Vue.js からみた AtomicDesign medium.com

▼Atomic Designを考案した人のブログ

bradfrost.com

そして、みんなで認識合わせる時には、発音しないといけないので
読み方と意味を調べてみた。
これが正しいかどうかは分からないので参考程度にお願いします。

Atom

  • 読み方:アトム
  • 意味:原子

【Molecule】

  • 読み方:モリキュール
  • 意味:分子

【Organism】

【Template】

  • 読み方:テンプレート
  • 意味:雛形

考案した人のブログにあるPageという概念は
デザイナーから見た個別のページと
デザインされるべきテンプレートを呼び分けるということなので
Vueのコンポーネント設計では存在しない、はず。

なるほどー!めっちゃ設計概念として分かりみある!!

Vue.jsの人気UIフレームワークまとめ2018秋

お久しぶりです。ベル子です。

Vue.jsのUIフレームワークについてのまとめ記事(英語)を見つけてツイートしたところ、
それなりにいいねがついたので
「みんな、UIフレームワークをめっちゃ探してるな」と感じとり
人気のあるフレームワークを日本語でまとめてみることにしました。

人気のあるフレームワークは以下のブログ記事などを参考にしています。

11 Vue UI Component Libraries You Should Know In 2018

海外のVue.js用UIフレームワーク紹介記事を読み漁り、どの記事にもだいたい出てきているフレームワーク
女の勘でチョイスしました。

人気がありそうなVue.jsのUIフレームワーク

Vuetify

vuetifyjs.com

Quasar

quasar-framework.org

Element

Element

Vue Material

vuematerial.io

BootstrapVue

bootstrap-vue.js.org

Buefy

buefy.github.io

どのフレームワークがどのくらい人気なの?

Star Historyを皆さんご存知ですか。
ちなみに私は知りませんでした。
githubのスター獲得の歴史をグラフで確認できるサービスです。

丁寧な解説記事はこちら。

Star history - GitHubのスター獲得履歴をグラフ化して比較できるWebサービス | ソフトアンテナブログ

そのStar Historyを使いまして、
女の勘で選んだベストフレームワークのスター獲得曲線を確認してみます。

Star history

見ておわかりのとおり、Elementは先発のVueのUIフレームワークで、スター数も飛び抜けて多いです。

どのフレームワークも右肩上がりでスターが増えていますね。
一度つけたstarをunstarすることってなかなかないと思うので、右肩上がりじゃないと逆に気になって夜も眠れませぬが、
見てお分かりのようにVueのフレームワーク自体の人気が急激に出てきているのがお分かりいただけると思います。

実は先発のフレームワークであるvue-materialも人気があったのですが、
こちらの曲線を見てお分かりのように後発のマテリアルデザインフレームワークである
Vuetifyが人気をかっさらっていってしまったようです。

Vue.js自体の人気について

ちなみにここで、世界三大jsフレームワークである、Vue.js、React、AngularのStar Historyも確認してみましょう。

Star history

ご覧になってお分かりのように、ついにVue.jsがReactを抜きました。(めでたいぜ!)

私がVueを初めた数年前はフロント界隈のイベントにおいてVue勢はややマニアック勢だったのですが、
もはや巷ではReactおじさんからVueおじさんへ変身しているおじさん勢も出てきています。

twitter.com

ごらん。女の勘を。
もはや、メインストリーム!!!!

Elementについて

ちなみに私はフレームワークは一通り候補を調べて人気があるものを、
使ってみて判断する派です。

それで2018年6月にVueのUIフレームワークを選定するにあたり、
当時最も人気と思われたElementとVuetifyを使ってみました。

2018年6月時点で、Elementはレスポンシブレイアウトに対応していない状態で
コンポーネントの詰め合わせという様相が強く統合的なフレームワークという感じではありませんでした。
そのため、レイアウトされたページの見本がなく、綺麗にレイアウトするのがかなり大変でした。

ということで、当時は結果的に楽で綺麗にレスポンシブ対応のレイアウトができるVuetifyを選択しました。

現在のElementのドキュメントを見る限りでは、
レスポンシブ対応されていて統合的なフレームワークとして使えそうに見えます。
アプリケーションに必要そうなコンポーネントもほぼ揃っているので
マテリアルデザインに抵抗がある人は使ってみてもよさそうだと思います。

github.com

リリースノートを見てみても活発に更新されてメンテナンスされているのが分かります。
一点、注意としては中国の方が開発されているので、突然中国語がドキュメントなどに登場したりします。

Vuetifyについて

Vuetifyは現在、本稼働案件で使用していますが、
目立った致命的なバグやレイアウト崩れなどはほとんどないように思います。
リリースノートを見てお分かりのとおり尋常じゃなく活発にリリースが行われています。

github.com

先日も新しいコンポーネントがリリースされました。
現在まで定期的にアップデートしていますが、
radioコンポーネントのイベントがinputからchangeになってしまったということ以外では
書き換えが発生するようなことはありませんでした。

一点注意点としてはVuetifyが採用しているcssプリプロセッサがstylusなのでstylusのloaderなどを入れたり
ファイルの分割などで少し工夫が必要かもしれません。

Vue.jsのUIフレームワークのデザイン分類

UIといえばデザインも非常に大事です。
デザインがアレなツールを使いたい人はあまりいないので、 できれば綺麗で好みのデザインに近いフレームワークを使いたいところです。
ということでデザインに着目して分類してみました。

マテリアルデザイン

  • Vuetify
  • Quasar
  • Vue Material

Bootstrap系

  • BootstrapVue
  • Buefy

独自デザイン系

  • Element

マテリアルデザインはレスポンシブ時の操作感まで考え抜かれていると感じることが多いです。
特に大きな時計が出てくるTimePickerは当初は正直「え、なにこれ。。」と思ったのですが
使い慣れてくるとスマホからでもPCからでも驚くほど素早く気持ちよく時間の入力ができます。

ただ、見慣れていないクライアントに「え、なにこれ。。」と言われてしまうかもしれないので
お好みで使っていただければと思います。

全体的にマテリアルデザインは洗練されている印象で、Bootstrap系は少しカジュアルな印象を与えます。
Elementについては、色が全体的に薄いのでオシャレ感はかなりありますが、
オシャレじゃなくていいからハッキリ見えるほうがいいというクライアント向けではなさそうです。

マテリアルデザインはよくアニメーションがついてくるので、それが煩わしいと感じる人もいるかもしれません。

フレームワーク選定について

ここで紹介したフレームワークコンポーネントがかなり充実しているものばかりなので
どれを採用しても良さそうな印象を受けます。

あとは、Vue.jsの場合はコンポーネントがいかにカスタマイズして使えるかということが重要になってくるので
その辺の使い勝手は多少、自分で使ってみて検討してみるのがいいです。

フレームワーク選定の時は、まずは人気があるものをリストアップし当たりをつけて、
ドキュメントなどを読み込んで絞り込み、
あとは実際に動かしてみて決めるというプロセスがいいように思います。

ドキュメントの出来がいいというのはフレームワークを選ぶ上でかなり大事です。
以下のポイントを重視するといいかなと思います。

  • 説明が分かりやすい。
  • 見本が豊富にある。
  • 概念の説明とAPIの仕様がきちんと分けて書いてある。
  • 目的の情報まで素早く到達できる。
  • 更新がマメに行われている。

今後も良さげなフレームワークが出てきたら紹介したいと思います★

Laravel 5.5で超絶便利なヘルパーoptionalってのできてるよ

この前、同僚にベル子たんに教えてもらったやつめっちゃ便利そうなのに何というヘルパーか忘れてしまいました!

と言われて、私も忘れてしまっていたという事件がありました。

現在、私がコアで開発してる案件は、5.7にアップデートしたので使えるのですが、
あまりにも"Trying to get property of non-object"を見すぎてしまったせいで
息を吸うように以下のように書いてしまいます。

<?php

$user_name = User::find(1) ? User::find(1)->name : '';

それか、php7で搭載されたnull合体演算子を使うともっと綺麗に書けます。

<?php

$user_name = User::find(1)->name ?? '';

何度も同じこと書かなくてよくなった!

PHP: 新機能 - Manual

これだけでも非常にありがたいのですが、
Laravel 5.5 で追加されたoptionalヘルパーを使うと息を吸うように
nullの時のエラーを回避できるようになります。

<?php

$user_name = optional(User::find(1))->name;

optionalを使うと、userが見つからなかった場合はnullを返して来ます。
厳密に空文字にしたい、などの時はnull合体演算子がいいかもしれません。

さらに Laravel 5.6.13 ではクロージャも第2引数で渡せるようになったらしいので
さらに使い勝手がよくなったっぽいいです。
すごい!さすが私のLaravel。

laravel-news.com

Laravelの公式ドキュメントはこちらです。

laravel.com

便利なものはどんどん使っていきたいところです。

git研修用スライド&Web API勉強会スライド

新卒が入って来た時に行ったgit研修用のスライドをSpeaker Deckで全世界に公開したので
Gitをこれから教えようと思ってるor初心からやり直したいという人がいたら参考にしてみてください。

あと8月のPG会で
初心者向けにWeb APIを超分かりやすく初歩の初歩から説明した際のスライドもアップしました。

話すことをすぐ忘れてしまうタイプなので
話すことをスライドに全部書いているため、スライドというか、もはや本w

あと時間管理ができなくて話しすぎるとよく言われてしまうので
これ以上は話さないぞという戒めでもあるw

git研修用は自分でも時々見返してしまうくらい頭がすっきりします。

ぜひ、ご一読ください!