- 投稿日:2020-03-24T18:21:33+09:00
【SEO】【canonical】重複ページ
canonicalとは
正式なURLを検索エンジンに伝えるURLの正規化を目的としたものです。
重複ページが存在している場合は検索エンジンに優先させるべきページを伝えることができます。重複ページとは
今回のテーマですが、重複ページの具体例を挙げると、ECサイトなどで色違いの商品がそれぞれ別urlで存在する場合や、リストの並び替えでurlが異なる場合もページの内容自体は同様で順序のみが異なるためこれに該当します。
今回は並び替えの例を紹介します。例
https://evsmart.net/spot/kanagawa/l141305/q141313/
上のページでは並び替えができ、並び替え後のurlでは
https://evsmart.net/spot/kanagawa/l141305/q141313/?p=1&l=20&o=2
このようなパラメータがつきますが、順序が変わっているだけでページの内容としては前者と重複しているため、どちらのページもデフォルト表示の
https://evsmart.net/spot/kanagawa/l141305/q141313/
のurlをcanonicalに書きます。canonical.html<link rel="canonical" href="https://evsmart.net/spot/kanagawa/l141305/q141313/" />従って実際のurlとcanonicalが異なるため、単純に例えば
$_SERVER['REQUEST_URI']
を使って一律に書くということはできません。あとcanonicalは絶対パスで書く必要があります。
終わりに
これだけだったら割と簡単です。
ただ実際にはページネーションと並び替えの両方があったりとか面倒くさいですよね。
ということでそういった場合の例も今度紹介したいと思います。
- 投稿日:2020-03-24T16:29:42+09:00
【Laravel】テストDBとしてSQLiteを設定する
config/database.php
config/database.phpにテスト用のDBとしてSQLiteを追加します。
database.php'connections' => [ 'testing_sqlite' => [ 'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '', ], ],
'database' => ':memory:'
ではインメモリの設定をしています。phpunit.xml
phpunit.xmlに
<server name="DB_CONNECTION" value="testing_sqlite"/>
を追加します。phpunit.xml(略) <php> <server name="APP_ENV" value="testing"/> <server name="DB_CONNECTION" value="testing_sqlite"/> <server name="BCRYPT_ROUNDS" value="4"/> <server name="CACHE_DRIVER" value="array"/> <server name="MAIL_DRIVER" value="array"/> <server name="QUEUE_CONNECTION" value="sync"/> <server name="SESSION_DRIVER" value="array"/> </php> </phpunit>自分は5.8ですが、Laravelのバージョンによってはタグが
<server />
ではなく<env />
となっている場合もあるようです。設定キャッシュをクリア
忘れないようにしましょう。
$ php artisan config:clear
これでテスト用のDBがSQLiteに設定されたと思います。
- 投稿日:2020-03-24T14:16:11+09:00
PHPStan level=max で "Function myFunction() has parameter $hoge with no value type specified in iterable type array. " が発生する場合の対処法
? Consider adding something like
array<Foo>
to the PHPDoc.PHPStan を
--level=max
で実行するとno value type specified in iterable type array.
と叱られ、「PHPDoc にarray<Foo>
っぽいものを書け」と言われるも、何をいっているのかわからない。さらに「Consider adding something like "array<Foo>" to the PHPDoc.」とググってみてもピンと来ない。
TL; DR
「引数」もしくは「返り値」の型宣言で
array
で配列を指定しているが「配列を構成する各要素の型が不明確なので PHPDoc で明示してね」という意味。
key
がint
型、値がstring
型の要素を持つ配列の場合は、array<int,string>
と記載します。この型<キーの型, 値の型>
の記法を「ジェネリクス記法」と呼びます。引数の型宣言のarrayをPHPDocに記載する際に、その構成内容をジェネリクス記法で明示する場合の変更例/** * myDemoFunction * - * @param array $data + * @param array<int,string> $data * @return string */ function myDemoFunction(array $data): string { ... }TS;DR
no value type specified in iterable type array.
の説明
iterable type
とは「foreach
でループ可能な型」を言います。ここではarray
なので「配列」を指します。
no value type specified
とあるので「(配列の)値の型が指定されていません」という意味になり、「iterable
なarray
の、value
のtype
が指定されていません。」という意味です。日本語でいうと「
foreach
ループをした際の、各々の値(要素)の型が明記されていない状態である」と PHPStan が型々言っているのです。関数/メソッドの引数と返り値、そしてオブジェクトのプロパティは比較的に型がつけやすいところですが、現状で無法地帯な箇所があります。
そうです、配列の内部構造です。実際のところ、PHPDocに
@param array
や@return array
と書くことはmixed
と書くのとあまり大きな違いはありません。(「array shapes記法(Object-like arrays)と旧PSR-5記法で型をつける」 @ Qiita より)
つまり、下記のように PHPDoc に引数の型を記載したとしても、問題は同じということです。
$items[] = new Foo(1); $items[] = new Foo(2); $items[] = new Foo(3); /** * @param array $items * @return string */ function myFunc(array $items): string foreach($items as $item){ // ここで $items が配列というのはわかっていても、 // $item の型が「配列」なのか「クラスオブジェクト」なのか // 全体を見ないとわからない。 } return $result; }では、どうやって子要素を指定してあげればいいのでしょうか。
phpDocumentor や PhpStorm を含めた複数の処理系がサポートする PHPDoc のデファクトな仕様では
Book[]
のように記述することで[new Book("a"), new Book("b"), new Book("c")]
のような Book クラスのオブジェクトだけが並んだ配列を表現することができます。
.../** * @return Books[] */ function getBook(): array { // ... }(「array shapes記法(Object-like arrays)と旧PSR-5記法で型をつける」 @ Qiita より)
では、以下のように
@param Foo[] $items
と指定すれば良い気がします。$items[] = new Foo(1); $items[] = new Foo(2); $items[] = new Foo(3); /** * @param Foo[] $items * @return string */ function myFunc(array $items): string foreach($items as $item){ // ここで $items が配列というのはわかっていても、 // $item の型が「配列」なのか「クラスオブジェクト」なのか // 全体を見ないとわからない。 } return $result; }しかし、上記のように記載しても PHPStan のワーニングは回避できません。
この記法には弱点があります。PHPには複数のイテレータがありますが、
[]
は飽くまで配列を表すので、ジェネレータやArratIterator
、あるいはその他のコレクションクラスなどは表現できないのです。(「array shapes記法(Object-like arrays)と旧PSR-5記法で型をつける」 @ Qiita より)
? Consider adding something like array<Foo> to the PHPDoc.
の説明先のワーニングの後にサジェスチョンされている
? Consider adding something like array<Foo> to the PHPDoc.
は、「PHPDoc にarray<Foo>
っぽい何かを追記することを検討してください」という意味です。では「
array<Foo>
っぽい何か」(something likearray<Foo>
)とは「A型 < B型 >」の形式で「親要素の型<子要素の型>」と「何か指定してください」という意味です。つまり、array<Foo>
の場合は「配列で、各要素はFoo
クラスである」ということを意味します。従来記法では値の型のみを指定できましたが、ジェネリクス記法はキーと値の型を同時に指定できます。
/** * @return ArraryObject<int, Book> */ function getBook(): array { // ... }(「array shapes記法(Object-like arrays)と旧PSR-5記法で型をつける」 @ Qiita より)
このジェネリクス記法でやっと
--level=max
で PHPStan を実行してもワーニングが表示されなくなりました。 ?具体例
PHPStanを--level=maxで実行する例$ ./vendor/bin/phpstan analyse src --level=max 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% ------ ------------------------------------------------------------------------- Line functions.php ------ ------------------------------------------------------------------------- 12 Function KEINOS\MyApp\myDemoFunction() has parameter $data with no value type specified in iterable type array. ? Consider adding something like array<Foo> to the PHPDoc. You can turn off this check by setting checkMissingIterableValueType: false in your phpstan.neon. 12 Function KEINOS\MyApp\myDemoFunction() return type has no value type specified in iterable type array. ? Consider adding something like array<Foo> to the PHPDoc. You can turn off this check by setting checkMissingIterableValueType: false in your phpstan.neon. ------ ------------------------------------------------------------------------- [ERROR] Found 2 errors問題のソース<?php declare(strict_types=1); namespace KEINOS\MyApp; /** * myDemoFunction * * @param array $data * @return array */ function myDemoFunction(array $data): array { $result = []; foreach($data as $item){ $result[] = $item; } return $result; }修正後のソース<?php declare(strict_types=1); namespace KEINOS\MyApp; /** * myDemoFunction * * @param array<int,string> $data * @return array<int,string> */ function myDemoFunction(array $data): array { $result = []; foreach($data as $item){ $result[] = $item; } return $result; }環境情報$ ./vendor/bin/phpstan --version PHPStan - PHP Static Analysis Tool 0.12.17 $ php --version PHP 7.4.4 (cli) (built: Mar 19 2020 22:02:49) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies with Xdebug v2.9.3, Copyright (c) 2002-2020, by Derick Rethans参考文献
- array shapes記法(Object-like arrays)と旧PSR-5記法で型をつける @ Qiita
- PSR-5ジェネリクス記法についての個人的見解 | PhpStormに実装されてない未来のPHPDocとPSR-5ジェネリクス記法 @ Qiita
- 投稿日:2020-03-24T14:16:11+09:00
PHPStan level=max で"Function myFunction() has parameter $hoge with no value type specified in iterable type array. "
? Consider adding something like
array<Foo>
to the PHPDoc.PHPStan を
--level=max
で実行するとno value type specified in iterable type array.
と叱られ、「PHPDoc にarray<Foo>
っぽいものを書け」と言われるも、何をいっているのかわからない。さらに「Consider adding something like "array<Foo>" to the PHPDoc.」とググってみてもピンと来ない。
TL; DR
「引数」もしくは「返り値」の型宣言で
array
で配列を指定しているが「配列を構成する各要素の型が不明確なので PHPDoc で明示してね」という意味。
key
がint
型、値がstring
型の要素を持つ配列の場合は、array<int,string>
と記載します。この型<キーの型, 値の型>
の記法を「ジェネリクス記法」と呼びます。引数の型宣言のarrayをPHPDocに記載する際に、その構成内容をジェネリクス記法で明示する場合の変更例/** * myDemoFunction * - * @param array $data + * @param array<int,string> $data * @return string */ function myDemoFunction(array $data): string { ... }TS;DR
(以下「array shapes記法(Object-like arrays)と旧PSR-5記法で型をつける」 @ Qiita より引用)
関数/メソッドの引数と返り値、そしてオブジェクトのプロパティは比較的に型がつけやすいところですが、現状で無法地帯な箇所があります。
そうです、配列の内部構造です。実際のところ、PHPDocに@param array
や@return array
と書くことはmixed
と書くのとあまり大きな違いはありません。
.../** * @return Books[] */ function getBook(): array { // ... }この記法には弱点があります。PHPには複数のイテレータがありますが、
[]
は飽くまで配列を表すので、ジェネレータやArratIterator
、あるいはその他のコレクションクラスなどは表現できないのです。
...
従来記法では値の型のみを指定できましたが、ジェネリクス記法はキーと値の型を同時に指定できます。/** * @return ArraryObject<int, Book> */ function getBook(): array { // ... }具体例
環境情報$ ./vendor/bin/phpstan --version PHPStan - PHP Static Analysis Tool 0.12.17 $ php --version PHP 7.4.4 (cli) (built: Mar 19 2020 22:02:49) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies with Xdebug v2.9.3, Copyright (c) 2002-2020, by Derick RethansPHPStanを--level=maxで実行する例$ ./vendor/bin/phpstan analyse src --level=max 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% ------ ------------------------------------------------------------------------- Line functions.php ------ ------------------------------------------------------------------------- 12 Function KEINOS\MyApp\myDemoFunction() has parameter $data with no value type specified in iterable type array. ? Consider adding something like array<Foo> to the PHPDoc. You can turn off this check by setting checkMissingIterableValueType: false in your phpstan.neon. 12 Function KEINOS\MyApp\myDemoFunction() return type has no value type specified in iterable type array. ? Consider adding something like array<Foo> to the PHPDoc. You can turn off this check by setting checkMissingIterableValueType: false in your phpstan.neon. ------ ------------------------------------------------------------------------- [ERROR] Found 2 errors問題のソース<?php declare(strict_types=1); namespace KEINOS\MyApp; /** * myDemoFunction * * @param array $data * @return array */ function myDemoFunction(array $data): array { $result = []; foreach($data as $item){ $result[] = $item; } return $result; }修正後のソース<?php declare(strict_types=1); namespace KEINOS\MyApp; /** * myDemoFunction * * @param array<int,string> $data * @return array<int,string> */ function myDemoFunction(array $data): array { $result = []; foreach($data as $item){ $result[] = $item; } return $result; }参考文献
- array shapes記法(Object-like arrays)と旧PSR-5記法で型をつける @ Qiita
- PSR-5ジェネリクス記法についての個人的見解 | PhpStormに実装されてない未来のPHPDocとPSR-5ジェネリクス記法 @ Qiita
- 投稿日:2020-03-24T11:46:22+09:00
Laravel Auditingでデータベースの変更を監視する
『誰がいつ、データをいじったか知りたい』というデータの監視が必要な時に、Laravel Auditingが便利です。
概要
Laravel Auditingはデータベース上の指定した監視対象のテーブルに追加・更新・削除などの変更が行われた場合に、誰がどのような変更を行ったかを自動でログ保存するパッケージです。
インストール & 設定
インストール
composerで行います。
composer require owen-it/laravel-auditing設定
インストール後、
config/app.php
にプロバイダーを追加します。config/app.php'providers' => [ + OwenIt\Auditing\AuditingServiceProvider::class, ],追加したら、Laravel Auditing用の設定ファイルを作成するために以下のコマンドを実行します。
php artisan vendor:publish --provider "OwenIt\Auditing\AuditingServiceProvider" --tag="config"実行すると、
config/audit.php
というファイルが作成されます。特にカスタマイズする必要がなければそのまま、データベースのマイグレーションファイルを以下のコマンドで作成します。php artisan vendor:publish --provider "OwenIt\Auditing\AuditingServiceProvider" --tag="migrations"実行すると、auditsテーブルを作成するための
database/migrations/yyyy_mm_dd_nnnnnn_create_audits_table.php
というファイルが作成されます。作成後にマイグレーションしてauditsテーブルを作成します。php artisan migrate監視
基本的な設定
設定が完了したら、監視したいデータのモデルに追記します。例えばitemsテーブルのデータを監視したい場合は、
app/Item.php<?php namespace App; use Illuminate\Database\Eloquent\Model; + use OwenIt\Auditing\Contracts\Auditable; + + class Item extends Model implements Auditable - class Item extends Model { + use \OwenIt\Auditing\Auditable;ちょっとややこしいのですが、インターフェイスは
OwenIt\Auditing\Contracts\Auditable
、traitは\OwenIt\Auditing\Auditable
を指定します。これらを追記するだけで、監視対象となります。実際に監視
では、このテーブルに対して新規登録・変更・削除を行ってみましょう。これらを実行すると、auditsテーブルに以下のようなデータが追加されます。
id user_type user_id event auditable_type auditable_id old_values new_values url ip_address user_agent tags created_at updated_at 1 NULL NULL updated App\Item 2 {"name":"\u8ffd\u52a01"} {"name":"\u8ffd\u52a02"} http://localhost:8000/items/2 127.0.0.1 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15 NULL 2020-03-23 12:20:34 2020-03-23 12:20:34 2 App\User 1 created App\Item 3 [] {"name":"\u307b\u3052","stock":"10","id":3} http://localhost:8000/items 127.0.0.1 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15 NULL 2020-03-23 12:21:39 2020-03-23 12:21:39 3 App\User 1 deleted App\Item 3 {"id":"3","name":"\u307b\u3052","stock":"10"} [] http://localhost:8000/items/3 127.0.0.1 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15 NULL 2020-03-23 12:51:57 2020-03-23 12:51:57 監視対象のデータは以下のような内容が蓄積されます。
フィールド名 蓄積内容 id 監視データのID user_type ユーザモデルのクラス名 user_id ユーザID event 実行したイベント auditable_type 更新したデータモデルのクラス名 auditable_id 更新したデータのID old_values 更新前のデータ new_values 更新後のデータ url 実行されたURL ip_address IPアドレス user_agent ユーザエージェント tags タグ created_at 作成日時(= データが更新された日時) updated_at 更新日時 あまりないと思いますが、ログインせずにデータを更新した場合は、
user_type
,user_id
がNULLになります。監視データの取得
監視データは監視対象のモデルに関連しています。
<?php $item = Item::find(1); // 該当データの全ての監視データ $audits = $item->audits; // 該当データの最初の監視データ $first = $item->audits()->first(); // 該当データの直近の監視データ $latest = $item->audits()->latest()->first(); // 該当データの監視データのID指定 $audit = $item->audits()->find(1);監視データもEloquentモデルなので、直接取得できます。
<?php use OwenIt\Auditing\Models\Audit; // 全件取得 $audits = Audit::all(); // ID指定 $audit = Audit::find(1);カスタマイズ
コンソールコマンドでの実行も監視したい
デフォルトでは、コンソールコマンドやジョブの更新は監視対象外となっています。監視したい場合は、
config/audit.php
で設定を変更します。config/audit.php- 'console' => false, + 'console' => true,監視するフィールドを絞り込みたい
デフォルトでは、登録・更新日時以外を対象とします。監視するフィールドを絞り込みたい場合は監視対象モデルに設定を追記します。
app/Item.php<?php namespace App; use Illuminate\Database\Eloquent\Model; use OwenIt\Auditing\Contracts\Auditable; class Item extends Model implements Auditable { use \OwenIt\Auditing\Auditable; + protected $auditInclude = [ + 'name', + ];逆に除外したい場合は、
$auditExclude
に指定することで、除外できます。蓄積するデータを変えたい
蓄積するデータをカスタマイズしたい場合は、独自のResolverを作成します。Laravel Auditingでは以下のResolver用インターフェイスが用意されています。
インターフェイス 概要 OwenIt\Auditing\Contracts\IpAddressResolver IPアドレス用I/F OwenIt\Auditing\Contracts\UrlResolver URL用I/F OwenIt\Auditing\Contracts\UserAgentResolver ユーザエージェント用I/F OwenIt\Auditing\Contracts\UserResolver ユーザモデル用I/F これらを実装することで、蓄積するデータを変えることができます。
app/resolvers/IpAddressResolver.php<?php namespace App\Resolvers; use Illuminate\Support\Facades\Request; class IpAddressResolver implements \OwenIt\Auditing\Contracts\IpAddressResolver { /** * {@inheritdoc} */ public static function resolve(): string { return Request::header('HTTP_X_FORWARDED_FOR', '0.0.0.0'); } }独自Resolverは
config/audit.php
で設定することで反映されます。config/audit.phpreturn [ 'resolver' = [ 'user' => OwenIt\Auditing\Resolvers\UserResolver::class, - 'ip_address' => OwenIt\Auditing\Resolvers\IpAddressResolver::class, + 'ip_address' => App\Resolvers\IpAddressResolver::class, 'user_agent' => OwenIt\Auditing\Resolvers\UserAgentResolver::class, 'url' => OwenIt\Auditing\Resolvers\UrlResolver::class, ], ];マスクしたい
監視データは変更前・後の値をそのまま保存しますが、場合によってはマスクして保存したい時もあります。その場合は、監視対象のモデルに設定することで、マスクできます。
app/Item.php<?php namespace App; use Illuminate\Database\Eloquent\Model; use OwenIt\Auditing\Contracts\Auditable; +use OwenIt\Auditing\Redactors\LeftRedactor; class Item extends Model implements Auditable { use \OwenIt\Auditing\Auditable; + protected $attributeModifiers = [ + 'title' => LeftRedactor::class, + ];
LeftRedactor
は左から90%マスクし残り10%はそのまま、RightRedactor
は右から90%マスクします。マスク部分は#####
で保存されます。特定のイベントのみ監視したい
デフォルトは追加・更新・削除を監視しますが、設定によりイベントを絞り込むことができます。例えば削除のみ監視したい場合は以下のように設定します。
全体
config/audit.php'events' => [ - 'created', - 'updated', 'deleted', - 'restored', ],特定の監視対象のみ
app/Item.php<?php namespace App; use Illuminate\Database\Eloquent\Model; use OwenIt\Auditing\Contracts\Auditable; class Item extends Model implements Auditable { use \OwenIt\Auditing\Auditable; + protected $auditEvents = [ + 'deleted', + ];監視ログの上限を設ける
デフォルトでは監視ログは延々と蓄積され続けます。上限を設けたい場合は以下のように設定するこで、上限を超えた古いデータが削除されていきます。
全体
config/audit.php- 'threshold' => 0, // 0だと上限なし + 'threshold' => 10,特定の監視対象のみ
app/Item.php<?php namespace App; use Illuminate\Database\Eloquent\Model; use OwenIt\Auditing\Contracts\Auditable; class Item extends Model implements Auditable { use \OwenIt\Auditing\Auditable; + protected $auditThreshold = 10;
- 投稿日:2020-03-24T10:45:37+09:00
【PHP】出したエラーの数々
- 投稿日:2020-03-24T09:44:25+09:00
Phoenixお気軽API開発①:PHP的ハックを応用してNode.js Express/Go的な軽量APIをPhoenixで実現してみた
fukuoka.ex/kokura.exのpiacereです
ご覧いただいて、ありがとうございます前々作は「PHP的気軽さでWebアプリを作る方法」、前作はこれを応用して「リアルタイムフロントであるLiveViewにも気軽さを導入」してみましたが、今作では、Phoenix API開発に、Node.js Express/Go的な軽量APIのテイストを付与してみます
この対応により、APIも、WebアプリやLiveView同様、ルーティング/MVCが不要になります
また、従来通りのmix phx.gen.jsonによるScaffold(≒コード自動生成)で構築されるAPIだと、JSONフォーマットがカスタマイズしにくかったり、カスタムフィールドを乗せるのにもノウハウが必要ですが、ここでは、シンプルなJSONテンプレートによるAPI構築を実現するので、普段のPhoenix使いでは体験できない気楽さを感じていただけたら幸いです
本コラムの検証環境
本コラムは、以下環境で検証しています(Windowsで実施していますが、Linuxやmacでも動作する想定です)
- Windows 10
- Elixir 1.10.1 ※最新版のインストール手順はコチラ
- Phoenix 1.4.15 ※最新版のインストール手順はコチラ
- Node.js 12.14.0
なおPhoenixは、1.3系でも動作します(ElixirもPhoenixのバージョンに準じた古いものでも大丈夫です)
事前準備:PHP的ハック導入済みPhoenix PJの作成
下記コラムの手順で、PHP的ハック導入済みのPhoenix PJを作成します
ElixirでもPHP的に気軽なWebアプリ開発をしたい(Phoenixより薄いWebFW実現?)
https://qiita.com/piacerex/items/25c82153976dcb6d57c1ここまでが終わったら、ブラウザで
「http://localhost:4000/abc/def?abc=xyz123」
にアクセスすると、以下のようなページが表示されることを確認して、準備完了です
手順①:REST APIをルーティング不要に
まずはルーティングで、前作同様、「*path_」というワイルドカード指定をすることで、多階層のURLパスを「path_」パラメータから取得できるようにします
接続先のコントローラは、API専用のマッピングコントローラとします
なお、前作の続きでやられている場合は、getやpostのワイルドカード指定とぶつかるため、getやpostの分は、コメントアウトや削除しておいてください
lib/basic_web/router.exdefmodule BasicWeb.Router do … scope "/", BasicWeb do pipe_through :browser get "/", PageController, :index # get "/*path_", PageController, :index # <-- remove here # post "/*path_", PageController, :index # <-- remove here # live "/*path_", LiveViewController # <-- remove here get "/*path_", ApiController, :index # <-- add here …手順②:html.leexの複数階層フォルダ指定可能に
ここは上記のコラム内で実施済みなので、特にやることはありません
手順③:パスの自動マッピング、GETパラメータの素通し
API専用のマッピングコントローラを追加します
「path_」パラメータのフォルダ階層リストを、json.eexのパスにマッピングします
また、mix phx.newで生成されたコントローラが「_params」という感じでアンダースコアで未使用にしているので、これを解除し、renderの第3引数に渡すことで、GETパラメータを素通しさせます
lib/basic_web/controllers/api_controller.exdefmodule BasicWeb.ApiController do use BasicWeb, :controller def index( conn, params ) do template = if params[ "path_" ] == nil, do: "index.json", else: to_path_string( params[ "path_" ] ) <> ".json" render( conn, template, params: params ) end def to_path_string( path_list ), do: String.slice( Enum.reduce( path_list, "", & "#{ &2 }#{ &1 }/" ), 0..-2 ) end「path_」パラメータ未指定時のためのindex.json.eexを追加します
なお、JSONテンプレートは、templates配下に「api」というフォルダを掘って、配置することにします
lib/basic_web/templates/json/index.json.eex%{ }手順④:json.eexをビュー向けにテンプレート展開
API専用のビューを追加します
PhoenixにおけるJSONレンダリングは、Phonenixのビュー配下で、マップもしくはマップリストをJSONに変換することで実現されるため、コントローラでのrender()、つまりeexテンプレート展開(<%= ~ %>)のように文字列展開されると、都合が悪いです
そこで、ビューのrender()にて、Code.eval_string()を使って、自前でテンプレート展開を行います
Elixirのビルトインモジュールや自作モジュールは、Code.eval_string()で展開可能ですが、入力パラメータは自前でバインディングをかけなければならないため、Code.eval_string()の第2引数にてバインディングを行います
ちなみに、Code.eva_file()というもっと便利そうなものもあるのですが、Code.eval_string()のようなバインディングができないため、今回の目的には沿いませんでした
Phoenixは、カスタムテンプレートエンジンを定義できるので、JSON専用テンプレートを構築することもできるのですが、今回は、バインディングもできる強力なCode.eval_string()の恩恵に預かることにしました
lib/basic_web/views/api_view.exdefmodule BasicWeb.ApiView do use BasicWeb, :view def render( path, %{ view_template: view_template, params: params } ) do File.read!( "lib/basic_web/templates/api/#{ view_template }.eex" ) |> Code.eval_string( [ params: params ] ) |> elem( 0 ) end endなお、Webページテンプレート同様、「@ param」で入力パラメータ指定可能にしたかったのですが、Code.eval系はモジュールアトリビュートが通常変数と扱いが異なるためNGで、EEx.eval系は@が利用できますが文字列返却になってしまうため、断念しました
手順⑤:json.eexを追加する
1)templates/api直下にjson.eex追加
これで、json.eexを追加するだけで、新たなAPIが追加可能となったので、追加してみます
JSONテンプレートは、Elixirのマップとリストで構成し、中身は通常のマップやリストと同様、Elixirコードを記述できます
まずはシンプルに、マップ1つだけとし、入力パラメータ/リテラル/Elixirビルトインモジュール関数を値に設定します
lib/basic_web/templates/api/sample.json.eex%{ id: params[ "id" ], name: "Elixir", version: System.version() }RESTクライアントで
「http://localhost:4000/sample?id=123」
にアクセスすると、以下のようなページが表示されます入力パラメータや、Elixirビルトインモジュール関数が実行され、その結果がJSONとして返却されます
2)templates/api配下のサブフォルダ下にjson.eex追加
apiフォルダ配下にフォルダを掘って、その配下にjson.eexを配置しても動きます
「abc」というフォルダを掘り、「def.json.eex」というJSONテンプレートを追加します
今度は、リストマップで構成し、各マップの値をランダム値で変動させてみます
lib/basic_web/templates/api/abc/def.json.eex[ %{ greeting: "I'm def-1", type: "plane API", random_no: :rand.uniform }, %{ greeting: "I'm def-2", type: "plane API", random_no: :rand.uniform }, %{ greeting: "I'm def-3", type: "plane API", random_no: :rand.uniform } ]RESTクライアントで
「http://localhost:4000/abc/def」
にアクセスすると、以下のようなページが表示されますランダム値が展開されたJSONオブジェクトを梱包するJSON配列が返却されました
終わり
JSONテンプレート追加だけで実現できるお気軽API開発を実現してみました
前々作のPHP的ハックをAPIに応用すると、Scaffoldを用いることなく、ルーティング/MVCにも煩わされない、軽量なAPI開発が可能となることがお分かりいただけたでしょうか?
Phoniexは、ファットなWebフレームワークだと誤解されがちですが、ここまでの3部作で見てきたように、ちょっとした工夫を施すことで、軽量フレームワークに化ける「プログレッシブWebフレームワーク」としての一面があり、そこが上手く共有できていたら嬉しいです
さて、次回以降では、より本格的なAPI利用に向けて、REST API対応(POST/PUT/DELETEやURLとしてのid指定)や、軽量APIからのDB利用、APIバージョン管理とWebページ共存なども行っていきたいと思います
p.s.このコラムが、面白かったり、役に立ったら…
- 投稿日:2020-03-24T06:54:28+09:00
本日の学習内容Part3
- 投稿日:2020-03-24T00:49:16+09:00
Wordpress プラグインの作成と管理画面の作り方
プラグインの作成
プラグインを作るにはプラグインフォルダを作成し、プラグインの設定項目を記入する。
プラグインフォルダは、
wp-content/plugins
に作成する。プラグインフォルダの最初のファイルはプラグイン名と同じようにする。ここでは
test-plugin.php
にした。このファイルに最低限記入が必要なのは次のような内容。
<?php // wp-content/plugins/test-plugin/test-plugin.php /* Plugin Name: プラグイン名(他と被らないようにする) Description: プラグインの説明文 Author: 作者名 Version: 0.1 */これだけだと何の変化もないので、
Hello World
と表示してみる。<?php /* Plugin Name: Test plugin Description: A test plugin to demonstrate wordpress functionality Author: Test Name Version: 0.1 */ function test_function() { echo '<h1>Hello World</h1>'; } add_action( 'wp_footer', 'test_function' );プラグイン一覧画面に作成したプラグインが表示されてるので有効化する。
wp_footer
にアクションを追加したので、トップページを開くと画面下にHello World
が表示されるはず。プラグインにメニューと設定画面を追加する
次に管理画面を作成してみる。
管理画面は、
add_menu_page()
に簡単な設定項目を記入するだけ。<?php /* Plugin Name: Test plugin Description: A test plugin to demonstrate wordpress functionality Author: Test Name Version: 0.1 */ function test_function() { echo '<h1>Hello World</h1>'; } function test_plugin_setup_menu(){ add_menu_page( __( 'TestPlugin', 'textdomain' ), // ページタイトル 'Test Plugin', // メニュータイトル 'manage_options', // 'test-plugin', // メニューslug 'test_function', // 実行する関数 'dashicons-chart-pie', // メニューに表示するアイコン 6 // メニューの表示位置 ); } add_action('admin_menu', 'test_plugin_setup_menu');メニューのリンクをクリックすると、
add_menu_page()
で指定した関数が実行されている。あとはHTMLとCSSでページを作っていくだけ。
- 投稿日:2020-03-24T00:06:48+09:00
Mac Laravelでライブラリ「laravel-dompdf」をインストールする
目的
- Laravelのライブラリインストールが初めてだったので実施方法をまとめる
- ライブラリ「laravel-dompdf」のインストール方法をまとめる
実施環境
- ハードウェア環境
項目 情報 OS macOS Catalina(10.15.3) ハードウェア MacBook Air (11-inch ,2012) プロセッサ 1.7 GHz デュアルコアIntel Core i5 メモリ 8 GB 1600 MHz DDR3 グラフィックス Intel HD Graphics 4000 1536 MB
- ソフトウェア環境
項目 情報 PHP バージョン 7.4.3 Laravel バージョン 7.0.8
- ライブラリ環境
項目 情報 備考 laravel-dompdf v0.8.6 アプリ名ディレクトリに移動後コマンド`composer show -i 実施方法概要
- インストール
- 設定
- 確認
実施方法詳細
- ローカル開発環境を前提に説明を記載する。
インストール
下記コマンドを実行してライブラリ「laravel-dompdf」をインストールする。
$ composer require barryvdh/laravel-dompdf設定
下記コマンドを実行して設定を記載するファイルを開く。
$ vi アプリ名ディレクトリ/app.php先のコマンドを実行後、viのコマンドモードにて下記のコマンドを入力して設定を追記する行を検索する。
/'providers' => [検索にヒットした部分が下記の様な記載になっていることを確認する。
app.php'providers' => [ /* * Laravel Framework Service Providers... */ Illuminate\Auth\AuthServiceProvider::class, Illuminate\Broadcasting\BroadcastServiceProvider::class, Illuminate\Bus\BusServiceProvider::class, Illuminate\Cache\CacheServiceProvider::class, Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, Illuminate\Cookie\CookieServiceProvider::class, Illuminate\Database\DatabaseServiceProvider::class, Illuminate\Encryption\EncryptionServiceProvider::class, Illuminate\Filesystem\FilesystemServiceProvider::class, Illuminate\Foundation\Providers\FoundationServiceProvider::class, Illuminate\Hashing\HashServiceProvider::class, Illuminate\Mail\MailServiceProvider::class, Illuminate\Notifications\NotificationServiceProvider::class, Illuminate\Pagination\PaginationServiceProvider::class, Illuminate\Pipeline\PipelineServiceProvider::class, Illuminate\Queue\QueueServiceProvider::class, Illuminate\Redis\RedisServiceProvider::class, Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, Illuminate\Session\SessionServiceProvider::class, Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, /* * Package Service Providers... */ /* * Application Service Providers... */ App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, ],先に「app.php」ファイルを検索したファイルの
App\Providers\RouteServiceProvider::class,
の後に下記の内容を追記する。Barryvdh\DomPDF\ServiceProvider::class,先の内容を追記後、下記の様に「app.php」ファイルが記載されていることを確認する。
app.php'providers' => [ /* * Laravel Framework Service Providers... */ Illuminate\Auth\AuthServiceProvider::class, Illuminate\Broadcasting\BroadcastServiceProvider::class, Illuminate\Bus\BusServiceProvider::class, Illuminate\Cache\CacheServiceProvider::class, Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, Illuminate\Cookie\CookieServiceProvider::class, Illuminate\Database\DatabaseServiceProvider::class, Illuminate\Encryption\EncryptionServiceProvider::class, Illuminate\Filesystem\FilesystemServiceProvider::class, Illuminate\Foundation\Providers\FoundationServiceProvider::class, Illuminate\Hashing\HashServiceProvider::class, Illuminate\Mail\MailServiceProvider::class, Illuminate\Notifications\NotificationServiceProvider::class, Illuminate\Pagination\PaginationServiceProvider::class, Illuminate\Pipeline\PipelineServiceProvider::class, Illuminate\Queue\QueueServiceProvider::class, Illuminate\Redis\RedisServiceProvider::class, Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, Illuminate\Session\SessionServiceProvider::class, Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, /* * Package Service Providers... */ /* * Application Service Providers... */ App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, Barryvdh\DomPDF\ServiceProvider::class, ],先のコマンドを実行後、viのコマンドモードにて下記のコマンドを入力して設定を追記する行を検索する。
/'aliases' => [検索にヒットした部分が下記の様な記載になっていることを確認する。
app.php'aliases' => [ 'App' => Illuminate\Support\Facades\App::class, 'Arr' => Illuminate\Support\Arr::class, 'Artisan' => Illuminate\Support\Facades\Artisan::class, 'Auth' => Illuminate\Support\Facades\Auth::class, 'Blade' => Illuminate\Support\Facades\Blade::class, 'Broadcast' => Illuminate\Support\Facades\Broadcast::class, 'Bus' => Illuminate\Support\Facades\Bus::class, 'Cache' => Illuminate\Support\Facades\Cache::class, 'Config' => Illuminate\Support\Facades\Config::class, 'Cookie' => Illuminate\Support\Facades\Cookie::class, 'Crypt' => Illuminate\Support\Facades\Crypt::class, 'DB' => Illuminate\Support\Facades\DB::class, 'Eloquent' => Illuminate\Database\Eloquent\Model::class, 'Event' => Illuminate\Support\Facades\Event::class, 'File' => Illuminate\Support\Facades\File::class, 'Gate' => Illuminate\Support\Facades\Gate::class, 'Hash' => Illuminate\Support\Facades\Hash::class, 'Http' => Illuminate\Support\Facades\Http::class, 'Lang' => Illuminate\Support\Facades\Lang::class, 'Log' => Illuminate\Support\Facades\Log::class, 'Mail' => Illuminate\Support\Facades\Mail::class, 'Notification' => Illuminate\Support\Facades\Notification::class, 'Password' => Illuminate\Support\Facades\Password::class, 'Queue' => Illuminate\Support\Facades\Queue::class, 'Redirect' => Illuminate\Support\Facades\Redirect::class, 'Redis' => Illuminate\Support\Facades\Redis::class, 'Request' => Illuminate\Support\Facades\Request::class, 'Response' => Illuminate\Support\Facades\Response::class, 'Route' => Illuminate\Support\Facades\Route::class, 'Schema' => Illuminate\Support\Facades\Schema::class, 'Session' => Illuminate\Support\Facades\Session::class, 'Storage' => Illuminate\Support\Facades\Storage::class, 'Str' => Illuminate\Support\Str::class, 'URL' => Illuminate\Support\Facades\URL::class, 'Validator' => Illuminate\Support\Facades\Validator::class, 'View' => Illuminate\Support\Facades\View::class, ],先に「app.php」ファイルを検索したファイルの
'View' => Illuminate\Support\Facades\View::class,
の後に下記の内容を追記する。'PDF' => Barryvdh\DomPDF\Facade::class,先の内容を追記後、下記の様に「app.php」ファイルが記載されていることを確認する。
app.php'aliases' => [ 'App' => Illuminate\Support\Facades\App::class, 'Arr' => Illuminate\Support\Arr::class, 'Artisan' => Illuminate\Support\Facades\Artisan::class, 'Auth' => Illuminate\Support\Facades\Auth::class, 'Blade' => Illuminate\Support\Facades\Blade::class, 'Broadcast' => Illuminate\Support\Facades\Broadcast::class, 'Bus' => Illuminate\Support\Facades\Bus::class, 'Cache' => Illuminate\Support\Facades\Cache::class, 'Config' => Illuminate\Support\Facades\Config::class, 'Cookie' => Illuminate\Support\Facades\Cookie::class, 'Crypt' => Illuminate\Support\Facades\Crypt::class, 'DB' => Illuminate\Support\Facades\DB::class, 'Eloquent' => Illuminate\Database\Eloquent\Model::class, 'Event' => Illuminate\Support\Facades\Event::class, 'File' => Illuminate\Support\Facades\File::class, 'Gate' => Illuminate\Support\Facades\Gate::class, 'Hash' => Illuminate\Support\Facades\Hash::class, 'Http' => Illuminate\Support\Facades\Http::class, 'Lang' => Illuminate\Support\Facades\Lang::class, 'Log' => Illuminate\Support\Facades\Log::class, 'Mail' => Illuminate\Support\Facades\Mail::class, 'Notification' => Illuminate\Support\Facades\Notification::class, 'Password' => Illuminate\Support\Facades\Password::class, 'Queue' => Illuminate\Support\Facades\Queue::class, 'Redirect' => Illuminate\Support\Facades\Redirect::class, 'Redis' => Illuminate\Support\Facades\Redis::class, 'Request' => Illuminate\Support\Facades\Request::class, 'Response' => Illuminate\Support\Facades\Response::class, 'Route' => Illuminate\Support\Facades\Route::class, 'Schema' => Illuminate\Support\Facades\Schema::class, 'Session' => Illuminate\Support\Facades\Session::class, 'Storage' => Illuminate\Support\Facades\Storage::class, 'Str' => Illuminate\Support\Str::class, 'URL' => Illuminate\Support\Facades\URL::class, 'Validator' => Illuminate\Support\Facades\Validator::class, 'View' => Illuminate\Support\Facades\View::class, 'PDF' => Barryvdh\DomPDF\Facade::class, ],確認
下記コマンドをアプリ名ルートディレクトリで実行してライブラリ導入確認のためのコントローラを作成する。
$ php artisan make:controller PdfController先のコマンドで作成されたコントローラを下記の様に修正する。
アプリ名ルートディレクトリ/app/Http/Controllers/pdf.Controller.php<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use PDF; class PdfController extends Controller { public function test(){ $pdf = PDF::loadHTML('<h1>test</h1>'); return $pdf->stream(); } }ルーティングファイルに下記の記載を追記する。
アプリ名ルートディレクトリ/routes/web.phpRoute::get('/pdf', 'PdfController@test');下記のリンクにアクセスする。
下記の様にPDF形式で「test」の文字が表示されていることを確認する。
上記の様に表示されればLaravelのライブラリ「laravel-dompdf」のインストールは完了である。
参考文献
- 投稿日:2020-03-24T00:04:54+09:00