- 投稿日:2022-02-01T20:13:09+09:00
�【PHPで学ぶ】オブジェクト思考って何で大事なの?
はじめに エンジニアを1年やって何となく使ってたオブジェクト思考をしっかり理解したい! そう思い基礎から勉強し直したので備忘録として残します。 サンプルコードはPHPを使いますが他のオブジェクト思考言語にも応用できるかと思います。 注意点としては、コードを乗せていますが全部理解する必要はなく、「このメソッドでこういう事をしてるのか」 ぐらいのざっくり理解で問題ないです! できる限りシンプルに解説しますが言葉足らずや間違いがあればご指摘お願いします では行きましょう! なぜオブジェクト思考は理解しづらいのか それはズバリ「何が良いか分からないから」です。 わざわざ抽象的な概念であるオブジェクト思考を使わずともプログラムは作れるしなんかやたらファイルも多くなって逆によく分からない! そう思う初学者の人もいると思います。(実際私も同じ事を考えてました) オブジェクト思考のメリットはたくさんありますが、最も良さを感じられるのが「拡張性」だと思ってます。 保守性とか再利用性とかって実務で働いたりしないと実感しにくい部分でもあるので初学者でもオブジェクト思考の良さを実感できる拡張性にフォーカスを当てて解説しようと思います。 今回の記事を見てなんとなくでも良いのでオブジェクト思考のメリットを感じて頂ければ幸いです。 実際のコードの違い プレイヤー同士が1~10のカードを引いて数字の大きい方が勝利というシンプルなプログラムを例とします。 オブジェクト思考を使わないコード メインプログラムが全ての責務を負っている状態 function makePlayer() { $name1 = 'プレイヤー1'; $name2 = 'プレイヤー2'; $player1 = [ 'name' => $name1, 'score' => 0 ]; $player2 = [ 'name' => $name2, 'score' => 0 ]; return [$player1, $player2]; } function draw($players) { $cards = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // プレイヤー1がランダムで1枚引く $arrKey1 = array_rand($cards, 1); $score1 = $cards[$arrKey1]; $players[0]['score'] = $score1; // プレイヤー2がランダムで1枚引く $arrKey2 = array_rand($cards, 1); $score2 = $cards[$arrKey2]; $players[1]['score'] = $score2; return $players; } function juge($playerScoreArr) { // 勝敗判定 if ($playerScoreArr[0]['score'] > $playerScoreArr[1]['score']) { echo '勝者は' . $playerScoreArr[0]['name'] . 'です'; } elseif ($playerScoreArr[1]['score'] > $playerScoreArr[0]['score']) { echo '勝者は' . $playerScoreArr[1]['name'] . 'です'; } elseif ($playerScoreArr[0]['score'] === $playerScoreArr[1]['score']) { echo '引き分けです'; } } // 関数呼び出し $players = makePlayer(); $playerScoreArr = draw($players); juge($playerScoreArr); 着目点はここだけでOKです // 関数呼び出し $players = makePlayer(); $playerScoreArr = draw($players); juge($playerScoreArr); オブジェクト思考を使ったコード メインプログラム(Gameクラスのstart関数)が各アクターに責務を委譲してる状態。 実際にはインターフェースで処理をまとめたり、アクターをさらに分割した方が良いと思いますが今回は割愛 class Card { private array $cards = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; public function draw() { $arrKey = array_rand($this->cards, 1); return $this->cards[$arrKey]; } } class player { private int $drawCard = 0; public function draw(Card $card) { $this->drawCard = $card->draw(); } public function getDrawCard() { return $this->drawCard; } } class TwoPlayerRule { private $player1 = ''; private $player2 = ''; public function __construct() { $this->player1 = new Player(); $this->player2 = new Player(); } public function draw() { $card1 = new Card(); $card2 = new Card(); $this->player1->draw($card1); $this->player2->draw($card2); } public function juge() { $score1 = $this->player1->getDrawCard(); $score2 = $this->player2->getDrawCard(); if ($score1 > $score2) { echo 'プレイヤー1の勝利です'; exit; } elseif ($score2 > $score1) { echo 'プレイヤー2の勝利です'; } elseif ($score1 === $score2) { echo '引き分けです'; } } } class Game { private int $playerNum = 0; public function __construct(int $playerNum) { $this->playerNum = $playerNum; } public function start() { $rule = $this->getRule(); $rule->draw(); $rule->juge(); } public function getRule() { $rule = ''; if ($this->playerNum === 2) { $rule = new TwoPlayerRule(); } return $rule; } } // Gameクラスの引数には参加人数を入力 $game = new Game(2); $game->start(); ちなみにメインプログラムはここだけ↓ ①ルールを取得 ②カードを引く ③勝敗を判定 public function start() { $rule = $this->getRule(); $rule->draw(); $rule->juge(); } 多分コード数が多くてオブジェクト思考を使った方が分かりにくい! そう思った人もいると思いますが、オブジェクト思考の強みは拡張性の高さです。 ここから機能追加をしていきます プレイヤー数を追加 オブジェクト思考を使わないコード 判定ロジックが少々複雑なのと処理の内容は重要ではないので今回は割愛させて頂きました <?php function makePlayer() { $name1 = 'プレイヤー1'; $name2 = 'プレイヤー2'; $name3 = 'プレイヤー3'; $player1 = [ 'name' => $name1, 'score' => 0 ]; $player2 = [ 'name' => $name2, 'score' => 0 ]; $player3 = [ 'name' => $name3, 'score' => 0 ]; return [$player1, $player2, $player3]; } function draw($players) { $cards = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // プレイヤー1がランダムで1枚引く $arrKey1 = array_rand($cards, 1); $score1 = $cards[$arrKey1]; $players[0]['score'] = $score1; // プレイヤー2がランダムで1枚引く $arrKey2 = array_rand($cards, 1); $score2 = $cards[$arrKey2]; $players[1]['score'] = $score2; // プレイヤー3がランダムで1枚引く $arrKey3 = array_rand($cards, 1); $score3 = $cards[$arrKey3]; $players[2]['score'] = $score3; return $players; } function juge($playerScoreArr) { // 勝敗判定 if ($playerScoreArr[0]['score'] > $playerScoreArr[1]['score'] && $playerScoreArr[0]['score'] > $playerScoreArr[2]['score']) { echo '勝者は' . $playerScoreArr[0]['name'] . 'です'; } elseif ($playerScoreArr[1]['score'] > $playerScoreArr[0]['score'] && $playerScoreArr[1]['score'] > $playerScoreArr[2]['score']) { echo '勝者は' . $playerScoreArr[1]['name'] . 'です'; } elseif ($playerScoreArr[2]['score'] > $playerScoreArr[1]['score'] && $playerScoreArr[2]['score'] > $playerScoreArr[0]['score']) { echo '勝者は' . $playerScoreArr[2]['name'] . 'です'; } elseif () { // 以下割愛 } } $players = makePlayer(); $playerScoreArr = draw($players); juge($playerScoreArr); オブジェクト思考を使ったコード こちらもロジックは省略しますね class ThreePlayerRule { // 追加 private $player3 = ''; public function draw() { // 追加 // 省略 } public function juge() { // 追加 // 省略 } 両者の違い この二つの大きな違いは既存プログラムを変更してるのかという点です。 (正確にはGameクラスのgetRuleメソッドの分岐処理は必要ですが、ほぼ修正なし) オブジェクト思考を使用してるプログラムは仕様変更がある度にメインプログラムを変更してるのに対してオブジェクト思考を使ったコードはThreePlayerRuleクラスというクラスを新規に追加しただけで既存のコードには触れていません。 このように一つの仕様変更に対して新しくコードを追加するだけで対応できるオブジェクト思考プログラミングはシステムの拡張性が高いと言われる理由です。 現実世界で例えると自転車のタイヤが壊れたら自転車本体を1から作り直すのが非オブジェクト思考でタイヤのみを交換するのがオブジェクト思考というイメージです。 非オブジェクト思考のコードの問題点 問題点はいくつかありますが、重要なのはプログラム同士が密に結合してる事です。 人数を増やしたい場合makePlayerメソッドを修正する必要があり、drawメソッドも修正が必要になり、さらにはjugeメソッドも修正する必要があります。 プログラムが大きくなればなるほど一つの追加機能で他の多くの箇所を修正しなければいけなくなります。 さらに一つの修正で他のプログラムでバグが起こりやすくなり、あっという間に技術的負債の完成といった感じになるのです。 既存コードをできる限り修正せずに開発ができるようしっかり設計をすることが重要になってきますね! まとめ オブジェクト思考はシステムの拡張性が高い オブジェクト思考は既存コードの修正を最小限にしてくれる メインプログラムのコードがシンプルになり可読性が上がる 最後に 私自身エンジニアとしては1年程ですがオブジェクト思考をほとんど使わずに無理やりif文などで実装してる案件に数回アサインした経験があるのですが、1つのファイルで何千行というファイルを見たときは絶望しましたし、そういった状態になると修正するのが難しくなりどんどん技術的負債となってしまします。数日間ソースを読み解くだけみたいな日々で本当に苦しかったです笑 この記事を見て少しでもオブジェクト思考に興味を持って、開発してくれる方が増えると嬉しいです!
- 投稿日:2022-02-01T19:43:08+09:00
[PHP]入力値がSJISで表現できるか調べる
概要 ある文字列がSJISで表現できるかどうかを調べます。 手順 デフォルトの文字コードで表現された文字列を、SJISに変換して再度元の文字コードに変換。 その結果が同一であればSJISで表現できる文字列。(表現できない場合は変換時に?などで置き換えられる為) 結果 <?php // デフォルト文字コードをUTF-8とする // SJISで表現できない文字 $a = '?'; var_dump($a); #=> string(4) "?" //問題なく出力される // UTF-8からSJISに変換 $converted = mb_convert_encoding($a, 'SJIS', 'UTF-8'); var_dump($converted); #=> string(1) "?" //表現されない // SJISからUTF-8に変換 $new_a = mb_convert_encoding($converted, 'UTF-8', 'SJIS'); var_dump($new_a); #=> string(1) "?" //表現されない // $a !== $new_a なので"?"はSJISで表現できない まとめ この辺の関数を使用してもうまくチェックできなかったので、今回のような方法を取りました。 何か他に良い方法があったりしたら教えていただけるとありがたいです。
- 投稿日:2022-02-01T19:13:23+09:00
PHP 「===」 と 「==」の違い
基本的なことですがわかっていなかったので調べてみました。 先に結論を書きますと "==="は値とデータの型が完全に一致している場合 "=="は値のみ一致している場合 ということでした。 例 $array = ["15", 15]; var_dump($array);//出力:array(2){[0] => string(2) "15", [1] => int(15)} // 例1 foreach ($array as $v) { if($v === 15){ echo "YES" . " "; }else{ echo "NO" . " "; } } //出力:NO YES // 例2 foreach ($array as $v) { if($v == 15){ echo "YES" . " "; }else{ echo "NO" . " "; } } //出力:YES YES データの型はstring=文字列, int=整数のことを表しています。 例1の条件式は$v === 15 なのでint(整数)の15の場合のみ"YES"、それ以外は"NO"を表示するので 出力:NO YES となります 例2の条件式は$v == 15 なのでint(整数)、string(文字列)関係なく15の場合に"YES"を表示するので 出力:YES YES となります こちらの記事でさらに詳しくわかりやすい説明をされていたので、深堀したい方はこちらをご覧になってみてください。 参考:https://qiita.com/atsushi586/items/adca2368d9a760ad862d
- 投稿日:2022-02-01T17:10:21+09:00
オブジェクト指向について簡単にまとめとく
オブジェクト指向についてざっと解説 オブジェクトとは、その意味の通りモノとして考えてシステム構築をしていく考え方のこと。 例えば、簡単な掲示板を作ろうとしたときに、$post='投稿したよ'と変数で管理する場合を考える。 ここで、さらに機能を追加したいな〜となり、$post['like']=0や$post['text']='投稿したよ'といったように 配列で作成したとする。 さらに機能を追加して、投稿の詳細ページを閲覧できる関数showを追加したくなったとする。 この調子で機能が増えていくとそれを管理することがかなり大変になってくる。 そこで役に立つのがオブジェクト指向である。 オブジェクト指向を使うことで、postに関する変数や関数を管理しやすくなる。 詳しい使い方は省くが、オブジェクト指向を使うことで、以下のように簡単に関数などを使えるようになる。 $post->text = '投稿したよ'; //テキストの追加 $post->like = 0; //いいねの数 $post->show(); //showメソッドを使う プロパティとは プロパティとは、簡単にいうとオブジェクトに関連づいたデータのこと。 言い換えると、クラス内の変数のようなもの。 プロパティ宣言とは? プロパティにはアクセス修飾子なるものがあります。 アクセス修飾子はpublic、private、protectedからできています。 それぞれ性質は以下の通りです。 ・public→クラス外からでもアクセス可 ・private→同じクラス内ならアクセス可 ・protected→同クラス内と継承先のクラスのメソッドからアクセス可 コンストラクターとは コンストラクターとは、クラスのインスタンス(クラスを変数に入れたもの)を作成する際に必ず実行される関数のこと。 書き方はpublic function __construct(引数)です。 例えば、以下のようなコードを考えます。 <?php class Post {//postクラス //プロパティ public $text; public $like = 0; //コンストラクター public function __construct($text){ $this->text = $text; } } //ここの引数がコンストラクターの$textに渡される $post = new Post("hello world! 投稿をしたよ!"); //Postクラスのtextを表示→hello world! 投稿をしたよ! echo $post->text; まとめ オブジェクト指向について忘れる事が多かったのでQiitaにまとめました。 ある程度オブジェクト指向を理解している人向けかつ自分用のメモみたいに作ったので見にくかったらすみません! クソお世話になったサイトさんたち ・【PHP】クラスの基本 | インスタンス | プロパティとメソッド | オブジェクト指向 ・PHP オブジェクトのプロパティ ありがとうございましたぁ!クソお世話になりましたぁ!
- 投稿日:2022-02-01T16:32:23+09:00
【Doctrine】QueryBuilderでデータを取得したい
QueryBuilderでの生成に苦戦したときのメモ書きです。 それでもよければ参考程度に。。。 Doctrine ORM(Object Relational Mapping)ライブラリの1つで、 SQLを書かずオブジェクト指向でデータベースの操作ができる優れものです! データを取得してみる シンプルにやるとこんなの test.sql SELECT * FROM dtb_customer WHERE age = 20; CustomerRepository.php $query = $this->createQueryBuilder('c') ->where('c.age = :adult') ->setParameter('adult', 20) ->getQuery(); $customer = $query->getResult(); 抽出条件を追加 test.sql SELECT * FROM dtb_customer WHERE age = 20 AND id >= 10; CustomerRepository.php $query = $this->createQueryBuilder('c') ->where('c.age = :adult') ->andWhere('c.id >= :userid') ->setParameter('adult', 20) ->setParameter('userid', 10) ->getQuery(); $customer = $query->getResult(); IN句 test.sql SELECT * FROM dtb_customer WHERE id IN(2, 5); CustomerRepository.php $query = $this->createQueryBuilder('c') ->where('c.age IN(:adult1, :adult2')) ->setParameter('adult1', 2) ->setParameter('adult2', 5) ->getQuery(); $customer = $query->getResult(); 変数にパラメータを格納したいとき test.sql SELECT * FROM dtb_customer WHERE age = 20; CustomerRepository.php $adult_age = 20; $query = $this->createQueryBuilder('c') ->where('c.age = :adult') ->setParameter('adult', $adult_age) ->setParameter('email_mobile', $username) ->getQuery(); $customer = $query->getResult(); ORDER BY test.sql SELECT * FROM dtb_customer ORDER BY age ASC; CustomerRepository.php $adult_age = 20; $query = $this->createQueryBuilder('c') ->orderBy('c.age', 'ASC'); ->getQuery(); $customer = $query->getResult(); JOIN part1 test.sql SELECT * FROM dtb_customer AS c INNER JOIN dtb_order AS o ON c.id = o.customer_id; CustomerRepository.php $query = $this->createQueryBuilder('c') ->leftJoin('c.dtb_order o'); ->getQuery(); $customer = $query->getResult(); Doctrineくんはdtb_customerとdtb_orderが関連していることを知っているため、 JOINの条件を自動的に追加してくれます。 JOIN part2 test.sql SELECT * FROM dtb_customer INNER JOIN dtb_order ON c.id = 10; CustomerRepository.php $query = $this->createQueryBuilder('c') ->leftJoin('c.dtb_order o ON c.id = 10'); ->getQuery(); $customer = $query->getResult(); 独自の結合条件を使いたいならこう。
- 投稿日:2022-02-01T04:26:45+09:00
[laravel]php artisan migrate が通らないときの解決例
エラー内容 $ php artisan migrate Migrating: 2022_01_29_163203_create_projects_table Illuminate\Database\QueryException SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'projects' already exists (SQL: create table `projects` (`id` bigint unsigned not null auto_increment primary key) default character set utf8mb4 collate 'utf8mb4_unicode_ci') at vendor/laravel/framework/src/Illuminate/Database/Connection.php:712 708▕ // If an exception occurs when attempting to run a query, we'll format the error 709▕ // message to include the bindings with SQL, which will make this exception a 710▕ // lot more helpful to the developer instead of just the database's errors. 711▕ catch (Exception $e) { ➜ 712▕ throw new QueryException( 713▕ $query, $this->prepareBindings($bindings), $e 714▕ ); 715▕ } 716▕ } +9 vendor frames 10 database/migrations/2022_01_29_163203_create_projects_table.php:32 Illuminate\Support\Facades\Facade::__callStatic("create") +21 vendor frames 32 artisan:37 Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput)) まず結論(解決方法) mysqlに既に他のlaravelプロジェクトでテーブル出来上がっているため、そのテーブルを消すことでmigrateが通るようになります。 なぜ記事を書いたのか 久しぶりにdockerを使わないでlaravelの環境構築したところ、みたことのない種類のエラーに出会い躓きました。 そのためぱっと見どこにエラー文が書いてあるかわからず、解決まで時間がかかったので備忘録の意味も含めて記載しようと思いました。 エラー文はどこにあるのか 筆者は最初は ➜ 712▕ throw new QueryException( を見て、その文字で検索して結局解決できず、試行錯誤して偶然解決までたどり着きました。 解決後にエラーをよくよくみたところ SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'projects' already exists ~ というエラー文があり、「既にテーブルが入っている」と言っていたことに気づきました。 結論ですが、エラー文はSQLSTATE[42S01]:~ の部分に書いてあります。 まとめ 今回の記事でお伝えしたかったことは下記の2点です。 エラー解決の具体例を探している場合:DBに他のプロジェクトのテーブルが入っていないか確認する エラー文が何を言っているかわからず困っている場合:SQLSTATE[42S01]:~の部分を読んでみる