default eye-catch image.

PythonでUpcast/Downcast出来ない件と多態性のサンプル

継承・カプセル化・多態性 オブジェクト指向の必須要素はこれ。継承、カプセル化・多態性。 異種リストを書けば全部試せるので試してみる。 多態性実現のためにオブジェクトのUpcast/Downcastを調べようとしたら存在しない!。 結論として、PythonにおいてはオブジェクトのUpcast/Downcastは出来ない。 動的型付けなんで、Upcastは勝手に上位クラスだと思って触れば良いのだけど、 形だけでも上位クラスにキャストしておいた方が可読性は上がると思うので残念。 abstractメソッドを定義するだけでパッケージを要求するとか切ない。 from abc import ABCMeta, abstractmethod # 抽象基底クラス class Base: __attr = 100 @abstractmethod def hoge(self): pass class Derived1(Base): __attr = 200 def hoge(self): print(self.__attr) class Derived2(Base): __attr = 300 def hoge(self): print(self.__attr) vals = [Derived1(), Derived2()] for val in vals: val.hoge() なお、Downcastについては、以下のようにしている例がある。 class SubDerived2(Derived1): __attr = 500 def hoge(self): print(self.__attr) def myfunc(self): print(\"myfunc is called\") derived = Derived2() derived.__class__ = SubDerived2 derived.myfunc() 確かに、Upcastは動的型付けの範囲内だが、実現可能でDowncastは何らかの指示が必要で、 必要な方にだけ手段が用意されているのは合理的な気もする。 関数 defで定義する。最初の行に書いた文字列リテラルはdocstring。PHPDoc的なやつだろうか。 関数のスコープからローカル->グローバル->ビルトインの順にシンボルを参照できる。 関数スコープから外のスコープのシンボルは書けない。(global指定して初めて書ける)。 フィボナッチ数列。 def fib(n): \"\"\"Print a Fibonacci series up to n.\"\"\" a,b = 0,1 while a < n: print(a,end=' ') a,b=b,a+b print() fib(100) # 0 1 1 2 3 5 8 13 21 34 55 89 キーワード引数も使える。 def myfunc(hoge,fuga=1): print(hoge) print(fuga)

default eye-catch image.

Laravel Accessor/Mutatorを使って透過的にフィールドを暗号化/復号するサンプル

DBに入っているデータを決まった書式/形式に変換して表示したり、 逆に逆変換して保存する例は多いかと思います。 変換,逆変換の実装方法は以下みたいな感じかと..。 いずれも変換/逆変換の存在を忘れて仕様が抜けたり、 同じことを他でも書くコードクローンが発生する原因になる。 Controllerにダラダラと変換/逆変換を書く EloquentにオレオレSetter/Getterを書く Accessor/Mutatorを使うことで上記の原因を無くすことができます。 Accessor/Mutator Eloquentのメンバ変数(つまり、テーブルのフィールド)へのアクセスを ある規則をもってEloquentに定義したSetter/Getterを仲介するように強制できます。 [clink implicit=\"false\" url=\"https://laravel.com/docs/5.8/eloquent-mutators\" imgurl=\"http://laravel.jp/assets/img/logo-head.png\" title=\"Eloquent: Mutators Introduction\" excerpt=\"Accessors and mutators allow you to format Eloquent attribute values when you retrieve or set them on model instances. For example, you may want to use the Laravel encrypter to encrypt a value while it is stored in the database, and then automatically decrypt the attribute when you access it on an Eloquent model.\"] Accessors and mutators allow you to format Eloquent attribute values when you retrieve or set them on model instances. For example, you may want to use the Laravel encrypter to encrypt a value while it is stored in the database, and then automatically decrypt the attribute when you access it on an Eloquent model. In addition to custom accessors and mutators, Eloquent can also automatically cast date fields to Carbon instances or even cast text fields to JSON. 暗号化/復号 サンプル 標題の通りですが、Accessor/Mutatorを使ってフィールドを暗号化/復号してみます。 Cryptファサードを使ってAES-256-CBCの暗号化/復号を行う対です。 secretvalueというフィールドにAES256CBCで暗号化して書き込み、復号して読み込みます。 class User extends Authenticatable { use Notifiable; /** * Get the user\'s secretvalue. * * @param string $value * @return string */ public function getSecretvalueAttribute($value) { return decrypt($value); } /** * Set the user\'s secretvalue. * * @param string $value * @return string */ public function setSecretvalueAttribute($value) { $this->attributes[\'secretvalue\'] = encrypt($value); $this->save(); } } 透過的に呼び出す例です。 Userのsecretvalueフィールドに\"hogehoge\"という値を設定しています。 hogehogeという平文を暗号化してsecretvalueフィールドに書き込む処理は使う側には見えません。 Route::get(\'/sample/setvalue\',function(){ AppUser::find(1)->secretvalue = \'hogehoge\'; }); Userのsecretvalueフィールドを読み込んで出力しています。 暗号化済み文字列を復号する処理は使う側には見えません。 Route::get(\'/sample/getvalue\',function(){ echo AppUser::find(1)->secretvalue; }); より広い用途で使える 暗号化/復号はかなり直球な使い方ですが、ビジネスロジック内の定型処理など 積極的に使おうとするとAccessor/Mutatorに掃き出せるケースがありそうです。

default eye-catch image.

インデントとブロック,比較と、比較の連接,辞書オブジェクト,リストとタプル,if … elif … else,for,while,iteratableオブジェクト

他言語からの流入組が最小工数でPythonを学ぶシリーズです。 Python文法詳解を読みながらアウトプットしていきます。 [arst_toc tag=\"h4\"] いくら何でも公式を上から順番に読むのは時間がかかり過ぎるし、 550ページとかある本なんて読みたくないので、なるべく薄くて枠だけ書いてある本が良いなと。 Python文法詳解posted with amazlet at 19.05.25石本 敦夫 オライリージャパン 売り上げランキング: 432,601Amazon.co.jpで詳細を見る インデントとブロック ブロックはbeginや { などの予約語ではなく、インデントを使う。 公式は以下。 [clink implicit=\"false\" url=\"https://docs.python.org/2.0/ref/indentation.html\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"2.1.7 Indentation\" excerpt=\"Leading whitespace (spaces and tabs) at the beginning of a logical line is used to compute the indentation level of the line, which in turn is used to determine the grouping of statements.\"] 公式に面白い例が載ってた。 見た目上、インデントが無茶苦茶だけれども文法的には合理性がある例。 要するにインデントとインデント解除のconsistencyがあればOK。 def perm(l): # Compute the list of all permutations of l if len(l) <= 1: return [l] r = [] for i in range(len(l)): s = l[:i] + l[i+1:] p = perm(s) for x in p: r.append(l[i:i+1] + x) return r コーデイング規約によると、お作法としてスペースの個数は4個。だそうだ。 タブではなくスペースでインデントするなんて信じられない...。 エディタにタブ=スペース4個の設定を追加しないと。 言語仕様上の制約はインデントのconsistencyだけなので、 実際のインデント数はプロジェクトによるとのこと。ほうほう。 ただ、スペースとタブの混在禁止はこの際ハッキリさせましょう、という流れ。 比較と、比較の連接 比較演算子について公式は以下。 [clink implicit=\"false\" url=\"https://docs.python.org/ja/3/library/stdtypes.html#comparisons\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"2.1.7 Indentation\" title=\"比較\" excerpt=\"Python には 8 種の比較演算があります。比較演算の優先順位は全て同じです (ブール演算より高い優先順位です)。比較は任意に連鎖できます; \"] 比較演算子を連接できる。PHPとRubyには無い感じ。 a=50 # 通常 if (0 < a) and (a < 100): print("OK") else: print("NG") # 連接 if 0 < a < 100: print("OK") else: print("NG") 辞書オブジェクト KeyValue型。本家は以下。Keyは変更不能で一意でなければならない、と書いてある。 [clink implicit=\"false\" url=\"https://docs.python.org/ja/3/tutorial/datastructures.html#dictionaries\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"5.5. 辞書型 (dictionary)\" excerpt=\"もう一つ、有用な型が Python に組み込まれています。それは 辞書 (dictionary)です。辞書は他の言語にも 連想記憶 (associated memory) や 連想配列 (associative array) という名前で存在することがあります。ある範囲の数でインデクス化されているシーケンスと異なり、辞書は キー (key) でインデクス化されています。\"] a = {1:\'one\',2:\'two\'} print(a) # {1:\'one\',2:\'two\'} # 代入 print(a[1]) # \'one\' a[1] = \'壱\' print(a[1]) # \'壱\' # 削除 del a[1] print(a[1]) # エラー キーを文字列にして連想配列みたくできる。 連想配列のキーにできるかどうかは、その型が更新可能であるかどうか? b = {\'hoge1\':\'fuga1\',\'hoge2\':\'fuga2\'} print(b) # {\'hoge1\':\'fuga1\',\'hoge2\':\'fuga2\'} リストとタプル 似て非なる配列的データ構造、リストとタプル。 リストの方は更新可能。タプルは更新不能。 任意の型を要素としてとれるので、配列ではなくリスト。 L1 = [1,2,3,4,5] print(L1) # [1,2,3,4,5] L2 = (1,2,3,4,5) print(L2) # (1,2,3,4,5) L1[0] = \'hoge\' print(L1) # [\'hoge\', 2, 3, 4, 5] L2[0] = 100 # TypeError: \'tuple\' object does not support item assignment 序数を使ってシーケンシャルアクセスできる。 ary = [1,5,3,4,9] print(ary[3]) # 4 str = \'abcdef\' print(str[1]) # b 負の序数を使うと後ろから数える。 ary = [1,5,3,4,9] print(ary[-1]) # 4 レンジも使える。 ary = [1,5,3,4,9] print(ary[1:3]) # [5,3,4] print(ary[-3:-1]) # [5,3,4] print(ary[:3]) # [1,5,3] レンジ指定で要素を更新できる。 ary = [1,5,3,4,9] ary[2:3] = [100,200] print(ary)# [1, 5, 100, 200, 4, 9] if ... elif ... else elseif が elif。 論理式が偽になるのは\"数値の0\"、空のコレクション、Noneオブジェクト。それ以外は真。 a = 50 if 0 < a < 35: print("OK") elif 35 <= a < 100: print("So much") else: print("NG") for,while forループとwhileループがある。iteratableオブジェクトを指定できる。 whileループについて。 str = \"aiueo\" stop = len(str) i=0 while i < stop: print(str[i]) i += 1 # a # i # u # e # o forループについて。 str = \'abcde\' for c in str: print(c) # a # b # c # d # e # continue, break str = \'abcde\' for c in str: if c == \'c\': continue if c == \'e\': break print(c) # a # b # d whileループ、forループの終了時に実行するブロックをelseで定義できる。 breakで中断した場合は実行されない。 str = \'abcde\' for c in str: print(c) else: print(\'ループ終了\') # a # b # c # d # e # ループ終了 iteratableオブジェクト よくあるパターン。要素の順序を走査するためのインターフェースを実装したオブジェクト。 本家によるとこんな感じ。 [clink implicit=\"false\" url=\"https://docs.python.org/ja/3/glossary.html#term-iterable\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"iterable\" excerpt=\"(反復可能オブジェクト) 要素を一度に 1 つずつ返せるオブジェクトです。 反復可能オブジェクトの例には、(list, str, tuple といった) 全てのシーケンス型や、 dict や ファイルオブジェクト といった幾つかの非シーケンス型、 あるいは Sequence 意味論を実装した __iter__() メソッドか __getitem__() メソッドを持つ任意のクラスのインスタンスが含まれます。\"]

default eye-catch image.

Model Binding と 1枚のBladeで CRUD する

1枚のBladeで確認画面付きCRUDを実現できると、Bladeの枚数が格段に少なくなって良さそう。 その前にまずModelBindingで単なるUserを1枚のBladeでCRUDしてみる。 1枚のBladeが複数の機能で使われることになり、Bladeの中に要素と制御が増えていくため、 実は、Bladeの枚数が増えたとしても1つのBladeを単純にした方が良いのかもしれないが、 1度作っておくとずっと使えるかもしれないので、そこまでやってみる。 やること Laravelに最初から付いてくるUserを使って、name,email,passwordのCRUDをする。 URL(route)は以下。showのパラメタをOptionalにして、あればUpdate、なければCreateする。 Update、Createは、本質的に分けるべきと考えてURLを別にしてある。 firstOrNew()を使うと、あればUserインスタンスを読み込んでくれる。 なければインスタンスを作る。ただしレコードは作らない。新規作成操作時にレコードを作成する。 <?php /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the \"web\" middleware group. Now create something great! | */ Route::get(\'/user/{user?}\',\'UserController@show\'); Route::post(\'/user/\',\'UserController@add\')->name(\'postAddUser\'); Route::post(\'/user/{user}\',\'UserController@edit\')->name(\'postEditUser\'); コントローラ コントローラは以下。無条件に保存するだけなのでほとんど何も書いてない。 条件が増えてくるとそれなりに行数が増える。 ModelBindingの良さは、タイプヒンティングでEloquentのインスタンスを受けられること。 変数を受けてEloquentインスタンスを探す手間がバッサリ無い。 RequestValidatorは載せていません。 <?php namespace AppHttpControllers; use AppHttpRequestsAddUserRequest; use AppHttpRequestsEditUserRequest; use AppUser; class UserController extends Controller { public function show($id=null) { $user = User::firstOrNew([\'id\'=>$id]); return view(\'user\',compact(\'user\')); } public function add(User $user,AddUserRequest $request) { $user->fill($request->only([\'name\',\'email\',\'password\']))->save(); return view(\'user\',compact(\'user\')); } public function edit(User $user,EditUserRequest $request) { $user->fill($request->only([\'name\',\'email\',\'password\']))->save(); return view(\'user\',compact(\'user\')); } } Blade 肝心のBladeは以下。これだけなのに結構書かないといけない。 laravelcollective/htmlは大分前にLaravelから外れていて、使わない方が良いのかも。 自力でHTMLを書くのと大して労力が変わらない可能性がある。 結構書かないといけないから1枚にしたいのか、複数枚でよければあまり書かなくて良いのか、 微妙なところ。Laravel5.7なのでBootstrap4。validation用のクラスが全然違う。 親Blade(layouts.app)は何でも良いので載せていません。 @extends(\'layouts.app\') @section(\'content\') @if (isset($user->id))編集 @else 追加 @endif @if ($user->wasRecentlyCreated) {!! Form::model($user,[\'route\'=>[\'postEditUser\',$user->id],\'class\'=>\'form-horizontal\'])!!} @else {!! Form::model($user,[\'route\'=>[\'postAddUser\'],\'class\'=>\'form-horizontal\'])!!} @endif {!! Form::label(\'name\', \'名前 :\') !!} @if($errors->has(\'name\')) {!! Form::text(\'name\',$user->name,[\'class\'=>\'form-control is-invalid\']) !!} @else {!! Form::text(\'name\',$user->name,[\'class\'=>\'form-control\']) !!} @endif {!! $errors->first(\'name\') !!} {!! Form::label(\'email\', \'email :\') !!} @if($errors->has(\'email\')) {!! Form::email(\'email\',$user->email,[\'class\'=>\'form-control is-invalid\']) !!} @else {!! Form::email(\'email\',$user->email,[\'class\'=>\'form-control\']) !!} @endif {!! $errors->first(\'email\') !!} {!! Form::label(\'password\', \'password :\') !!} @if($errors->has(\'password\')) {!! Form::password(\'password\',[\'class\'=>\'form-control is-invalid\']) !!} @else {!! Form::password(\'password\',[\'class\'=>\'form-control\']) !!} @endif {!! $errors->first(\'password\') !!} @if($user->wasRecentlyCreated) {!! Form::submit(\'保存\',[\'class\'=>\'btn btn-primary form-control col-sm-2\']) !!} @else {!! Form::submit(\'新規作成\',[\'class\'=>\'btn btn-primary form-control col-sm-2\']) !!} @endif {!! Form::close() !!} @endsection まとめ relationもないし懸案の確認画面もないので、単純。 次回、has a、has many relation版と、確認画面付きの版を試します。

default eye-catch image.

上下左右に黒帯を出さずにYouTube動画(16:9)のIFRAMEのアスペクト比を変える

YouTubeの動画は16:9なので、16:9以外のアスペクト比にすると、 動画が内接するように上下左右に黒帯が出る。 アスペクト比を変えた矩形にクリッピングして、 それをレスポンシブにする例がなかったので自作してみた。 どういうことかというと、下のイラストみたいな感じ。 大きい方の矩形がYouTubePlayerのIFRAME。 内接する小さい方が作りたいアスペクト比の矩形。 明るい方だけを抜き出してレスポンシブにします。 (動画の余った部分は欠けます) コード達 16:9(56.25%)を53.88%に切り抜いてレスポンシブ化するHTML,CSS,JS。 Chrome,Safari(PC)とFirefoxで確認済み。 HTMLは以下。 <div class=\"youtube_wrapper\"> <iframe class=\"youtube_content\" id=\"youtube_content\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media;\" src=\"https://www.youtube.com/embed/{video-id}\"></iframe> </div> CSSは以下。 .youtube_wrapper { position:relative; width:100%; overflow:hidden; margin:auto; margin-bottom:8px; } .youtube_wrapper:before { content:\"\"; display:block; padding-top: 53.88%; } .youtube_content { position:absolute; top:50%; left:50%; margin:auto; transform: translate(-50%,-50%); width:100%; } IFRAMEの最初の高さだけCSS化できなかった...。 最初の高さを設定するJSは以下。jQueryにするなんなりしてください。 window.onload = function(){ var elem = document.getElementById(\'youtube_content\'); if (elem) { elem.style.height = elem.clientWidth * 0.5625 + \'px\'; } };

default eye-catch image.

ミドルウエア

この記事は自分の勉強のために書いています。 ソースはLaravel5.7の公式ドキュメントです。 新しいことは何もないので通常はそちらを参照してください。 [clink implicit=\"false\" url=\"https://readouble.com/laravel/5.7/ja/middleware.html\" imgurl=\"http://laravel.jp/assets/img/header.jpg\" title=\"Laravel 5.7 ミドルウェア\" excerpt=\"ミドルウェア\"] [arst_toc tag=\"h4\"] アプリケーションに送られてきたリクエストを途中でフィルタするのがミドルウェア。 認証とかCORSとかCSRFとか、デフォルトでミドルウェアが用意されているけれども、 ミドルウェアを自作することができる。 ミドルウェアの定義 新しいミドルウェアの作成 ミドルウェアを作成するartisanコマンドは以下の通り。 こうすると、app/Http/Middlewareの中にCheckCostクラスが作られる。 $ php artisan make:middleware CheckCost; CheckCostクラスの中でhandle()メソッドを実装する。 $requestを$nextに流す際に$closureを評価するように動作し、 結果として$requestと$nextの間のフィルタ処理を$closureに書くことができる。 <?php namespace AppHttpMiddleware; use Closure; class CheckCost { /** * 送信されたきたリクエストをフィルタする * * @param IlluminateHttpRequest $request * @param Closure $next * @return mixed */ public function handle($request, $closure $next) { if ($request->value < 100) { return redirect('hoge'); } return $next($request); } } ミドルウェアを複数使用するとき、 前の段のミドルウェアを通過した後、次の段のミドルウェアを通過する。 Before Middleware/ After Middleware 公式だと仰々しい名前が付いているのだけれども、 $nextを呼んだ後に処理をするか、呼ぶ前に処理をするか、の違いを表現できる。 つまり、リクエストを完了する前の処理なのか、後の処理なのか。 <?php namespace AppHttpMiddleware; use Closure; class CheckCost { /** * Before Middleware * リクエストを評価する前に処理する * * @param IlluminateHttpRequest $request * @param Closure $next * @return mixed */ public function handle($request, $closure $next) { //アクションを実行 return $next($request); } } namespace AppHttpMiddleware; use Closure; class CheckCost { /** * After Middleware * リクエストを評価した後に処理する * * @param IlluminateHttpRequest $request * @param Closure $next * @return mixed */ public function handle($request, $closure $next) { $response = $next($request); return $response; } } 登録 グローバルミドルウェア 要は全てのHTTPリクエストについてミドルウェアを通すやり方。 App/Http/Kernel.phpに書く。Kernelクラスにある$middlewareという配列に グローバルミドルウェアにしたいやつを並べていく。 <?php namespace AppHttp; use IlluminateFoundationHttpKernel as HttpKernel; class Kernel extends HttpKernel { protected $middleware = [ ... ] } 特定のルートのみにミドルウェアを設定 特定のルートに限定してミドルウェアを設定する場合もApp/Http/Kernel.phpに書く。 まず、$routeMiddlewareにミドルウェアの短縮キーを書く <?php $routeMiddleware = [ \'auth\' => AppHttpMiddlewareAuthenticate::class, \'auth.basic\' => IlluminateAuthMiddlewareAuthenticateWithBasicAuth::class, ... ]; そして、routes.phpでそれぞれのルートに定義した短縮キーを指定する。 短縮キーではなく完全なクラス名を書いても良いみたい。 ミドルウェアを複数書くと、その順番に通すことになる。 Route::get(\'admin/profile\', function() { })->middleware(\'auth\'); Route::get2(\'admin/profile2\', function() { })->middleware(\'CheckCost::class\'); Route::get3(\'admin/profile3\', function() { })->middleware(\'auth\',\'guest\'); ミドルウェアグループ 複数のミドルウェアをグループ化して、そのグループのミドルウェアを一気に当てることができる。 App/Http/Kernel.phpに書く。Kernelクラスにある$middlewareGroupという配列に グループ名をキー、ミドルウェアの配列を値として書いていく。以下のように。 <?php $middlewareGroup = [ \'web\' => [ \'auth\' => AppHttpMiddlewareAuthenticate::class, \'auth.basic\' => IlluminateAuthMiddlewareAuthenticateWithBasicAuth::class, ... ], ... ]; routes.phpの中では、それぞれのルートに定義したグループ名を与える。 例えば、以下だと\'/\'に対して\'web\'ミドルウェアグループを与えている。 つまり\'/\'に\'web\'ミドルウェアグループに定義したミドルウェアが順番にあたる。 Route::get(\'/\',function(){ })->middleware(\'web\'); routes/web.php に書いたルートには自動的に\'web\'ミドルウェアグループがあたる。 ミドルウェアパラメタ ミドルウェアパラメタの書き方 ミドルウェア実行時に引数を与えることができる。 書き方は以下の通り。handle()をオーバーライドする際に、引数として受けるパラメタを追加する。 例えば、パラメタ$paramを受け取るミドルウェアは以下の通り。 クエリパラメタにある$valが$paramである場合に限り処理を書いている。 <?php namespace AppHttpMiddleware; use Closure; class CheckCost { /** * 送信されたきたリクエストをフィルタする * * @param IlluminateHttpRequest $request * @param Closure $next * @return mixed */ public function handle($request, $closure $next, $param) { if ($request->val == $param) { return redirect(\'hoge\'); } return $next($request); } } ミドルウェア指定時に以下のようにパラメタを渡す。 T.B.D. 終了処理ミドルウェア T.B.D. サンプル 実行時間を計測するミドルウェアの試作 こちらを参考に試しに作ってみた。 https://qiita.com/niisan-tokyo/items/663300f8df1c6c89f0ae $ php artisan make:middleware TimerMiddleware Middleware created successfully. TimerMiddleware本体はこちら。 <?php namespace AppHttpMiddleware; use Closure; class TimerMiddleware { /** * Handle an incoming request. * * @param IlluminateHttpRequest $request * @param Closure $next * @return mixed */ public function handle($request, Closure $next) { $before = microtime(true); $res = $next($request); Log::debug(mictotime(true) - $before); return $res; } } ミドルウェアの登録は、app/Http/Kernel.phpの$routeMiddlewareに追加。 protected $routeMiddleware = [ .. AppHttpMiddlewareTimerMiddleware::class, ]; ルートの定義はこちら。 Route::get(\'timer\',function(){ })->middleware(\'timer\'); /timer にアクセスすると、storages/logs/laravel.logにログが残る。 ... [2019-01-29 16:05:07] local.DEBUG: 0.019489049911499

default eye-catch image.

ルーティング

この記事は自分の勉強のために書いています。 ソースはLaravel5.7の公式ドキュメントです。 新しいことは何もないので通常はそちらを参照してください。 [clink implicit=\"false\" url=\"https://readouble.com/laravel/5.7/ja/routing.html\" imgurl=\"http://laravel.jp/assets/img/header.jpg\" title=\"Laravel 5.7 ルーティング\" excerpt=\"基本的なルーティング\"] [arst_toc tag=\"h4\"] 基本的なルーティング routes.phpの書き方 HTTP動詞に対応させたroutes.phpの書き方。RESTfulAPIを書く場合は必要なんだと思う。 anyを使うと全ての動詞に対応する。matchを使うと対応する動詞を選べる。 No 動詞 文法 1 GET Route::get($url,$callback); 2 POST Route::post($url,$callback); 3 PUT Route::put($url,$callback); 4 PATCH Route::patch($url,$callback); 5 DELETE Route::delete($url,$callback); 6 OPTIONS Route::options($url,$callback); 7 全て Route::any($url,$callback); 8 選んだ動詞 Route::match($array_of_verb,$url,$callback); リダイレクトルート 単純にリダイレクトをかける場合は、わざわざコントローラを通さなくて良い。 (コントローラを通してた...)。 通常302で、301もルーティングだけで対応可能。 No HTTP Status Code 文法 1 302 Route::redirect(\'here\',\'there\'); or Route::redirect(\'here\',\'there\',302); 2 302 Route::redirect(\'here\',\'there\',301); or Route::permanentRedirect(\'hear\',\'there\'); Viewルート ルートから直接ビューを返す場合はコントローラを介さなくてもよい。 No文法 1Route::view($url,$view_name); 2Route::view($url,$view_name,$array_of_parameters); ex, Route::view($url,$view,[\'param1\'=>100,\'param2\'=>200]); CSRF保護 webルートに定義したPOST,PUT,DELETEへのルートへ送信するフォームは 一緒にCSRFトークンを送る必要がある(CSRFは別にまとめる予定)。 ルートパラメータ URLの動作に必要なパラメタをクエリ文字列ではなくURLに含める書き方。 必須パラメータとしておけば省略は不可となり存在チェックを省略できる。 必須パラメータ Curly bracketsでパラメタ名を囲んで定義する。 受けるクロージャはそのパラメタ名をもつ引数を使用する。 Route::get(\'application/{application_id}\', function($application_id) { return $application_id; }); 任意パラメータ Curly brackets内のパラメタ名を\"?\"で終わらすと任意パラメータになる。 受けるクロージャでは必ずデフォルト引数を設定する。 Route::get(\'user/{name?}\',function($name = null) { return $name; }); 正規表現制約 ルートパラメタのフォーマットを正規表現で制約できる(そんなことできるんだ...)。 書き方は以下の通り。URLとクロージャは触らない。 Route::get(\'user/{name}\', function($name) { return $name; })->where(\'name\',\'[A-Za-z]+\'); グローバル制約 RouteServiceProviderのbootメソッドに全てのルートパラメタを制約するルールを書ける。 例えば以下のように書いておくと、全てのルートの\"id\"という名前のクエリパラメタが数値に制約される。 /** * */ public function boot() { Route::pattern(\'id\',\'[0-9]+\'); parent::boot(); } /** * routes.php */ Route::get(\'user/{id}\',function($id) { return $id; // $idは数値に制約 }); 名前付きルート 定義したルートに名前を付けておくことで、そのルートのURLを名前から引くことができる。 名前付きルート 例えばuser/{id}に対するルートにmypageという名前を付けるには以下。 Route::get(\'user/{id}\',function($id) { return $id; })->name(\'mypage\'); 名前付きルートへのURL取得 このルートへのURLはroute()により取得できる。 ルートが必須/任意パラメータを必要とする場合は第2引数で値を与える。 redirect()->route(\'mypage\',[\'id\'=>100]); ルートグループ 複数のルートで共通の属性(=ミドルウェアと名前空間)をまとめて書ける。 グループはネストすることができる。ミドルウェアと名前空間は別途記載。 ミドルウェアによるグループ化 複数のルートで共通のミドルウェアを外出しする書き方は以下。 (もちろん個別に書くこともできる) Route::middleware([\'first\',\'second\'])->group(function() { Route::get(\'/\',function(){ // firstミドルウェア、secondミドルウェアを順番に使用 }); Route::get(\'/hoge/{id}\',function($id){ // firstミドルウェア、secondミドルウェアを順番に使用 }); }); 名前空間によるグループ化 複数のルートで同じPHP名前空間を使用する書き方は以下。 こうすることで、各コントローラのファイルの先頭に名前空間を書かなくてもよくなる。 Route::namespace(\'Namespace1\')->group(function() { // AppHttpControllersNamespace1 名前空間下のコントローラ }); })/ <?php namespace App/Http/Controllers/Namespace1; // <- これが不要になる サブドメインによるグループ化 複数のルートが同じサブドメイン配下にあるような配置にする書き方は以下。(こんなことできるのか!)。 Route::domain(\'{subdomainname}.myapp.test\')->group(function() { Route::get(\'user/{id}\',function($id) { // subdomainname.myapp.test/user/100 }); }); ルートプレフィックスによるグループ化 複数のルートが同じ文字列からスタートするように配置する書き方は以下。 Route::prefix(\'sameprefix\')->group(function() { Route::get(\'user/{id}\',function($id) { // /sameprefix/user/100 }); Route::get(\'group/{id}\',function($id) { // /sameprefix/group/200 }); }); ルート名プレフィックスによるグループ化 複数のルートのルート名が同じ文字列からスタートするように配置する書き方は以下。 Route::name(\'sameprefix\')->group(function() { Route::get(\'user/{id}\',function($id) { // \'sameprefix.user\' というルート名 })->name(\'user\'); Route::get(\'group/{id}\',function($id) { // \'sameprefix.group\' というルート名 }); }); モデル結合ルート ルートで与えた情報(多くの場合でモデルID)をコントローラに渡して コントローラの中でモデルインスタンスを引くという使い方がほとんどなので、 ルートに含めたモデルIDからモデルインスタンスを解決してコントローラでそれを使うことができる。 当然、クロージャを直接書いても同じ。 暗黙のモデル結合ルート どうやってルートパラメータとモデルインスタンスを紐づけるかというと、 なんと、PHPのタイプヒントを使って紐づける。 以下、$userがAppUserクラスであるとタイプヒントされている。 ルートパラメータにuserがあるから、クロージャの中ではAppUserのインスタンスを $userを使って引いたオブジェクトを使用できる。 わざわざ$userを使ってAppUser::find($user)と書いたりエラーケースを書くのを省略できる。 AppUser::find($user)が見つからなかったらルートは404を返す。すごいー。 Route::get(\'users/{user}\',function(AppUser $user) { // AppUser $user を使用できる。 }); キー名がカスタマイズされたモデル結合ルート モデルの主キーがidではないときimplicitにidからModelを引くことが出来ない。 モデル側でgetRouteKeyName()をオーバーライドして主キーを返せば良い。 ルートパラメータを新しい主キーであるものとしてインスタンスを探してくれる。 Route::get(\'users/{user}\',function(AppUser $user) { // AppUser.userid が $user であるインスタンスを使用できる。 }); /** * モデルの中 * */ public function getRouteKeyName() { return \'userid\'; } 明示的なモデル結合ルート ルート名とタイプヒントを使ってimplicitにモデル結合する方法とは別に、 RouteServiceProviderクラスのboot()メソッドの中でexplicitにモデル結合を定義できる。 タイプヒントがなくても良いというのならexplicitに書く意味もあると思うのだけど、 タイプヒントが必要なのであれば、implicitな記法に対して冗長な気がするんだけども...。 /** * RouteServiceProvider * */ public function boot() { parent::boot(); Route::model(\'user\',AppUser::class); } /** * routes.php * */ Route::get(\'user/{user}\',function(AppUser $user) { }); 依存解決ロジックのカスタマイズ 「ルートパラメータをモデルID、タイプヒントをモデルクラスとして、モデルIDを主キー(または別のキー) を使って検索する」という単純なロジックとは異なるロジックを定義する方法が用意されている。 例えばモデルを主キーとは関係ないフィールドで検索したいときは以下みたいに書く。 /** * RouteServiceProvider * */ public function boot() { parent::boot(); Route::bind(\'user\', function($value) { return AppUser::where(\'name\',$value)->first() ?? abort(404); }); } モデル側でresoluveRoutingBind()をオーバーライドすることでも実現可能。 /** * モデル * */ public function resolveRoutingBind($value) { return $this->where(\'name\',$value)->first() ?? abort(404); } その他 フォールバックルート フォールバックルートを書いておくと、routes.phpに書いたどのルートにも当たらなかったときに使われる。 通常404になるところを受ける。書く場合は最後に配置しないといけない。 Route::fallback(function(){ // do something when 404. });

default eye-catch image.

とりあえずチャットアプリを作ってみる

Kotlin製のChat UI Libraryを写経しながらAndroidアプリを作る練習。 もちろんサーバ側は無いので、文字列決め打ちで出してるだけ。 昔作ったJava製のものと基本的なことは変わっていない様子。 https://github.com/bassaer/ChatMessageView.git null安全性の絶対的な安心感。これに尽きる。。 自分のような素人が書いてもぬるぽで落ちないというのは奇跡!。 Swiftとそっくりという噂なので、 もしかするとKotlinからSwiftに移植するのは簡単なのかも。

default eye-catch image.

一覧から詳細の起動_一覧側(MainActivity)

一覧から詳細を起動する一覧側(MainActivity.kt)の実装。 ListViewのsetOnItemClickListener()の書き方。 setOnItemClickListener()の引数はAdapterView.OnItemClickListenerオブジェクトを取る。 OnItemClickListenerオブジェクトは1つの仮想関数(Single Abstract Method)を持っていて、 それを実装したものを渡すことになる。 本来、OnItemClicListenerを派生したオブジェクトでSAMを実装したものを渡す、という とても長い記述になるのだけれども、仮想関数が1個だけならば代わりにラムダ式を1個渡せば良い。 (SAM変換)。 ActivityからIntentを取得してIntentからActivityを起動する、という書き方。 Intentを取得するときにパラメタ(今回の場合はArticleインスタンス)が渡る。 package com.example.ikuty.myapplication import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.widget.ListView import com.example.ikuty.myapplication.model.Article class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val listAdapter = ArticleListAdapter(applicationContext) val dummyArticle1 = dummyArticle(\"ikuty.com記事1\",\"ikuty1\") val dummyArticle2 = dummyArticle(\"ikuty.com記事2\",\"ikuty2\") listAdapter.articles = listOf(dummyArticle1,dummyArticle2) val listView: ListView = findViewById(R.id.list_view) as ListView listView.adapter = listAdapter listView.setOnItemClickListener { adapterView,view, position, id -> val article = listAdapter.articles[position] ArticleActivity.intent(this, article).let { startActivity(it) } } } }

default eye-catch image.

一覧から詳細の起動 – ParcelableなインスタンスをIntentに付与してActivityを起動する

記事一覧画面でアイテムを触るとそのアイテムの詳細画面が開くというのをやりたい。 記事詳細画面のController(Activity)を書く。 Intent まずIntent。Activityを起動する際にパラメタの役割を果たす。 Intentには「意図」「目的」の意味がある。Activityを起動する意図とか目的とか。 ベースとなるIntentに付加的な情報を付与(putExtra)することで「意図」「目的」となる。 Intentには基本型の他にカスタムオブジェクトを付与することができる。 ただし、カスタムオブジェクトはParcelableインターフェースを実装している必要がある。 あるActivityから別のActivityを起動する際のテクニックとして、 起動される側のActivityが持つintent()メソッドを使ってIntentを作成し(つまりパラメタを渡し)、 作成したIntent経由でActivityを起動する。 companionオブジェクトにfun intent(context: Context, article: Article): Intentを作る。 このintent()を経由すれば起動に必要なパラメタ(今回はarticle: Article)を付与したIntentを取得できる。 intent()内ではputExtra()を使ってarticleとIntentを紐づけている。 onCreate(savedInstanceState: Bundle?) onCreate()が発火したときにはIntentにParcalableなArticleインスタンスが付与済み。 Activityが持つintentからgetParcelableExtra()によりArticleインスタンスを取得して、 ビューにそのArticleインスタンスを表示する。 package com.example.ikuty.myapplication import android.content.Context import android.content.Intent import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.webkit.WebView import com.example.ikuty.myapplication.model.Article import com.example.ikuty.myapplication.view.ArticleView class ArticleActivity: AppCompatActivity() { companion object { private const val ARTICLE_EXTRA: String = \"article\" fun intent(context: Context,article: Article): Intent = Intent(context, ArticleActivity::class.java) .putExtra(ARTICLE_EXTRA, article) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_article) val articleView = findViewById(R.id.article_view) as ArticleView val webView = findViewById(R.id.web_view) as WebView val article: Article = intent.getParcelableExtra(ARTICLE_EXTRA) articleView.setArticle(article) webView.loadUrl(article.url) } }