20210122のPHPに関する記事は15件です。

PHPでログイン機能

前回の続きです。
PHPカレンダー:https://qiita.com/x49_n/items/64a04a85b72c6d581421

ログイン機能作成

前回作成したカレンダーにログイン機能を追加していきます。

ご参考に:https://www.youtube.com/watch?v=uCvPMe5wsNk

ログイン機能

・新規登録(ユーザー登録)
・ログイン+ログイン確認画面
・ログアウト+ログアウト確認画面

ER図

ER図.png

簡単ですが、ER図を作成しました。(本当はツールを使って作成したかった、、、)
ER図動画化したものについてはTwitterにて公開しています。
https://twitter.com/x49_n/status/1352600436616830976

次回は予定入力欄を作成します!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelで画像を表示する方法

PHP、Laravelの初学者です。

Laravelで画像を表示させる方法がわかったので、メモしておきたいと思います。

※ 使用しているLaravelのバージョンは6.xです。違うバージョンをご使用の方はご注意ください。
また、私はMacを使用しているので、Windowsをご使用の方もご注意ください。

Laravelで画像を表示させる方法

①「自分のプロジェクト/storage/app/public」の中に利用したい画像を置く。

 私は「/storage/app/public」にtop-page.jpgを入れました(以下の画像をご覧ください)。

image.png

② シンボリックリンクを張る

Laravel6.xの公式ドキュメントを見たところ、
「/storage/app/public」においた画像を使えるようにする(Webからのアクセスを許す)には、
「『public/storage』から『storage/app/public』へシンボリックリンクを張る」という作業が必要になるようです。

Laravel6.xの公式ドキュメント

シンボリックリンクって何だろう??

シンボリックリンクとは、オペレーティングシステム(OS)のファイルシステムの機能の一つで、特定のファイルやディレクトリを指し示す別のファイルを作成し、それを通じて本体を参照できるようにする仕組み。

こちらから引用

んー。まだ難しい。
もう少し噛み砕いて説明された記事がありました。

シンボリックリンクとは

要は「代理人」のような役割を持つのが、このシンボリックリンクだそうです。
例を引用させていただくと

例えば、ファイルAと、シンボリックリンクA’があるとしましょう。
シンボリックリンクA’は、ファイルAのシンボリックリンクです。このシンボリックリンクA’を開くと、ファイルAが開きます。
ファイルAを直接開いたのと同じ結果です。

要は、代理人みたいなものですね。
シンボリックリンクは、自分がそのファイルやフォルダであるかのように振る舞います。

とのことです。

さて、前置きが長くなりましたが、このシンボリックリンクを張るには、以下のコマンドを実行しましょう。

ターミナル
php artisan storage:link

③シンボリックリンクを張ることができたら、ビューファイルに以下のように記述すれば画像を表示できます。

index.blade.php
<img src="/storage/top-page.jpg">

image.png

こんな感じで成功しました!

以上です。
何かありましたらコメントで教えていただけますと幸いです。

ここまで読んでいただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JSとLaravelでAPI通信する際に日付データを合わせる(unixtime <-> Date)

概要

サーバーさん => Sさん
フロントさん => Fさん

あるときこんなやりとりを目撃しました。

Sさん < 日付のフォーマットは20200222で送ってください
Fさん < ここは2020/02/22で送ってください
Sさん < この場合は2020-02-22で送ってください
続く....

そのとき第三者の私はこう思いました。

私 < Unixtimeでやりとりすれば、同じフォーマットでやりとりできるんじゃね?と...

やってみた。

javascript
// Date -> UnixTime
new Date().getTime() / 1000;
// 1611320223.828

// UnixTime -> Date
new Date(unixTime * 1000);
// Fri Jan 22 2021 21:57:44 GMT+0900 (日本標準時)

※バックエンドはLaravelを使用しています

php
// Date -> UnixTime
$date = new DateTime('now');
$date->format('U');
// "1611320663"

// UnixTime -> Date
$dateTime = new DateTime('@' . (int)$unixTime);
$dateTime->setTimeZone(new DateTimeZone('Asia/Tokyo'));
// 2021-01-22 22:04:23.0 Asia/Tokyo (+09:00)

感想

一旦これでUnixtimeでやりとりできそう!
もっと良いやり方などがありましたら教えてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

フレームワークとは

今回はフレームワークについて学習をしました。フレームワークとは何か、Ruby,PHP,JavaScript,CSSの代表的なフレームワークを公式サイトとURLと共に紹介します。
説明不足があると思いますので、気になったフレームワークを公式サイトで確認してみて下さい。

フレームワークとは

「フレームワーク」という言葉には、「枠組み」という意味があり、アプリケーション開発においては、テンプレートの役割を持っています。テンプレートの機能は、開発を行う際に頻繁に必要とされる「基礎的な機能」をまとめて提供してくれるものです。
例えば、ログイン認証機能、新規投稿機能、検索機能などアプリケーション開発においてほとんど共通して使われる機能がパッケージ化されています。
そのため、フレームワークを用いることで開発を高速化させることが出来ます。
どれくらい便利なものかというと、ログイン認証を仮に自分で1から作成したとします。その場合、作らなければならないのが、ユーザーネーム、パスワードの欄を作り、それを認証、弾く機能、更にそれらを管理する画面など様々なところを作っていかなければなりません。しかし、フレームワークを使うことで、これらのベースとして使える骨組み部分がパッケージ化されているので、あとは自分好みに改良するだけで済みます。このパッケージのことをフレームワークと呼びます。もっと簡単に言えばコンピュータ言語を使用して何か開発したいと考えたとき、その手助けをしてくれる便利ツールの1つと考えておけば良いでしょう。
私がよくお世話になっているものだとRubyならRuby on Rails、CSSならBootstrapなどがあります。

おすすめなフレームワーク

フレームワークには言語ごとにたくさんの種類があります。しかし、ここでは私が実際におすすめされた・よく聞くフレームワークを紹介していきます。

Rubyのオススメフレームワーク3種

- Ruby on Rails 公式サイト <https://rubyonrails.org/>
- Sinatra 公式サイト <http://sinatrarb.com/intro-ja.html>
- Padrino 公式サイト <http://padrinorb.com/>

Ruby on Rails
「Rubyのフレームワークは?」と聞けば、ほとんど返ってくる答えが「Ruby on Rails」と言われるほど有名です。正直に言ってしまえば、Rubyでアプリ、システム開発を行いたいなら、Ruby on Railsを選択すれば間違いないでしょう。私自身一番最初に手をるけたのがRuby on Railsでした。また、素早くかつ効率的に開発するために、2つの大原則を設定しているのが特徴です。
それらの原則について私が紹介した記事がありますので、読んでみてください。
「DRY原則・CoC原則とは」
https://qiita.com/Shirosan10/items/84fe66955398e91f7136
このように徹底的に規約に従って行動することで、Webアプリ開発の高速化を目指します。

Sinatra

Rubyで記述されているオープンソースのフレームワークになります。フレームワークの中では、最も少ないコードでWebアプリなどの開発が可能です。
そのため学習コストが少なくて済み、最も手軽にフレームワークを使用した開発を始めることができます。

Padrino

先述の、Sinatraをベースに設計されたフレームワークになります。Sinatraにはなかった機能群があり、かつRuby on Railsよりも少ないコードでWebアプリ開発ができるので、初心者だけど、少々複雑なものを開発したい人向けです。

PHPのオススメフレームワーク3種

- Laravel 公式サイト <https://laravel.com/>
- CakePHP 公式サイト <https://cakephp.org/jp>
- FuelPHP 公式サイト <http://fuelphp.jp/>

Laravel

数あるPHPのフレームワークで、Laravelは2011年に登場した最も若いフレームワークです。今現在注目度が高く、その理由は「Love beautiful code? We do too」という理念どおり、初心者であっても美しいコードを書ける点だと思います。コードが書きやすく、すぐに書き始めることができるので、PHPを習得した人が真っ先に使用できます。

CakePHP

PHPの中でも有名なフレームワークです。「ケーキを焼くように簡単に開発」を理念として開発されており、Ruby onRailsの影響も強く受けています。
特徴的なのは、MVC(モデル・ビュー・コントローラ)モデルと呼ばれる実装デザインに関する先駆けになったことです。
MVCモデルについて気になった方はこちらも読んでみてください。
拙いながらMVCについて図解を入れて解説しています。
「MVCモデルについて」
https://qiita.com/Shirosan10/items/d12416700f7e2329cb09

FuelPHP

FuelPHPもLaravelと同じように、比較的最近登場したフレームワークです。そのため、ほかのPHPフレームワークの良いところ吸収しているので、使いやすいフレームワークです。
特徴としては、動作が軽量でフレームワークの規約が少ないため、自由にコードを記述出来ることが挙げられます。

JavaScriptのオススメフレームワーク3種

- AngularJS 公式サイト <https://angularjs.org/>
- Vue.js 公式サイト <https://jp.vuejs.org/index.html>
- React 公式サイト <https://ja.reactjs.org/tutorial/tutorial.html>

AngularJS

AngularJSはJavaScriptでできたWebフレームワークです。見た目の動きをきれいに作るために使用されます。
例えば、SPA(Single-page application)と呼ばれる、「ページを移動しなくてもなめらかにページ内のコンテンツが現れるような1ページで完結するアプリケーション」の開発を行う時などに採用されます。

Vue.js

Vue.jsはAngularJSよりも後に開発されいます。そのため、他のフレームワークの良いところを組み込んだ設計になっています。
AngularJSよりも規約が少ないため、比較的自由にコードを記述出来、JavaScriptを学習したばかりの初心者におすすめされます。

React

有名なSNS「Facebook」が中心となって開発したフレームワークです。React Nativeと呼ばれる、全く同じソースコードでiOSとAndoroidアプリ両方を動作させることが可能なため、モバイルアプリ開発に適しています。

CSSのオススメフレームワーク

Bootstrap 公式サイト <https://getbootstrap.com/>

Bootstrapは「Webデザインフレームワーク」と呼ばれ、CSS/JavaScriptから構成されています。Twitter社より提供されるフレームワークになるため、Twitterのようなフラットなデザインを簡単に作成することができます。クラスを指定するだけで簡単にレスポンシブデザインに対応したページを作ることが出来ます。

今回の記事は以上になります。
色々と拙い部分があるかと思います。
また、自分が実際に使っておらず調べたり、教科書に書いて有りそうな説明なってしまったものもあり、説明が淡々としすぎてしまいました。今後、自分で使いたいなと思ってメモしていたので理解が深まったら更新して行こうと思います。
間違っている点、理解が不完全な点があればご指摘いただければ幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WP_Queryのクエリ参考

WordPress

WordPressのWP_Queryスニペットコードまとめ

wp_query.php
<?php
$args = array(
    // ↓ 特定の「著者」に関連付けられた投稿を表示する場合
    'author' => '1,2,3',                       // 著者IDを指定
    'author_name' => 'solecolor',              // user_nicenameを指定(名前ではありません)
    'author__in' => array( 2 , 6 ),            // 著者IDを配列で指定(著者IDを含む記事を絞り込む)
    'author__not_in' => array( 2 , 6 ),        // 著者IDを配列で指定(著者IDを含まない記事を絞り込む)

    // ↓ 特定の「カテゴリー」に関連付けられた投稿を表示する場合
    'cat' => 5,                                // カテゴリーIDを指定
    'category_name' => 'daily, news',          // カテゴリースラッグを指定(複数の場合は「,」で区切る)
    'category__and' => array( 2 , 6 ),         // カテゴリーIDを配列で指定(カテゴリーIDを含む記事を絞り込む)
    'category__in' => array( 2 , 6 ),          // カテゴリーIDを配列で指定(カテゴリーIDを含む記事を絞り込む)
    'category__not_in' => array( 2 , 6 ),      // カテゴリーIDを配列で指定(カテゴリーIDを含まない記事を絞り込む)

    // ↓ 特定の「タグ」に関連付けられた投稿を表示する場合
    'tag' => 'cooking',                        // タグスラッグを指定
    'tag_id' => 5,                             // タグIDを指定
    'tag__and' => array( 2 , 6 ),              // タグIDを配列で指定(タグIDを含む記事を絞り込む)
    'tag__in' => array( 2 , 6 ),               // タグIDを配列で指定(タグIDを含む記事を絞り込む)
    'tag__not_in' => array( 2 , 6 ),           // タグIDを配列で指定(タグIDを含まない記事を絞り込む)
    'tag_slug__and' => array( 'red', 'blue' ), // タグスラッグを配列で指定(タグスラッグを含む記事を絞り込む)
    'tag_slug__in' => array( 'red', 'blue' ),  // タグスラッグを配列で指定(タグスラッグを含む記事を絞り込む)

   // ↓ 特定の「タクソノミー」に関連付けられた投稿を表示する場合(以下は複数のタクソノミーにてAND検索)
    'tax_query' => array(                          // タクソノミーパラメーターを指定
        'relation' => 'AND',                       // タクソノミーの検索条件に 'AND' か 'OR' が使用可能
            array(
                'taxonomy' => 'color',             // タクソノミーを指定
                'field' => 'slug',                 // term_id(デフォルト),name,slug のいずれかのタームの種類を選択
                'terms' => array( 'red', 'blue' ), // ターム(文字列かIDを指定)
                'include_children' => true,        // 階層を持つタクソノミーの場合に、子孫タクソノミーを含めるかどうか
                'operator' => 'IN'                 // 演算子'IN','NOT IN','AND','EXISTS'(4.1.0以降),'NOT EXISTS'(4.1.0以降)が利用可能
            ),
            array(
                'taxonomy' => 'actor',
                'field' => 'id',
                'terms' => array( 103, 115, 206 ),
                'include_children' => false,
                'operator' => 'NOT IN'
            )
    ),

    // ↓ 特定の「投稿&固定ページ」に関連付けられた投稿を表示する場合
    'p' => 1,                                      // 投稿IDを指定
    'name' => 'hello-world',                       // 投稿スラッグを指定
    'page_id' => 1,                                // 固定ページのIDを指定
    'pagename' => 'sample-page',                   // ページスラッグを指定
    'pagename' => 'contact_us/canada',             // 子ページを表示する場合、スラッシュ区切りで親と子のスラッグを指定
    'post_parent' => 1,                            // ページIDを指定した子ページを表示
    'post_parent__in' => array( 1, 2, 3 ),         // 配列の親ページIDを含む投稿を表示
    'post_parent__not_in' => array( 1, 2, 3 ),     // 配列の親ページIDを含まない投稿を表示
    'post__in' => array( 1, 2, 3 ),                // 配列の投稿IDを含む投稿を表示
    'post__not_in' => array( 1, 2, 3 ),            // 配列の投稿IDを含まない投稿を表示

    // ↓ 特定の「パスワード」に関連付けられた投稿を表示する場合
    'has_password' => true,                        // パスワード付きの投稿を表示( true or false )
    'post_password' => 'zxcvbn',                   // 特定のパスワードが付いた投稿を表示

    // ↓ 特定の「タイプ」に関連付けられた投稿を表示する場合
    'post_type' => array( 
            'post',              // 投稿
            'page',              // 固定ページ
            'revision',          // リビジョン
            'attachment',        // 添付ファイル
            'custom-post-type'   // カスタム投稿タイプ
    ),
    'post_type' => 'any',        // すべてのタイプを含めて表示(リビジョンと'exclude_from_search'がtrueにセットされたものを除く)

    // ↓ 特定の「投稿ステータス」に関連付けられた投稿を表示する場合
    'post_status' => array(      // 投稿ステータスを指定 (デフォルト'publish')        
            'publish',           // 公開された投稿、または固定ページを表示
            'pending',           // レビュー待ちの投稿を表示
            'draft',             // 下書きの投稿を表示
            'auto-draft'         // コンテンツのない、新しく作成された投稿を表示
            'future',            // 予約公開設定された投稿を表示
            'private',           // ログインしていないユーザーには見えない投稿を表示
            'inherit',           // リビジョンを表示
            'trash',             // ゴミ箱に入った投稿を表示
     ),
    'post_status' => 'any',      // すべてのステータスを表示(投稿タイプで'exclude_from_search'がtrueにセットされたものを除く)

    // ↓ ページ送りパラメーターを設定する場合
    'posts_per_page' => 10,            // 1ページあたりに表示する投稿数を指定(-1を指定するとすべての投稿を表示)
    'posts_per_archive_page' => 10,    // 1ページあたりに表示する投稿数(アーカイブページのみ)
    'nopaging' => false,               // ページ送りを使用するか、すべての投稿を表示するか、(デフォルトはfalseでページ送りを使用)
    'paged' => 6,                      // ページ番号6の記事を表示
    'paged' => get_query_var('paged'), // 現在のページから投稿を表示
    'offset' => 3,                     // 設定した数だけ、ずらして表示(例では4番目の投稿から表示)
    'ignore_sticky_posts' => false,    // 先頭固定表示投稿を無視するかどうか(デフォルト値は0で先頭固定表示投稿を無視しない)

    // ↓ 「投稿の並び順」を指定する場合
    'order' => 'DESC',         // 'ASC' 昇順  (1, 2, 3; a, b, c)
                               // 'DESC' 降順 (3, 2, 1; c, b, a)

    'orderby' => 'date',       // デフォルト値'date' 複数のオプションを渡すことが可能
                               // 例:'orderby' => 'menu_order title'

                               // その他のオプション ↓
                               //'none'     並び替えなし
                               //'ID'       投稿IDで並び替え
                               //'author'   著者で並び替え
                               //'title'    タイトルで並び替え
                               //'name'     Order by post name(post slug)
                               //'modified' 更新日で並び替え
                               //'parent'   親ページIDで並び替え
                               //'rand'     ランダム順
                               //'comment_count'   コメント数で並び替え
                               //'menu_order'      ページの表示順で並び替え
                               //'meta_value'      アルファベット順で並び替え(数値ではうまくいかない)
                               //'meta_value_num'  数値で並び替え
                               //'post__in'        post__inで配列で指定された投稿IDの並び順を維持して表示

    // ↓ 特定の「時間や日付の期間」に関連付けられた投稿を表示する場合
    'year' => 2015,            // 4桁の年を数字で指定(2015など)
    'monthnum' => 4,           // 月を数字で指定( 1~12 )
    'w' =>  25,                // 年内の週を数字で指定( 0~53 )
    'day' => 17,               // 月内の日を数字で指定( 1~31 )
    'hour' => 13,              // 時間を数字で指定( 0~23 )
    'minute' => 19,            // 分を数字で指定( 0~60 )
    'second' => 30,            // 秒を数字で指定( 0~60 )
    'm' => 201404,             // 年と月を数字で指定 ( 201508など )

    // ↓ 「○年○月○日から○年○月○日の範囲の投稿情報」を表示する場合(投稿日の検索が自由自在!)
    'date_query' => array(
         array(
            'year' => 2015,                 // 4桁の年を数字で指定(2015など)
            'month' => 8                    // 月を数字で指定( 1~12 )
            'week' => 31                    // 年内の週を数字で指定( 0~53 )
            'day' => 5                      // 月内の日を数字で指定( 1~31 )
            'hour' => 2                     // 時間を数字で指定( 0~23 )
            'minute' => 3                   // 分を数字で指定( 0~60 )
            'second' => 36                  // 秒を数字で指定( 0~60 )
            'after' => 'January 1st, 2013', // 指定した日付以降の投稿を取得。strtotime()と互換性のある文字列で'after'=>'2015/08/31'などでもOK
            'before' => array(              // 指定した日付以前の投稿を取得。strtotime()と互換性のある文字列で'before'=>'2015/08/31'などでもOK
                  'year'  => 2013,          // 4桁の年を数字で指定(2015など) デフォルトは空
                  'month' => 2,             // 年内の月を数字で指定( 1~12 ) デフォルトは12
                  'day'   => 28,            // 月内の日を数字で指定( 1~31 ) デフォルトは月内の末日
             ),
            'inclusive' => true,            //「after」または「before」パラメーターで指定された値を含むかどうか
            'compare' => '=',               // 使用可能な値は '=', '!=', '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN', 'EXISTS' , and 'NOT EXISTS'
            'column' => 'post_date',        // 照会するカラムを指定。デフォルトは「post_date」
            'relation' => 'AND',            // OR または AND デフォルトは「AND」
        ),
    ),

    // ↓ 特定の「カスタムフィールド」に関連付けられた投稿を表示する場合
    'meta_key' => 'key',        // カスタムフィールドのキーを指定
    'meta_value' => 'value',    // カスタムフィールドの値を指定
    'meta_value_num' => 10,     // カスタムフィールドの値を指定
    'meta_compare' => '=',      //「meta_value」をテストする演算子。使える値は'!='、'>'、'>='、'<'、'=' デフォルト値は'='
    'meta_query' => array(      // カスタムフィールドパラメーター
         'relation' => 'AND',   // 「AND」または「OR」を指定。meta_query内の配列が「2つ以上」の場合に限る。meta_query配列が1つの場合は使用しない。
              array(
                   'key' => 'color',     // カスタムフィールドのキー。
                   'value' => 'blue',    // カスタムフィールドの値 (注意 compareの値が'IN'、'NOT IN'、'BETWEEN'、'NOT BETWEEN'のみ配列をサポート)
                   'type' => 'CHAR',     // カスタムフィールドタイプ。タイプについては以下の「meta_queryで使えるデータ型」参照
                   'compare' => '=',     // 演算子を指定 デフォルト値は'=' 演算子の種類については以下「meta_queryで指定できる演算子の種類」参照
              ),
              array(
                   'key' => 'price',
                   'value' => array( 1,200 ),
                   'compare' => 'NOT LIKE',
               )
    ),

    // ↓ 適切な権限を持っているユーザーのプライベートの記事を表示する場合
    'perm' => 'readable'     // 使える値は’readable’と’editable’

    // ↓ キャッシュ系のパラメーター
    'cache_results' => true,                // 投稿情報をキャッシュするかどうか デフォルトはtrue
    'update_post_term_cache' => true,       // 投稿タームキャッシュを更新するかどうか デフォルトはtrue
    'update_post_meta_cache' => true,       // 投稿メタキャッシュを更新するかどうか デフォルトはtrue
    'no_found_rows' => false,               // カウントをスキップする? tureでパフォーマンスが向上する可能性があるかも デフォルトはfalse

    // ↓ 検索系のパラメーター
    's' => $s, // 検索からクエリーストリング値を渡します。 
    'exact' => true, //タイトル/投稿の全体から正確なキーワードで検索するか デフォルト値はfalse
    'sentence' => true, //語句(フレーズ検索)で検索するか デフォルト値はfalse

    // ↓ 投稿フィールドパラメーター
    'fields' => 'ids'  //1つのフィールドで返すか全てのフィールドで返すか デフォルトでは全てのフィールドが返される
                        // 使用できる値↓ 
                        // 'ids' 投稿のIDの配列を返します
                        // 'id=>parent' 連想配列を返します

);

$query = new WP_Query( $args );

if ( $the_query->have_posts() ) :
while ( $the_query->have_posts() ) : $the_query->the_post();
  // 何かしらの処理
endwhile;
endif;

// 投稿データのリセット
wp_reset_query();
?>

簡単な説明

データ 内容
CHAR 文字
NUMERIC ‘SIGNED’の別名
DECIMAL 浮動小数点数
SIGNED 整数(符号あり)
UNSIGNED 整数(符号なし)
DATE 日付
DATETIME 日時
TIME 時刻
BINARY バイナリー
比較演算子 内容
= 値と一致する
!= 値と一致しない
> 値より大きい
>= 値以上
< 値より小さい
<= 値以下
LIKE 値で指定した文字列に一致する場合
NOT LIKE 値で指定した文字列に一致しない場合
IN 値(配列)で指定した何れかに一致する場合
NOT IN 値(配列)で指定した何れかにも一致しない場合
BETWEEN 2つの値で指定した範囲内(境界を含む)場合
NOT BETWEEN 2つの値で指定した範囲外の場合

タクソノミーで設定したACFの取得

wp_query.php
$taxonomy_name = 'com_category';
$taxonomys     = get_the_terms( $post->ID, $taxonomy_name);
if(!is_wp_error($taxonomys) && count($taxonomys)){
    foreach($taxonomys as $taxonomy){
        // print_r($taxonomy);
        $staff_title       = $taxonomy->name;
        $staff_description = $taxonomy->description;
        $staff_img         = get_field('staff_img', $taxonomy_name . "_" . $taxonomy->term_id);

        echo '<p>' . $staff_title . '</p>';
        echo '<p>' . $staff_description . '</p>';
        echo '<p><img src="' . $staff_img . '"></p>';

    }
}

参考記事

http://notnil-creative.com/blog/archives/1288
https://elearn.jp/wpman/column/c20110906_01.html
https://hirashimatakumi.com/blog/164.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPのバージョンを8.0にアップデートする

課題

PHPのバージョンをアップデートする

現状のバージョンを確認する

下記のコマンドでバージョンを確認

ターミナル
$ php -v
PHP 7.4.7 (cli)

*ちなみにですが今回はHomebrewを使ってアップデートしようと思います
Homebrewのインストールがまだという方はこちらをソッと置いておきます
(https://brew.sh/index_ja.html)

PHP8系を検索する

次にHomebrewコマンドを使って現在インストールができるPHPのバージョンを検索します

ターミナル
$ brew search php@8
==> Formulae
php@8.0

最新のPHP 8.0 をインストールしてみる

どのバージョンをインストールするか決めたら
PHP 8.0を Homebrew経由でインストールします

ターミナル
$ brew install php@8.0

PATHを通そう

インストールは無事にされたと思いますが、まだ終わりではありません
PATHを通す必要があるので下記コマンドにて設定を上書きします

ターミナル
$ echo 'export PATH="/usr/local/opt/php@8.0/bin:$PATH"' >> ~/.bash_profile
$ echo 'export PATH="/usr/local/opt/php@8.0/sbin:$PATH"' >> ~/.bash_profile

PHPとの冒険の再出発へ

上書きまで できたら下記コマンドでPHPを再スタートさせます

ターミナル
$ brew services start php@8.0

ここで一旦ターミナルを閉じます。
大事なことなのでもう一度言います。

ここで一旦ターミナルを閉じます。

ターミナルをもう一度起動させたらバージョンを確認してみます

ターミナル
$ php -v
PHP 8.0.1 (cli)

これで完了です!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP入門 文法編

0.what

php文法を自分なりに整理していきます.

1.画面表示

1.1."Hello World"

index.php
<?php
//基本は print(); の形
print("Hello World");
//Hello World

1.2.数字、文字、日本語表示

index.php
//number
print(123);
//改行
print('<br>');
print("日本語");
//123
//日本語

2.演算

2.1.基本演算

index.php
//"."で連結.
print(7 + 3."<br>"); //10
print(7 - 3."<br>"); //4
print(7 * 3."<br>"); //21
print(7 / 3."<br>"); //2.3333333333333
print(7 % 3."<br>"); //1

2.2.変数

index.php
//$後の先頭は英文字か_
$test = 123;
print($test);
var_dump($test);
//123int(123)

$num1 = 123;
$num2 = 456;
$num3 = $num1.$num2;
print($num3.'<br>');
var_dump($num3); 
//string(6) "123456"

2.3.定数

index.php
const NUMBER = 7;
const NUMBER = 0;
print(NUMBER.'<br>'); //7

3.配列

3.1.初期化

index.php
//初期化
$array = [1,2,3,'A'];
print($array.'<br>');     //Array
print($array[1].'<br>');  //2
var_dump($array);
//array(4) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [3]=> string(1) "A" }

3.2.多次元配列

index.php
$array_2 = [[1,2,3], [4,5,6], [7,8,9]];
print($array_2[1][1]."<br>"); //5
var_dump($array_2);
//array(3) { [0]=> array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } [1]=> array(3) { [0]=> int(4) [1]=> int(5) [2]=> int(6) } [2]=> array(3) { [0]=> int(7) [1]=> int(8) [2]=> int(9) } }

3.3.連想配列

keyとvalueを持つ辞書型の配列.

index.php
$array_member = [
    1 => 'one',
    2 => 'two',
    3 => 'three'
];
print($array_member[1]."<br>"); //one
var_dump($array_member);
//array(3) { [1]=> string(3) "one" [2]=> string(3) "two" [3]=> string(5) "three" }

3.4.配列がvalue

index.php
$array_member_2 = [
    'A' => [
        'id' => 0,
        'height' => 180,
        'weight' => 56
    ],
    'B' => [
        'id' => 1,
        'height' => 140,
        'weight' => 55
    ]
    ];
print($array_member_2['A']['height']."<br>"); //180

4.条件分岐

4.1.if else

index.php
$num = 0;
if ($num > 0){
    print($num." is +"."<br>");
} else if ($num == 0){
    print($num." is 0 "."<br>");
} else{
    print($num." is -"."<br>");
} //0 is 0

var_dump($argv);
$num = $argv[1];
if ($num%2 == 0){
    echo('偶数'.PHP_EOL);
}
else{
    echo('奇数'.PHP_EOL);
}

4.2.型まで比較

index.php
one_n = 1;
$one_s = '1';
if ($one_n === $one_s){
    print("型は同じ<br>");
}else{
    print("型が違う<br>");
} //型が違う

4.3.空データか確認

index.php
$str=null;
if (empty($str)){
    print("空データ<br>");
} //空データ

他にもisset,is_nullなどで調べる.

4.4.三項演算子

index.php
$max = 80;
$comment = $max == 100 ? 'True' : 'False';
print($comment."<br>");

4.5.swhich

index.php
$data=4;
switch($data){
    case 1:
        print("1");
        break;
    case 2:
        print("2");
    case 3:
        print("3");
    case 4:
        print("4");
        break;
    default:
        print("no");
} //4

5.繰り返し処理

5.1.foreach文

index.php
$numbers = [
    "one", "two", "three", "four", "five"
];
foreach($numbers as $number){
    print($number."<br>");
}
//one
//two
//three
//four
//five

5.2.連想配列取得

index.php
$numbers_2 = [
    1=>"one", 
    2=>"two",
    3=> "three"
];
foreach($numbers_2 as $key => $value){
    print("key=".$key."value=".$value."<br>");
}
//key=1value=one
//key=2value=two
//key=3value=three

5.3.for文

index.php
for ($i=1; $i<=100; $i++){
    if ($i % 15 == 0){
        echo('FizzBuzz'.PHP_EOL);
    }
    elseif($i % 3 == 0){
        echo('Fizz'.PHP_EOL);
    }
    elseif($i % 5 == 0){
        echo('Buzz'.PHP_EOL);
    }
    else{
        echo($i.PHP_EOL);
    }
}

5.4.while文

index.php
$j = 10;
while($j > 0){
    print($j." ");
    $j--;
} //10 9 8 7 6 5 4 3 2 1

5.5.do while文

index.php
do{print($j);
    $j++;}
while($j < 5); //01234

6.関数

6.1.関数の基本

index.php
function sumP($int1, $int2){
    return $int1+$int2;
}
$sum_number = sumP(4,7); //11

6.2.文字列関数

index.php
$str1 = "abc";
$str2 = "日本語";
//文字列の長さ(日本語は3bit)
print(strlen($str1)."<br>"); //3
print(strlen($str2)."<br>"); //9
//基本はmb_strlen()を使用
print(mb_strlen($str2)."<br>"); //3

//文字列の置換
print(str_replace('b','B',$str1)); //aBc

//文字列で分割
$pro = "34 44 33 22";
foreach(explode(" ", $pro) as $pr){
    print($pr." ");
} // 34 44 33 22

//特定の文字が含まれるか
$str3 = "eggs@gg";
print(preg_match('/@/',$str3)."<br>"); //1

//文字列取得 スライス的な役割
print(mb_substr($str3, 2)."<br>"); //gs@gg

6.3.配列関数

index.php
$foods = ["pizza", "ramen", "yakiniku"];
//追加
array_push($foods, "potato");

7'オブジェクト指向

スクリーンショット 2021-01-22 14.04.47.png

7.クラス

7.1.クラスとインスタンス

instance.php
<?php
class Product{
    //アクセス修飾子
    private $product = [];

    //クラス呼び出し時
    function __construct($product){
        //$thisはこのクラスの意味
        //このクラスのproductは引数のproduct
        $this->porduct = $product;
    }

    public function getProduct(){
        print($this->porduct);
    }
    public function addProduct($item){
        //.=で追加
        $this->porduct .= $item;
    }

    public static function getStaticProduct($str){
        echo $str;
    }
}
//インスタンス化
$instance = new Product('test');
$instance->getProduct();  //test
print('<br>'); 

$instance->addProduct('add');
$instance->getProduct(); //testadd
print('<br>');

Product::getStaticProduct('string');
print('<br>'); //string

7.2.継承

extends.php
<?php
//親クラス
class Animal{
    public $cry = "bowbow!";

    public function bow(){
        print($this->cry."<br>");
    }
}

//子クラス
class Dog extends Animal{
    public $cry = "DOG!";
}

$dog = new Dog();
$dog->bow(); //DOG!

子クラスにfinalをつけると継承はそれ以降できなくなる.

7.3.抽象クラス

extends.php
//抽象クラス
abstract class ProductAbstract{
    public function echoProduct(){
        print("super class");
    }
    //抽象クラスは中身をかけない
    abstract public function get();
}

class prodct extends ProductAbstract{
    //必ず抽象関数を入れる
    function get(){
        print("Hello!");
    }
}

7.4.インターフェース

interface.php
<?php
//インターフェースは抽象関数しか入れることができない
interface ProductInterface{
    public function getProduct();
}
interface NewInterface{
    public function Product();
}

//継承はいくつでも可能
class Products implements ProductInterface, NewInterface{
    function getProduct(){
        print("Hello ");
    }
    function Product(){
        print("3,2,1<br>");
    }
}

7.5.トレイト

trait.php
<?php
trait ProductTrait{
    public function getProduct(){
        print("purduct");
    }
}
trait NewsTrait{
    public function getNews(){
        print("News");    
    }        
}

class Product{
    use ProductTrait;
    use NewsTrait;

    public function getInformation(){
        print("Hello");
    }
}
$instance = new Product();
$instance->getInformation();
$instance->getProduct();
$instance->getNews();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Instagram HTML 埋め込み

よく仕様が変わって表示されなくなるそうですが、まだ僕はペーペーなので体験してません。

実装

コード

<?php
    $count = '6'; //画像取得数。ここは好きな表示枚数に変更
    $id = '*****'; //InstagramビジネスアカウントID。取得方法は後述
    $token = '*****'; // アクセス。取得方法は後述
    $json = file_get_contents("https://graph.facebook.com/v9.0/{$id}?fields=name%2Cmedia.limit({$count})%7Bcaption%2Cmedia_url%2Cthumbnail_url%2Cpermalink%7D&access_token={$token}");

    $json = mb_convert_encoding($json, 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN');
    $obj = json_decode($json, t $insta  foreach ((array)$obj['media']['data'] as $k => $v) {
    if ($v['thumbnail_url']) {
        $data = [
            'img' => $v['thumbnail_url'], // 動画ならサムネ
            'caption' => $v['caption'],   // キャプション
            'link' => $v['permalink'],    // パーマリンク
        ];
    } else {
        $data = [
            'img' => $v['media_url'],
            'caption' => $v['caption'],
            'link' => $v['permalink'],
        ];
    }
    $insta[] = $data;
    foreach ($insta as $k => $v){
        echo '<li><a href="'.$v['link'].'" target="_blank"><img src="'.$v['img'].'" alt="'.$v['caption'].'"></a></li>';
    };
?>

Instagram グラフAPIの取得方法

  1. 表示させるインスタアカウントをプロアカウントに
  2. 当該インスタアカウントの管理者として、Facebookページ作成→当該Instagramアカウント紐付け
  3. Instagram グラフAPI からInstagramアカウントのAPI取得
    1. 1時間有効API取得
    2. 上記1.を使用して、2ヶ月有効API取得
    3. 上記2.を使用して、ずっと有効API取得
  4. ずっと有効API取得と、ずっと有効API取得を取得する途中で手に入るtokenを、上記コードの$id $tokenに突っ込む

ちなみに

僕が実装するときもあまりふんだんに情報があるわけではなかったです。
どうせまた変わるかもしれないですし、とりあえず方法のみ。
詳細は実際にやりながらアウトラインつかむ感じでいい気がしました。
どうせまた変わるかもしれないですし。
とりあえず
PHPで埋め込んで
InstagramグラフAPIを3つ取得して最後のやつ使う
あたりを頭の端っこの方に入れとくといいかもです。

どうせまた変わるかもしれないですし。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel checkboxで複数取得する方法 

はじめに

inputのcheckboxを使って

1. 複数チェックの取得
2. チェックしたデータを保持する方法

をまとめてみました。

テーブル設計とリレーション

今回はusersテーブルcategoriesテーブル使っていきます。

userは複数のcategoryを持ち、categoryも複数のuserに紐づけられているので

多対多のリレーションになります。

テーブル設計

◎usersテーブル

migration
public function up()
   {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
        });
   }



◎categoriesテーブル

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


◎category_userテーブル

migration
public function up()
   {
        Schema::create('category_user', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned();
            $table->integer('category_id')->unsigned();
            $table->foreign('user_id')->references('id')->on('users')->OnDelete('cascade');
            $table->foreign('category_id')->references('id')->on('categories')->OnDelete('cascade');
            $table->timestamps();
        });
   }



category_userテーブルで

migration
$table->foreign('user_id')->references('id')->on('users')->OnDelete('cascade');
$table->foreign('category_id')->references('id')->on('categories')->OnDelete('cascade');

でOnDelete('cascade')をつけておきましょう。 後々でdetachのときに一緒に削除するためです

リレーション設計

User.php
public function categories()
{
    return $this->belongsToMany(Category::class);
}

ここまで出来たら次はコントローラーの作成に入ります

コントローラー設計

Contorller
public function update(Request $request)
    {
        $user = Auth::user();

        $user->categories()->detach();
        $user->categories()->attach($request->category);


$user->categories()でリレーションをして、attach($request->category)でリクエストするカテゴリーを紐付けます。

テーブルでOnDelete('cascade')をつけたのは、detachで削除するときに、user_id, category_idを削除するためです。

次はBladeの設計です

Bladeの設計

ここが結構悩みました。 データの取得は出来たのですが、編集するときにデータの保持するのがなかなか詰まりました。

最初はoldを使ってデータ保持をやろうとしたのですがなかなかうまく行かず、oldを使わないでcheckedが含まれてるときでif文を作りました。

blade
@foreach ($categories as $category)
   @if($user->categories->contains('id', $category->id))
     <input type="checkbox" name="category[]" value="{{ $category->id}}" checked>
   @else
     <input type="checkbox" name="category[]" value="{{ $category->id}}">
   @endif 
    <label for="">
     {{ $category->name }}
    </label>
@endforeach

指定したidを判定するためにコレクションのcontains() メソッドを使って、指定したidが含まれているか確認しました。

ただ、oldを使ってデータ保持もやりたかったので、三項演算子を使って下記のようにも書いてみました。

blade
@foreach($categories as $category)
  <input type="checkbox" name="category[]" id=""  value="{{ $category->id }}" 
       {{ $category->id == old('category', $user->categories->contains('id', $category->id) ?? '') ? 'checked' : ''}}  >
    <label for="category">
       {{ $category->name  }}
     </label>  
@endforeach

保存して、また編集画面に遷移してもチェックボックスにcheckがついています!
色々なやり方がありますがご参考までに!!

参考

Laravel7.x コレクション contains

Laravel7.x リレーション

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP初心者】Laravelを2週間触ってみて理解したこと②【ルーティング】

1.はじめに

こんにちは。nakaです。
昨晩ホームベーカリーをセットし、今朝は小麦のいい匂いで朝を迎えました( ˘ω˘ )
パンが大好きなのでとても癒されました・・・

さて本題に入ります!前回に引き続きLaravelについてまとめたいと思います。
前回の記事:【PHP初心者】Laravelを2週間触ってみて理解したこと

今回はルーティングについてです。

2.ルーティングとは

初めてLaravelのソースを見たとき、「えーとControllerやろー・・・・ん?」
controllerにリクエストのURLがなくてびっくりしました。
Laravelは代わりにルーティングと呼ばれるものがその役割を担っているようです。
ルーティングのルートが「経路」と訳されるように、ユーザからのリクエストを受け取り、ある処理を実行するまでの案内人のようなイメージですね。

ルーティングは「routes/web.php」で設定できます。
Larabelをインストール後は、デフォルトのページの設定がされています。

web.php
<?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('/', function () {
    return view('welcome');
});

3.ルート定義メソッド

それでは上記ファイル(web.php)に書くHTTPリクエストに定義できるメソッドを見てみましょう。
メソッドでよく見るものは「GET」「POST」ですかね。

// GETリクエスト
Route::get($uri, $callback);

// POSTリクエスト
Route::post($uri, $callback);

// putリクエスト
Route::put($uri, $callback);

// patchリクエスト
Route::patch($uri, $callback);

// deleteリクエスト
Route::delete($uri, $callback);

ちなみにルーティングにはルートファザードを使います。
ファザードとはクラスをインスタン化せずに実行できる機能のことです。

4.コントローラーのルーティング

基本の形

例えばコントローラーへルーティングする際は、以下のように書きます。

web.php
Route::get('sample', 'SampleController@index');

http://xxx/sampleへアクセスすると、SampleControllerのindex()が呼ばれます。

パラメータ付き

またURLからパラメーターを取得したい場合は、以下のように書きます。

web.php
Route::get('sample/edit/{id}', 'SampleController@edit');
SampleContoroller.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SampleController extends Controller
{
    public function edit($id)
    {
        print_r($id);
    }
}

http://xxx/sample/edit/1へアクセスすると、「1」というパラメータを取得し、SampleContoroller.phpのedit()の$idという変数で値を取得できます。

ルーティングに名前を付ける

またルーティングに名前をつけることもできます。
URLをわざわざ書かなくも名前で呼び出せます。

web.php
Route::get('sample', 'SampleController@index')->name('sample.index');

6.リダイレクトのルーティング

上記の名前をつけると便利

web.php
Route::get('sample', 'SampleController@index')->name('sample.index');
SampleContoroller.php
return redirect()->route('sample.index');

5.ルートのグループ化

ルーティングをグループ化できます。

URLをまとめるときに使う「prefix」

管理者と会員など権限が複数の時に使うと便利

// ごちゃごちゃ
Route::get('admin/top', 'AdminContoroller@top');
Route::get('admin/setting', 'AdminContoroller@setting');
// 見やすい
Route::prefix('admin')->group(function () {
    Route::get('top','AdminContoroller@top');
    Route::get('setting','AdminContoroller@setting');
});

名前空間をまとめる「namespace」

配置先をまとめることができます。
以下の場合は、"App\Http\Controllers\Admin"のディレクトリ内を示しています。

App\Http\Controllers\Admin\TopColler@top
App\Http\Controllers\Admin\SettingContoroller@setting
// ごちゃごちゃ
Route::get('top','Admin\TopColler@top');
Route::get('setting','Admin\SettingContoroller@setting');
// 見やすい
Route::namespace('Admin')->group(function () {
    Route::get('top','TopColler@top');
    Route::get('setting','SettingContoroller@setting');
});

他ミドルウェアをまとめる「middleware」、ドメインをまとめる「domain」、ルート名をまとめることもできます。複数を一気に設定もできます(今回は割愛)

6.さいごに

忘れていましたがルーティングはphp artisan route:listで一覧が確認できます。ルーティングを理解するとソースの流れがわかりますね。
次回はBladeについて書く予定です(゜レ゜)
マークダウン少しづつ慣れてきた。

参考記事

Laravelルーティングの基本とよく使われるルーティングパターン
routesを使いこなす(3)prefixでgroupを分割

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon AWS Cloud9 開発環境を速攻で作る方法(令和最新版)

はじめに

以前、記事を書いた時は、公式ドキュメントに従うことで開発環境が速攻で作れました

ですが、デフォルトのプラットフォームが
Amazon LinuxからAmazon Linux 2 に変更になったからなのか、
公式ドキュメント通りにしても開発環境が作れなくなっていました。

今回、開発環境をまた用意する必要があったので、改めて書くことにしました。

2021年1月時点でのやり方なので、
将来的には同じように実行しても、
開発環境が作れなくなっている可能性はあります。

予めご了承ください。

要件

  • Apache 2.4以上
  • PHP 7.4以上
  • MySQL(MariaDB) 10.2.10-MariaDB

PHPはデフォルトで7.2がインストールされていました。

ですが、今回用意しなければいけない環境はPHP7.4です。

yumにある最新のPHPは7.2までなので、
7.4を入れるためには、epelでリポジトリを追加する必要があります。

また、MySQLもデフォルトでmariadbがインストールされてました。

これにより、今まで sudo service mysqld restart とか書いてた部分は
sudo service mariadb restart という風に読み換える必要があります。

インスタンスを作成

https://ap-northeast-1.console.aws.amazon.com/cloud9/home?region=ap-northeast-1

  1. 上記のサイトにアクセス
  2. 画面右上のリージョン表記が「東京」であることを確認。違う場合は「東京」に変更
  3. 「Create environment」ボタンをクリック
  4. Step 1 Name environment
    • 開発環境の名前と説明。
    • 各項目の入力内容を次のとおり設定する
    • 「Name」に開発予定のプロジェクト名を入力する ※必須
      • なんでもいいが、英数字のみで入力するのが無難
    • 「Description」にプロジェクトの説明を入力する ※任意
      • 日本語を入力しても良し
  5. Step 2 Configure settings
    • 開発環境の基本的な設定。
    • 各項目の入力内容を次のとおり設定する
    • Environment type - Create a new instance for environment (EC2)
    • Instance type - t2.micro (1 GiB RAM + 1 vCPU)
    • Platform - Amazon Linux 2 (recommended)
    • Cost-saving setting - After 30 minutes (default)
    • IAM role - AWSServiceRoleForAWESCloud9 ※変更できない
    • Network settings (advanced) - 何も変更しない
    • Add new tag - 何も設定しない
    • ここまで出来たら「Next step」ボタンをクリック
  6. Step 3 Review
    • 設定内容の確認。
    • 「Create environment」ボタンをクリック
  7. 開発環境の構築が完了するまで待つ
    • ネットワーク速度にもよるが、だいたい5分以内に完了する
    • ターミナル画面( bash - "ip-***-**-**-***" みたいなのが書いてあるタブ)が操作できるようになったらOK

【初回のみ】環境構築

外観のカスタマイズ

デフォルトだと文字サイズが12pxで、老眼にはつらいので、もうちょっち上げます

  1. 画面左上 ツールバー Cloud9アイコン -> Preferences (Ctrl-,) をクリック
  2. サイドバーから User Settings -> Terminal をクリック
    • Font Size を 14 にする
    • Antialiased Fonts を チェック
    • Blinking Cursor を チェック
  3. サイドバーから User Settings -> Code Editor (Ace) をクリック
    • Font Size を 14 にする
    • Antialiased Fonts を チェック

あとデフォルト背景が白のため、ドライアイにもきついので、黒系の外観にします

  1. 画面左上 ツールバー View -> Themes -> UI Themes -> Jett Dark をクリック
  2. 画面左上 ツールバー View -> Themes -> Monokai をクリック
    • お好みで。私は Sublime Text 3 をよく使ってたので

【重要】隠しファイルを表示する設定にします。「 .htaccess 」などを表示するためです

  1. 画面左上 プロジェクト名の右の歯車アイコンをクリック
  2. Show Hidden Files をクリック

あとはPreferencesの設定項目をじっくり眺めてお好みでカスタマイズしてください

yum のアップデート

公式ドキュメントと同じく、とりあえずyumを更新します

ターミナル画面( bash - "ip-***-**-**-***" みたいなのが書いてあるタブ)で、
以下のコマンドをコピペしてEnterキーを押して実行します

sudo yum -y update

2021.01.22 時点では、特に何も更新されませんでした

Apache が入っているかを確認

httpd -V

Apache/2.4.46 が最初からインストールされてました。
このままでいいので作業なし。

PHP が入っているかを確認

php -v
PHP 7.2.24 (cli) (built: Oct 31 2019 18:27:08) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies

入ってますが、7.2なので、要件より古いですね。
そこで、PHP7.4にアップデートします

PHP 7.4 のインストール

ターミナル画面( bash - "ip-***-**-**-***" みたいなのが書いてあるタブ)で、
以下のコマンドを上から順に実行する。

単に全行コピペしてCtrl+Vで貼り付ければOK
(貼り付けた瞬間に実行されるので注意)

# Amazon Linux でepel-releaseパッケージをインストール出来るようにする
sudo amazon-linux-extras install epel
# epel-releaseパッケージをインストール
sudo yum install epel-release
# remiリポジトリを使えるようにする
sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
# PHP7.4をインストールする
sudo yum install -y php74 php74-php php74-php-fpm php74-php-mbstring
# 現時点ではphpコマンドはPHP7.2を使用しているので、リンクをつけなおす
sudo unlink /usr/bin/php
# これでPHP7.4が使用される
sudo ln -s /usr/bin/php74 /usr/bin/php

改めて、PHPのバージョンを確認

php -v
PHP 7.4.14 (cli) (built: Jan  5 2021 10:45:06) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies

これで要件を満たしました

MySQL(MariaDB) が入っているかを確認

mysql -V
mysql  Ver 15.1 Distrib 10.2.10-MariaDB, for Linux (x86_64) using  EditLine wrapper

入ってますが、これだけではダメで、mariadb-serverもインストールする必要があります
sudo service mariadb start とかを使えるようにするためです

MariaDB-Server をインストールする

sudo yum install mariadb-server

また注意があって、Amazon Linux2ではmysqldではなく、mariadbが使用されます。

そのため、公式ドキュメントに書いてある
sudo service mysqld start && sudo service mysqld status みたいなコマンドをそのまま打っても、
Unit mysqld.service could not be found. というエラーが出て実行されません。

sudo service mariadb start && sudo service mariadb status と言う風に、
mysqldmariadb に読み換える必要があります

ウェブサイトの設定

現時点では、 sudo service httpd start としたところでウェブページが表示されません
それを表示されるようにします。書き込み権限割り当てとかそんな感じのことしてます。
一種のおまじないです。

ターミナル画面( bash - "ip-***-**-**-***" みたいなのが書いてあるタブ)で、
以下のコマンドを上から順に実行する。

単に全行コピペしてCtrl+Vで貼り付ければOK
(貼り付けた瞬間に実行されるので注意)

sudo groupadd web-content # Create a group named web-content.
sudo usermod -G web-content -a ec2-user # Add the user ec2-user (your default user for this environment) to the group web-content.
sudo usermod -G web-content -a apache # Add the user apache (Apache HTTP Server) to the group web-content.
sudo chown -R ec2-user:web-content /var/www/html # Change the owner of /var/www/html and its files to user ec2-user and group web-content.
sudo find /var/www/html -type f -exec chmod u=rw,g=rx,o=rx {} \; # Change all file permissions within /var/www/html to user read/write, group read-only, and others read/execute. 
sudo find /var/www/html -type d -exec chmod u=rwx,g=rx,o=rx {} \; # Change /var/www/html directory permissions to user read/write/execute, group read/execute, and others read/execute.

sudo touch /var/www/html/index.php && sudo chown -R ec2-user:web-content /var/www/html/index.php && sudo chmod u=rw,g=rx,o=rx /var/www/html/index.php && sudo printf '%s\n%s\n%s' '<?php' '  phpinfo();' '?>' >> /var/www/html/index.php

MY_INSTANCE_ID=$(curl http://169.254.169.254/latest/meta-data/instance-id) # Get the ID of the instance for the environment, and store it temporarily.
MY_SECURITY_GROUP_ID=$(aws ec2 describe-instances --instance-id $MY_INSTANCE_ID --query 'Reservations[].Instances[0].SecurityGroups[0].GroupId' --output text) # Get the ID of the security group associated with the instance, and store it temporarily.
aws ec2 authorize-security-group-ingress --group-id $MY_SECURITY_GROUP_ID --protocol tcp --cidr 0.0.0.0/0 --port 80 # Add an inbound rule to the security group to allow all incoming IPv4-based traffic over port 80.
aws ec2 authorize-security-group-ingress --group-id $MY_SECURITY_GROUP_ID --ip-permissions IpProtocol=tcp,Ipv6Ranges='[{CidrIpv6=::/0}]',FromPort=80,ToPort=80 # Add an inbound rule to the security group to allow all incoming IPv6-based traffic over port 80.
MY_SUBNET_ID=$(aws ec2 describe-instances --instance-id $MY_INSTANCE_ID --query 'Reservations[].Instances[0].SubnetId' --output text) # Get the ID of the subnet associated with the instance, and store it temporarily.
MY_NETWORK_ACL_ID=$(aws ec2 describe-network-acls --filters Name=association.subnet-id,Values=$MY_SUBNET_ID --query 'NetworkAcls[].Associations[0].NetworkAclId' --output text) # Get the ID of the network ACL associated with the subnet, and store it temporarily.
aws ec2 create-network-acl-entry --network-acl-id $MY_NETWORK_ACL_ID --ingress --protocol tcp --rule-action allow --rule-number 10000 --cidr-block 0.0.0.0/0 --port-range From=80,To=80 # Add an inbound rule to the network ACL to allow all IPv4-based traffic over port 80. Advanced users: change this suggested rule number as desired.
aws ec2 create-network-acl-entry --network-acl-id $MY_NETWORK_ACL_ID --ingress --protocol tcp --rule-action allow --rule-number 10100 --ipv6-cidr-block ::/0 --port-range From=80,To=80 # Add an inbound rule to the network ACL to allow all IPv6-based traffic over port 80. Advanced users: change this suggested rule number as desired.

MY_PUBLIC_IP=$(curl http://169.254.169.254/latest/meta-data/public-ipv4) && echo http://$MY_PUBLIC_IP/index.php # Get the URL to the index.php file within the web server root.

シンボリックリンクを設定

Apacheのドキュメントルートは /var/www/html/index.php になっています。
ただ、Cloud9のデフォルトディレクトリは /home/ec2-user/environment/ になっています。
ドキュメントルートをお気に入りのパスに設定してもいいんですが、
私は横着なのでCloud9のデフォルトディレクトリの下にドキュメントルートのシンボリックリンクを貼って、
すぐに開発作業が出来るようにしました。

Apacheの設定ファイルの記述的に、
/home/{{ サイト名 }}/public_html/ みたいな構成にしたほうがよさそうですが、
動けばなんでもええねん。

sudo ln -s /var/www/html /home/ec2-user/environment/

タイムゾーンを日本時間に変更

Cloud9のデフォルトのタイムゾーン設定はUTC(国際標準時)になっています。
JST(日本時間)のほうが何かと都合が良いので、タイムゾーンを変更します

sudo cp /etc/sysconfig/clock /etc/sysconfig/clock.org

sudo vim /etc/sysconfig/clock

「ZONE="UTC"」を「ZONE="Asia/Tokyo"」へ書き換えます。

ZONE="UTC"
UTC=trueZONE="Asia/Tokyo"
UTC=true

ローカル時間を日本時間に変更します

sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

また、PHPのデフォルトタイムゾーンが設定されていないので、
現時点では date() 関数を使ってもUTC時間が表示されます。
同様に日本時間を設定しましょう

# php.ini の場所を探す
sudo php -i | grep php.ini
# デフォルトのファイルを残しておく
sudo cp /etc/opt/remi/php74/php.ini /etc/opt/remi/php74/php.ini.org
# php.ini を編集する
sudo vi /etc/opt/remi/php74/php.ini
[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
;date.timezone =


[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
;date.timezone =
date.timezone = "Asia/Tokyo"

Apacheのリダイレクト設定を有効にする

Apacheのリダイレクト設定を有効にします。
これをしないと、PHPフレームワークが動かないためです

sudo cp /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.org
sudo vi /etc/httpd/conf/httpd.conf

/Directory "/var/www/html" を入力して、ドキュメントルートへの記述を探し、
AllowOverride NoneAllowOverride All に変更する

    # AllowOverride controls what directives may be placed in .htaccess files.
    # It can be "All", "None", or any combination of the keywords:
    #   Options FileInfo AuthConfig Limit
    #
    #AllowOverride None
    AllowOverride All

ここまでできたら再起動します(IPアドレスが変わるので注意)

sudo reboot

再起動後、動作確認

cat /etc/sysconfig/clock
ZONE="Asia/Tokyo"
UTC=true
cat /etc/localtime
TZif2
        pYp;LMTJDTJSTTZif2

e¤ppYp;ۭLMTJDTJST
JST-9
date
Fri Jan 22 14:58:02 JST 2021
php -r 'print(date("Y-m-d H:i:s") . "\n");'
2021-01-22 14:58:27

ブラウザで表示確認

ターミナル画面(bash - "ip-**---**"みたいなのが書いてあるタブ)で、
以下のコマンドを上から順に実行する。

sudo service mariadb start
sudo service httpd start
curl -q http://169.254.169.254/latest/meta-data/public-ipv4 -w "\n"

最後のコマンドで、IPアドレスが表示されます。
それが開発環境のURLです。
そのIPアドレスをコピペして、ブラウザのURL欄に貼り付けてアクセスします。

phpinfoの内容が出てくればOKです。

以上で環境構築は完了です。お疲れ様でした。

【次回以降】開発環境を立ち上げるには

インスタンスの起動

https://ap-northeast-1.console.aws.amazon.com/cloud9/home?region=ap-northeast-1

  1. 上記のサイトにアクセス(リージョン確認などの手順は割愛)
  2. 任意のプロジェクトをクリック
  3. 「Open IDE」ボタンをクリック

WEBサーバーを立ち上げる

ターミナル画面( bash - "ip-***-**-**-***" みたいなのが書いてあるタブ)で、
以下のコマンドを実行する。

sudo service mariadb start; sudo service httpd start; curl -q http://169.254.169.254/latest/meta-data/public-ipv4 -w "\n"

表示されるIPアドレスをコピペして、ブラウザのURL欄に貼り付けてアクセスします。

開発環境を終了するには

単にタブを閉じれば良いです。
無操作状態が30分継続すれば、自動でインスタンスを停止してくれます。

参考サイト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】繰り返し処理は@includeを使う。PHPはビューではなくコントローラに記述する(リファクタリング)

リファクタリングの個人メモです。

  • ビューの中に繰り返し処理を記述している場合は、処理を別ファイルに切り出し、@includeで呼び出す

  • PHPの処理はビューの中に書かず、コントローラで渡す(他でも使う処理の場合は自作のヘルパー関数を作って渡す)

リファクタリング事例

リファクタリング前のコード

以下のように同じコードが繰り返されている処理をリファクタリングする。

phones.balde.php
  <div class="p-service__list">
      <section>
          <h2 class="c-page-headline">iphone</h2>
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'iphone' )
              <section class="p-service-section">
                  <div>
                  <span class="p-service-section__img-border">
                      <img class="p-service-section__img" src="{{ asset('/img/service/'. $service['logo_file_name']) }}" alt="{{ $service['name'] }}">
                  </span>
                  </div>
                  <div>
                      <h3 class="p-service-section__name">{{ $service['name'] }}</h3>
                      <p class="p-service-section__lead">{{ $service['lead'] }}</p>
                  </div>
              </section>
              @endif
          @endforeach
      </section>
      <section>
          <h2 class="c-page-headline">iphone</h2>
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'android' )
              <section class="p-service-section">
                  <div>
                  <span class="p-service-section__img-border">
                      <img class="p-service-section__img" src="{{ asset('/img/service/'. $service['logo_file_name']) }}" alt="{{ $service['name'] }}">
                  </span>
                  </div>
                  <div>
                      <h3 class="p-service-section__name">{{ $service['name'] }}</h3>
                      <p class="p-service-section__lead">{{ $service['lead'] }}</p>
                  </div>
              </section>
              @endif
          @endforeach
      </section>
      <section>
          <h2 class="c-page-headline">xperia</h2>
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'xperia' )
              <section class="p-service-section">
                  <div>
                  <span class="p-service-section__img-border">
                      <img class="p-service-section__img" src="{{ asset('/img/service/'. $service['logo_file_name']) }}" alt="{{ $service['name'] }}">
                  </span>
                  </div>
                  <div>
                      <h3 class="p-service-section__name">{{ $service['name'] }}</h3>
                      <p class="p-service-section__lead">{{ $service['lead'] }}</p>
                  </div>
              </section>
              @endif
          @endforeach
      </section>
  </div>
</div>

コードを見るとsectionが繰り返されているのがわかる。

繰り返しのコード
<section>
          <h2 class="c-page-headline">iphone</h2>
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'iphone' )
              <section class="p-service-section">
                  <div>
                  <span class="p-service-section__img-border">
                      <img class="p-service-section__img" src="{{ asset('/img/service/'. $service['logo_file_name']) }}" alt="{{ $service['name'] }}">
                  </span>
                  </div>
                  <div>
                      <h3 class="p-service-section__name">{{ $service['name'] }}</h3>
                      <p class="p-service-section__lead">{{ $service['lead'] }}</p>
                  </div>
              </section>
              @endif
          @endforeach
      </section>

@foreach(__('service') as $service)
言語ファイル( resources > lang > ja )のservice.phpに記述された配列を、$serviceとして一つづつ抜き出している。

@if( $service['category'] === 'iphone' )
抜き出した配列のキー名categoryの値がiphoneなら以下の処理を実行する。

このように、categoryの値によって表示するタイトルとデータを変えている処理。


リファクタリング

1. 繰り返しのコードを別ファイルに移動する。

繰り返されているコードを別ファイルに記述する。ここでは、section.blade.phpを作成。

呼び出しは@includeを使う。

section.blade.php
<section>
    <h2 class="c-page-headline">{{ $categoryTitle }}</h2> 
    @foreach($services as $service)
        <section class="p-service-section">
            <div>
            <span class="p-service-section__img-border">
                <img class="p-service-section__img" src="{{ asset('/img/service/'. $service['logo_file_name']) }}" alt="{{ $service['name'] }}">
            </span>
            </div>
            <div>
                <h3 class="p-service-section__name">{{ $service['name'] }}</h3>
                <p class="p-service-section__lead">{{ $service['lead'] }}</p>
            </div>
        </section>
    @endforeach
</section>

[1] <h2 class="c-page-headline">{{ $categoryTitle }}</h2>

h2タグのデータは変数$categoryTitleとし、@includeした時に渡すようにする。



[2] @foreach($services as $service)

以前の処理
@foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'iphone' )

foreachでデータを一つづつ取得し、指定のカテゴリ名に一致する場合のみ実行(フィルタリング)していた処理を変更し、フィルタリング後のデータを$servicesとして渡すようにする。


2. PHPの処理をコントローラに移動する

コントローラ(リファクタリング前)
public function index(Request $request)
    {
        return view('contents.service.index');
    }

元の状態はビューファイルを返しているだけ。


コントローラ(リファクタリング後)
public function index(Request $request)
    {
        foreach(__('service') as $service){
            if( isset($service['category'])){
                $services[$service['category']][] = $service;
            }
        }

        return view('contents.service.index', $services);
    }

リファクタリング後はビューの中のforeach処理をコントローラ内で実行し、ビューに渡している。

▼foreachの処理

        foreach(__('service') as $service){
            if( isset($service['category'])){
                $services[$service['category']][] = $service;

if( isset($service['category']))
変数$serviceのキーcategoryに値があるか確認。

$services[$service['category']][] = $service;
変数$servicesを新たに用意。

キー名がカテゴリー名となる連想配列にforeachで抜き出した値($service)を追加していく。



▼注意点
配列に要素を追加する場合はarray[] = '追加する値'とする。

array[キー名]のようにキー名を指定して代入すると上書きになってしまう。


3. ビューの修正(@includeで読み込む)

切り出したビューを読み込む。

@include('ビュー', ['変数名'=>'渡すデータ'])
┗ ビューはresources > views 配下のディレクトリパスで指定。
 ┗ ディレクトリはドットでつなぐ。
 ┗ 第2変数でビューにデータを渡す。

        <div class="p-service__list">
            @include('contents.service.section', ['categoryTitle'=>"iphone", 'services'=>$iphone])

            @include('contents.service.section', ['categoryTitle'=>"android", 'services'=>$android])

            @include('contents.service.section', ['categoryTitle'=>"xperia", 'services'=>$xperia])
        </div>

あんなに長かったビューが驚くほどシンプルになった。。
繰り返し処理があったら@includeが便利。


(補足)PHP処理のリファクタリングの考え方

リファクタリングLevel 0

リファクタリングなし

.blade.php
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'iphone' )
                //処理
              @endif
          @endforeach
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'android' )
                //処理
              @endif
          @endforeach
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'xperia' )
                //処理
              @endif
          @endforeach

 ↓ 処理を外部ファイルに移動し@importで読み込む

PHP処理をビューファイルの情報に

リファクタリングLevel 1

.blade.php
        @php
           foreach(__('service') as $service){
              if( isset($service['category']) && $service['category'] === 'iphone' ){
                 $iphone[] = $service;
              } 
              elseif( isset($service['category']) && $service['category'] === 'android' ){
                 $android[] = $service;
              }
              elseif( isset($service['category']) && $service['category'] === 'xperia' )
                 $xperia[] = $service;
              }
           }
        @endphp

        <div class="p-service__list">
            @include('contents.service.section', ['categoryTitle'=>"iphone", 'services'=>$iphone])

            @include('contents.service.section', ['categoryTitle'=>"android", 'services'=>$android])

            @include('contents.service.section', ['categoryTitle'=>"xperia", 'services'=>$xperia])
        </div>

 ↓ PHP処理をコントローラに移動

リファクタリングLevel 2

コントローラ
public function index(Request $request)
    {
           foreach(__('service') as $service){
              if( isset($service['category']) && $service['category'] === 'iphone' ){
                 $iphone[] = $service;
              } 
              elseif( isset($service['category']) && $service['category'] === 'android' ){
                 $android[] = $service;
              }
              elseif( isset($service['category']) && $service['category'] === 'xperia' )
                 $xperia[] = $service;
              }
           }

        return view('contents.service.index', compact('iphone', 'android', 'xperia'));
    }

ビューのPHP処理をそのままコントローラに移動。(★基本的に、PHP処理はビューではなくコントローラで行う)

compact('変数名', '変数名', '変数名',,,,,)
compactメソッドは指定した変数を連想配列にしてくれる。
キー名は変数名、値は変数に入っている値が自動的に適用される。

※compactの注意点
変数は$変数名ではなく、'変数名'で渡す。

 ↓ ビューに連想配列を渡す(compactメソッドを使わない)

リファクタリングLevel 3

コントローラ
public function index(Request $request)
    {
        foreach(__('service') as $service){
            if( isset($service['category']) && $service['category'] === 'iphone' ){
                $services["iphone"][] = $service;
            }
            else if( isset($service['category']) && $service['category'] === 'android' ){
                $services["android"][] = $service;
            }
            else if( isset($service['category']) && $service['category'] === 'xperia' ){
                $services["xperia"][] = $service;
            }
        }

        return view('contents.service.index', $services);
    }

$services["キー名"][] = 値
変数$servicesを新たに定義し、指定したキーに値を追加していく。

 ↓ カテゴリ名の分岐にifを使わない

リファクタリングLevel 4

コントローラ
    public function index(Request $request)
    {
        foreach(__('service') as $service){
            if( isset($service['category'])){
                $services[$service['category']][] = $service;
            }
        }

        return view('contents.service.index', $services);
    }

if( isset($service['category']))
ifを使うのは対象の値の存在確認のみ。

$services[$service['category']][]
キー名はテキストでベタ打ちせず、$service['category'で動的に指定する。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

cakephp4 - find('list') キー/値のペア出力で値を配列にする

<select> エレメントを生成するfind('list')を使う際に、出力する値を配列にしたい場合。

キー/値のペア出力
SampleController.php
$query = $articles->find('list', [
    'keyField' => 'slug',
    'valueField' => 'title'
]);
$data = $query->toArray();

参考:https://book.cakephp.org/3/ja/orm/retrieving-data-and-resultsets.html

キー/値(配列)のペア出力
SampleController.php
$query = $articles->find('list', [
     'keyField' => 'slug',
     'valueField' => function ($entity){
                       return [
                          'id' => $entity->id,
                          'title' => $entity->title,
                        ];}
]);
$data = $query->toArray();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel 8.x 時点での認証の実装

Laravel は多彩な認証ライブラリをリリースしています。

  • Tailwind CSS && Blade Templates で出来た Laravel Breeze
  • Tailwind CSS && (Livewire || Inertia) で出来た Laravel Jetstream
  • OAuth 2 プロバイダ実装の Passport
  • OAuth 2 ログイン実装の Socialite
  • (レガシーパッケージとなっていますが) Bootstrap && (React || Vue) で出来た laravel UI

どれが要件に合うのかという所でもかなり悩みどころですが、とにかく選択肢は豊富です。

今回はこれらのライブラリについて深掘りしていくより、 Laravel 自体の認証の実装方法について調べてみます。

イントロダクション

Laravel Authenticationページでは、最初にこのように書かれています。

多くのウェブアプリケーションは、ユーザがアプリケーションで認証して「ログイン」するための方法を提供しています。この機能をウェブアプリケーションに実装することは、複雑でリスクを伴う可能性があります。このため、Laravelは、認証を素早く、安全に、簡単に実装するために必要なツールを提供するように努めています。
Laravelの認証機能は「ガード」と「プロバイダ」で構成されています。ガードは、各リクエストに対してユーザーがどのように認証されるかを定義します。例えば、Laravelにはセッションガードが搭載されており、セッションストレージとクッキーを使用して状態を維持します。
プロバイダーは、永続的なストレージからユーザーを取得する方法を定義します。LaravelはEloquentとデータベースクエリビルダを使ったユーザーの取得をサポートしています。しかし、アプリケーションに必要に応じて追加のプロバイダを自由に定義することができます。
アプリケーションの認証設定ファイルはconfig/auth.phpにあります。このファイルには、Laravelの認証サービスの動作を微調整するためのいくつかのオプションが含まれています。
www.DeepL.com/Translator (無料版)で翻訳しました。

Config

認証情報の設定は config/auth.php にまとまっています。

<?php

return [
    // デフォルト(Guard未指定)での利用 Guard(`Auth::user()` などした時に使われるもの)
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    // Guard の実装リスト。 Middleware の `auth:api` や `Auth::guard('api')` のように明示指定出来る
    'guards' => [
        'web' => [
            // SessionGuard を利用し、プロバイダは `users` (後述のキー値)
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            // TokenGuard を利用し、プロバイダは `users`, ハッシュ化はしない
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],
    ],

    'providers' => [
        // 'users' プロバイダは Eloquent を利用する
        // 'users' というキー名は上記 Guard の 'provider' に指定するもの
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],

        // 代わりに DatabaseUserProvider を指定することも可能
        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],
// ... 以下省略
];

Guard

どのリクエスト情報(session だとか Authorization ヘッダーだとか)からユーザ情報を取得出来るかの実装です。

<?php

namespace Illuminate\Contracts\Auth;

interface Guard
{
    /**
     * 現在のユーザが認証されているかどうか
     *
     * @return bool
     */
    public function check();

    /**
     * 現在のユーザがゲスト(未ログイン)かどうか
     *
     * @return bool
     */
    public function guest();

    /**
     * 現在の認証されたユーザーを取得する
     *
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function user();

    /**
     * 現在の認証されたユーザーの ID を取得する
     *
     * @return int|string|null
     */
    public function id();

    /**
     * ユーザーの秘匿情報をバリデートする
     *
     * @param  array  $credentials
     * @return bool
     */
    public function validate(array $credentials = []);

    /**
     * 現在のユーザーを設定する
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @return void
     */
    public function setUser(Authenticatable $user);
}

このインターフェースの実装となります。

これに対する Laravel 上での実装は RequestGuard, TokenGuard, SessionGuard の三つになります。

RequestGuard

これは HTTP Request 内の任意の要素を使ってログイン判定をする最も簡単な Guard です。

Auth::viaRequest('custom-token', function (Request $request) {
  return User::where('token', $request->token)->first();
});

このようにして新しいドライバーをクロージャで作ることが出来ます。

return [
  'guards' => [
    'api' => [
      'driver' => 'custom-token',
    ],
  ],
];

config/auth.php でこのようにドライバーを指定することで機能します。簡単。

これはステートレスなのでセッションを張らずに認証が可能です(代わりに毎回同じトークンを渡す必要があります)。

TokenGuard

これはリクエストからトークンを取得し、それをもって認証する Guard です。

  • クエリストリング /foo?api_token=aaa
  • リクエストデータ curl --data '{"api_token":"aaa"}'
  • Bearer トークン Authorization: Bearer aaa
  • パスワード https://foo:aaa@example.com

これらを探索し、トークンが見つかればそれを利用してログインを試みます。 api_token というキー名は config で上書きが可能です。

これもステートレスです。

SessionGuard

これはクッキーとストレージにセッション情報を残し、ステートフルに認証する Guard です。

普通にブラウザでアクセスする要件ではまずこれを使うのが基本になると思います。


UserProvider

Guard は「クライアントから送られてきた認証情報をどう取得するか」を定義したものに対し、 UserProvider は「取得した認証情報からデータベース内のユーザー情報をどう取得するか」を定義したものです。

<?php

namespace Illuminate\Contracts\Auth;

interface UserProvider
{
    /**
     * ユニークIDでユーザーを取得する
     *
     * @param  mixed  $identifier
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveById($identifier);

    /**
     * ユニークIDと "remember me" トークンでユーザーを取得する
     *
     * @param  mixed  $identifier
     * @param  string  $token
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByToken($identifier, $token);

    /**
     * 渡されたユーザーの "remember me" トークンを更新する
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param  string  $token
     * @return void
     */
    public function updateRememberToken(Authenticatable $user, $token);

    /**
     * 渡された秘匿情報からユーザーを取得する
     *
     * @param  array  $credentials
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByCredentials(array $credentials);

    /**
     * 渡された秘匿情報を使ってユーザーをバリデートする
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param  array  $credentials
     * @return bool
     */
    public function validateCredentials(Authenticatable $user, array $credentials);
}

インターフェースはこのようになっています。

remember token が必須になっているのは若干モヤっとする設計ですが、基本的に渡されたパラメータでデータベースからユーザーを取得するメソッドを実装することになります。

Laravel では EloquentUserProviderDatabaseUserProvider が実装されています。

EloquentUserProvider

最初からある App\Models\User クラスのような ORM を使って取得する手法です。最も一般的です。

DatabaseUserProvider

コンフィグからテーブル名を指定して password カラムを使って取得する手法です。カラム名が password 固定なのがモヤっとするというか、使いづらい所ですね。


ちなみに RequestGuard を利用する場合はこの UserProvider を通さずにログインすることが出来ます(指定したクロージャで DB にアクセスしているので)。


この UserProvider で利用されるユーザー情報は Illuminate\Contracts\Auth\Authenticatable インターフェースを実装したクラスである必要があります(Eloquent である必要はありません)。

<?php

namespace Illuminate\Contracts\Auth;

interface Authenticatable
{
    /**
     * ユーザーのユニークIDのカラム名を取得する
     *
     * @return string
     */
    public function getAuthIdentifierName();

    /**
     * ユーザーのユニークIDを取得する
     *
     * @return mixed
     */
    public function getAuthIdentifier();

    /**
     * ユーザーのパスワードを取得する
     *
     * @return string
     */
    public function getAuthPassword();

    /**
     * "remember me" セッション用のトークンを取得する
     *
     * @return string
     */
    public function getRememberToken();

    /**
     * "remember me" セッションのトークンを設定する
     *
     * @param  string  $value
     * @return void
     */
    public function setRememberToken($value);

    /**
     * "remember me" トークンのカラム名を取得する
     *
     * @return string
     */
    public function getRememberTokenName();
}

またこの Authenticatable のデフォルト実装 trait として Illuminate\Auth\Authenticatable を利用することが出来ます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelでDBに保存した画像を表示する方法

はじめに

前回Laravelで画像のパスをDBに保存する方法をご紹介したので、今回は画像を表示する方法関してご紹介していきたいと思います。

保存する方法に関しては以下の記事をご参考ください。
Laravelで画像をアップロードし、パスをDBに保存する方法

こちらでは、画像のパスがすでにDBに保存されている前提で進めており、ファイル名等は全て前回作成したものを利用していきます。

-各バージョン
-laravel 6.x
-PHP 7.4.9
-mySQL 5.7.30

コントローラーを記述する

今回はindexアクションを使っていきたいと思います。

DBファザード使用するため、useで宣言し、そのあとに書いていきます。

BookController
use Illuminate\Support\Facades\DB;

public function index()
{
  $books = DB::table('books')->orderBy('created_at', 'desc')->get();

  return view('index', compact('books'));
}

まず、前回作成したbooksテーブルのデータを取得し、orderBy()created_at、つまり作成した日時をdesc順に並べるように指定しています。descとはdescendの略で、下に降りるという意味になるので、指定することによって作成した日時が新しいものが先頭に来るようになります。
そして取得したものを$booksという変数に格納しています。

続いて格納したものをreturnでビューに渡すようにします。
index.blade.phpというビューをこのあと作成するので、indexを指定し、compact()メソッドで変数をビューに渡すことができるようになっています。compact()を使用するときは$マークはつけずに記述して問題ありません。

また、今回はデータが少ないため全てのデータを取得していますが、もしデータが多く一覧画面では一部のデータのみ表示させたい場合は、以下のように書くこともできます。

BookController
use Illuminate\Support\Facades\DB;

public function index()
{
  $books = DB::table('books')
  ->select('id','title', 'author', 'created_at') //ここを追加
  ->orderBy('created_at', 'desc')
  ->get();

  return view('index', compact('books'));
}

select()でデータを選択することができるので、表示したいカラム名を指定することができます。
この場合は別途、全てのデータを表示する画面を作成するといいでしょう。

ビューを作成する

新しくデータを表示するindex.blade.phpを作成し、一覧を表示させるコードを書いていきます。
なお、今回はデータが少ないので全てのデータが表示されるようにしていきます。

index.blade.php
@foreach('books' as 'book')
  <table>
    <tr>
      <th>タイトル</th><td>{{ $book->title }}</td>
    </tr>
    <tr>
      <th>作者</th><td>{{ $book->author }}</td>
    </tr>
    <tr>
      <th>投稿日</th><td>{{ $book->created_at }}</td>
    </tr>
    <tr>
      <th>画像</th><td><img src="{{ asset('uploads/books/' . $book->image) }}" alt="book-image"></td>
    </tr>
  </table>
endforeach

BookControllerでbooksテーブルのデータを$booksに格納しているので、データをforeachで回して表示するようにしています。
画像に関しては、DBには画像のパスしか保存されていないため、画像の保存先を指定する必要があります。画像は前回Publicフォルダ内のuploads/booksに保存したので、今回はそのように指定しています。
また、見えやすくするようにtableを使用しましたが、便宜に合わせてulpタグで表示することも可能です。

ルーティングを指定する

index.blade.phpに接続したら、BookController内のindexアクションが起動するようにします。

web.php
Route::get('index', 'BookController@index')->name('index');

以上で完成です!

さいごに

今回はDBに保存している画像のパスを表示させる方法に関して解説しました。

もっとこういうやり方あるよーという場合は、やさしくコメントで教えていただけるとありがたいです!
ここまで読んでくださりありがとうございました!

参考

Laravel Image CRUD : How to fetch the image in Laravel

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む