shigechi-64's diary

自由・自主・自立・自尊

N予備校で学んでいる

最近、N予備校で学生時代の勉強をやり直しています。

N予備校では中学・高校で習うことがだいたい網羅されている印象ですが、コンピュータサイエンス系の授業もかなり充実しています。

もともとはそのコンピュータサイエンス系の講義をを受講しようと思っていたのですが、中学・高校の科目内容を眺めているうちにそれらをやり直したい気持ちがふつふつと湧いてきて、先にそちらから受講することにしました。

今は中学の授業を8割くらい終えたところです。(中学といってもなかなか侮れません。数学などは本質的な理解をしていなかったことも多いので、大人の理解力をもったうえでやり直すことには意義があると感じました)

中学を終えたら高校の内容に入っていきたいのですが、全教科やっているとかなりの分量になるので数学を中心に復習していこうと思っています。(中学は国・数・英の3科目のみだったので全て受講しました)

Web系エンジニア界隈では機械学習の盛り上がりに伴い、数学を学び直す必要性を感じている人が増えているように思いますが、高校レベルの数学の学び直しにはN予備校は最適なのではないかと思います。

ちなみに無料で受講できる範囲はごく一部なので、本格的に学習しようと思うと月額の課金が必要になります。が、月に1000円程度なのでそこまで負担にはならない金額ではあります。

時間ができたら世界史とか物理とかもやってみたいのですが、数学の学び直しが主目的なので他の科目に充てる時間がとれるかどうかは微妙かなと感じています。他にも学ぶべきことはたくさんあるので・・・(大学の授業も並行で受けないといけないし)

時間をうまくやりくりして優先順位を意識していくようにしたいと思います。

www.nnn.ed.nico

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を使う必要がある