メール送信時のcontroller・mailableクラスの変数の書き方

– to, cc, bccなど宛先はcontrollerで書く
– mailableクラスのコンストラクターに引数を渡す
– constructorに初期値を設定し、callback関数でルートからプレビューを行うこともできる
– メール本文は、text, htmlどちらも可能。改行はbrタグ

### controller

use Illuminate\Support\Facades\Mail;
use App\Mail\TestMail;
public function index()
    {
        //
        $name = '山本太郎';
        $date = '2020/02/11';
        $status = '完了';
        $to = 'test@gmail.com';
        $cc = 'cc@gmail.com';
        
        Mail::to($to)
            ->cc($cc)
            ->send(new TestMail($name, $date, $status));
        return "its works";
    }

### app/Mail/TestMail.php
$ php artisan make:mail TestMail

public function __construct($name='山田太郎', $date='2020/01/01', $status='テスト')
    {
        //
        $this->title = $date . 'テスト送信';
        $this->date = $date;
        $this->name = $name;
        $this->status = $status;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this
            ->from('from@example.com')
            ->subject($this->title)
            ->view('email.test')->with([
                'name' => $this->name,
                'date' => $this->date,
                'status' => $this->status,
            ]);
    }

### view

{{ $date }}<br>
メール本文<br>
{{ $name }}さんのステータスは{{ $status}}です

### routeプレビュー時

Route::get('/send', 'MailController@index');
Route::get('/send/preview', function(){
	return new App\Mail\TestMail();
});

メールの送信方法と、メール内容の設定はMVCで切り離されているので、非常に管理しやすいように思います。

mailtrapによるテスト送信の流れ

Dev、STG環境でのメール送信にmailtrapを使用できる
https://mailtrap.io/

### 特徴
– 開発用
– Inboxに保管できるメール数の上限は50通
– Inboxはひとつだけ

### .env
Integrationsの”Laravel”の内容を記述します

MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"

### make:mail
$ php artisan make:mail TestMail

app/Mail/TestMail.php が生成される

public function build()
    {
        return $this
            ->from('from@example.com')
            ->subject('テスト送信')
            ->view('email.test');
    }

### メール本文
resources/views/email/test.blade.php

メール本文

### route

Route::get('/send', 'MailController@index');

### controller
$ php artisan make:controller –resource MailController

public function index()
    {
        //
        Mail::to('test@gmail.com')
            ->send(new TestMail());
        return "its works";
    }

$ php artisan serve –host 192.168.33.10 –port 8000

mailtrapを確認すると、送信済となっています。
controllerから、メールを送る際に変数を扱いたいと思うので、続けてやっていきたいと思います。

Promiseとは?

### 「同期処理」と「非同期処理」
– 同期処理は上から下へ順番に1つずつ処理
– 非同期処理は時間が掛かりそうな処理を実行した後に、結果を待たずにすぐ次の処理を実行

## promise
引数「resolve」に取得したい値を設定することで、非同期処理の結果を格納

var result = new Promise(function(resolve){
		resolve(new Date);
	});

	result.then(function(data){
		console.log(data.getFullYear());
	});

then()の処理を実行することで、階層が複雑化しない

Pusherの挙動

DevToolでコンソールを開きながら、getting startedで挙動を確認します。

### index.html

<h1>Pusher Test</h1>
    <p>
        Try publishing an event to channel <code>my-channel</code>
        with event name <code>my-event</code>.
    </p>
     <script src="https://js.pusher.com/5.0/pusher.min.js"></script>
    <script>
        Pusher.logToConsole = true;

        var pusher = new Pusher('****************',{
            cluster: 'ap3',
            forceTLS: true
        });

        var channel = pusher.subscribe('my-channel');
        channel.bind('my-event', function(data){
            alert(JSON.stringify(data));
        });
    </script>

### server side
$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require pusher/pusher-php-server

require __DIR__ . '/vendor/autoload.php';

$options = array(
	'cluster' => 'ap3',
	'useTLS' => true
);
$pusher = new Pusher\Pusher(
	'********************',
	'********************',
	'******',
	$options
);

$data['message'] = 'hello world';
$pusher->trigger('my-channel', 'my-event', $data);

server側からclient側でデータが渡っていることを確認できます。

vue.jsのmountedとは?

公式: mounted
https://jp.vuejs.org/v2/api/index.html#mounted
elがマウントされた丁度後に呼ばれる
-> ビュー全体がレンダリングされた後にのみ実行される
-> createdはDOMがまだ作られていない状態、mountedはDOMが生成された直後の状態

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js"></script>
	<script>
    	new Vue({
    		el: '#chat',
    		data: {
    			message: '',
    			messages: []
    		},
    		methods: {
    		},
    		mounted(){
    			this.$nextTrick(function(){
    				
    			})
    		} 
    	});
    </script>

### createdとmounted

new Vue({
    		el: '#app',
    		data: {
    		},
    		methods: {
    			showEl : function(){
    				console.log(this.$el)
    			}
    		},
    		created(){
    				console.log('created')
    				console.log(this.$el)
    		},
    		mounted(){
    			    console.log('created')
    				console.log(this.$el)
    		} 
    	});

createdを実装するイメージが湧かないが、mountedの使い方はわかりました。

axiosとは?

– axiosは、HTTP通信を簡単に行うことができるJavascriptライブラリ
https://github.com/axios/axios
– 主にJsonの取得に利用される

### Features
– Make XMLHttpRequests from the browser
– Make http requests from node.js
– Supports the Promise API
– Intercept request and response
– Transform request and response data
– Cancel requests
– Automatic transforms for JSON data
– Client side support for protecting against XSRF

## サンプル
### npm install
$ npm install axios –save

### index.js
– axios.get(‘${url}’), axios.post(‘${url}’)と実装する
– get, post以外にも、request, delete, head, options, put, patch, getUriなどがある

var express = require('express');
var router = express.Router();

// axiosのインスタンス生成
const axiosBase = require('axios');
const axios = axiosBase.create({
	baseURL = '192.168.33.10:8000',
	headers: {
		'Content-Type' : 'application/json',
		'X-Requested-With': 'XMLHttpRequest'
	},
	responseType: 'json'
});

router.get('/', function(req, res, next){
	axios.get('/title')
	.then(function(response){
		res.render('index', response.data);
	})

	.catch(function(error){
		console.log('Error!! occurred in Backend.')
	});
});

module.exports = router;

### res, err, finally

<script>
		axios.get(${url})
		.then(res => console.log(res.data))
		.catch(err => console.error(err))
		.finally(res => console.log('finally'))
	</script>

XMLHttpRequestを生成せずに処理できるので、確かに使い勝手良さそうです。

Laravel 6.xでEventを作成してPusherからメッセージ

### event作成
$ php artisan make:event Test
app/Events/Test.php

class Test implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public $message;

    public function __construct($message)
    {
        //
        $this->message = $message;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new Channel('test_channel');
    }

    public function broadcastAs()
    {
        return 'test_event';
    }
}

### config/broadcasting.php

'default' => env('BROADCAST_DRIVER', 'pusher'),
'connections' => [

        'pusher' => [
            'driver' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'app_id' => env('PUSHER_APP_ID'),
            'options' => [
                'cluster' => env('PUSHER_APP_CLUSTER'),
                'useTLS' => true,
            ],
        ],

### web.php

Route::get('/sent', function(){
	event(new \App\Events\Test('テストメッセージ'));
});

Route::get('receive', function(){
return <<<HTML

<!DOCTYPE html>
<head>
	<title>Pusher Test</title>
	<script src="https://js.pusher.com/3.2/pusher.min.js"></script>
	<script>
		Pusher.logToConsole = true;

		var pusher = new Pusher('******************',{
			cluster : 'ap3',
			forceTLS: true
		});

		var pusherChannel = pusher.subscribe('test_channel');

		pusherChannel.bind('test_event', function(data){
			alert(data.message);
		});
	</script>
</head>
HTML;
});

broadcastdriverが初期値のnullのままだと動かないので注意が必要です。

Laravel Event

公式
https://readouble.com/laravel/6.x/ja/events.html
– アプリケーションで発生する様々なイベントを購読し、リッスンする為に使用
– イベントクラスは、app/Eventsディレクトリに格納
– リスナは、app/Listenersディレクトリに保存

## Event利用の流れ
– Event定義
– Eventに対応するListenerを定義
– Eventを仕込む

### 1. app/Providers/EventServiceProvider.php
Eventと対応するListenerを登録する

protected $listen = [
        Registered::class => [
            SendEmailVerificationNotification::class,
        ],
        'App\Events\AccessDetection' => [
            'App\Listeners\AccessDetectionListener',
        ]
    ];

### 2. イベント/リスナ生成
$ php artisan event:generate
※EventServiceProvider.phpで定義していないと、作成されない。

app/Events/AccessDetection.php

public function __construct()
    {
        //
        $this->param = $value;
    }

app/Listeners/AccessDetectionListener.php

public function handle(AccessDetection $event)
    {
        //
        dump('Access Detected param=' . $event->param);
    }

### route

use App\Events\AccessDetection;
Route::get('event', function(){
	event(new AccessDetection(str_random(10)));
	return 'hoge';
})

$ php composer.phar require laravel/helpers
$ php composer.phar require –dev beyondcode/laravel-dump-server

$ php artisan dump-server

Laravel Var Dump Server
=======================

[OK] Server listening on tcp://127.0.0.1:9912

// Quit the server with CONTROL-C.

GET http://192.168.33.10:8000/event
———————————–

———— ——————————————-
date Thu, 06 Feb 2020 12:10:49 +0000
controller “Closure”
source AccessDetectionListener.php on line 30
file app/Listeners/AccessDetectionListener.php
———— ——————————————-

“Access Detected param=oWhPChPRSh”

なるほど、EventServiceProviderでEventを登録し、Eventsで定義したeventsをListenerで受け取り、controller(route)でevent(new ${eventName})と書いて、event発火の流れでしょうか。大まかな流れは理解しました。

laravel-websockets

Laravel WebScokets
Git hub: laravel-websockets
Official Document: laravel-websockets

– Drop-in Pusher replacement, SSL support, Laravel Echo support and a debug dashboard are just some of its features.

### How to use
https://docs.beyondco.de/laravel-websockets/1.0/getting-started/introduction.html

### package install
$ php composer.phar require beyondcode/laravel-websockets

### publish migration file
$ php artisan vendor:publish –provider=”BeyondCode\LaravelWebSockets\WebSocketsServiceProvider” –tag=”migrations”
Copied File [/vendor/beyondcode/laravel-websockets/database/migrations/create_websockets_statistics_entries_table.php.stub] To [/database/migrations/2020_02_07_062321_create_websockets_statistics_entries_table.php]

$ php artisan migrate
+——————————-+
| Tables_in_chat |
+——————————-+
| failed_jobs |
| messages |
| migrations |
| password_resets |
| users |
| websockets_statistics_entries |
+——————————-+

mysql> describe websockets_statistics_entries;
+————————-+——————+——+—–+———+—————-+
| Field | Type | Null | Key | Default | Extra |
+————————-+——————+——+—–+———+—————-+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| app_id | varchar(255) | NO | | NULL | |
| peak_connection_count | int(11) | NO | | NULL | |
| websocket_message_count | int(11) | NO | | NULL | |
| api_message_count | int(11) | NO | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+————————-+——————+——+—–+———+—————-+
※アクセス情報が保存される

### Websocket Configuration File
$ php artisan vendor:publish –provider=”BeyondCode\LaravelWebSockets\WebSocketsServiceProvider” –tag=”config”
Copied File [/vendor/beyondcode/laravel-websockets/config/websockets.php] To [/config/websockets.php]

### config/broadcasting.php

'pusher' => [
            'driver' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'app_id' => env('PUSHER_APP_ID'),
            'options' => [
                'cluster' => env('PUSHER_APP_CLUSTER'),
                'encrypted' => true,
                'host' => '192.168.33.10', // ご自身のホスト名
                'port' => 6001,
                'scheme' => 'http' // or https
            ],
        ],

### .env

PUSHER_APP_ID=1
PUSHER_APP_KEY=laravelWebsockets
PUSHER_APP_SECRET=laravelWebsockets
PUSHER_APP_CLUSTER=ap1

$ php artisan serve –host 192.168.33.10 –port 8000
$ php artisan websockets:serve
Starting the WebSocket server on port 6001…

app.js:28574 WebSocket connection to ‘wss://192.168.33.10/app/laravelWebsockets?protocol=7&client=js&version=5.0.3&flash=false’ failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED

何故だ。。。

node.jsのhttpモジュールの中身ってどうなってるの?

node.jsはノンブロッキング処理

// nodeが用意しているhttpモジュール
var http = require('http');
var server = http.createServer();

// server.onでイベント待ち受け
// 引数はリクエストとレスポンスのオブジェクト
server.on('request', function(req, res){
	// httpのステータスコード200
	res.writeHead(200, {'Content-Type': 'text/plain'});
	res.write('hello world');
	res.end();
});

// サーバを待ち受け状態する
server.listen(1337);
console.log("server listening...");

httpモジュールを読み込んで、リクエストに対してレスポンスを記述している。listenはサーバのポート。
さて、この具体的に”http module”って何? 公式ドキュメントを見ます。

HTTP | Node.js
https://nodejs.org/api/http.html#http_http
– Class: http.Agent
— new Agent([options]), agent.createConnection(options[, callback]), agent.keepSocketAlive(socket), agent.reuseSocket(socket, request), agent.destroy(), agent.freeSockets, agent.getName(options), agent.maxFreeSockets, agent.maxSockets, agent.requests, agent.sockets
– Class: http.ClientRequest
— Event: ‘abort’, Event: ‘connect’, Event: ‘continue’, Event: ‘information’, Event: ‘response’, Event: ‘socket’, Event: ‘timeout’, Event: ‘upgrade’, request.abort(), request.aborted, request.end([data[, encoding]][, callback]), request.flushHeaders(), request.getHeader(name), request.maxHeadersCount, request.path, request.removeHeader(name), request.reusedSocket, request.setHeader(name, value), request.setNoDelay([noDelay]), request.setSocketKeepAlive([enable][, initialDelay]), request.setTimeout(timeout[, callback]), request.socket, request.writableEnded, request.writableFinished, request.write(chunk[, encoding][, callback])
– Class: http.Server
— Event: ‘checkContinue’, Event: ‘checkExpectation’, Event: ‘clientError’, Event: ‘close’, Event: ‘connect’, Event: ‘connection’, Event: ‘request’, Event: ‘upgrade’, server.close([callback]), server.headersTimeout, server.listen(), server.listening, server.maxHeadersCount, server.setTimeout([msecs][, callback]), server.timeout, server.keepAliveTimeout
– Class: http.ServerResponse
— Event: ‘close’, Event: ‘finish’, response.addTrailers(headers), response.cork(), response.end([data[, encoding]][, callback]), response.flushHeaders(), response.getHeader(name), response.getHeaderNames(), response.getHeaders(), response.hasHeader(name), response.headersSent, response.removeHeader(name), response.sendDate, response.setHeader(name, value), response.setTimeout(msecs[, callback]), response.socket, response.statusCode, response.statusMessage, response.uncork(), response.writableEnded, response.writableFinished, response.write(chunk[, encoding][, callback]), response.writeContinue(), response.writeHead(statusCode[, statusMessage][, headers]), response.writeProcessing()
– Class: http.IncomingMessage
— Event: ‘aborted’, Event: ‘close’, message.aborted, message.complete, message.destroy([error]), message.headers, message.httpVersion, message.method, message.rawHeaders, message.rawTrailers, message.setTimeout(msecs[, callback]), message.socket, message.statusCode, message.statusMessage, message.trailers, message.url
– http.METHODS
– http.STATUS_CODES
– http.createServer([options][, requestListener])
– http.get(options[, callback])
– http.get(url[, options][, callback])
– http.globalAgent
– http.maxHeaderSize
– http.request(options[, callback])
– http.request(url[, options][, callback])

主に(1)agent, (2)client request, (3)server, (4)server response, (5)incoming responseを提供しています。

apacheのgit hubを見ます。 https://github.com/apache/httpd
apacheのモジュールと比べると大分シンプルなように見えるが、それにしても、量的にボリュームがあるな。
あれ、待てよ、各言語やフレームワークでビルトインサーバーの機能があるけど、あれって、nodeのhttpモジュールのように、requestやresponseを各言語でserver機能として書いているってこと??
こりゃ参ったね。。 何をやっても、結局、C言語やらないと駄目って結論になるな。