20201116のPHPに関する記事は14件です。

最近のエラーまとめ

最近発生したエラーの振り返り

簡単な内容のエラーではあるが、記事にしてアウトプットする。

【この記事の目的】

✅どんなエラーがあったのか記録の意味をこめて記載
✅アウトプットを継続する力をつける
✅簡単な内容から文章にし、記事を書くことに慣れる

1.SQL が開けないエラー

SQLの実行コードを行ったが開けなかった。

docker-compose app mysql -h db -u book_log -D book_log -p

上記のようにコードを記述して実行した所

No such command: app

docker-compose と app の間の exec が抜けてしまっていた凡ミス。
今後コードを良く見返して実行するように注意する。

2.SQLへのデータ登録エラー

SQL登録の際に以下のエラーが発生

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near

確認の結果、カンマの不要な箇所にカンマをしてしまっていたことが原因であった。

3.変数に関するエラー

PHPでのアプリケーションの起動の際に以下のエラーが発生

Undefined variable: reviews in /var/www/html/book_log.php on line 145

関数の引数としていた変数が存在しない返送となっていた。
実際に存在する変数を記述することにより、エラー解消。

4.foreach に関するエラー

PHPでのアプリケーションの起動の際に以下のエラーが発生

PHP Warning:  Invalid argument supplied for foreach() in /var/www/html/book_log.php on line 118

3のエラーとつながっている内容となり、
PHPからSQLデータを呼び出して、表示させる際のforeach文の中の変数が上記エラーと同じく存在しない変数で
あったためこのようなエラーが発生していた。

3のエラーと同じく、正しい変数を記述してエラー解消

5.まとめ

プログラミング学習を続けることはインプットしたことをアウトプットし、
そのアウトプットで発生したエラーを解消することの繰り返しである。
1つエラーが発生したら自分のスキルが一つ上がることを信じて、一つずつ解消して
スキルアップを続けていきたい。

今は人に読んでもらうというよりは自分の為に書いている内容となっており、
拙い文章であるが、アウトプットを継続することでより洗練された文章にしていく。
今後も可能な限り週1回程度のペースでQittaへ何らかのアウトプットを続けていく予定。

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

Laravelでデータベース(MySQL)を作成し、接続をする。

こんにちは!
今回の内容は前回の続きになります。

まだ見てない方は是非こちらの方もご参照ください!
LaravelでMySQLにログインするまで

それでは本題に入っていきます。

開発環境

macOS Big Sur 11.0.1
composer 2.0.7
Laravel Framework 8.13.0
PHP 7.3.22
MySQL 8.0.22

データベースを作成する

前回の続きであるMySQLにログインした状態でターミナルから

mysql> show databases;

と入力すると、

+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.01 sec)

このように表示されます。

次にデータベースを作成するコマンド

mysql> CREATE DATABASE `laravel_test`;

と入力すると

Query OK, 1 row affected (0.01 sec)

ちゃんとデータベースが作成できたようです。

show databases(); で確認してみると、、、

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| laravel_test       |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

laravel_testが新たに追加されてデータベースが作成できていることがわかります。

データベースに接続する

ターミナルでphp artisan tinkerと入力すると、

Psy Shell v0.10.4 (PHP 7.3.22-(to be removed in future macOS) — cli) by Justin Hileman
>>>

と表示されました。
ここでデータベースに接続するコマンドDB::connection();を入力します。

>>> DB::connection();
=> Illuminate\Database\MySqlConnection {#3326}

これで前回、.envファイルやdatabase.phpファイルで入力したデータベースに接続できました!


この先はマイグレーションファイルを実行すればテーブルを作成することができます。

マイグレーションやテーブル作成がわからない方は続きも読んでみてください!

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

予約システムの中で毎週、n週間に1度、nヶ月に1度といった定期的な予約が出来る処理を実装する

社内で使用するサービスの改修を行う中で
定期的な予約を行う仕様があるのですが
定期的な予約の期間も仕様で定められていて
その予約も毎週1回の指定しか出来なかったので
今回、n週間にn度、nヶ月に1度といった仕様を追加しました。
より良い処理があればコメント頂けると嬉しいです!

function.php
// 月ごと開催のテンプレート
/*
 *return 'Y-m-d'
 */
function monthTemplate($end_date, $month_week, $week_count, $period) {
    $start = date("Y-m-d");
    $end = date('Y-m-d', strtotime($end_date . 'last day of this month'));
    $weekly = $month_week;
    $d_start = strtotime($start . " first day of 0 month");
    $d_end   = strtotime($end . " first day of 1 month");

    $target = [[$weekly, $week_count]];

    $list = [];
    $d = $d_start;
    $c = 0;
    while ($d < $d_end) {
        foreach ($target as $val) {
            $list[] = date("Y-m-d", strtotime(date("Y-m-d", $d) . sprintf("first %s of 0 month +%d week", $val[0], $val[1] - 1)));
        }
        $d = strtotime(date("Y-m-d", $d) . '+' . $period . 'month');
    }
    sort($list);
    $list = array_filter($list, function($x) use($start, $end) {
        return $x >= $start and  $x <= $end;
    });
    return $list;
}

// 週間ごと開催のテンプレート
function weekTemplate($end_date, $weekly, $period) {
    $start = date('Y-m-d');
    $target_week = date('w', strtotime($start));
    $target_sunday = date("Y-m-d", strtotime("-" . $target_week . "day", strtotime($start)));
    $start_monday = date('Y-m-d', strtotime('+ 1 week', strtotime($target_sunday)));
    $end  = $end_date;
    $d_end   = strtotime($end . 'last day of this month');
    $list = [];
    $d = strtotime($start_monday);
    while ($d < $d_end) {
        foreach ($weekly as $val) {
            $day = date('Y-m-d', strtotime('next ' . $val . date('Y-m-d', $d)));
            $list[] = $day;
        }
        $d = strtotime(date('Y-m-d', $d) . '+' . $period . 'week');
    }
    sort($list);
    $list = array_filter($list, function($x) use($start) {
        return $x >= $start;
    });
    return $list;
}

つまづいた点

コードの中に文章がある?となって調べました。
公式→https://www.php.net/manual/ja/datetime.formats.relative.php
これ→'last day of this month'

 $end = date('Y-m-d', strtotime($end_date . 'last day of this month'));

解釈として
last day of this month → 月の最終日を指定
first day of 1 month → 月の最初の日を指定
正直まだ雰囲気でしか理解していないのでもっと相対的な書式を使って覚えていきたい。

今回参考にした内容 → https://teratail.com/questions/191105
こちらでは第n曜日と指定をして取得しているので
それを改修してみたのだが

if(date("Y-m",$d)==substr($day,0,7)) $list[]=$day;

こちらの処理をいまいち理解しておらず
2週に一回の指定をした際に月を跨ぐと
第1週目の取得されるであろう日にちが取得できずにいて悩んだが
きちんとコードを読めば、また参考した内容の求めている内容を理解していれば
すぐに気付けただろう箇所
僕なりの解釈で言うと
毎月の第n曜日を指定するので毎月の終わりで締めないと
第n曜日がずれていってしまうので
このif文で条件をつけている

改めて気づいたこと
再改修したことがあれば更新します。

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

LaravelプロジェクトをGithubからクローンする時にすること

こんにちは、くりぱんです。

この記事で実現できること

  • GithubからCloneする
  • GithubからLaravelプロジェクトをCloneした後の、開発環境の構築

開発環境

  • OS:MacOS
  • フレームワーク:Laravel
  • バージョン管理ツール:Git
  • Github

説明

LaraveのプロジェクトをGithubからCloneしただけだと、vendarディレクトリや.envファイルがなく、php artisan serveしてもエラーが出て、実際に開発できる環境にはありません。
今回は、Laravelのプロジェクトに参画して、GithubからCloneしたけど、どうしたらいいの!っていう人向けに、どうやって開発環境を整えるのかを説明していこうと思います。

実装の流れ

  1. GithubからClone
  2. composer install
  3. .envの作成
  4. データベース設定

実装

GithubからClone

まずは、Laravelのプロジェクトをクローンしたい場所にターミナルでcdコマンドを実行して移動しておきましょう!
今回は、/Applications/MAMP/htdocs配下にクローンしていきます。
皆さんは自由な場所にクローンしてくださいね!

$ cd /Applications/MAMP/htdocs/

次に、Githubからのクローンです。ターミナルで下記のコマンドを実行しましょう。

$ git clone LaravelプロジェクトURL

これでLaravelプロジェクトが先程cdコマンドで移動したディレクトリにクローンされています。

composer update or composer install

クローンしてきたLaravelプロジェクトを見てみると、vendarディレクトリが無いことがわかります。
なので、作っちゃいましょ!
まずはcdコマンドでLaravelプロジェクトディレクトリまで移動しましょう。

$ cd Laravelプロジェクトディレクトリ

それが終わったら、vendarディレクトリを作っていきます。

$ composer install

これでphp artisan serveをすると、サーバーは起動します。

.envの作成

次に、.envを作成していきましょう。
これがないと、500エラーなどがでて全くサイトを確認できないですからね!
実は、すでに.envの元となるファイルは存在するので、それをコピーするだけなんです。
なので、以下のコマンドを実行してください!

$ cp .env.example .env

次に、.envの中のAPP_KEYを発行します。

$ php artisan key:generate

このAPP_KEYは、暗号化やパスワードリセット等のセキュリティーでとても大事な役割を担っています。

そして、下記のコマンドで念の為キャッシュをクリアしておきましょう!

$ php artisan config:clear

これで、.envの設定はOKです。
現場などで、他に.envの設定がある場合は設定しましょう!

データベース設定

データベースが必要な場合は、下記のコマンドたちも実行していきましょう!

$ php artisan migrate

seederファイルもある場合は、下記のコマンドを実行してください。

$ php artisan db:seed

ReflectionException : Class 〇〇 does not existclass 〇〇 not foundなどのエラーが出た場合は、下記のコマンドでオートロードの定義をしましょう。

$ composer dump-autoload

それが終わったら、再マイグレーションとシーダーの実行をしましょう。

$ php artisan migrate:refresh --seed

ブラウザで確認して、問題なければOKです。

最後に

今回は、LaravelプロジェクトをGithubからcloneしたはいいけど、その後がわからない!サーバー立ち上がらないし、変なエラーでる!っていう時の備忘録でした。
当記事を、最後まで見ていただきありがとうございました。!

Twitterもやってます!
プログラミングや金融知識についてやエンジニアの現実についてつぶやいています!
よかったら見てみてくださいね!

https://twitter.com/sakuslife

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

アロー関数で複数行書けるようにしたいよねっていうプルリク

PHP7.4で導入された便利機能アロー関数、みんな使ってますか?
私はあんまり。

$x = 1;
$fn = fn($y) => $x+$y;

$fn(1); // 2
$fn(10); // 11

やはり1文しか書けないのと括弧で括れないので微妙に使い辛いんですよね。
ちょっとした確認とかにぱっと書けるのはいいのですが、1文ではあまり本格的な関数を書くことができません。
それなら普通に関数書くわってなりがちです。

ということでなんか{}で括って複数行書けるプルリクが来ていました。
提出者のNuno MaduroLaravel ZeroとかPestとか作っていて実績がある人です。
またプルリク自体の評価や注目度もとても高いです。
01.png

Adds support to multi-line arrow functions

みんなも知ってのとおり、PHP7.4でアロー関数が導入されました。
そしてこのプルリクエストでは、アロー関数に複数行の記述を許可します。
例を見てみましょう。

$users = [/** */];
$guestsIds = [/** */];
$repository = /** */;

$guests = array_filter($users, fn ($user) {
    $guest = $repository->findByUserId($user->id);

    return $guest !== null && in_array($guest->id, $guestsIds);
});

このプルリクはPHP8.1での導入を目指していて、近日中にRFCを立てる予定です。

この機能の利点は、以下のように書くことができることです。

-$values = array_filter($values, function ($value) use ($first, $second, $third, $four) {
+$values = array_filter($values, fn ($value) {

・複数行アロー関数は、外部スコープの変数値を直接参照できるのでuseキーワードが必要ありません。
・それに、fn (/** */) {は大抵function (/** */) use (/** */) {よりシンプルです。

PHPコアへのプルリクは初めてなので、なにか改善すべき点があったら教えてください??

テストコード

$foo = fn() {};
var_dump($foo()); // null

$foo = fn() => 1;
var_dump($foo()); // 1

$foo = fn() { return 2; };
var_dump($foo()); // 2

$foo = fn($x) => $x;
var_dump($foo(3)); // 3

$foo = fn($x) { return $x; };
var_dump($foo(4)); // 4

$foo = fn($x, $y) => $x + $y;
var_dump($foo(4, 1)); // 5

$foo = fn($x, $y) { return $x + $y; };
var_dump($foo(5, 1)); // 6

$var = 7;
$foo = fn() => $var;
var_dump($foo()); // 7

$var = 8;
$foo = fn() { return $var; };
var_dump($foo()); // 8

$foo = fn($var) => $var;
var_dump($foo(9)); // 9

$foo = fn($var) { return $var; };
var_dump($foo(10)); // 10

$var = 11;
$foo = fn() => ++$var;
var_dump($var); // 11
var_dump($foo()); // 12
var_dump($var); // 11

$var = 13;
$foo = fn() { return ++$var; };
var_dump($var); // 13
var_dump($foo()); // 14
var_dump($var); // 13

$var = 14;
var_dump((fn() => fn() => $var)()()); // 14

$var = 15;
var_dump((fn() => function() use($var) { return $var; })()()); // 15

$var = 16;
var_dump((fn() { return fn() { return $var; }; })()()); // 16

var_dump((fn() { return fn() {}; })()()); // null

これぞ求めていたアロー関数ってかんじですね。
最後のような使い方はやめてくれよって思いますが。

レビュー

いつものNikita
もうちょっと変数キャプチャを考える必要があります。
以下では外部の$xをバインドする必要はありません。

$x = 1;
$fn = fn() {
    $x = 2;
    return $x;
};

他にも幾つか疑問や懸念が来ています。

気になる構文

PHPでは素の{}にスコープがないんですよね。

$x = 1;
{
  $x++;
}
var_dump($x); // 2

ここにアロー関数が並ぶとこうなります。

$x = 1;
{$x++;}
var_dump($x); // 2

$x = 1;
(fn()=>{$x++;})();
var_dump($x); // 1

どうなんだろう。

まあ、現時点で既にこんなだから、今さら気にすることでもないか。

$x = 1;
(fn()=>$x++)();
var_dump($x);

感想

近日中に作ると言っておきながらRFCはまだ見当たりません。
またRFCやメーリングリストでは、構文や変数キャプチャ周りについて、色々と議論が発生しているようです。

とはいえ評価も高いし総論反対もほとんどいないようなので、PHP8.1か、まあ遅くても8.2あたりで入るのではないかなとは思います。
これで本格的にアロー関数を導入できそうですね。

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

ランダム文字にてパスワード生成

こんにちは。@mashiです。

本日はパスワードをランダムにて生成するメソッドを記載いたします。
最近ランダム系ばかりアップしていますがたまたまです。

下記メソッドは初期の仮パスワードやパスワードリセット等で利用できるかと思います。
また、パスワードの1文字ずつ読み仮名が無い場合は「0」と「O」や「l」と「1」などが分かりづらい懸念があるため除外しています。

function makePassword($length) {
  $password = '';
  
  for ($i = 0; $i < $length; $i++) {
    $password .= substr(str_shuffle('23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ-_'), 0, 1);
  }

  return $password;
}

lengthの桁数のパスワードが返ってくる単純なメソッドです。
ログインアカウント管理をすることって多いと思うので、どこかで使うかもしれないための備忘録です。

なにかあればコメントをいただければ幸いです。

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

PHPが無料で使える!XFREEを使ってみた!

Webhookやってみたい!

IoTなどでよく目にする、Webhookをするには、PHPなどの言語が動くサーバーが必要です(適当)そこで、PHPが無料で使える!XFREEを使ってみました!

XFREEに登録してみる

無料レンタルサーバーのXFREEにアクセスします。

スクリーンショット 2020-11-16 11.26.41.png

ページの下の方にあるオレンジのボタンをクリック

スクリーンショット 2020-11-16 11.26.48.png

メールアドレスを登録します。

スクリーンショット 2020-11-16 11.26.59.png

届いたメールのURLをクリックして、登録を進めましょう。個人情報とかを登録すると管理パネルにアクセスできるようになります。

スクリーンショット 2020-11-16 11.32.21.png

登録が終わると、サーバIDの設定になります。なんか適当に入れましょう。

スクリーンショット 2020-11-16 11.33.07.png

登録が終わったら、「無料レンタルサーバー」をクリックします

スクリーンショット 2020-11-16 11.33.18.png

今回は、PHPをやりたいので、まんなかの「利用を開始する」を押します。

スクリーンショット 2020-11-16 11.33.40.png

初期設定が行われるので、「無料レンタルサーバー」をクリックします

スクリーンショット 2020-11-16 11.33.51.png

「管理パネルログイン」が増えているのでクリックします。

スクリーンショット 2020-11-16 11.34.04.png

右上の「FTPアカウント設定」を押します

スクリーンショット 2020-11-16 11.52.59.png

表の右側にある「ログイン」を押します。

スクリーンショット 2020-11-16 11.53.45.png

ファイルマネージャーが開きます!Web上でファイルを編集できます!便利!

スクリーンショット 2020-11-16 11.53.53.png

index.htmlをクリックするとファイル名が変更できるので、index.phpに変更します。

スクリーンショット 2020-11-16 12.08.44.png

次に、鉛筆アイコンを押してファイルの中身を編集します。このとき、左上の文字コードをUTF-8にしましょう。

index.php
<?php
echo "hello world";

これで、設定は完了です!

アクセスしてみよう!

FTPアカウント設定の画面に書いてある「対象ドメイン」をブラウザのURLに入力すると、ページにアクセスできます。

スクリーンショット 2020-11-16 11.52.59.png

ただ、設定してすぐはDNSなどが登録されていないため見れない模様。

スクリーンショット 2020-11-16 11.55.30.png

しばらく時間が立つと見ることができました!

スクリーンショット 2020-11-16 12.15.01.png

これで、無料サーバーでPHPを作ることができました!

あとはPHPのプログラムをいい感じに書けばWebhookとかにも使えますね!

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

配列をカウントする場合 どうする? count関数

配列の要素数をカウントする場合

count関数

$array1 = ['apple', 'orange', 'melon', 'apple', 'pineapple'];

$count = count($array1);

出力結果:5

配列には5つの要素があるためカウント関数では5とカウントされて出力されています。

多次元配列をカウントする場合

それでは同じ方法で多次元配列をカウントしてみます

$array1 = [
['apple', 'orange', 'melon', 'apple', 'pineapple'],
['apple', 'orange', 'melon', 'apple', 'pineapple'],
['apple', 'orange', 'melon', 'apple', 'pineapple'],
];

echo count($array1);

出力結果:3

出力結果が「15」となるはずなのに「3」と出力されました。
配列の中の孫要素数ではなく、子要素のみをカウントしているようです。

複雑な配列構造であってもカウントする方法とは?

COUNT_RECURSIVEの登場です。

$array1 = [
    ['apple','orange', 'melon', 'apple','pineapple'],
    ['apple','orange', 'melon', 'apple','pineapple'],
    ['apple','orange', 'melon', 'apple','pineapple']
    ];


 echo count($array1 , COUNT_RECURSIVE);

出力結果:18


出力結果が18と出力されました。
配列の子要素数[3]、孫要素[15]で合計18といったカウント方法です。

それでは孫要素のみをカウントしたい場合どうする方法があるでしょうか?

どうしても孫要素のみカウントしたい!

以下

$array1 = [
    ['apple','orange', 'melon', 'apple','pineapple'],
    ['apple','orange', 'melon', 'apple','pineapple'],
    ['apple','orange', 'melon', 'apple','pineapple']
    ];


 echo count($array1 , COUNT_RECURSIVE)-count($array1);

こんな方法があるようです。
多分使わないと思います。

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

EC-CUBE4 ServiceクラスOrderHelperの拡張方法

EC-CUBE4のOrderHelper.phpの拡張する機会がありましたので、その方法をご紹介します。

Step1. services.yamlに追記

app/config/eccube/services.yamlに以下のような記述を追記します。

services.yaml
services:
    ...
    Customize\Service\*任意のファイル名*:
        decorates: Eccube\Service\OrderHelper

以下に自分の例を置いておきます。

services.yaml
services:
    ...
    Customize\Service\OrderHelper:
        decorates: Eccube\Service\OrderHelper

Step2. Customize/Serviceフォルダ内に拡張ファイルを用意

app/Customize/Service/に上記で指定したファイルの作成をする。

OrderHelper.php
<?php
namespace Customize\Service;

use Eccube\Service\OrderHelper as BaseOrderHelper;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\EntityManagerInterface;
use Eccube\Entity\CartItem;
use Eccube\Entity\Master\OrderItemType;
use Eccube\Entity\OrderItem;
use Eccube\Repository\DeliveryRepository;
use Eccube\Repository\Master\DeviceTypeRepository;
use Eccube\Repository\Master\OrderItemTypeRepository;
use Eccube\Repository\Master\OrderStatusRepository;
use Eccube\Repository\Master\PrefRepository;
use Eccube\Repository\OrderRepository;
use Eccube\Repository\PaymentRepository;
use SunCat\MobileDetectBundle\DeviceDetector\MobileDetector;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

class OrderHelper extends BaseOrderHelper
{

}

Step3. 変更したい関数をオーバーライドする

変更したい関数をオーバーライドすることで処理に手を加えることが可能になります。
例では、createOrderItemsFromCartItemsをオーバーライドしています。

OrderHelper.php
<?php
namespace Customize\Service;

use Eccube\Service\OrderHelper as BaseOrderHelper;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\EntityManagerInterface;
use Eccube\Entity\CartItem;
use Eccube\Entity\Master\OrderItemType;
use Eccube\Entity\OrderItem;
use Eccube\Repository\DeliveryRepository;
use Eccube\Repository\Master\DeviceTypeRepository;
use Eccube\Repository\Master\OrderItemTypeRepository;
use Eccube\Repository\Master\OrderStatusRepository;
use Eccube\Repository\Master\PrefRepository;
use Eccube\Repository\OrderRepository;
use Eccube\Repository\PaymentRepository;
use SunCat\MobileDetectBundle\DeviceDetector\MobileDetector;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

class OrderHelper extends BaseOrderHelper
{
    /**
     * @param Collection|ArrayCollection|CartItem[] $CartItems
     *
     * @return OrderItem[]
     */
    protected function createOrderItemsFromCartItems($CartItems)
    {
       //任意の処理
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel ページネーション 検索条件が引き継がれない

検索条件が引き継がれない問題発生

検索機能を実装して、ページネーションで次のページに遷移したとき検索条件が引き継がれない
問題が発生して解決したので記事にしました。

検索をかけて、URLを確認すると,下記のように表示されています。

category_id=1&page=1
//検索フォームで検索した結果

そして、2ページ目に遷移すると、
page=2
//ページ遷移した結果

あ、category_id=1が消えていました。
現状、bladeのページネーションのコードを確認すると下記のように設定されています。
 
{{ $projects->links() }}

下記のように変更すると、ページ遷移してもcategory_id=1&page=2
のように引き継がれました。

 
{{ $projects->appends(request()->input())->links() }}

参考記事

https://blog.dododori.com/create/program/laravel-search-page/

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

[PHP]staticについて学習してみた

はじめに

 業務でPHPを使うことが多いのですが、static変数をなんとなく使っていたり、他のメソッドとの違いがわからずずっとモヤモヤしていたので、この機会に学習しようと思いました。

 学習前は、staticをつけるとクラス内のどこからでもメソッドや変数を呼び出すことができるという認識だけです。グローバル変数と何が違う?使うメリットって?どういう場合に使う?など頭の中に?しか浮かばない状況です。

 学習用の備忘録として記事を残して行きます。間違ってるところがあれば指摘していただければと思います。

クラスとインスタンス

 static変数を理解するにはクラスとインスタンスの概念を理解する必要があります。

  • クラスは、ある実体を生成するために定義された概念のようなもの。よく設計図に例えられる。
  • インスタンスは、クラス(設計図)を基に作られた実体のこと。

 僕は、クラスはたい焼きの型で、インスタンスはその型から作られたたい焼きと覚えています。一度設計図を定義してしまうと、再利用もでき、たい焼きの中身をあんこにしたりカスタードクリームにするなどカスタムすることもできます。

 実際に例としてクラスを基にインスタンスを生成してみます。

<?php
class A{
    public $b = 1;
    public $c = 2;
}
?>

上記のクラスAを呼び出すには、

$a = new A();

とするとそのクラスを使うことができるようになります。この状態をクラスがインスタンス化するといいます。通常はインスタンスを生成する時点で、必要な分のメモリが確保されます。

このメモリを確保するタイミングが、staticを理解する上で重要なポイントになります。

なぜstatic変数を使うのか?

 基本はnew演算子を使うと、変数やメソッドを使うことができますが、インスタンスを生成することでいくつか困ることがでてきます。

  • クラスに定数を持たせた時に、インスタンスを生成するたびに同じデータなのにメモリがその分だけ消費されるメモリが無駄になること。
  • インスタンスを生成するたびにメモリを確保してしまうと、クラスで同じ変数を共有したくてもできない。

などの問題が出てきます。

これを解決してくれるのが、static変数です。

static変数は、基本的にプログラムが開始されるタイミングでメモリを確保します。

なので、最初に一度だけメモリを確保するだけでアクセスできるので、決まった値を参照するときなどにメモリを無駄にせずに使うことができます。

また、static変数は、インスタンスを生成しなくても直接、クラスの変数にアクセスすることができます。

これは、staticで変数を定義するとクラスそのものに属することになり、インスタンスとの依存関係がなくなるからです。

class A {
    public static $a = 1;
}

上記のstatic変数を使用するには、

$b = A::$a

とするとどこからでも変数を使うことができます。

まとめ

 今回はstatic変数について学習しました。今までなんとなく変数やメソッドを使ってきましたが、どちらもそれぞれメリット・デメリットがあると思うので、それをしっかりと把握した上でなぜそこでstaticを使っているかなどを考えながらソースを書いていきたいと思います。

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

LaravelでMySQLにログインするまで

今回はLaravelでMySQLにログインするまでを説明します!

開発環境

macOS Big Sur 11.0.1
composer 2.0.7
Laravel Framework 8.13.0
PHP 7.3.22
MySQL 8.0.22

.envの編集

.envファイルの編集をしていきます

.env(編集前)
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=

DB_DATABASE,DB_USERNAME,DB_PASSWORDをそれぞれ変更、修正します。(名前は適宜変更してください)

.env(編集後)
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_test
DB_USERNAME=test_user
DB_PASSWORD=pass

今回はこのように設定しました。

config/database.phpの編集

.envファイルの編集に伴い、database.phpのファイルも修正します。

database.php(編集前)
'mysql' => [
    ~~~
    'database' => env('DB_DATABASE', 'forge'),
    'username' => env('DB_USERNAME', 'forge'),
    'password' => env('DB_PASSWORD', ''),
    ~~~
],
database.php(編集前)
'mysql' => [
    ~~~
    'database' => env('DB_DATABASE', 'laravel_test'),
    'username' => env('DB_USERNAME', 'test_user'),
    'password' => env('DB_PASSWORD', 'pass'),
    ~~~
],

それぞれ.envファイルに設定した値を入力してください。

MySQLにログインする

以上で準備が整ったので、データベースに接続します。
ターミナルを開いてプロジェクトルートディレクトリに移動して下記を入力してください。

mysql -root

すると、

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 8.0.22 Homebrew

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

と表示されました!


次はデータベースを作成し、接続するところまで説明するので是非、続きも読んでみてください。
Laravelでデータベース(MySQL)を作成し、接続をする。

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

PHPでゴッドオブジェクトを作らないためにできる3つの対策

はじめに

この記事はLaracastsという動画学習プラットフォームの講座シリーズ「Whip Monstrous Code Into Shape」の下記エピソードをまとめたものです。
God Object Cleanup #1: Pass-Through
God Object Cleanup #2: Traits and Socks
God Object Cleanup #3: Value Objects

この講座シリーズの内容がめちゃくちゃ素晴らしくて、動画だと見返すのが面倒なので文章にまとめようと思ったのがきっかけです。すべての権利はLaracastsおよび動画作者のJeffery Wayさんに帰属します。また、以降の記述はJeffery Wayさんの主張を自分なりに解釈したもので、私個人の考えではないということにご注意ください。

ゴッドオブジェクトとは

wikipediaによると

In object-oriented programming, a God object is an object that knows too much or does too much. The God object is an example of an anti-pattern.
訳:オブジェクト指向プログラミングにおけるゴッドオブジェクトとは、多くを知りすぎていたり、多くのことをやりすぎているオブジェクトのことで、アンチパターンの一例である。

例えば1つのクラス、よくあるのがUserクラスにビジネスロジックを全部書いちゃってメソッドやプロパティが膨大に膨れ上がっているようなやつですね。

前提例

Laracastsのように、登録したユーザーが動画を視聴できる有料システムを開発中だとします。

ケース1

「よし、ユーザーがお気に入りした動画数や、視聴完了済みの動画数、視聴途中の動画数を取得できるようにしよう」

User.php
<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    .
    .
    .

    public function favoritesCount()
    {
        return 15;
    }

    public function completionsCount()
    {
        return 100;
    }

    public function experience()
    {
        return 123456;
    }
}

上記3つのメソッドで終わるなら問題ありませんが、そんなはずはなく…。ロジックをUser.phpに集約させようとすると、このクラスはすぐに膨れ上がってしまいます。

対処法1. Pass-Throughパターン

上記メソッドはすべて統計に関するものなので、新たにStatsクラスを作成し、それを返すメソッドを作ります。元々あったメソッドはStatsクラスにまとめて移しましょう。

User.php
<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    .
    .
    .
    public function stats()
    {
        return new Stats($this);
    }
}

移動したメソッド名の...Countは冗長になるので消します。

Stats.php
<?php


namespace App;


class Stats
{

    /**
     * Stats constructor.
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function favorites()
    {
        return 15;
    }

    public function completions()
    {
        return 100;
    }

    public function experience()
    {
        return 123456;
    }
}

これで$user->stats($user)->favorites() // 15というようにアクセスできるようになりました。

ケース2

「ユーザーのビデオ視聴状態に関するロジックを追加したい!」

「リレーションを取得したり、参照して視聴状態を完了、未完了にしたりするメソッドを追加しよう。」

User.php
<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    .
    .
    .

    public function completions()
    {
        // completionsリレーションを取得
    }

    public function complete()
    {
        // リレーションを参照して項目を完了とする
    }

    public function uncomplete()
    {
        // リレーションを参照して項目を未完了とする
    }

}

「あー、あとはビデオ視聴済みかどうかの判定と、動画視聴ページでスレッドを開始する処理、返信をする処理も追加しなきゃ」

User.php
    .
    .
    .
    public function completed(Video $video)
    {
        // ビデオ視聴済みか判定
    }

    public function startConversation(Conversation $conversation)
    {
        // 現在のユーザー向けに新しいスレッドを作成
    }

    public function replyTo(Conversation $conversation, Reply $reply)
    {
        // スレッド内で返信を追加
    }

早速クラスが大きくなる予感がしてきましたね。

対処法2. Traitを使う

同じようなロジックはtraitに移してみましょう。完了状態に関するロジックをまとめるため、Completable traitを作成します。

Completable.php
<?php


namespace App;


trait Completable
{
    public function completions()
    {
        // completionsリレーションを取得
    }

    public function complete()
    {
        // リレーションを参照して項目を完了とする
    }

    public function uncomplete()
    {
        // リレーションを参照して項目を未完了とする
    }

    public function completed(Video $video)
    {
        // ビデオ視聴済みか判定
    }

}

同じように、残り二つのメソッドはフォーラム関係のメソッドなのでParticipatesInForum traitを作成して移しましょう。

ParticipatesInForum.php
<?php


namespace App;


trait ParticipatesInForum
{
    public function startConversation(Conversation $conversation)
    {
        // 現在のユーザー向けに新しいスレッドを作成
    }

    public function replyTo(Conversation $conversation, Reply $reply)
    {
        // スレッド内で返信を追加
    }
}

User.phpで作成したtraitを使用しましょう。

User.php
<?php

namespace App;


use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Completable;
    use ParticipatesInForum;
    .
    .
    .

}

Userクラスがすっきりしましたね!
動画内でJefferyさんは、もしほかの場所で使われないとしてもTraitsを使うのはありだと主張しています。Traitを使って似たようなロジックをまとめることは、物が散らかった部屋でジャンル分けした箱の中に物を振り分けて片付けるようなものだと考えています。

ケース3

「収益関連の処理を追加しよう!」

「収益を出すメソッドと、数値をドル表示に整形するメソッドを追加して、と」

Performance.php
<?php


namespace App;

use Illuminate\Database\Eloquent\Model;


class Performance extends Model
{
    .
    .
    .
    public function revenue()
    {
        return $this->revenue; // 8600
    }

    public function revenueInDollars()
    {
        return $this->revenue() / 100; //86
    }

    public function revenueAsCurrency()
    {
        return money_format('$%i', $this->revenueInDollars()); // $86.00
    }

}

早速revenueに関するロジックが増えてきました。

対処法3. 値オブジェクトを使う

上記のケースでは、全てのメソッドにはrevenueという接頭辞がついており、revenueという値を操作する処理であるということが共通しています。この場合、revenueに振る舞いを持たせることは有効に作用するので、値オブジェクトを使ってみましょう。

ただし、値オブジェクトは作成する意味があるときにだけ使いましょう。DDDでなんでもかんでもプリミティブを値オブジェクトにする人がいますが、それほどばかげたことはありません。

ただしRevenueVOとかRevenueValueObjectとか命名するのはやめましょう。Revenueとするのが本来あるべき姿です。

Revenueクラスを作成したら、先ほどのメソッド(revenue()以外)をこちらに移し、メソッド名も適したものに変更します。

Revenue.php
<?php


namespace App;


class Revenue
{
    private $revenue; // Immutableにする

    /**
     * Revenue constructor.
     */
    public function __construct($revenue)
    {
        $this->revenue = $revenue;
    }

    public function inDollars()
    {
        return $this->revenue() / 100; //86
    }

    public function asCurrency()
    {
        return money_format('$%i', $this->inDollars()); // $86.00
    }

}

さあ、値オブジェクトを作ったら、どうやってアクセスすれば良いでしょうか?

$performance->revenueこんな感じでアクセスしたいわけです。

今のままだと、上記のやり方では8600を取得するだけです。正解はこうです。

Performance.php
<?php


namespace App;

use Illuminate\Database\Eloquent\Model;


class Performance extends Model
{
    public function getRevenueAttribute($revenue)
    {
        return new Revenue($revenue);
    }

}

Eloquentのカスタムアクセサ―によって、revenueプロパティにアクセスしようとしたときに、getRevenueAttribute($revenue)メソッドが定義されている場合、同メソッドを呼び出してくれます。上記の場合、Revenueインスタンスを返してくれるというわけです。

これで、$performance->revenue->asCurrency //$86.00 こんな風にアクセスすることができるようになります。

また、もしecho $performance->revenue->asCurrency とやって出力しようとすると、型が異なるのでエラーになります。文字列として出力した場合は__toString()メソッドを定義してあげましょう。そうすると、文字列としてアクセスしようとした場合の処理を定義することができます。

Performance.php
   .
   .
   .
   public function __toString()
   {
       return (string) $this->asCurrency();
   }

おわりに

以上、いかがでしたでしょうか。
Jeffery Wayさんの「Whip Monstrous Code Into Shape」では、他にもMonstrousなコードを綺麗にするためのテクニックがたくさん紹介されています。時間ができたら都度参考になった他のエピソードもまとめようと思います。

間違っているところなどありましたら是非是非ご指摘お待ちしております!

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

※学習用メモ デザインパターン:Strategy編[PHP]

Strategyパターンとはどういうものか

Strategyとは戦略という意味です。
このパターンはアルゴリズム(戦略)を実行する際に、どのアルゴリズムを使用するか選択できるというものとなります。

Strategy パターンは、アプリケーションで使用されるアルゴリズムを動的に切り替える必要がある際に有用である。Strategy パターンはアルゴリズムのセットを定義する方法を提供し、これらを交換可能にすることを目的としている。Strategy パターンにより、アルゴリズムを使用者から独立したまま様々に変化させることができるようになる。(wikipediaより)

共通のインターフェースを定義することで、使用側(クライアント)はそのインターフェースに伴った処理を実行すればよいので、具体的な実装に依存することがなくなります。

どういう時に使用可能なのか

・アプリケーションで使用されるアルゴリズムを動的に切り替える必要がある場合

[参考サイト]
https://ja.wikipedia.org/wiki/Strategy_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3
https://qiita.com/nooboolean/items/5c0d0f651474ff34130a
https://qiita.com/i-tanaka730/items/4d00c884b7ce1594f42a

Strategyパターンを用いるメリット

・アルゴリズムを切り替えるというパターンの性質上、if文やswitch文などの条件分岐をスマートにすることができる。普段であれば場合分けする部分を、具体的なアルゴリズムを記述するクラスに格納することでコードが非常にシンプルになり可読性、拡張性が上がる。

サンプルコード

仕様

競馬の馬券内3頭を選ぶ
・馬情報から各々のアルゴリズムを用いて結果を取得
・今回はランダムと枠順内側から順番

馬情報クラス

HorseData.class.php
class HorseData
{
    private $age = 0;
    private $name = '';
    private $flameNum = 0;
    private $popularNum = 0;

    public function __construct($age, $name, $flameNum, $popularNum)
    {
        $this->age = $age;
        $this->name = $name;
        $this->flameNum = $flameNum;
        $this->popularNum = $popularNum;
    }

    public function getAge()
    {
        return $this->age;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getFlameNum()
    {
        return $this->flameNum;
    }

    public function getPopularNum()
    {
        return $this->popularNum;
    }
}

アルゴリズムの処理の大枠を示すインターフェース

interface TripleSingleInterface
{
    public function getResult($horseDataList);
}

ランダムで選択

Random.class.php
class Random implements TripleSingleInterface
{
    public function getResult($horseDataList)
    {
        $resultData = array();

        $randomArray = range(1, count($horseDataList));
        shuffle($randomArray);

        foreach ($horseDataList as $horseData)
        {
            for ($i = 0; $i < 3; $i++) {
                if ($horseData->getFlameNum() == $randomArray[$i]) {
                    $resultData[$i] = $horseData;
                }
            }
        }

        ksort($resultData);

        return $resultData;
    }
}

枠順昇順に選択

FrameAscendingOrder.class.php
class FrameAscendingOrder implements TripleSingleInterface
{
    public function getResult($horseDataList)
    {
        $resultData = array();

        uasort($horseDataList, function($a, $b){
            return $a->getFlameNum() > $b->getFlameNum();
        });

        $i = 0;
        foreach ($horseDataList as $horseData){
            if ($i > 2) {
                break;
            }
            $resultData[$i] = $horseData;
            $i++;
        }

        return $resultData;
    }
}

三頭選ぶ大枠の処理クラス

TripleSingle.class.php
class TripleSingle
{
    private $TripleSingleAlgorithm;

    public function __construct($TripleSingleAlgorithm)
    {
        $this->TripleSingleAlgorithm = $TripleSingleAlgorithm;
    }

    public function getParam($horseDataList)
    {
        return $this->TripleSingleAlgorithm->getResult($horseDataList);;
    }
}

使ってみる

あらかじめデータを用意し結果を取得

// 本当はDBとかから取得すると良い
$testHorseDataArray = array(
    array(
        'age' => 3,
        'name' => 'コントレイル',
        'flame_num' => 1,
        'popular_num' => 1
    ),
    array(
        'age' => 5,
        'name' => 'アーモンドアイ',
        'flame_num' => 3,
        'popular_num' => 2
    ),
    array(
        'age' => 3,
        'name' => 'デアリングタクト',
        'flame_num' => 4,
        'popular_num' => 3
    ),
    array(
        'age' => 4,
        'name' => 'サートゥルナーリア',
        'flame_num' => 5,
        'popular_num' => 4
    ),
    array(
        'age' => 6,
        'name' => 'キセキ',
        'flame_num' => 2,
        'popular_num' => 5
    ),
);

$testHorseDataList = array();

foreach ($testHorseDataArray as $testHorseData) {
    $testHorseDataList[] = new HorseData($testHorseData['age'], $testHorseData['name'], $testHorseData['flame_num'], $testHorseData['popular_num']);
}

$randomChoice = new Random();
$flameAscendingOrderChoice = new FrameAscendingOrder();

$choice1 = new TripleSingle($randomChoice);
$choice2 = new TripleSingle($flameAscendingOrderChoice);

var_dump($choice1->getParam($testHorseDataList));
var_dump($choice2->getParam($testHorseDataList));

/*結果
array(3) {
  [0]=>
  object(HorseData)#1 (4) {
    ["age":"HorseData":private]=>
    int(3)
    ["name":"HorseData":private]=>
    string(18) "コントレイル"
    ["flameNum":"HorseData":private]=>
    int(1)
    ["popularNum":"HorseData":private]=>
    int(1)
  }
  [1]=>
  object(HorseData)#3 (4) {
    ["age":"HorseData":private]=>
    int(3)
    ["name":"HorseData":private]=>
    string(24) "デアリングタクト"
    ["flameNum":"HorseData":private]=>
    int(4)
    ["popularNum":"HorseData":private]=>
    int(3)
  }
  [2]=>
  object(HorseData)#5 (4) {
    ["age":"HorseData":private]=>
    int(6)
    ["name":"HorseData":private]=>
    string(9) "キセキ"
    ["flameNum":"HorseData":private]=>
    int(2)
    ["popularNum":"HorseData":private]=>
    int(5)
  }
}
array(3) {
  [0]=>
  object(HorseData)#1 (4) {
    ["age":"HorseData":private]=>
    int(3)
    ["name":"HorseData":private]=>
    string(18) "コントレイル"
    ["flameNum":"HorseData":private]=>
    int(1)
    ["popularNum":"HorseData":private]=>
    int(1)
  }
  [1]=>
  object(HorseData)#5 (4) {
    ["age":"HorseData":private]=>
    int(6)
    ["name":"HorseData":private]=>
    string(9) "キセキ"
    ["flameNum":"HorseData":private]=>
    int(2)
    ["popularNum":"HorseData":private]=>
    int(5)
  }
  [2]=>
  object(HorseData)#2 (4) {
    ["age":"HorseData":private]=>
    int(5)
    ["name":"HorseData":private]=>
    string(21) "アーモンドアイ"
    ["flameNum":"HorseData":private]=>
    int(3)
    ["popularNum":"HorseData":private]=>
    int(2)
  }
}
 */

まとめ

・同様なデータから多数の方法を用いて結果を出したい場合は多くあるため、この考え方自体は多様な部分で使用できると感じた。
・余談だが、この競馬のものを機械学習と結び付けて多様なアルゴリズムで切り替えて分析してみるのもおもしろそうだなぁ。。と感じました。(笑)

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