Laravel 日付の表示変換の書き方

日付の表示変換は、controllerではなく、viewに変数を渡して、view側で表示を変える。

## 前準備
admin用のテンプレートをlayoutsフォルダ配下に格納する
./resources/view/layouts/admin.blade.php

{{asset()}}はpublicディレクトリにリンクする

<link href="{{asset('css/app.css')}}" rel="stylesheet">
<link href="{{asset('css/libs.css')}}" rel="stylesheet">

./resources/view/admin/index.blade.php

@extends('layouts.admin')

route

Route::get('/admin', function(){
	return view('admin.index');
});

./resources/view/asset/sass/app.scss

#admin-page {
	padding-top: 0px;
}

$ gulp

## データ取り込み
Controller: AdminUserController.php

public function index()
    {
        //
        $users = User::all();

        return view('admin.users.index', compact('users'));
    }

## View
view: index.blade.php

<table class="table table-striped">
		<thead>
		<tr>
		  <th>Id</th>
		  <th>Name</th>
		  <th>Email</th>
		  <th>Created</th>
		  <th>Updated</th>
		</tr>
		</thead>
		<tbody>
		@if($users)
			@foreach($users as $user)
			<tr>
			  <td>{{$user->id}}</td>
			  <td>{{$user->name}}</td>
			  <td>{{$user->email}}</td>
			  <td>{{$user->created_at->diffForHumans()}}</td>
			  <td>{{$user->updated_at->diffForHumans()}}</td>
			</tr>
			@endforeach
		@endif
		</tbody>
	</table>

日付で表示を変えるのはわかったが、カラム同士の値の引き算で時間を計算する際も、view側で書くのだろか?要検討。

Laravelでnpm&Gulpの使い方

まずnode.js & npm が入っているか確認
$ node -v
v8.16.2
$ npm -v
6.4.1

※入っていなければ、repoを追加
$ curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash -
$ yum install -y gcc-c++ make
$ sudo yum install -y nodejs

gulpが入っているか確認
$ gulp -v
CLI version: 2.2.0
Local version: Unknown

※入っていなければ、npmインストール
$ npm i --global gulp

### npm install
プロジェクトのディレクトリでnpm install
$ npm install

dependencyでエラーになるときは、package.jsonを削除して、npm install。その後、必要なモジュールを入れます。

$ npm install gulp-sass –save-dev
$ npm install node-sass@latest
$ npm install laravel-elixir –save-dev

gulpが使えるようになります。
$ gulp

### assetの配置
resourceにjs,sassなどを配置し、gulp, webpackでpublicにコンパイルする
./resources/assets/js
./resources/assets/css
※よくCSSを./public/css/style.cssで編集するチュートリアルを見るが、実際に開発する際には、resources内で開発する

### gulp file編集

var elixir = require('laravel-elixir');

elixir(function(mix) {
    mix.sass('app.scss')
    .styles([
    	'libs/blog-post.css',
    	'libs/bootstrap.css',
    	'libs/bootstrap.min.css',
    	'libs/font-awesome.css',
    	'libs/metisMenu.css',
    	'libs/sb-admin-2.css',
    	'libs/styles.css'
    ], './public/css/libs.css')

    .scripts([
    	'libs/bootstrap.js',
    	'libs/bootstrap.min.js',
    	'libs/jquery.js',
    	'libs/metisMenu.js',
    	'libs/sb-admin-2.js',
    	'libs/scripts.js'
    ], './public/js/libs.js')
});

### gulp実行
$ gulp

フロントエンドとサーバーサイドを分けて開発するのであれば、sassへのコンパイルはフロント側でやればいいのかもしれないが、フルスタックエンジニアで開発する場合や、保守開発でのフロント編集の場合は、そのまま/resources/assetsで編集して、./publicにコンパイルすれば良いという事です。
こう考えると、フロントエンジニアでもLaravelの仕組みが分かっているとかなりコミュニケーションが楽になりそうですし、フロントエンド・サーバーサイドのスキル・興味関心に応じてどういう体制にするか考えるポイントになりそうです。

プロジェクト作成からmake:controller –resourceまでの一連の流れ

make:authとした後、先にViewファイル, migration, modelを作ってから、Controllerを作成して挙動を確認する

$ php artisan migrate
$ php artisan make:auth

resourcesにファイルを作成
./resources/views/admin/index.blade.php
./resources/views/admin/users/index.blade.php
./resources/views/admin/users/create.blade.php
./resources/views/admin/users/edit.blade.php
./resources/views/admin/posts/index.blade.php
./resources/views/admin/posts/create.blade.php
./resources/views/admin/posts/edit.blade.php
./resources/views/admin/categories/index.blade.php
./resources/views/admin/categories/edit.blade.php

migration file:user

Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('role_id')->index()->unsigned()->nullable();
            $table->integer('is_active')->default(0);
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });

$ php artisan make:model Role -m

migration file: role

Schema::create('roles', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->timestamps();
        });

Model: Role.php

public function role(){
        return $this->belongsTo('App\Role');
    }

$ php artisan migrate:refresh

insert into roles (name) values (‘administrator’);
insert into roles (name) values (‘author’);
insert into roles (name) values (‘subscriber’);

update users set role_id=1 where id=1;

$ php artisan tinker
Psy Shell v0.7.2 (PHP 7.1.7 — cli) by Justin Hileman
>>> $user = App\User::find(1);
=> App\User {#647
id: 1,
role_id: 1,
is_active: 0,
name: “peter”,
email: “peter@gmail.com”,
created_at: “2019-12-13 13:52:49”,
updated_at: “2019-12-13 13:52:49”,
}
>>> $user->role
=> App\Role {#637
id: 1,
name: “administrator”,
created_at: null,
updated_at: null,
}

Route

Route::resource('admin/users', 'AdminUsersController');

$ php artisan make:controller –resource Adm inUsersController

Controller: AdminUsersController.php

public function index()
    {
        //
        return view('admin.users.index');
    }

フロントのテンプレートファイルを流し込んで、controllerを作り、ログイン機能は後から実装するのかと思っていましたが、そうではなくmake:authは一番最初に実行し、ユーザモデルを先に作ってしまうんですね👻

Laravelからのメール送信

実際のアプリケーションではSMTPにAWS SESを使いますが、ここでは便宜上、mailgunを使って、仕組みを理解します。

### mailgun
https://www.mailgun.com/
sign inして、認証します。

Laravelからメールを送信する際には、.envファイルから設定します。

デフォルト

MAIL_DRIVER=mailgun
MAILGUN_DOMAIN=sandboxf*********************.org
MAILGUN_SECRET=*******************-********-********

設定ファイル
./config/mail.php
 送信元のメールアドレス、名前を設定する

'from' => ['address' => 'name@hpscript.com', 'name' => 'hpscript'],

./config/services.php

./resources/view/emails/test.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title><Document></Document></title>
</head>
<body>
	
</body>
</html>
Routeuse Illuminate\Support\Facades\Mail;

Route::get('/', function () {
    // return view('welcome');
    $data = [
    	'title'=>"hi Hpscript",
    	"content"=>"This is Laravel first mailgun mail"
    ];

    Mail::send('emails.test', $data, function($message){
    		$message->to('*****@gmail.com', '${name}')->subject('Hello, good day!');
    });

});

mailgunを使用するにはドライバを入れる
公式ドキュメント参照:https://readouble.com/laravel/*/ja/mail.html
$ php composer.phar require guzzlehttp/guzzle

./vendor/guzzle/src/Client.php

private function configureDefaults(array $config)
    {
        $defaults = [
            'allow_redirects' => RedirectMiddleware::$defaultSettings,
            'http_errors'     => true,
            'decode_content'  => true,
            'verify'          => false,
            'cookies'         => false
        ];
        // ...
      }

LaravelでのSessionの使い方

## sessionの表示
Requestメソッドを使う

public function index(Request $request)
    {
        return $request->session()->all();
    }

## Set session
$request->session()->put([‘$key’=>’$value’]);とします

public function index(Request $request)
    {
        $request->session()->put(['peter'=>'artist']);
        return view('home');
    }

## Global function of session
globaの場合は、putを書く必要がない

public function index(Request $request)
    {
        session(['peter'=>'hoge']);
        
        return $request->session()->all();
        // return view('home');
    }

## sessionの読み込み

public function index(Request $request)
    {
        return $request->session()->get('peter');
        // return view('home');
    }

globalの場合

return session('peter');

## session削除

public function index(Request $request)
    {
        $request->session()->forget('peter');
        return $request->session()->all();
    }

## sessionデータを全て削除

$request->session()->flush();

## flush

$request->session()->flush('message', 'Post has been created');
return $request->session()->get('message');

sessionが使えるということは、cookieも使えるのでしょうか?
sessionやcookieを扱えると、アプリケーションの幅が広がりますね。

LaravelのMiddleware Security/Protection

Middlewareを学びます。Middlewareというと何を思い浮かべるでしょうか?
ミドルウェアはその名の通り、OSとアプリケーションの中間にあるソフトウェアという意味です。
フレームワークLaravelでのMiddlewareとはどういう意味でしょうか?そもそもLaravel自体はOSではなく、アプリケーションのソフトウェアです。そのフレームワークにミドルウェアという機能があるのは何か虎に睨まれたようです。では具体的に見ていきましょう。

まず、新規にプロジェクトを立ち上げます
$ php artisan make:auth
$ php artisan migrate

./app/Http/kernel.php

protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'can' => \Illuminate\Foundation\Http\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    ];

./app/Http/Middleware
Authenticate.php
EncryptCookies.php
RedirectIfAutenticated.php
VerifyCsrfToken.php

Authenticate.php

public function handle($request, Closure $next, $guard = null)
    {
        if (Auth::guard($guard)->guest()) {
            if ($request->ajax() || $request->wantsJson()) {
                return response('Unauthorized.', 401);
            } else {
                return redirect()->guest('login');
            }
        }

        return $next($request);
    }

$ php artisan make:middleware RoleMiddleware
Middleware created successfully.

$ php artisan down

Kernel.php

protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'can' => \Illuminate\Foundation\Http\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'role'=>\App\Http\Middleware\RoleMiddleware::class,
    ];

Route

Route::get('/admin/user/roles', ['middleware'=>'role', function(){

	return "Middleware role";
}]);

app/Http/Middleware/RoleMiddleware.php

public function handle($request, Closure $next)
    {
        return redirect('/');

        return $next($request);
    }

/admin/user/roles にアクセスするとリダイレクトされます。

Laravel ファイルアップロード時のコントローラーの書き方

普通にHTMLで書いた場合

<form action="/uploadfile" method="post" enctype="multipart/form-data">
	@csrf
	<div class="form-group">
		<input type="file" class="form-control-file" name="fileToUpload" id="exampleInputFile">
	</div>
	<button type="submit" class="btn btn-primary">Submit</button>
</form>

collectiveのForm::openのthird parameterに’files’=>trueを追加する
Form:file(‘file’)とする

{!! Form::open(['method'=>'POST', 'action'=>'PostsController@store', 'files'=>true]) !!}
		{{ csrf_field()}}

		<div class="form-group">
			{!! Form::file('file', ['class'=>'form-controll']) !!}
		</div>

		<div class="form-group">
			{!! Form::label('title', 'Title') !!}
			{!! Form::text('title', null, ['class'=>'form-controll']) !!}
		</div>

		<div class="form-group">
			{!! Form::submit('Create Post', ['class'=>'btn btn-primary']) !!}

		</div>
	{!! Form::close() !!}

controller

public function store(CreatePostRequest $request)
    {
        return $request->file('file');
    }

オリジナル名、ファイルサイズ

public function store(CreatePostRequest $request)
    {
        $file = $request->file('file');
        
        echo "<br>";
        echo $file->getClientOriginalName();

        echo "<br>";
        echo $file->getClientSize();
    }

$ php artisan make:migration add_path_column_to_posts –table=posts
migration file

public function up()
    {
        Schema::table('posts', function (Blueprint $table) {
            //
            $table->string('path');
        });
    }

public function down()
    {
        Schema::table('posts', function (Blueprint $table) {
            //
            $talbe->dropColumn('path');
        });
    }

$ php artisan migrate

Post.php

protected $fillable = [
		'user_id',
		'title',
		'content',
		'path'
	];

PostsController.php

public function store(CreatePostRequest $request)
    {

        $input = $request->all();

        if($file = $request->file('file')){
            $name = $file->getClientOriginalName();

            $file->move('./images', $name);

            $input['path'] = $name;

        }

        Post::create($input);
    }

view

<ul>
		@foreach($posts as $post)

		<div class-"image-container">
			<img height="100" src="images/{{$post->path}}">
		</div>
		<li><a href="{{ route('posts.show', $post->id) }}">{{$post->title}}</a></li>
		
		@endforeach
	</ul>

images/{{$post->path}} は、accessorsでimages/を省略する

Model:Post.php

public $directory = "/images/";

public function getPathAttribute($value){

		return $this->directory . $value;
	}

View: index.php

<div class-"image-container">
			<img height="100" src="{{$post->path}}">
		</div>

今回はシンプルな書き方で、ファイルに対する拡張子やファイルサイズのバリデーションをつけていませんが、コントローラーもしくはrequestsでバリデーションを付ければ良いでしょう。

ファイルの格納場所はサーバー内ですが、これをS3にする場合はmove()の箇所を変える必要があります。

Laravel QueryScopeのサンプル

Query Scopeはoperationのショートカット機能

e.g. latest

public function index()
    {
        $posts = Post::latest()->get();

        // return $posts;
        return view("posts.index", compact('posts'));
    }

order by created desc | ascと一緒です。

$posts = Post::orderBy('id', 'desc')->get();

qeuryScopeを自作する
Model: Post.php
public function static scope.${functionName}がコンベンション。${functionName}はcamelCaseで書く

public static function scopeLatest($query){
			return $query->orderBy('id', 'desc')->get();
	}

PostsController

public function index()
    {
        $posts = Post::latest();

        // return $posts;
        return view("posts.index", compact('posts'));
    }

queryのインクルード機能のようなものか。