expressのincludeでSyntaxError: Unexpected identifier in /*/index.ejs while compiling ejs

GET / 500 1.492 ms – 1426
SyntaxError: Unexpected identifier in /*/index.ejs while compiling ejs

If the above error is not helpful, you may want to try EJS-Lint:
https://github.com/RyanZim/EJS-Lint
Or, if you meant to create an async function, pass `async: true` as an option.

includeの有無でエラーが生じるので色々試したところ、エラーが消えました。
## before

<% include header.ejs %>
	<h1>Posts</h1>
	<ul>
		<% for (var i=0; i<posts.length; i++) { %>
			<li>
				<a href="/posts/<%= i %>"><%= posts&#91;i&#93;.title %></a>
			</li>
		<% } %>
	</ul>
<% include footer.ejs %>

## after

<% include('header.ejs') %>
	<h1>Posts</h1>
	<ul>
		<% for (var i=0; i<posts.length; i++) { %>
			<li>
				<a href="/posts/<%= i %>"><%= posts&#91;i&#93;.title %></a>
			</li>
		<% } %>
	</ul>
<% include('footer.ejs') %>

ページ数が増えれば増えるほど、includeを使わない手はないので、includeでエラーがあるとドキッとします。

express 2

Regular Expression

app.get('/items/:id([0-9]+)', function(req, res){
	res.send('item no: ' + req.params.id);
});

file

app.get('/hello.txt', function(req, res){
	res.sendfile(__dirname + '/public/hello.txt');
});

public folder読み込み

app.use(express.static(__dirname + '/public'));

– app.useで読み込む処理をmiddlewareと言う

var logger = require('morgan');
app.use(logger('dev'));
app.use(function(req, res, next){
	console.log('my custom middleware');
	next();
})

$ sudo npm install nodemon -g
$ nodemon app

$ npm install ejs

app.get('/', function(req, res){
	res.render('index');
});

app.get('/', function(req, res){
	res.render('index', {title: 'title'});
});
<body>
	<h1><%= title %></h1>
	hello from index ejs!
</body>
app.param('id', function(req, res, next, id){
	var users = ['yamda', 'nakamura', 'kobayashi'];
	req.params.name = users[id];
	next();
});
app.get('/hello/:id', function(req, res){
	res.send('hello ' + req.params.name);
})

app.get('/bye/:id', function(req, res){
	res.send('hello ' + req.params.name);
})

### Post

var bodyParser = require('body-parser');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
// ディレクトリ
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
// middleware
// app.use(app.router);
var logger = require('morgan');
app.use(logger('dev'));
app.use(express.static(__dirname + '/public'));

app.get('/new', function(req, res){
	res.render('new');
});

app.post('/create', function(req, res){
  //
  	res.send(req.body.name);
})

view, routingの設計をServerサイドの言語ではなく、JavaScriptで書いていける。

express

what is ‘express’ ?
-> Fast, unopinionated, minimalist web framework for Node.js

$ sudo npm install express -g
$ sudo npm install -g express-generator
$ express -h

$ express sample
$ cd sample
$ npm install
$ DEBUG=sample:* npm start
http://192.168.33.10:3000/

app.js

var express = require('express'),
	app = express();

// app.use(app.router);

app.get('/', function(req, res){
	res.send('hello world');
});

app.get('/wine', function(req, res){
	res.send('JACOB\'S CREEK');
});

app.listen(3000);
console.log("server starting ... ")
app.get('/users/:name', function(req, res){
	res.send('hello' + req.params.name);
});
app.get('/users/:name?', function(req, res){
	if (req.params.name){
		res.send('hello ' + req.params.name);
	} else {
		res.send('hello nobady!');
	}
});

リクエストに対してルーティングをやっていることはわかるが、サーバーの記述がよくわからない。apacheのように、サーバーを立てているのか? 既存のサーバー環境(nginx, apache等)で動かす場合は、どういう記述になるのか?
-> Node.jsをサービス化(デーモン化)して、nginxまたはApacheからリバースプロキシで接続

リバースプロキシとは?
->特定のサーバへのリクエストが必ず通過するように設置されたプロキシサーバ

ん? いまいちよくわかりません。

Laravel + Ajaxでbladeの一部の値(ログインユーザ順のデータ)を自動更新させたい

– bladeの一部の値(ログインユーザ順のデータ)を自動更新させたい

## 初期
controller
-> 自分以外のユーザをログイン順に取得している。

$users = User::where('id', '!=', $user_id)->orderBy('last_login','DESC')->get();

– controllerでjson形式で返せるらしい
-> ルーティングでgetとpostに対応してajaxのメソッドに繋げます
## Route

Route::get('/hoge/new', 'HogesController@create');
Route::match(['get', 'post'], '/hoge/getuser/', 'HogesController@getUser');

## Controller

public function getUser(){
        $user_id = 1;
        $users = User::where('id', '!=', $user_id)->orderBy('last_login','DESC')->get(['id','name','hoge']);
        $json = ["userData" => $userData];
        return response()->json($json);
    }

## view
-setTimeoutで5秒ごとにgetメソッドでcontrollerからデータを取得する
-while( table.rows[ 0 ] ) table.deleteRow( 0 ); でtableのrowsを全て削除し、jsonデータを入れたtd, trをappendする

<table class="table" id="userList">
</table>
// 省略

<script>
$(function(){
			getData()
		})

		function getData(){
			$.ajaxSetup({
				headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')}
			});
			$.get({
				url: '/hoge/getuser',
				method: 'GET',
				dataType: 'json',
			})
			.done(function(data){
				
				var table = document.getElementById('userList');
				while( table.rows[ 0 ] ) table.deleteRow( 0 );
				var rows = "";
				for(var i = 0; i < data.userData.length; i++){
					rows += "<tr>";
					rows += "<td>";
					rows += "hoge";
					rows += "</td>";
					rows += "</tr>";
				}
				$("#userList").append(rows);
			})
			.fail(function(){

			})
			setTimeout("getData()", 5000);
		}
</script>

これ思ってたより複雑だな。jsonで持ってくる場合、hasOneやbelongsToのデータはController側で整形しないとダメか。

ajaxのjsonデータは、getで取得するURLを叩くと、jsonを見ることが可能で、デバッグには良いが、商用環境で->get() とするとセキュリティ上問題があるので、viewで使用するカラムのみ ->get([‘columName’]) で取得するようにする。

laravel whereで複数の一致条件 ~ a =A, b = B もしくは a = B, b = A

Controllerでa =A, b = B もしくは a = B, b = A を探したい時。

複数のwhereの場合は、whereを繋げれば良いのですが、複数のwhereのどちらかの場合は、where whereをorWhereで繋げれば良い。

$messages = Hotels::where(function($q) use ($id, $user_id){
            $q->where('user_id', $id)->where('second_id', $user_id);
        })->orWhere(function($q) use ($id, $user_id){
            $q->where('user_id', $user_id)->where('second_id', $id);
        })->get();

割と簡単に書けます。

Collectiveでのcheckboxの書き方

– view側でnameをブランケットで記述する。
– controllerのwhereInでチェックした値を呼び出せる

### blade

<div class="item">
	{!! Form::checkbox('category[]', 1, 1, ['class'=>'form-check-input']) !!}
	{!! Form::label('category[]', 'シティホテル', ['class'=>'form-check-label']) !!} 
</div>
<div class="item">
	{!! Form::checkbox('category[]', 2, 1, ['class'=>'form-check-input']) !!}
	{!! Form::label('category[]', 'ビジネスホテル', ['class'=>'form-check-label']) !!} 
</div>

### controller

if($request->has('category')){
            $Hotels = Message::where('id', $id)->whereIn('category_id', $request->input('category'))->orderBy('created_at','ASC')->paginate(2)->onEachSide(1);
        } else

input::all()の場合は、配列で渡ってくるので、そのままDBに流し込めば良い。
controller側で配列を作ろうとしたら、checkboxの最後の値しか渡ってこずに失敗しました。

onEachSide()が効かない時

bladeのpaginationでonEachSideを実装するも、Laravel6.xで全て表示されてしまう。

{{$users->onEachSide(1)->appends(request()->input())->render()}}
{{$users->appends(request()->input())->onEachSide(1)->render()}}

Controller側で、onEachSide()を付ける。

$users = User::orderBy('updated_at','DESC')->paginate(10)->onEachSide(1);

公式では、デフォルトで3つと書かれているが、機能しない。何故だか釈然としないが、controllerで対応する。
https://readouble.com/laravel/6.x/ja/pagination.html

blade内でのswitch文の書き方

idなどの判定で分岐が3つ以上になると、if文が冗長に感じてくるのでswitch文を使用したくなる。

@switch($hoge->id)
	@case(1)
		<li></li>
		@break
	@case(2)
	@case(3)
		<li></li>
		@break
	@default
		<li></li>
@endswitch

@case(1, 2) と書くとエラーになるので注意が必要
一般的にインデントは半角スペース4つだが、テーブル内などで分岐処理を書くと、分岐処理をしているところだけインデントが4つ右にずれるが、これはDOMにした時に揃うように左にずらすべきなのだろうか?
綺麗なコードを書いていると見られたいので、DOMのインデントを揃えたい、という欲求が頭を巡るが、レジストリの処理は殆どの変わらないし、コードの保守性は規約に沿ったほうが高まるように思えるので、いつも葛藤になる。

date型レコードの範囲内のYYYYMMの連続した配列を作る

mysql内に、 ‘2019-10-05′,’2020-01-05’,’2020-02-13’などのレコードがあった場合に、その範囲内の連続したYYYYMMの配列(2019-10, 2019-11, 2019-12, 2020-01)を作りたい時
-> 最初と最後のUnixTime範囲内で、1ヵ月づつ増えるFor文を作る。

        $last = Hoge::where('user_id', $id)->orderBy('time','ASC')->first();
        $last_month = substr($last['time'], 0, 7);
        $last_unix = strtotime('first day of ' . $last_month);
        $latest = Hoge::where('user_id', $id)->orderBy('time','DESC')->first();
        $latest_month = substr($latest['time'], 0, 7);
        $latest_unix = strtotime('last day of ' . $latest_month);

        for($time = $last_unix; $time <= $latest_unix; $time = strtotime('+1 month', $time)){
            $Ym[] = date('Ym', $time);
        }

取得したデータをそのままUnixTimeにして 1ヵ月づつ増やすと、 $last_month + 1 month > $latest_month となる場合があるので、$last_monthを月初の日付、$latest_monthを月末の日付に変換してからFor文を回す必要がある。

う〜ん、最初にDBのデータ型考える際に、ロジックの計算処理を考えてデータ型を決めれるようになるな。

laravelでcsv出力

### view
csvダウンロードボタンを作り、postメソッドのhiddenで引数を渡します。

{!! Form::open(['method'=>'POST', 'action'=>['HogeController@download'] ]) !!}
 <input type="hidden" name="period" value="{{ $inputs&#91;'data'&#93; }}">
 {!! Form::submit('CSVダウンロード',['class'=>'btn', 'name'=>'csv']) !!}
{!! Form::close() !!}

### route
csvファイルをreturnするだけなので、設計のコンベンションが無ければディレクトリは然程きにする必要なし。

Route::post('/dir/csv', 'HogeController@download');

### controller

use Symfony\Component\HttpFoundation\StreamedResponse;
public function download(Request $request){
// 省略

// CSV生成
        $response = new StreamedResponse(function() use($data1, $data2){
                $stream = fopen('php://output', 'w');
                stream_filter_prepend($stream, 'convert.iconv.utf-8/cp932//TRANSLIT');
                fputcsv($stream, ['id','raw1','raw2','raw3','raw4','raw5']);
                foreach($data as $value){
                     fputcsv($stream, ['id',$value->raw1,$value->raw2,$value->raw3,$value->raw4,$value->raw5]);
                }
                fclose($stream);
        });
        $response->headers->set('Content-Type', 'application/octet-stream');
        $response->headers->set('Content-Disposition', 'attachment; filename="user.csv"');
        return $response;

特段ストレージやS3などに一時保存しなくて良いので、シンプルだ。
上記はユーザがダウンロードボタンを押した時の処理だが、月次のバッチ処理によるcsv出力なども比較的簡単に実装できそうだ。