shigechi-64's diary

自由・自主・自立・自尊

Laravel Collectiveのlink_to系から生成されるリンク文字列にBootstrapのglyphiconを挿入できるかどうかで悩んだ

結論

たぶんできないっぽいです。aタグはviewに直接書いてしまい、URL部分にrouteを使う方法を採りましたが他にやり方はあるかもしれません。

そもそもLaravel Collectiveって何

本題に入る前に気になったので。Laravelで使用できる外部ライブラリってのは判るのですが、何を目的に存在しているのか気になったので公式をあたってみました。

laravelcollective.com

「私たちはLaravelフレームワークのコアから削除されてしまったコンポーネント群をメンテナンスしています。あなたの愛する、素晴らしいLaravelの機能を使い続けることができるのです」

なるほど、そういう目的だったのですね。単にHTMLの生成をヘルプしてくれるライブラリだと思ってました。Laravel Collectiveの一面としてそういう機能も持っている、ということですね。

何がしたいか?

下記のようにテキストとBootstrapのglyphiconを含んだリンクを生成したい。

<a href="https://foo/user/id"><span class="glyphicon glyphicon-home"></span> マイページ</a>

見た目はこんな感じです。
f:id:shigechi-64:20180301114417p:plain

link_to系を使うとspanタグを入れられない

link_to系はいくつか存在します。htmlリンクを生成するという点では同じだけど、生成の仕方がいくつかの方法から選べる、ということのようです。状況に応じて便利なほうを使ってね、ということでしょうか。

https://laravelcollective.com/docs/5.0/html#generating-urls

で、共通してリンクの文字列は第2引数で指定するんですが、ここにspanタグを入れてもエスケープされてしまうのでうまくglyphiconが表示されません。いろいろ試したけどだめだった。もし方法があれば誰か教えてください。

routeを使う

しょうがないので下記のように書きました。

<li><a href="{{ route('users.show', ['id' => Auth::user()->id]) }}"><span class="glyphicon glyphicon-home"></span> マイページ</a></li>

aタグは直接書いてしまいます。URL部分にrouteを使うだけ。

リンク文字列の前にglyphicon入れたいっていう場面はけっこうあると思うんですが、そういう場合link_to系は使えないってことなんですかね?わかりません。とりあえず↑のやり方でやってみました。

参考

Laravel Collectiveでのhtml生成については以下の記事でよくまとめられています。

blog.motikan2010.com

paizaのスキルチェックで使える標準入力取得用コード(PHP)

入力が1行の場合と複数行の場合があるのでそれぞれ掲載してみる。

paiza_get_oneline.php

<?php

//1行入力
$single_line_input = trim(fgets(STDIN));
//不要な改行コードを取り除く
$single_line_input = str_replace(array("\r\n","\r","\n"), '', $single_line_input);
//スペースで分解して配列に格納
$array = explode(" ", $single_line_input);

取得したい値がひとつだけで、配列に格納する必要がない場合もあるのでそのあたりは臨機応変に。

paiza_get_multiline.php

<?php

//1行入力、スペースで分解して配列に格納
$first_line_input = trim(fgets(STDIN));
$first_line_input = str_replace(array("\r\n","\r","\n"), '', $first_line_input);
$array_first_line = explode(" ", $first_line_input);

//続く入力を格納する配列
$array_below_lines = [];

// 指定の回数だけループ
for ( $i = 0; $i < $array_first_line[0]; $i++) {
    $below_line = trim(fgets(STDIN));
    $below_line = str_replace(array("\r\n","\r","\n"), '', $below_line);
    $below_line = explode(" ", $below_line);
    array_push($array_below_lines, $below_line);
}

1行目で取得した入力(配列)の[0]番目分だけループを回してそれ以下の入力を取得するパターン。
これも問題によって微妙に変える必要はありますが、入力されるデータの個数を指定する箇所が必ずあるのでその分だけループを回せばOKかと思います。

だいたいこの2パターンのどちらか、もしくは組み合わせでいけると思います。

瞬間英作文用のWebサービスを作ってみた

瞬間英作文という英語のトレーニングがあって、一時期けっこうやってました。

公式サイトにも説明がありますが、簡単な英文を使って、日本語から英語に瞬時に変換する「英語回路」を作る、というのが目的のトレーニングです。(瞬時に変換する、というところがポイントで、そのために簡単な英文を使うのが特徴です)

英語上達完全マップ●瞬間英作文

教材も公式のものが出版されているほか、探せば適した教材がいろいろ出てくるのではないでしょうか。

どんどん話すための瞬間英作文トレーニング (CD BOOK)

どんどん話すための瞬間英作文トレーニング (CD BOOK)

私も↑の教材を使ってやっていたんですが、英文自体は単純なのでそのうち覚えてしまい飽きてきます。また、中学校の英語の教科書に出てくるような単純だけど普段の生活ではあんまり使わないよねみたいなフレーズも多くて、トレーニングとしては効果があるのは実感しながらも、もっと実用的なフレーズで練習できたらなぁと思っていました。

また普段映画やテレビなどを見ていると、あっこのフレーズいいな、みたいなことがけっこうあるのでそれを記録できて、教材として使えたら便利じゃね?と思って覚えたてのLaravelで作ってみました。

My 瞬間英作文

機能としてはごく単純で、気に入ったフレーズを登録しておき、日本語→英語の順でランダムに表示させることができます。
まだ実装していませんが、他の人が登録したフレーズをいいねして自分の再生リストに加える機能なども今後作っていきたいと思ってます。

LaravelアプリケーションをHerokuにデプロイ後、謎のエラーに苦しんだ話

結論

Laravelで作ったアプリケーションは/vendor以下がデフォルトで.gitignoreに含まれている為、gitリポジトリに変更が反映されない。

従って、/vendor下のファイルに修正を加えた場合、シンプルにHerokuにデプロイしただけではHerokuアプリにはその変更は反映されていない。

Heroku側で直接そのファイルを修正する方法を模索したがうまくいかなかった為、結局/vendor以下を.gitignoreから外してリポジトリに含めるようにしたところ、エラーが解消されたという話。

そもそもの問題

ログインページだけを作ってとりあえずHerokuにデプロイして動作確認をしようとしたら、

The email field is required.

と言われる。これはログイン時にメールアドレスが空のままログインボタンを押した場合に出力されるメッセージで、Laravelにデフォルトで実装されているものである。どうもpostLogin時のバリデーションで引っかかっているみたいだけど開発環境では問題なし。むむむ。

DBを開発環境と合わせてみた

特に何の根拠もなく開発環境と本番でDBが違うのがだめなのか?と思ってHeroku側で使うDBをPostgreSQLからMySQLに変更してみた(Herokuアプリの作成からやり直した)。

本当に何の根拠もなかったけど違うとこってそこしかないやろーとこのときは思っていました。この過程で「開発・本番等価」という考え方を知る。エラーが解消するかどうかは別にして、開発と本番でDBを同じにしてバチはあたるまい。

sizukutamago.hatenablog.com

まあ結果エラー解消しなかったんですが。がびーん。

postLogin時の流れを深掘り

最初からこれをやれば良かったんですが、ログイン処理中のどこでこのエラーが吐かれるのかを追跡してみる。まずはルーティングから。

routes.php

// ログイン認証
Route::get('login', 'Auth\AuthController@getLogin')->name('login.get');
Route::post('login', 'Auth\AuthController@postLogin')->name('login.post');
Route::get('logout', 'Auth\AuthController@getLogout')->name('logout.get');

AuthControllerのpostLoginアクションに飛びます。でもpostLoginアクションの実体はAuthControllerにはなく、
/vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php
に含まれているようです。

AuthenticatesUsers.php

・
・
public function postLogin(Request $request)
{
        $this->validate($request, [
            $this->loginUsername() => 'required', 'password' => 'required',
        ]);
・
・

うーんここは怪しい雰囲気です。passwordがrequiredなのはいい。loginUsername()っていうのが超あやしい。

なぜかというと今回、Laravelの標準で用意されているusersテーブルのマイグレーションファイルをそのまま使ってはいないからです。具体的には、標準だと「email」になっているカラムのカラム名を「member_id」に変更しているのです。

なんかその辺が原因なんじゃないかなーと思いつつ同じファイル内にあるloginUsername()を見てみる。

・
・
public function loginUsername()
{
        return property_exists($this, 'username') ? $this->username : 'email';
}
・
・

んーこれっぽいです。この関数はその名の通りユーザ名を返すのだろうけど、最初に設定されているのが「email」なのだな。でもそんなカラム名はないので前掲のエラーとなって現れるということのような気がする。

ということはここを「member_id」に変えてやればうまくいきそう。でもこれ開発時に既出の問題で、ちゃんと直したはずなんだけど・・・

/vendor以下は無視される

ローカルリポジトリではエラーが出ないので、リモートではどうなっているのかと思いGithubでソースを確認しようとしたところ・・・/vendorディレクトリが存在しない!

どうやらLaravelではデフォルトで/vendorディレクトリが.gitignoreに含まれており、リポジトリには含まれないようになっているみたい。よくわからないのですが環境ごとに用意してねってことのようです。

https://qiita.com/zaburo/items/bc448a9fbf2d35194302qiita.com

Heroku側で直接ファイル編集したらよくね?

リポジトリには含まれていないけど、Heroku側にも必ずこのファイルはあるはずなので、じゃあそれを直接修正したらよさそうねってことでheroku run bashして/vendor/laravel/framework/src/Illuminate/Foundation/Authに移動。

それからAuthenticatesUsers.phpvimかなんかで編集っと・・・あれれvimがない?てかどうやって編集したらいいの?nano?わかりません。いろいろ調べてみたけどよくわからず、同じように悩んでいる人はStackOverflowで見つけた。

stackoverflow.com

なんかheroku-vimっていうプラグインを入れればvimが使えるようになるっぽいけどどうもうまく入れられない。↑でもそういう人いますね。んーどうしようと思ってたら同じ記事に以下の回答が・・・

gyazo.com

なんかすごい回りくどいけど確かにこの方法でもできそう。よし、やってみよう!てことでGistにAuthenticatesUsers.phpを登録して該当箇所を修正、Heroku側からwgetでファイルを取得。できた!なんかもっと簡単な方法絶対あると思うけどとりあえずこれでいってみよう!と満足してこの日は作業終了しました。

編集がリセットされる??

よーし、これで大丈夫なはずだぞーと思って再度動作確認。・・・あれ、直ってないけど・・・なんで?

もう一度/vendor/laravel/framework/src/Illuminate/Foundation/Authに移動してファイルのタイムスタンプを確認してみると、修正していないファイルと同じタイムスタンプになっている。なんでやー!

調べてみるとどうもHerokuではgitを経由しないファイルの書き換えはリセットされてしまうらしい。どこで見た情報かはちょっと忘れました。

/vendor以下もリポジトリに含めてしまう

なんかお作法的にはだめっぽいけどもういいやと思って/vendor以下もリポジトリに含めることにしました。結果、無事修正が反映されてエラーは出なくなりました。

デフォルトのUsersマイグレーションをいじらなければ/vendor以下を修正する必要もなかったかもと思ったけど、これからたぶんいじらないといけない場面も出てくると思うんだよなー。どうするのが正しかったんだろう・・・よくわかりませんがとりあえず今の自分にできるのはこれが精一杯でした。

あまり時間もかけていられないのでとりあえずこの方法で行きたいと思います。こういう純粋なプログラミング以外のことではまるのってけっこうストレスですけどひとつひとつ解決していくしかないですね。

Laravel5.1の認可機能でユーザの権限管理をしたい

Webアプリケーションにおいてユーザの権限管理をしたい場面というのはよくあると思います。管理者だけに見せたいページや機能とかですね。Laravelでどうやるのかなーと思っていろいろ調べていました。

公式を読むと、組み込みで用意されている「認証」というのがまずあって(最初から用意されているAuthControllerとかを使うやつ)、それとは別に「認可」ロジックをとりまとめる手段が用意されているのだそう。ふむ。

認可 5.1 Laravel

で、こちら↓できれいにまとまっていたのでそのままでうまくいくかと思いきや、5.3と5.1では認可機能の仕様がけっこう違うみたいだったので公式を参考に試してみました。

参考にしたサイト:
Laravel5 でURLベースの権限管理をする方法 | Crane & to.

認可 5.3 Laravel

どうもアビリティというのを定義して、コントローラから参照するという形らしい。大規模アプリ向けにポリシーという方法もあるみたいだけど、よくわからないのでそちらは今回スルーします。

アビリティを定義してみる

アビリティは

app/Providers/AuthServiceProvider.php

で定義します。

public function boot( GateContract $gate )
{
        $this->registerPolicies( $gate );

        //'admin'アビリティを定義
        //levelが60以上のユーザを管理者として認可する
        $gate->define( 'admin', function ( $user ) {
                return $user->level >= 60;
        });
}

↑のように定義することでログイン中のユーザのlevelカラムが60以上かどうか(管理者かどうか)を判定します。(levelカラムがユーザレベルを表すという設計にしているという前提です)

コントローラ側の実装

今回はユーザ登録機能を管理者のみに許可したいという前提。routes.phpには下記のようにルーティングを設定しています。

// ユーザ登録
Route::get('signup', 'Auth\AuthController@getRegister')->name('signup.get');
Route::post('signup', 'Auth\AuthController@postRegister')->name('signup.post');

このsignup.getというルーティングに管理者以外はアクセスできないようにしたいので、先程定義したadminアビリティをAuthControllerのgetRegisterアクションにて使用します。実際にはgetRegisterアクションはトレイトなので、コードの実体は/vendor/laravel/framework/src/Illuminate/Foundation/Auth/RegistersUsers.phpにあります。

RegistersUsers.php

use Gate;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
.
.
.
public function getRegister()
{
        if(Gate::denies('admin')){
                abort(403);
        }
        return view('auth.register');
}

上記実装でテストしてみたところ、管理者以外のユーザで403エラーを捕捉できたので一応目的は達成したはずです。(エラー画面はちゃんと作り込むとして)

これであってるのかどうかもちょっとわからないのですが、一応想定の動きをするのでたぶんあってるということにしておきたいと思います。
違うよ、って人がいたら教えて頂けると助かりますm(__)m

放送大学の授業はおもしろいと思う

この春から大学生になり、手始めに英語の科目を履修してみましたがなかなかおもしろいです。

英語の文法というよりは、なぜそういう考え方になるのかという言語の成り立ちの背景的な部分の説明がたまに入るのでそれが興味深いです。
「空間と前置詞」という授業でふむふむと思ったところなどをメモ的に残しておきます。

・英語は空間をコンパクトに把握する言語である

・日本語は助詞(いわゆる「てにをは」)の種類が非常に多い。助詞を使うとその語の役割が明確になるため、語順はそれほど問題にならない。英語にはそれにあたる言葉がないため、語順が重要になる(語順が正しくないと語の意味が明確にならない)

英語は最初にWho did what?に答えてしまい、あとからwhere, when, how, whyを付け加えていく。英語は次にどういう要素が来るのかが予め決まっている。(語順が非常に重要)

Tips.

道が下がっていなくても遠ざかっていく方向をdownで、逆に向かってくる方向をupで示す

walk down the street - 通りを歩いて行く
She walked up to me. - 彼女が私に近づいてきた

乗り物に乗るときのinとonについて。
大きめの公共交通機関の場合、onが普通(船、バス、電車、飛行機など)
タクシーや車などの小さめの私的な乗り物の場合はin(onを使うと車などによじ登っている感じになってしまう)
「電車に乗り込む」という動作を強調したい場合はget in the trainという使い方をすることも稀にある
自転車やバイクなど中に入れないものは厳密にonを使う必要がある

大学生になった

この春から放送大学に入学し、大学生になりました。

理由は学位が取りたいからです。

放送大学教養学部という学部のみなのですが、教養学部というだけあって分野が幅広く、いろいろと面白そうな科目があります。

しかしながら、できるだけ早く単位を取得して卒業することが目的なので得意な情報系と英語系を中心に単位修得を目指していこうと思っています。

ここにもノートとして受講の記録を残していきたいと思います。
(あまり時間をかけてもアレなので簡単に)

追記:
↑と思ったんですが、なかなか時間がとれず難しいですね。今は授業を受けて課題を提出するだけで精いっぱいになってしまっています。まあ単位がとれればそれでいいと言えばいいのですが・・・

www.ouj.ac.jp