20201019のPHPに関する記事は12件です。

PHP 一覧表示機能、fetch+whileをfetchAll+foreachに書き換える。

fetch+while

fetchは一件だけ取得するとそこで処理が終わる。whileでそれを繰り返すことで一覧表示できる。

while ($user = $users->fetch(PDO::FETCH_ASSOC)) : ?>
   <div class="msg">
    <img src="<?php print(htmlspecialchars($user['picture'])); ?>" />
    <p><span class="name"><?php print(htmlspecialchars($user['title'])); ?>です</span></p>
   </div>
<?php endwhile; ?>

fetchAll+foreach

fetchAllでデータを全件取得してそれを配列にまとめるので、その配列化したものをforeachで1件ずつ表示する。

$users = $users->fetchAll(PDO::FETCH_ASSOC);
  foreach ($users as $user) : ?>
   <img src="<?php print(htmlspecialchars($user['picture'])); ?>" />
   <p><span class="name"><?php print(htmlspecialchars($user['title'])); ?>です</span></p>
   <br>
<?php endforeach ?>

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

imagepng

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

【PHP】public・protected・private 適当に決めてない?

突然ですが
メソッドの前につける public・protected・private
適当に決めている人いませんか?

はい、僕です。

「これはまぁどこからも呼ばない気がするし private でいいかな。。」
「うーんこれあとで他で使いそうだなぁ。。。いや微妙だなぁ。。。よしとりあえず protected にしとくかっ。」

はい、僕です。

ということで、今回は いわゆる アクセス修飾子 について、
「可読性」という視点から、改めて考えたいと思います。

まず、おさらいです。

public・・・どこからでもアクセス可能
private・・・継承先のサブクラスからアクセス可能
protected・・・そのクラスからのみアクセス可能

ですね。

それは知ってるんです。それはみんな知ってるんですよ!!
知ってるだけで使えないんだったら、なんの意味があるっていうんですか!!!

はぁはぁ。。。
何なんだこれは。。。

話は変わりますが、
『人生』には、どうやっても変えられないものと、変えることができるものがあります。
過去の偉人たちも言っています。

民よ。変えられない「過去」に嘆くのならば、変えられる「未来」を見ようではないか。
by ナポレオン

「他人」は変えることはできない。変えることができるとしたら、それは「自分」である。
by アドラー

相手の「変」なところを変えようとしないで、心で受け入れようよ。そしたらそれが「恋」に変わるからさ。
by 紫式部

すいません嘘です。ググっても出てきません。

そんな感じで、クラス設計をする際も、
変えることができる属性と、変えることができない属性を、
明確に定義しておいた方がいいですよね。

では『人生』ではなく「牛丼」で考えてみましょう。

◯屋の牛丼を頼み、カウンターに置かれました。
この時点で、変えることができるものと、変えることができないものって何でしょう?

「牛丼を食す上で変えられないもの」
・ごはんの量
・値段

「牛丼を食す上で変えることができるもの」
・紅生姜の量
・お箸で食べるか、スプーンで食べるか

GyuDon.php
class GyuDon {
    private $riceAmount = 200; // ごはんの量
    private $price = 350; // 値段
    public $beniSyoga = '3袋'; // 紅生姜の量
    public $useChopsticks = true; // お箸を使う
}

変えることができないものというのは、
変えようとすると怒られます。

ごはんの量を変えてみましょう。

main.php
$gyudon = new GyuDon();

$gyudon->riceAmount = 300; // エラーが起きる

みなさん想像してみてください、牛丼が出されたあとに、
やっぱりご飯を大盛りにしてくれと頼んだら、確実に嫌な顔をされますよね。

次に、紅生姜の量を増やしてみようと思います。

main.php
$gyudon = new GyuDon();

$gyudon->beniSyoga = '10袋'; // エラーは起きない

はい、紅生姜をいくら盛ろうが、誰にも怒れませんね。

最後に

private・protected・public を なんとなくではなく、
変えることができるものと、変えることができないもの、という観点で決めるようにすると、
よりオブジェクト指向を深く理解できるのではないでしょうか。

それでは、みなさん、紅生姜は常識の範囲内でね:no_good:

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

【PHP】Carbon を使用して日付比較する際の覚え方

PHPer の皆さん、日付の比較どうしてますか〜?

おそらく

・strtotime関数
・DateTimeクラス
・Carbonライブラリ

などを使用されてるかと思います。

ちなみに簡単な日付の比較であれば、文字列のまま比較できることをご存知でしょうか?
まだご存知でない方は、こちらの記事をご覧ください。
https://qiita.com/fusekichi/items/5fa20a37a0220ee879e9

そして私ですが、基本的に Carbon ライブラリ を使用しています。

とっっても便利なため、
ここで Carbon ライブラリの使用方法を、
つらつら書き連ねてもよいのですが、

全てこの記事に書いてくれているので、、、
https://qiita.com/yudsuzuk/items/ff894bd0b76d4657741d

今回は Carbon で日付比較する際に、地味に分かりずらい関数の覚え方を、
満を辞して紹介しようと思います。

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

未経験からPHP+MySQLではじめてwebアプリの作成をしてみました。。

記事の概要

2020年3月からプログラミング学習を開始し、初めてwebアプリを作成したので、その記録となります。
用語の使い方やコーディングなど認識違いございましたら、ご指摘していただけると幸いです。

アプリ概要

学習の記録、記録した学習内容の閲覧やデータの集計を行える学習管理アプリ
スクリーンショット 2020-10-07 14.06.56.png
アプリurl : https://studydiaryportfolio.herokuapp.com/TopPage.php
Git hub : https://github.com/mu7kata/yuki_Portfolio

▼制作期間

・29日 85h

【内訳】
要件定義、WF作成:8h
コーディング:53h
デプロイ :23.5h 

▼制作環境

 【言語】
  HTML、CSS(レスポンジブ未対応)、PHP 7.3.9、JavaScript、jQuery2.2.2
 【DBMS】
  MySQL 5.7.26
 【フレームワーク】
  なし
 【開発環境】
  MacOS Catalina 10.15.5
 【バージョン管理】
  Git 2.24.3
 【本番環境】
  Heroku
↓Herokuでのデプロイまでの流れはこちらにまとめました。
https://qiita.com/kata_kata_1997/items/973fc1f31795ebf6fcfa

アプリを作成する目的

▼PHPを学習していく中で下記項目についての理解を深めるため

・関数の定義〜呼び出し
・GET送信とPOST送信
・セッション
・条件分岐(if文)と繰り返し処理(foreach文)などの基本構文
・SQL文
・DB接続(try~catchを用い、成功しているかどうか判断)
・DBデータの編集、画面への出力

なぜ学習管理アプリか

プログラミング学習をしていくなかで、学習状況の把握をすることで、モチベーションにつながると思ったから。
自分自身も実際に、学習した履歴をエクセルなどで管理していたが、より簡単に学習の管理ができないかと感じたから

主な機能

・ログイン機能
・学習内容の保存機能
・学習内容の表示機能
・学習内容の編集機能
・メモ機能

機能詳細

▼ログイン機能

・ユーザー情報の取得
→PDOクラスを用いてDBへ接続。
スクリーンショット 2020-10-14 0.49.10.png

▼学習内容の保存機能

・学習時間、カテゴリ、内容を保存することができる。カテゴリは編集が可能。
→入力に必要なSQL文のカラム名に、プレースホルダーを代入する形で学習内容の保存、取得する関数を作成。その後SQL文のプレースホルダーに値をバインドし、クエリを実行する。
スクリーンショット 2020-10-07 14.08.15.png

▼学習内容の表示機能

・学習内容の取得して表示。日付、カテゴリでの検索が可能。画面中央には指定した学習期間の合計学習時間を算出し表示している。
→クエリの実行結果を格納したオブジェクトを条件分岐(if文)や繰り返し処理(foreach文)などの基本構文をもちていてHTML上に表示。
スクリーンショット 2020-10-14 0.53.47.png

▼学習内容の編集機能

・学習内容の編集ボタンから、編集ページへ遷移。日付、時間、カテゴリ、内容を編集することができる。
→作成した学習内容取得関数内の引数に入力フォームに入力された出力条件を代入する形で出力、更新を実行。
スクリーンショット 2020-10-14 0.54.56.png

▼メモ機能
・その日の目標や、やることリストなどに使用することができる。
→セッションを使用し、入力の保持を実行
スクリーンショット 2020-10-14 0.55.50.png

工夫した点

■学習内容入力ページ内にその日保存した学習内容を表示。入力する際、内容が以前保存したものと同じだった場合にコピペして登録することができる。

■トップページに月毎の学習時間の合計を表示。詳細ボタンをクリックすると、自動でその月の学習内容を検索し詳細を表示できるように実装

■月単位での1日あたりの学習時間の平均を表示することで進捗度の可視化を実施。

スクリーンショット 2020-10-14 0.56.42.png

■カテゴリを自分で作成し、使用することができる。
↓カテゴリ編集画面
スクリーンショット 2020-10-14 0.57.50.png

■各ボタンにはテキストだけでなく、アイコンを使うことによってボタンを押した時に何が起こるのかを視覚的に伝え、より使いやすいデザインを意識。
↓各ボタンにアイコンを使用。
スクリーンショット 2020-10-14 0.58.15.png

苦労したこと

■Gitを使用したバージョン管理。
→初めてGitを使用しての開発となったが、リベースとマージの違いを理解できておらず、作成した内容がマージされるまでに苦戦した。(マージした思ったら書いたコードがまっさらになって焦りました。。)

■エラー対応
エラー内容をログで読み取り、エラー箇所の修正。分からなければグーグル検索、Qiita内で質問しての対処となりました。

【苦戦したエラー】
▼学習内容検索機能の実装
・検索フォームに入力した値はあっているはずなのにが一致せずデータを取得することができなかった。
→型が一致していなかったことが原因。検索対象の日付をint型に設定したところ改善

▼Herokuにデプロイ時のエラー処理
・ERROR 1044 (42000)
→ClearDB では既定以外に新しいデータベースを作ることはできないのに、作成する指示がエクスポートファイルに描かれていたことが原因。対象コードを削除して対応。

・404 not found(404エラー)
→ファイル名の大文字、小文字が混同してしまっていたことが原因。対象コードを修正して対応

課題

・レスポンシブ対応

・削除機能の追加

・計画的な設計
→簡単なワイヤフレーム、DB設計書を作成したものの、あまり活用せず、場当たり的な設計になり、時間がかかってしまった。ER図やクラス図などもしっかり作成して望みたい。

↓作成したワイヤフレーム

スクリーンショット 2020-10-12 22.42.09.png

↓テーブル設計

スクリーンショット 2020-10-12 22.41.23.png

・きれいなコード
コードのリファクタリングを行い、効率的なコードにしていきたい。

学んだこと

・Gitの使い方(add.~pushまで、ブランチをきっての作業)
・解決できないエラーに遭遇した場合の質問のコツ(エラーにまでの過程、試してみたことも付け加えるなど。)
・自分のアイデアが形になる楽しさ
・アウトプットの重要性。

最後に

学んだことを実践してみることで、あまり理解できていない部分に気付くことができ、理解が深まりました。この開発を経てアウトプット前提にインプットすることがやはり重要だと感じたので、これを意識して今後は学びを深めていこうと思います。

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

PHP8.0のJITコンパイラはPHP7.4+FFI(Go)にどこまで近づけるか

はじめに

以前このような記事を投稿しました

PHP7.4の新機能、FFIでGoを使ってみた
https://qiita.com/i4M1k0SU/items/7c0db12e047e0fbf7550

この記事ではPHP7.4での新機能、FFIからGoで実装した共有ライブラリを利用し、PHPだけでの処理の20倍以上ものパフォーマンスを出すことができました

今回はPHP8.0に追加されたJITコンパイラ機能を利用し、この結果にどこまで近づけるかを検証します

PHP8.0の導入

PHP8.0はこの記事を投稿した段階ではまだ正式なリリースがされていません

今回はmacを利用しているのですが、DockerやVMは使わずhomebrewからネイティブ環境に導入することにしました

https://github.com/shivammathur/homebrew-php

brew update
brew tap shivammathur/php
brew install shivammathur/php/php@8.0
brew link --overwrite --force php@8.0

JITコンパイラの有効化

php.iniを書き換えます

/usr/local/etc/php/8.0/php.ini
opcache.enable=1
opcache.enable_cli=1
# 以下は追記する必要があります
opcache.jit=1235
opcache.jit_buffer_size=128M

JITコンパイラでPHPはどれだけ速くなるか

以前書いた記事と全く同じ検証用コードです
ついでにPHP8.0+FFIも検証します

const COUNT = 40;

$ffi = FFI::cdef(
    'extern unsigned int calcFibonacci(unsigned int p0);',
    '[PATH]/fibonacci.dylib'
);

// 比較対象のPHP実装サンプル
function calcFibonacci(int $i): int {
    if ($i < 2) {
        return $i;
    }

    return calcFibonacci($i - 1) + calcFibonacci($i - 2);
}

// Go(FFI)実装の実行速度計測
$startTime = microtime(true);
for($i = 1; $i <= COUNT; $i++) {
    $ffi->calcFibonacci($i);
}
$time = microtime(true) - $startTime;
echo "Go(FFI): {$time}\n";

// PHP実装の実行速度計測
$startTime = microtime(true);
for($i = 1; $i <= COUNT; $i++) {
    calcFibonacci($i);
}
$time = microtime(true) - $startTime;
echo "PHP: {$time}\n";

結果

FFI(Go) PHP
PHP7.4 (前回の結果) 1.5760109424591 秒 33.616943836212 秒
PHP8.0 without JIT (and opcache) 1.8609459400177 秒 42.444294929504 秒
PHP8.0 with JIT 1.9577729701996 秒 9.5034010410309 秒

良い結果が出たのではないでしょうか

気になる点として、PHP8.0は何もしなくてもPHP7.4より速くなると言った内容の記事もありましたが、私の環境ではJITコンパイラ無効時はPHP7.4より遅くなっているようです
また、FFI利用時は誤差レベルかもしれませんがJITコンパイラの設定に関係なく、少し遅くなっているようです

とは言ってもJITコンパイラを有効にした場合の結果はとても優秀で、FFIには追いつかずともかなり大きな速度向上が見込めるのではないでしょうか

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

Laravel8 CRUD処理を使った投稿アプリを作成する その3 投稿保存処理の作成

目的

  • 投稿内容を保存する処理を追加する

実施環境

  • 筆者の実施環境を記載する。
  • ハードウェア環境
項目 情報
OS macOS Catalina(10.15.5)
ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
プロセッサ 2 GHz クアッドコアIntel Core i5
メモリ 32 GB 3733 MHz LPDDR4
グラフィックス Intel Iris Plus Graphics 1536 MB
  • ソフトウェア環境
項目 情報 備考
PHP バージョン 7.4.8 Homebrewを用いてこちらの方法で導入→Mac HomebrewでPHPをインストールする
Laravel バージョン 8.6.0 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う
MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする

前提条件

  • その1の記事の内容が完了していること。

前提情報

  • 作成するアプリ名は「laravel8_crud」とする。
  • 作成するMySQLのデータベース名は「laravel8_crud_DB」とする。
  • 下記に今回の作業のあとのソースコードのリモートリポジトリのリンクを記載する。
  • 投稿をデータベースのテーブルに保存したあとリダイレクトするURLはhttp://127.0.0.1:8000/inputとする。

概要

  1. ルーティング情報の記載
  2. コントローラファイルの記載
  3. モデルファイルの作成
  4. テーブルの作成
  5. ビューファイルの修正
  6. 確認

詳細

  1. ルーティング情報の記載

    1. laravel8_crudディレクトリで下記コマンドを実行してルーティングファイルを開く。

      $ vi routes/web.php 
      
    2. 開いたファイルに下記の行を追記する。

      laravel8_crud/routes/web.php
      Route::post('/save', [ContentController::class, 'save'])->name('save');
      
    3. 追記後のルーティングファイルの内容を下記に記載する。

      laravel8_crud/routes/web.php
      <?php
      
      use Illuminate\Support\Facades\Route;
      use App\Http\Controllers\ContentController;
      
      /*
      |--------------------------------------------------------------------------
      | 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');
      });
      
      Auth::routes();
      
      Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
      
      Route::get('/input', [ContentController::class, 'input'])->name('input');
      // 下記を追記する
      Route::post('/save', [ContentController::class, 'save'])->name('save');
      
  2. コントローラファイルの記載

    1. laravel8_crudディレクトリで下記コマンドを実行して作成したコントローラファイルを開く。

      $ vi app/Http/Controllers/ContentController.php 
      
    2. 下記の内容をクラス内に追記する。

      laravel8_crud/app/Http/Controllers/ContentController.php
      use App\Models\Content;
      // -----------省略-----------
      public function save(Request $request)
      {
          $input_content = new Content();
          $input_content->content = $request['content'];
          $input_content->save();
      
          return redirect(route('input'));
      }
      
    3. 追記後のコントローラファイルの内容を下記に記載する。

      laravel8_crud/app/Http/Controllers/ContentController.php
      <?php
      
      namespace App\Http\Controllers;
      
      use Illuminate\Http\Request;
      // 下記を追記する
      use App\Models\Content;
      
      class ContentController extends Controller
      {
          public function input()
          {
              return view('contents.input');
          }
      
          // 下記を追記する
          public function save(Request $request)
          {
              $input_content = new Content();
              $input_content->content = $request['content'];
              $input_content->save();
      
              return redirect(route('input'));
          }
          // 上記までを追記する
      }
      
  3. モデルファイルとマイグレーションファイルの作成

    1. laravel8_crudディレクトリで下記コマンドを実行してモデルファイルとマイグレーションファイルを作成する。

      $ php artisan make:model Content --migration
      
  4. テーブルの作成

    1. laravel8_crudディレクトリで下記コマンドを実行して作成したマイグレーションファイルを開く。(YYYY-MM-DDにはマイグレーションファイル作成日が、XXXXXXにはランダムな数字が入る。)

      $ vi database/migrations/YYYY_MM_DD_XXXXXX_create_contents_table.php
      
    2. 下記の様に記載を行う。記載後のマイグレーションファイルの内容を下記に記載する。

      laravel8_crud/database/migrations/YYYY_MM_DD_XXXXXX_create_contents_table.php
      <?php
      
      use Illuminate\Database\Migrations\Migration;
      use Illuminate\Database\Schema\Blueprint;
      use Illuminate\Support\Facades\Schema;
      
      class CreateContentsTable extends Migration
      {
          /**
           * Run the migrations.
           *
           * @return void
           */
          public function up()
          {
              Schema::create('contents', function (Blueprint $table) {
                  $table->id();
                  // 下記を追記する
                  $table->string('content');
                  $table->timestamps();
              });
          }
      
          /**
           * Reverse the migrations.
           *
           * @return void
           */
          public function down()
          {
              Schema::dropIfExists('contents');
          }
      }
      
    3. laravel8_crudディレクトリで下記コマンドを実行してマイグレーションを行い、テーブルを作成する。

      $ php artisan migrate
      
  5. ビューファイルの修正

    1. laravel8_crudディレクトリで下記コマンドを実行してビューファイルを開く。

      vi resources/views/contents/input.blade.php
      
    2. 作成して開いたビューファイルを下記の様に修正する。

      laravel8_crud/resources/views/contents/input.blade.php
      <h1>input</h1>
      
      <!-- 下記を修正 -->
      <form action="{{route('save')}}" method="post">
          @csrf
          <textarea name="content" cols="30" rows="10"></textarea>
          <input type="submit" value="送信">
      </form>
      
  6. 確認

    1. laravel8_crudディレクトリで下記コマンドを実行してローカルサーバを起動する。

      $ php artisan serve
      
    2. ブラウザで下記にアクセスする。

    3. テキストボックスに何かしらの文字列を入力して「送信」をクリックする。

      127_0_0_1_8000_input.png

    4. リダイレクト処理が実行されて今一度http://127.0.0.1:8000/inputのページが表示される。

      127_0_0_1_8000_input.png

    5. 下記コマンドを実行してMySQLにログインする。(MySQLのrootユーザのパスワードを忘れてしまった方はこちら→Mac ローカル環境の MySQL 8.0 のrootパスワードを忘れた時のリセット方法)

      $ mysql -u root -p
      
    6. 下記SQLを実行する。

      select * from laravel_crud_DB.contents;
      
    7. 下記の様に出力されれば本記事の作業は完了である。(contentの内容は皆さんがブラウザのテキストボックスに入力した内容)

      +----+--------------------------+---------------------+---------------------+
      | id | content                  | created_at          | updated_at          |
      +----+--------------------------+---------------------+---------------------+
      |  1 | これはテストです             | 2020-07-14 07:50:16 | 2020-07-14 07:50:16 |
      +----+--------------------------+---------------------+---------------------+
      
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Laravel]ファサード is 何

はじめに

初投稿です。

掌田 津耶乃さん著「PHPフレームワークLaravel入門」を学習中、しばしば意味の分からないワードが出てきていました。

「ファサード」

ワイ「なんだお前(思考停止)」

調べてみると、どうもLaravelに限った用語ではないそうなので、自分なりにまとめてみます。
記事を書く練習にもなるし。

ファサードの語源

語源というかそもそもの意味ですが、建築用語で「建築物の正面部分(デザイン)」を意味するフランス語だそうです。

建物の顔、入り口部分といった感じのイメージですかね。

確かに、フランスの古い建築物をググって調べてみると、正面からの見栄えが重視されてる気がします。

ITにおいてのファサード

正確には「ファサードパターン(Facade pattern)」と呼ばれており、コンピュータソフトウェアのデザインパターンの一つとのこと。

デザインパターンとは、wikiに乗ってる情報をまとめると、経験則に基づく典型的なパターンに名前を付け、再利用しやすいようにカタログ化した物、という理解で良いと思います。

話を戻すと、ファサード・パターンとは、単純な操作だけを持ったFacadeクラスを実装し、(メインシステムと異なる)サブシステムを結びつける事を言います。

建物(サブシステム)に対し正面玄関(ファサード)を実装するイメージですね。

これにより、サブシステムの詳細を知らなくてもファサードからサブシステムの機能を呼び出せます。

ファサードパターンのメリット

ファサードを用いることで、ユーザーが必要な機能のみに絞ってサブクラスから呼び出すことができます。
また、ユーザーはサブクラスの詳細を知る必要がないため、サブクラスの実装から解放されます。

要はサブクラスの中身は知らないけど、ファサードがあるから簡単に使えるよねって所でしょうか。
この辺は感覚としては分かりますが、言語化するのが難しいです。

ユーザー毎に使用するクラスを絞れるというのは明確なメリットだと思います。
ファサードクラスを見るだけでよく使う機能が分かりますし。

ちなみに、ファサードパターンはサブクラスの機能を直接呼び出すことを特に制限はしていません。
これは少し意外でした。

必要ならファサードで定義されていない機能も使っていいよってことですね。

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

【PHP】日付の簡単な比較

PHPer の皆さん、日付の比較どうしてますか〜?

おそらく

・strtotime関数
・DateTimeクラス
・Carbonライブラリ

などを使用されてるかと思います。

しかし、簡単な比較であれば、
文字列のまま比較演算子を使用できるのをご存知でしょうか。

自分もたまたまできることに気づいたので、
どの形式まで日付を比較できるのかを試してみたので、ご紹介します。

まずは YYYY-mm-dd 形式の比較です。

'2020-09-09' > '2020-09-08', //true
'2020-09-09' >= '2020-09-08', //true
'2020-09-09' > '2020-09-09', // false
'2020-09-09' >= '2020-09-09', //true

期待通り、比較結果がえられました。

続いて上記から 0 を省いてみます。

'2020-9-9' > '2020-9-8', //true
'2020-9-9' >= '2020-9-8', //true
'2020-9-9' > '2020-9-9', // false
'2020-9-9' >= '2020-9-9', //true

大丈夫そうです。

続いて YYYY/mm/dd 形式で比較してみましょう。(0 を省くのも一緒にやっちゃいます)

'2020/09/09' > '2020/09/08', //true
'2020/09/09' >= '2020/09/08', //true
'2020/09/09' > '2020/09/09', // false
'2020/09/09' >= '2020/09/09', //true
'2020/9/9' > '2020/9/8', //true
'2020/9/9' >= '2020/9/8', //true
'2020/9/9' > '2020/9/9', // false
'2020/9/9' >= '2020/9/9', //true

問題ないですね。

さらに日時で比較してみましょう。

'2020-09-09 09:09:09' > '2020-09-09 09:09:08', //true
'2020-09-09 09:09:09' >= '2020-09-09 09:09:08', //true
'2020-09-09 09:09:09' > '2020-09-09 09:09:09', // false
'2020-09-09 09:09:09' >= '2020-09-09 09:09:09', //true

'2020-9-9 9:9:9' > '2020-9-9 9:9:8', //true
'2020-9-9 9:9:9' >= '2020-9-9 9:9:8', //true
'2020-9-9 9:9:9' > '2020-9-9 9:9:9', // false
'2020-9-9 9:9:9' >= '2020-9-9 9:9:9', //true

'2020/09/09 09:09:09' > '2020/09/09 09:09:08', //true
'2020/09/09 09:09:09' >= '2020/09/09 09:09:08', //true
'2020/09/09 09:09:09' > '2020/09/09 09:09:09', // false
'2020/09/09 09:09:09' >= '2020/09/09 09:09:09', //true

'2020/9/9 9:9:9' > '2020/9/9 9:9:8', //true
'2020/9/9 9:9:9' >= '2020/9/9 9:9:8', //true
'2020/9/9 9:9:9' > '2020/9/9 9:9:9', // false
'2020/9/9 9:9:9' >= '2020/9/9 9:9:9' //true

すべて問題ありませんね!

まとめ

・簡単な比較であれば日付を文字列のまま比較できる
・形式は「Y-m-d」「Y/m/d」どちらも OK
・時間が含まれていても OK
・0 が省かれていても OK

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

【備忘録】LaravelでAuthを導入

Laravelのバージョンを確認

ターミナル
$ php artisan -v

Laravel Framework 7.x もしくは 6.xの場合

$ cd ~/[プロジェクトの階層]まで移動して、
Composerでlaravel/uiパッケージをインストール

ターミナル
$ composer require laravel/ui



② Authの認証に必要なルートとビューをスカフォールド
(スカフォールド(scaffold)...必要なフォルダ構成やソースなどを自動的にセットして、すぐに試せる状態までもっていってくれる)

ターミナル
$ php artisan ui vue --auth



③ 必要なJavaScriptパッケージをインストール

ターミナル
$ npm install && npm run dev

npm install → package.jsonに書かれた設定に基づいて、必要なJavaScriptパッケージをダウンロード・インストールするコマンド。
npm run dev → それらをコンパイルして実行可能な状態にするコマンド。



④ マイグレーションしてAuthの導入完了!

ターミナル
$ php artisan migrate




Laravel Framework 5.xの場合

下記のコマンドを実行していくだけ!
ターミナル
$ cd ~/[プロジェクトの階層]
$ php artisan make:auth
$ php artisan migrate

参考記事

【公式】Laravel 7.x 認証

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

【Laravel】ユニットテストのファイルを生成した時 にDatabase [mysql] not configured. が出た時の対処法

エラー状況

Laravelのユニットテストを実行するためにターミナルからテストファイルを生成しようとした時にエラーが出てしまい、生成ができずに行き詰まる。

以下のコマンドでテストファイルの生成を実行しようとする。

$ php artisan make:test SampleTest

But!!

Database [mysql] not configured. と表示され、生成ができない状態。
ファイル名

原因

vender/laravel/framework/src/illminate/Database/DatabaseManager.php
の該当lineを覗いてみるが、問題なさそう。というかよくわからない。

ここでさっきCirclCIを設定した時にconfig/database.phpを変更した事を思い出したので
そのファイルを確認したみると、一番下にこの記述をしている。

database.php
    'connections' => [
        'circle_test' => [
            'driver' => 'mysql',
            'host' => '127.0.0.1',
            'port' => '3306',
            'database' => 'circle_test',
            'username' => 'ubuntu',
            'password' => '',
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'strict' => true,
            'engine' => null,
        ],
    ]

CircleCI用に設定したコードですが、また新しく'connections'作って記述してました、、、
一旦このコードを全て消して、再度ターミナルからテストファイルを生成したら
普通にうまくいきました!!

総括

今回は単純な記述ミスによるエラーでした。
ただ同じエラーが発生した場合はconfig/database.php内の記述を疑ってみてください。
ググっても答えが出てこなかったので、参考になればと思います。

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

Humhubのカスタムモジュールの作り方(Widgetへのアイテム追加(->addItem))

はじめに

 前回までは、コールバックイベントによるAdminMenuへの項目の追加の様子を、公式のドキュメントとcustom_pageモジュールの実装を参照し、概説した。今回は、自作モジュールにおいて「ログインユーザーの参加スペースに応じて、トップナビゲーション部に表示するリスト項目を追加する」場面を考えてみよう。

プラグインモジュールの準備

前々回にしたがって、最低限必要な3つのファイルと、前回を参考にイベントに応じたコールバック関数を記述するEvents.phpファイルを用意することにしよう。今回は「topnavilist」モジュールとして開発すると想定する。ファイル構成例は、次のようになる。

topNaviList(フォルダ)
  ├── config.php
  ├── module.json
  ├── Module.php
  └── Events.php

それぞれのファイルの作成上の注意は、前回・前々回で述べたので省略するが、注意点としてidは小文字アルファベットのみで既存のどのモジュールとも競合しないユニークな名称であることを忘れないで欲しい。ここを誤るとhumhubシステムが起動しなくなることは先述の通りだ。

 この例では、はじめに module.json を書いてみる。例えば、次のような感じだ
module.json 記述例

{
    "id": "topnavlist",
    "name": "TopNav List",
    "description": "Qiitaでモジュール開発の解説するためのサンプルですよ。",
    "keywords": ["TopNav", "sample", "test"],
    "version": "0.0.1-test-qiita-sample",
    "humhub": {
        "minVersion": "1.6"
    },
    "license": "Apache 2.0",
    "require": {
        "php" : ">=7.3"
    },
    "homepage": "https://www.osintech.net".
    "authors" : [
        {
             "name": "Tomoyuki 'SISHO' Fuse",
             "email": "xxxxx@xxxxxx.com",
             "homepage": "https://qiita.com/tomofu74",
             "role": "Developper, Researcher",
             "favorite": "Friendly girl"
        },
        {
             "name": "Tama 'Homeless' Cat',
             "email": "xxxxx@xxxxx.com",
             "favorite": "Chaocule"
        }
   ]
}

 前々回でも注意したように、idは全て小文字で記載しておく方がよい。そのほか、"humhub"においては、動作の保証ができる最低限のバージョン番号を書いておく。大抵は、開発に使ったhumhubのバージョンを"minVersion"にしておけばよいと思うが、それ以降のhumhubバージョンアップで、以前のバージョンとは情報の取り扱いが大きく変わり、下位互換性がなくなってしまう場合などには注意が必要だ。例えば、2020年中のhumhubのバージョンアップは毎回仕様の変更が大きい。モジュール開発においては、公式情報( (https://docs.humhub.org/docs/develop/modules-migrate) )に注意を欠かせない。
 そのほか、必須でない項目について色々と記入できる。全くhumhubシステムで使われない情報を書き込んでもモジュールの動作には問題ないようだ。この際だから、会社の宣伝も入れてしまえ、という愛社精神をこっそりと表現できたりもする。

 次にModlue.phpを書いてみよう。前々回では、説明を省略したが、今回は公式の情報を補足として書き加えておく。Humhub公式では、BaseModuleClassとして2つの対応を挙げている。2つの対応の違いは、モジュールの使い方のスタイルによるものだ。グローバルに設定してシステムの機能として使うもの、スペース・ユーザーが使用を選択できるもの、の2つだ。
 今回、題材としてあつかう件では、全ログインユーザーに対して選択の余地を持たせる必要はないこととし、次のようにModule.phpを作成した。

<?php

namespace Osintech\humhub\modules\topnavilist;

use Yii;

class Module extends \humhub\components\Module
{
    public function getName()
    {
        return Yii::t('TopnavilistModule.base', 'TopNavi List');
    }
}

 注意点のひとつは、 namespace の決定だ。Humhub が拠り所としている PHP Yii2 フレームワーク では、ちょっと詳しい説明が見つからない。ひとつ言えることは、ファイルシステムとは関係なく名称を設定できるようだ。よって、「humhub\modules\」フォルダの下にあるから絶対に「humhum\modules」フォルダで書き始めなければならない、ということはない。ここは開発者にわかりやすいように、このモジュールのまとまりをみせるために、モジュールの中で共通の部分を持たせておくのがよいだろう。また、DBにデータを記録するようなモジュールを扱う場合、このネームスペースが情報の一部として記録されることがあるので、運用を始めたモジュールのnamespaceは変更しづらい、と思っておくとよい。将来も見据えた、センスあるネーミングを開発者で考えるもの、なのかな・・・。今回は、会社名とHumhubのモジュールだよっ、ということがわかるような名前にしてみた(私なりのセンス云々)。
 ひとつ、getName()という関数を作った。これは、Humhubのモジュール管理機能において表示される名称の文字列を返す関数だ。Humhubがモジュールとして感知すると、自動でこの関数が呼び出される(どこにも解説が見つけられないが、経験則およびシステムコアモジュールのソースを読むとそうらしい・・・という推測。どなたか、正確な情報源をご存知な方はコメントで教えてください。)
 今回は、getName()関数の中で、 Yii::t() によって得られる文字列を返却(return)するようにコーディングしている。これは、Humhubのinternationalizationに従って、文字列を多言語に変換する仕組みを呼び出すものだ。多言語への変換は、自動で翻訳機能が働くのではなく、別途辞書ファイルを作成して対応する文字列の置き換えをさせる仕組み。公式に、少しだけ触れられている。(https://docs.humhub.org/docs/develop/i18n/)
 internationalizationについて具体的な使い方は、今後の連載で記載することとしたい。

コールバックイベントの指定

 トップナビゲーション部に表示する項目を作り、その表示はユーザーの参加スペースに応じて切り替える、という機能を加えていく。まず、トップナビゲーション部に表示させるイベントのコールバックファンクションを作ろう。そのファンクションの中で、ユーザーの参加スペースを判定すれば良い、というわけだ。

姉様風言い回しでの、問題1) 本件に適切と思われる config.phpを作成し、配置なさい。
バルス的ではない、いきなり答え → config.php 記述例

use humhub\widgets\TopMenu;
use humhub\modules\topnavlist\Events;

return [
    'id' => 'example',
    'class' => 'humhub\modules\topnavilist\Module',
    'namespace' => 'humhub\modules\topnavilist',
    'events' => [
        [
           'class' => TopMenu::class,
           'event' => TopMenu::EVENT_INIT, 
           'callback' => [Events::class, 'onTopMenuInit']
        ]
    ]
];

このようなconfig.phpでなぜ適切と思われるかについては前回を振り返って確認してみて欲しい。

 さて、eventsに、TopMenu::class(すなわち、humhub\widgets\TopMenuクラス)のEVENT_INITでのイベントでのコールバックを要求している。コールバックファンクションは、Events::class(すなわち、humhub\modules\topnavlist\Eventsクラス)の onTopMenuInit だ。

問題2) EventsクラスにonTopMenuInitファンクションを作りなさい。
答え → Events.php 記述例

<?php

namespace humhub\modules\topnavlist;

use Yii;

class Events
{
     public static function onTopMenuInit($event)
     {
         try {
           // --> ここにあとで追記する <-- //
         } catch (\Throwable $e) {
             Yii::error($e);
         }
     }
}

 まずは、 こんな形でコールバックファンクションを設置した。コールバックファンクションは返り値のない、public で staticなものを宣言するのが一般的なようだ(これも経験と既存のコアモジュールの観察の結果に基づく見解。)。引数の $event は、config.php で記載したイベント情報から自動で渡される。try ~ catch は、phpの記述に基づくものであり、解説は別に譲る。 Yiiクラスをuseして、Yii::error() に catchしたエラーメッセージ を引数として渡している。こうしておくと、発生したエラーが humhub の 動作ログにエラーとして記録される。記録されたエラーは、humhubの管理者メニューのinformation の logging タブで読むことができる。邪道かもしれないが、筆者はこの仕組みを使ってコーディングの途中で自分のほしい情報の出力を行せながら、開発している。
 ここまでの記述で、このモジュールが、humhub\widgets\TopMenu が Initイベント を発火するときに作動する、onTopMenuInit コールバックを実装した topnavlist モジュールの基礎ができた。ここまでで、topnavlistフォルダ内に目的の4つのファイルが出来上がった。このtopnavlistフォルダを、humhubシステムのユーザーモジュールフォルダ(Module Loader Path: 公式情報( https://docs.humhub.org/docs/develop/environment#module-loader-path )にコピーすると、モジュールとして認識され、モジュールの作動準備ができあがりなのだ。さっそく、各自でこのモジュールを有効にしてみてほしい。
(有効にしても実際は何も発生しない。もし、エラーが出ている場合は、何かの書き間違いがあるかもしれないので表示されるエラーに従って修正対応してみてほしい。)

コールバックイベントの内容記述

 それでは、コールバックイベントの内容記述を検討しよう。今回の目標は「ログインユーザーの参加スペースに応じて、トップナビゲーション部に表示するリスト項目を追加する」なので、まずは”ログインユーザーの参加スペース”の情報を取得することにしよう。ログインユーザーの参加スペース情報は、 Space コアモジュールにある、 Membershipモデルの getUserSpaces()ファンクションを使って読み取ることができる。このファンクションに、ユーザーIDを引数で与えると、ユーザーIDに基づいて参加スペースの情報を返却してくれる。 
次の記述例のように、Event.phpを書き換えてみてほしい。

Event.php (getuserSpaces()記述例)

<?php

namespace humhub\modules\topnavlist;

use Yii;

class Events
{
     public static function onTopMenuInit($event)
     {
         try {
             $userSpaces = Membership::getUserSpaces(Yii::$app->user->id);
             Yii::error($userSpaces);
         } catch (\Throwable $e) {
             Yii::error($e);
         }
     }
}

 Yii::error()を使って、humhubシステムのログに情報を書き出してみた。 Administration -> Information -> logging タブにて、エラー情報を見ると、例えば、次のように表示される。
スクリーンショット 2020-10-18 23.36.30.png
 とにかく Spaceの情報が マルッと 出力されていることがわかる。この情報の羅列のなかで、例えば、IDが2であるスペースに参加しているかどうかを今回の例題目標の条件にしよう。特定の条件のデータだけ取り出す方法について、筆者は恥ずかしながら foreach で逐一チェックする方法しか知らない(もし、どなたか、他に処理効率のよい方法をご存知でしたら、コメント欄でご教授ください。)。
 最後に、いよいよ”トップナビゲーション部に表示するリスト項目を追加する”部分を記述する。これは、今回の onTopMenuInitに引数として渡されている、 \$event を用いることで実現する。今回の イベントは、 INITイベント(EVENT_INIT)であった。このイベントを発火しているのは、TopMenuクラス(humhub\widgets\TopMenu) だ。この発火元を取得するには、 $event->sender と記述する。今回は、この発火元に対して、”リスト項目を追加する”ので、TopMenuクラスのaddItem()ファンクションを使おう。結果として、Event.php を次のように書き換えてほしい。

Event.php (目標達成版)

<?php

namespace humhub\modules\topnavlist;

use Yii;
use humhub\modules\ui\icon\widgets\Icon;

class Events
{
     public static function onTopMenuInit($event)
     {
         try {
             $userSpaces = Membership::getUserSpaces(Yii::$app->user->id);

             foreach( $userSpaces as $space ) {

                if ($space["id"] == 2) {

                  $event->sender->addItem([
                    'label' => Yii::t('TopnavlistModule.base', 'TOPNAVLIST'),
                    'icon' => Icon::get('anchor'),
                    'sortOrder' => 99999,
                  ]);
                  break;

                }
            }

         } catch (\Throwable $e) {
             Yii::error($e);
         }
     }
}

 この変更のあとで、humhubシステムにいくつかのユーザーでログインしてみると、スペースID 2に参加しているユーザには、TopNav部に、アンカーのアイコンでTOPNAVLISTの見出しが表示されていることだろう。
スクリーンショット 2020-10-19 0.36.45.png

まとめ

 今回は、「ログインユーザーの参加スペースに応じて、トップナビゲーション部に表示するリスト項目を追加する」という機能を持ったモジュールの実装を実戦的に解説した。機能の実現方法については、システムのコールバックによりWidgetのクラスの初期化に自前の情報を addItem させる方法を採用した。

 次回は、今回作成したリスト項目をクリックして、別画面に遷移する機能を追加することを解説する予定。

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