20210503のPHPに関する記事は18件です。

EC2にPHPでWebAPIをサクッと作る

やりたいこと 検証用のEC2にWebAPIをサクッと作る PHPで実装 クライアントの要望でEC2上にPHPで簡単なRestAPIを作成することになった。 作成するのはPostとPutの2つのAPIのみ。 PHP自体が初なのでそもそもの実現方法から検討。 調べたところ生のPHPでもroutingなどは行えそうではあったが、Larvelの軽量フレームワークLumenであれば割と手軽に入れて実装自体も簡単そうだったので今回はLumenで対応。 https://lumen.laravel.com/ 手順 1. EC2にapacheをインストール まずは更新 sudo yum update -y 次にインストール sudo yum install httpd 2. EC2にphpをインストール EC2用のPHPを入れることで7系のPHPのインストールが行える。 (LumenがPHP7.3以降しか対応していない) sudo amazon-linux-extras install php7.4 -y 4. EC2にcomposerをインストール Lumenを利用するために必要なcomposerをインストールする。 公式サイトに記載があるコマンドをコピペして実行 https://getcomposer.org/download/ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" php -r "if (hash_file('sha384', 'composer-setup.php') === '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" php composer-setup.php php -r "unlink('composer-setup.php');" composerの本体はbin以下においておく sudo mv composer.phar /usr/local/bin/composer 5. Lumenのセットアップ まずはLumenで必要なライブラリをインストール sudo yum install php-mbstring php-pecl-memcached php-gd php-apcu php-xml -y 続いてLumenを入れていく composer global require "laravel/lumen-installer=~1.0" プロジェクトの雛形を作成する cd /var/www/html lumen new blog ※上記のコマンドの最後のblogがフォルダ名になるので自由に変える ※apache上で動かすので /var/www/html 配下に作成した。 なお、Lumenを入れずにcomposerの以下のコマンドでもプロジェクトの雛形は作成可能 composer create-project --prefer-dist laravel/lumen blog 6. 権限設定 Lumenのログとキャッシュのフォルダに書き込み権限を付与する。 これやらないとログが出せない。 cd /var/www/html/blog chmod -R 777 bootstrap chmod -R 777 storage 7. Apache Virtual Hostの設定 /etc/httpd/conf.d以下にvhost.confを作成する。 ※ファイル名は拡張子が.confであればなんでもOK 中身を記載する ※DocumentRootとServerNameでどのドメインでアクセスされたらどこをDocumentRootにするかを指定するための設定 <VirtualHost *:80> DocumentRoot /var/www/html/blog/public ServerName [EC2のパブリックDNS名称] <Directory "/var/www/html/blog/public"> # allow .htaccess AllowOverride All # Setting Laravel environment SetEnv APP_ENV development # Allow access Require all granted </Directory> </VirtualHost> 一旦これでポート80のhttpの通信に対応できるようになる。 Direcory以下を記載しないとRestのRountingが動かないので記載する。 APP_ENVとかは多分なくても動く。 ドメイン直下にアクセスするとLumenのバージョンが表示される。 Lumen (8.2.3) (Laravel Components ^8.0) 8. 完了 これで一通りの設定は完了 あとは好きなように実装するのみ。 参考 https://qiita.com/miriwo/items/b25f9d4d74b7103f6ff6
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Phalconをdocker imageに詰め込みたいとき

前提 DockerHubにあるfpmイメージをベースとして考えています。(debianベース) Phalcon4です。psrを入れる辺りを消せば3系でも使えたと思います。 とりあえず先に結論 素直にソースからビルドしましょう。(正しくはPhalconをCの形式にビルドされた後のソース) 以下は私が作っているdocker imageからphalconの本質的なインストール部分だけ持ってきたものです。 # install phalcon RUN pecl install -s psr \ && docker-php-ext-enable psr \ && pecl clear-cache \ && curl -LO https://github.com/phalcon/cphalcon/archive/v${PHALCON_VERSION}.tar.gz \ && tar xzf v${PHALCON_VERSION}.tar.gz \ && docker-php-ext-install -j$(nproc) ${PWD}/cphalcon-${PHALCON_VERSION}/build/php7/64bits \ && rm -rf v${PHALCON_VERSION}.tar.gz cphalcon-${PHALCON_VERSION} 色々試していたのですが、これが確実です。。。 少し前だともっとめんどくさくて、zephirのパーサーから入れないと駄目な感じでしたので、良くなったほうです。 もっとめんどくさかった頃(クリックで開く) 当時書いてたやつなので、ちょいちょい最適化されてないところもありますが。。。 # install php-psr RUN pecl install psr && docker-php-ext-enable psr # install zephir-parser RUN curl -LO https://github.com/phalcon/php-zephir-parser/archive/v${ZEPHIR_PARSER_VERSION}.tar.gz \ && tar xzf v${ZEPHIR_PARSER_VERSION}.tar.gz \ && docker-php-ext-install ${PWD}/php-zephir-parser-${ZEPHIR_PARSER_VERSION} \ && rm -rf php-zephir-parser-${ZEPHIR_PARSER_VERSION} v${ZEPHIR_PARSER_VERSION}.tar.gz # install phalcon RUN curl -LO https://github.com/phalcon/cphalcon/archive/v${PHALCON_VERSION}.tar.gz \ && tar xzf v${PHALCON_VERSION}.tar.gz \ && cd cphalcon-${PHALCON_VERSION} \ && curl -LO https://github.com/phalcon/zephir/releases/download/${ZEPHIR_VERSION}/zephir.phar \ && php zephir.phar fullclean && php zephir.phar compile \ && docker-php-ext-install ${PWD}/ext \ && cd .. \ && rm -rf v${PHALCON_VERSION}.tar.gz cphalcon-${PHALCON_VERSION} 色々試した いまはpearのライブラリも結構更新されるようになって、phalconのreleaseにもpearパッケージのソースがつくようになったりしてます。 そこで、peclで入れれないのかと試したのですが、途中でコンパイルが止まって最終的にコンパイル失敗になってしまうようでした。ディストリビューションによってはうまく動くのかも知れません。。。(私がやろうとしたのはdebianのbuster) 他にPackageCloudというサービスにもphalconのリポジトリはあります。 https://packagecloud.io/phalcon こちらで試しに入れようとしましたが、うまく入ったかな?とおもってapt-cacheコマンドでインストール出来るパッケージを探してみましたが無いことになってしまったので断念。 ppa:ondrej/phpとかも試してみようかとしましたが、そもそもppaがUbuntu用だったし、GPGのエラーが出たりといろいろ苦戦してしまい、結局入れれそうにありませんでした。。。 自分で用意するのが面倒な人に 自分の方で作っているイメージがあります。それを元にしてもいいと思います。 phalconはセマンティックバージョニングなのでメジャーバージョンとしてしかバージョンを付けてないですが。。。 最後に 結果的に、ソースからビルドして入れるのが最適だという結論になったのですが、もしもっと簡単に入れれる方法があったら教えてほしいです。。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Phalconの本家ソースはどこにあるのか

こんばんは。 Phalcon PHP Frameworkという元はPHPの拡張で書かれたフレームワークがあります。 このフレームワークはZephirと呼ばれるPHPライクな感じで書く言語で書かれています。 なぜこの記事を書いたのか 残念なことに、phalconのドキュメントは更新が遅いので新しく入った機能を使いたくなった場合、ドキュメントだけでは分からないので結局PRなどからzepのソースを読むことになるのです。 また、少し関係ないですが公式のドキュメントも日本語化されている方はページによってはmarkdownが崩れていたりと辛かったりします。(結局英語ドキュメントを見たほうが良いことに) なお、Phalconは予定では6からPHPソース版がリリースされるので、今後は読むのは楽になると思います。 ソースはどこにあるのか ソースは以下にあります。 2021/05/03現在、このリポジトリのphalconディレクトリにコアコードが置かれています。 読み方 まあ実際にzepという拡張子(Zephir言語で書かれたもの)は結構PHPライクなもので書かれているのでそこまで困らないと思います。(内部ソースを見るに、phpの関数も呼べるようです。) ちょっとメソッドを少し見てみましょう。Logger.zepのlog関数です。 /** * Logs with an arbitrary level. * * @param mixed level * @param string message */ public function log(level, message, array context = []) -> void { var intLevel; let intLevel = this->getLevelNumber(level); this->addMessage(intLevel, (string) message, context); } phpdocみたいなのも書かれていますし、まさにPHPみたいな感じです。 ただし気になるところとして、varとletがあります。 varは動的変数の定義です。Zephirでは変数定義が必要です。 そして、letは変数の初期化に使われます。 一応、一緒に初期化することも出来ますが(var a = "aaa")、phalconのソースではあまり見受けられません。 Zephirのドキュメントは以下にあります。 ちょっと面倒なこと これはphalconのソースなのですがphalconに問題があったときに、phalconに問題があるのか、zephirに問題があるのかを追っかけないと分からないです。(つらい) PHPの拡張であるものの、PHPとは若干異なる次元で動いてるためReflectionなどに影響があるときがあります。 普段の開発では 普段の開発だと、毎回毎回ドキュメントを追っかけることは流石にきついので、phalconにはide-stubsと呼ばれるIDE用のスタブがライブラリとして存在します。(LaravelのIDE helper的なやつです) このide-stubsは、zephirのソースにある https://github.com/zephir-lang/zephir/blob/development/Library/Stubs/Generator.php が生成を行います。 現状では自動で生成してpushされるような状態になっていないので、最新のものが出たとしても待つ必要があります。 また、zephir側の静的解析が雑な関係で部分的に返却する型や引数の情報がコアソースと異なる場合があるので注意しましょう。(早く色々解決してほしい) しかしながら無いよりはすごく助かるスタブなので、もしphalcon使っていて入れてない人は入れたほうが良いでしょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPは日本語のクラス名でもautoloadできる

テーブル名もカラム名も日本語のデータベースにORMを使おうとすると、エンティティクラス名が日本語になってしまったので、そもそも日本語のクラスってautoloadできるのか?というのを実験してみました。 環境 ubuntu 20.04 php 8.0 成果物 リポジトリはこちら ディレクトリ構造 . ├── composer.json ├── composer.lock ├── src │   ├── Foo │   │   └── Baa │   │   └── 日本人.php │   ├── bootstrap.php │   └── test.php └── vendor 日本語のクラスを用意 日本人.php <?php namespace Isanasan\Test; class 日本人 { public function あいさつ() { echo 'こんにちは' . PHP_EOL; } } autoloadの設定 composer.json { "require": { "php": "^8.0" }, "autoload":{ "psr-4": { "Isanasan\\Test\\":"src/Foo/Baa" } } } 呼び出し側 test.json <?php namespace Isanasan\Test; require_once __DIR__ . "/bootstrap.php"; $japanese = new 日本人(); $japanese->あいさつ(); 実行結果 % php ./src/test.php こんにちは まとめ 無事、日本語のクラスのautoloadとついでに日本語メソッドの実行の確認ができました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「テスト駆動開発」をPHPで写経してみた

モチベーション エンジニアをやっていると何を学んで何を学ばないのか、どういう順で学んでいくのか、という悩みはあるあるだと思います。 筆者はたまたま、これを読んだのと合わせて、これを聞いた時に今の自分に丁度良さそうだと感じたので早速やってみました。 使用言語には迷ったのですが、現職のメイン言語であるPHPを使いました。 成果物 https://github.com/isanasan/php-tddbook 4月1日に写経を開始してだいたい毎日約一時間ずつ作業を進めて完走できたのが4月30日なので、正味作業時間は30時間程度だと思います。 ハマったところ オブジェクトの型キャスト 第三章のvalueObjectパターンを適用するところで、引数をObject型で受け取ってキャストするのをphpでどうやるのか悩みました。いろいろ調べてこちらの記事を読み https://budougumi0617.github.io/2020/01/29/php-cast-original-class/ 「キャストのためにわざわざメソッド生やさなきゃダメなの?」となりましたが、筆者はキャストを行わず任意の型を引数で指定する実装にしました。 hashMapを使う 第十四章の為替レートを保持する仕組みをhashMapで実装しているところで、phpでどのように実装するか悩みました。ここではphpの配列を使って単純に実装しました。phpの配列はなんでも入れられるし、便利な組み込み関数がたくさんあるのでこういう時にやくにたちます。 testMethodの動的な呼び出し 第十八章の、テストケース名を問い合わせて戻ってきたオブジェクトを関数として実行する処理をphpでどうやるのか分からず悩みました。この部分はググり方から検討がつかなかったので、同じようにTDD本をphpで写経している人のリポジトリを探して参考にしました。 https://github.com/shin1x1/book-tdd-php PHPの現場でおなじみの新原さんのリポジトリを見付けてありがたく参考にさせていただきました。ここは結局call_user_func()で一発でした。 assert()が例外を投げてくれない 同じく十八章のassert()を使ってテストの失敗を検知する実装でassert()が例外を吐いてくれませんでした。ここはドキュメントを読んで、php.iniに zend.assertions = 1 を設定して解決しました。 まとめ 普段なにげなくコードを書いていて、自分のパフォーマンスが良いときもあれば悪いときもあるという感じなのですが、TDDという具体的な手法に則ってプログラミングすることで一皮剥けそうだなという気がしています。とはいえ日頃から実践して身につけてはじめて役に立つものだとも思うので、当面は意識的にTDDを実践してみようと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[PHP]今日は何の日かを教えてくれるLINE Botを作ってみる。

今回はLINE Messaging API SDKを使用して「今日は何の日」かを教えてくれるLINE Botを作成したいと思います。 完成イメージ githubリポジトリ https://github.com/tatemaru/whats_today_line_bot 注意事項 これは未経験文系エンジニアが作成したものです。温かい目で見守ってください。笑 また、日本記念日協会のサイトをスクレイピングして作成していますので、商業目的の利用などはしないように気をつけてください。 環境 Mac 11.2.2 PHP 7.3.24 Composer version 1.10.5 LINEBOTの始め方(APIキーの発行)などは下記のサイトがわかりやすいかと思います。 今回はすでにこのLINEBOTアカウントがあるという前提で進めます。 LINE BOTの作り方とは?Messaging APIを利用した作成法3選 手順 LINE Messaging API SDKとPHPQueryインストール LINE Messaging API SDK 下記のコマンドを実行してComposerを使ってLINE Messaging API SDKをインストールします。 Composerをインストールしていない人は先にPCにComposerをインストールしておいてください。 $ composer require linecorp/line-bot-sdk #メインのphpファイルを作成します。 $ touch line_bot.php スクレイピングに使用するPHPQuery(phpQuery-0.9.5.386-onefile.zip)をダウンロードしてphpQuery-onefile.phpという名前にリネームしておきます。 下記のような状態になっていれば大丈夫です。 . ├── composer.json ├── composer.lock ├── line_bot.php ├── phpQuery-onefile.php └── vendor 完成ソースコード 完成ファイルは下記にようになります。 line_bot.php <?php require('vendor/autoload.php'); use LINE\LINEBot\Constant\HTTPHeader; use LINE\LINEBot\HTTPClient\CurlHTTPClient; use LINE\LINEBot; // このphpファイルで使用する定数やメインのオブジェクトの定義 const channel_access_token = 'xxxxxxxxxxxxxxxxxx'; const channel_secret = 'xxxxxxxxxxxxxxxxxxxx'; $httpClient = new \LINE\LINEBot\HTTPClient\CurlHTTPClient(channel_access_token); $bot = new \LINE\LINEBot($httpClient, ['channelSecret' => channel_secret]); // ▼▼メイン処理▼▼ $resflg = resMassage($http_client, $bot); if($resflg === false){ $text = getTodayInfo(); $genText = textMessageBuilder($text); sendLineMessage($genText, $httpClient, $bot); } else { exit; } // ▲▲メイン処理▲▲ /** * メインのアクション。ユーザからのレスポンスかcronによる定期実行を切り分ける。 * cron実行の場合は$http_request_bodyがNULLになるのでそこでf¥$resflgがfalseで返る * * @param object $http_client * @param object $bot Message * @return bool $resflg 応答の有無のフラグ */ function resMassage($http_client, $bot){ $signature = $_SERVER['HTTP_' . HTTPHeader::LINE_SIGNATURE]; $http_request_body = file_get_contents('php://input'); // cron実行の場合はリターンさせる。 if(!$http_request_body){ $resflg = false; return $resflg; } $events = $bot->parseEventRequest($http_request_body, $signature); $event = $events[0]; $Receive_text = $event->getText(); $reply_token = $event->getReplyToken(); if($Receive_text === '今日は何の日?'){ //LINEBOTの応答 $text = getTodayInfo(); $bot->replyText($reply_token, $text); $resflg = true; return; } elseif(isset($Receive_text) && !($Receive_text === '今日は何の日?')) { //今日は何の日と聞かれなかったとき $text = '返信ありがとうございます。' ."\n".'よければ「今日は何の日?」と聞いてみてください。'; $bot->replyText($reply_token, $text); $resflg = true; return; } else{ //例外 $resflg = false; return $resflg; } } /** * 生成されたメッセージをBOTの友達全員に送付する。 * * @param string $sendMessage * @param object $httpClient * @param object $bot */ function sendLineMessage ($sendMessage, $httpClient, $bot){ $response = $bot->broadcast($sendMessage); error_log(date('Y/m/d H:i:s') .':'.'[HTTPS_status:' . $response->getHTTPStatus() . ']'."\n", 3, './debug.log'); } /** * 送信するテキストをTextMessageBuilderクラスから生成する。 * * @param string $lineText * @return object $textMessageBuilder */ function textMessageBuilder ($lineText) { $textMessageBuilder = new \LINE\LINEBot\MessageBuilder\TextMessageBuilder($lineText); return $textMessageBuilder; } /** * 日本記念日協会から今日の記念日をスクレイピングする。 * * @return string $sendText */ function getTodayInfo() { require_once("./phpQuery-onefile.php"); $contents = file_get_contents("https://www.kinenbi.gr.jp/"); $html = phpQuery::newDocument($contents); $todayInfo = phpQuery::newDocument($html)->find(".today_kinenbilist font")->text(); $sendText = date("m月d日") . 'には以下の記念日が登録されています。' ."\n" . $todayInfo ."\n" . '参照サイト(一般社団法人日本記念日協会)' ."\n" .'https://www.kinenbi.gr.jp/' ; return $sendText; } では順番にかんたんに解説します。 解説(1) 解説(1)ここは使用するcomposer、クラスなどを記載しています。 <?php require('vendor/autoload.php'); use LINE\LINEBot\Constant\HTTPHeader; use LINE\LINEBot\HTTPClient\CurlHTTPClient; use LINE\LINEBot; 解説(2) 解説(2)ここでは使用するAPIキーやプログラム全体で使用するオブジェクトを宣言しています。 色々なサイトを見ましたが、$httpClientや$botのインスタンスの仕方は大体下記のような書き方でした。 // このphpファイルで使用する定数やメインのオブジェクトの定義 const channel_access_token = 'xxxxxxxxxxxxxxxxxx'; const channel_secret = 'xxxxxxxxxxxxxxxxxxxx'; $httpClient = new \LINE\LINEBot\HTTPClient\CurlHTTPClient(channel_access_token); $bot = new \LINE\LINEBot($httpClient, ['channelSecret' => channel_secret]); 定数channel_access_tokenとchannel_secretには下記のキャプチャーを参考にしてください。 解説(3) 解説(3)ここで各メソッドを呼び出しています。resMassage()メソッドの返り値は応答メッセージならtrueを返して、そうでない場合(cron実行やphpをターミナルから直接実行)はfalseが返り、if文の最初のカッコに入ります。 $resflg = resMassage($http_client, $bot); if($resflg === false){ $text = getTodayInfo(); $genText = textMessageBuilder($text); sendLineMessage($genText, $httpClient, $bot); } else { exit; } 解説(4) 解説(4)ここが応答する場合のメインの処理になります。 function resMassage($http_client, $bot){ $signature = $_SERVER['HTTP_' . HTTPHeader::LINE_SIGNATURE]; $http_request_body = file_get_contents('php://input'); // cron実行の場合はリターンさせる。 if(!$http_request_body){ $resflg = false; return $resflg; } $events = $bot->parseEventRequest($http_request_body, $signature); $event = $events[0]; $Receive_text = $event->getText(); $reply_token = $event->getReplyToken(); if($Receive_text === '今日は何の日?'){ //LINEBOTの応答 $text = getTodayInfo(); $bot->replyText($reply_token, $text); $resflg = true; return; } elseif(isset($Receive_text) && !($Receive_text === '今日は何の日?')) { //今日は何の日と聞かれなかったとき $text = '返信ありがとうございます。' ."\n".'よければ「今日は何の日?」と聞いてみてください。'; $bot->replyText($reply_token, $text); $resflg = true; return; } else{ //例外 $resflg = false; return $resflg; } } 解説(4)-1 まず、LINE Messaging APIがリクエストに付与した署名を取得をここで取得しています。 $signature = $_SERVER['HTTP_' . HTTPHeader::LINE_SIGNATURE]; 解説(4)-2 次にここで、LINEからのリクエスト全体を読み込みます。 cronやターミナルからの実行の場合、ここが空になるので、if文でリターンさせています。 $http_request_body = file_get_contents('php://input'); // cron実行の場合はリターンさせる。 if(!$http_request_body){ $resflg = false; return $resflg; } 解説(4)-3 LINEの返信のトークンや受信したメッセージを取得しています。 $events = $bot->parseEventRequest($http_request_body, $signature); $event = $events[0]; $Receive_text = $event->getText(); $reply_token = $event->getReplyToken(); 解説(5) 解説(5) LINEの受信したメッセージの内容を元に条件を分けています。 getTodayInfo()メソッドは後ほど解説しますが、日本記念日協会のサイトをスクレイピングするメソッドです。 つまり、「今日な何の日?」と聞かれた場合のみスクレイピングして今日の記念日情報を返します。 if($Receive_text === '今日は何の日?'){ //LINEBOTの応答 $text = getTodayInfo(); $bot->replyText($reply_token, $text); $resflg = true; return; } elseif(isset($Receive_text) && !($Receive_text === '今日は何の日?')) { //今日は何の日と聞かれなかったとき $text = '返信ありがとうございます。' ."\n".'よければ「今日は何の日?」と聞いてみてください。'; $bot->replyText($reply_token, $text); $resflg = true; return; } else{ //例外 $resflg = false; return $resflg; } 途中のメソッドはDocコメントの通りなので解説は飛ばします 解説(6)日本記念日貴協会のスクレイピングの方法を解説します。 /** * 日本記念日協会から今日の記念日をスクレイピングする。 * * @return string $sendText */ function getTodayInfo() { require_once("./phpQuery-onefile.php"); $contents = file_get_contents("https://www.kinenbi.gr.jp/"); $html = phpQuery::newDocument($contents); $todayInfo = phpQuery::newDocument($html)->find(".today_kinenbilist font")->text(); $sendText = date("m月d日") . 'には以下の記念日が登録されています。' ."\n" . $todayInfo ."\n" . '参照サイト(一般社団法人日本記念日協会)' ."\n" .'https://www.kinenbi.gr.jp/' ; return $sendText; } today_kinenbilistクラスの中のfontタグの中に各記念日情報が書いてあることが下記のキャプチャーから確認できているので、そこを指定しています。 取得した記念日情報を$sendTextの変数の中に、わかりやすい形で代入して最後にリターンさせています。 Webhookの利用 応答メッセージに対応するにはWebhookに対応する必要があります。 Messaging API設定からSSL化したURLを入力してください。 「今日は何の日?」という応答メッセージに対応する必要のない場合(cronやターミナルからの実行のみ)はここの設定は不要です 使用してみる。 実際に起動させてみます。下記のコマンドを実行します。 $ php line_bot.php 上記のように通知が来たら成功です。 Webhookを利用している場合に、応答メッセージの確認もしてみます。 上記のようになれば成功です。 最後まで見ていただいてありがとうございました。よろしければLGTMをいいただけますと幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

個人メモ laravel 時間を指定してバッチを実行する

目的 作成したバッチ処理を時間を指定して動かす方法をメモ的にまとめる 方法 アプリ名ディレクトリ/todos/app/Console/Kernel.phpのschedule()メソッドに記載することで時間を指定してバッチ処理を実行する事ができる。 実行タイミングの指定方法は下記に詳しく記載されている。 https://readouble.com/laravel/6.x/ja/scheduling.html
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Lightsailでphpmyadminにアクセスする

Lightsailで、LAMP環境を立ち上げたのですが、phpmyadminへのアクセスを試みたものの、 セキュリティ設定がされているようで、試行錯誤したので、メモを残しておきます。 先人たちの教えに基づき、あれこれ試したものの、肝心のフォルダがないという惨劇。。。 /opt/bitnami/apps のフォルダが、、、ない。 設定ファイルの場所 で、結局、どこに目当てのファイルがあったかというと、、、ここにありました!! /opt/bitnami/apache/conf/bitnami/phpmyadmin.conf Require local を 以下の用意に書き換えます。 Alias /phpmyadmin "/opt/bitnami/phpmyadmin" <Directory "/opt/bitnami/phpmyadmin"> Options -Indexes +FollowSymLinks -MultiViews #AllowOverride All AllowOverride None #Require local <IfVersion < 2.3> Order allow,deny Allow from all Satisfy all </IfVersion> <IfVersion >= 2.3> Require all granted </IfVersion> ErrorDocument 403 "For security reasons, this URL is only accessible using localhost (127.0.0.1) as the hostname." # AuthType Basic # AuthName phpmyadmin # AuthUserFile "/opt/bitnami/apache/conf/users" # Require valid-user </Directory> 変更したら、以下のコマンドで、apacheを再起動します。 sudo /opt/bitnami/ctlscript.sh restart アクセスの確認 http://[IPアドレス]/phpmyadmin にアクセスしてみます。 以下のログイン画面が表示されれば、設定は成功です!! 補足: ユーザー名とパスワードの取得 phpmyadminのアクセスに必要なパスワードは、以下の手順で取得できます。 ホームディレクトリで、以下のコマンドを実行 cat bitnami_credentials パスワードが表示されるのでコピーする。 ユーザー名は、root です。  
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPのインストールから'Hello Worldを表示するまで'の環境構築

はじめに この度PHPを学習することとなりまして、環境構築を行いましたので手順を書き出しておきます。 作業環境 MacbookPro - macOS Catalina 10.15.7 PHPのインストール まずなんと言ってもPHPがないことにははじまりません。 実はMacには最初からPHPが入っているのですが、最新バージョンではない可能性があるので、 初期装備とは別にPHPをインストールしていきます。 Homebrewのインストール パッケージ管理システムのHomebrewをインストール。 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" PHPのインストール Homebrewでインストール可能なバーションを確認します。 % brew search php ==> Formulae brew-php-switcher php@7.2 phplint phpunit php php@7.3 phpmd php-code-sniffer php@7.4 phpmyadmin php-cs-fixer phpbrew phpstan この中で一番最新のphp7.4をインストールします。 brew install php@7.4 インストール後に出てくる指示の通りパスを通します。 echo 'export PATH="/usr/local/opt/php@7.2/bin:$PATH"' >> ~/.bash_profile echo 'export PATH="/usr/local/opt/php@7.2/sbin:$PATH"' >> ~/.bash_profile バージョンを確認してみて自分の望んだバージョンが表示されれば成功です。 % php -v PHP 7.4.18 (cli) (built: Apr 30 2021 09:55:21) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies with Zend OPcache v7.4.18, Copyright (c), by Zend Technologies MAMPをインストール MAMPはローカルのサーバー環境を提供してくれるオープンソースソフトウェアです。 これを用いてHello Worldを表示できるようにしていきます。 MAMPの公式サイトにアクセスしてFreeDownloadのボタンを押しましょう。 するとインストーラーがダウンロードされるので、インストーラーを開いて手順を進めるとインストールが完了します。 MAMPを開くとこんな感じの画面が表示されます。 Document rootと書いてあるところに記載されているパスのディレクトリに入ったファイルが表示されるようになっています。 web serverにApacheとNginxを選択することができます。今回はApacheを使用します。 hello_world.phpを作成 ターミナルからMAMPで指定されているhtdocsにvimを用いてhello_world.phpを作成します。 cd /Applications/MAMP/htdocs touch hello_world.php テキストエディタでhello_world.phpを開いて、以下を入力してください。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <?PHP echo 'Hello World' ?> </body> </html> ここまで準備ができたらMAMPを開いて右上のstartボタンを押します。 起動が完了してボタンが緑色に変わったらブラウザを開きましょう。 以下にアクセスするとHello Worldが表示されるはずです。 localhost:8888/hello_world.php お疲れさまでした!!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

6 Questions for Every PHP Developer in Laravel Interview

Going to give a "Laravel developer interview" is the so-called complicated thing for all those professionals who are good in practical things but zero in theory. We know developers are reasonably realistic, and they are only focused on real-time approaches for career goals. Still, the idea also has vital importance that you ignore in the 21st century. Here I will share the six questions for every PHP developer in Laravel Interview that you can't miss for your successful job interview behind recruiter. 6 Questions for Every PHP Developer in Laravel Interview 1). Can You Define What Laravel Is? Stop! Don't think they are asking something unmatchable from your profiles. Dear, they are only asking for the definition of Laravel but in a quite compelling manner. Your Laravel interview is not the high-school-based exam in which you will get answers like what is the meaning of Laravel? In professional talks, the format of asking questions is quite different from the usual question. Thus, the interviewer never asks directly about the definition of the Laravel Framework, and they use some other way to ask questions. What Answer do You Need to Give? An open-source PHP framework design on the MVC pattern is known as PHP. Taylor Otwell creates the framework, and this is the most eminent application for the blossom web application. 2). What are the Merits and Demerits of Laravel? The interviewer's next question can also ask from a PHP Developer is about the merits and demerits. When you want to become a Laravel Developer, you need to know the complete information of the program. Here I will discuss some pros and cons of Laravel that you can use in your interview for a good impression. Merits of Laravel Framework: 1). For the dynamic content goals, heavy blade template not worthy, and that's why Laravel Framework is the lightweight blade template-based framework to create high-profile real-time content layouts. 2). Code Readability is Hassle-Free 3). PHP framework built-in with Command Line Tool "Artisan" for developing Demerits of PHP: 1). Deep knowledge or programming is a must to work on PHP frameworks, and beginners can't deliver the best results 2). Ruby Gems and Python are the main competitors of Laravel 3). Development is not relatively less time-consuming process in the Laravel framework 4). Laravel comes with less inbuilt support 3). What do you mean by Events in Laravel? An application required actions that are the outcomes of events in the framework. Event is a crucial aspect attached to a program that can handle code. For smoother implementation, we required events. 4). Where to Store Events? Events Classes are mainly available in Events Directory or Apps Directory. Sometimes, beginners don't know the correct answer to this question, and that's why they can't answer this question. Laravel Development Company always tries to hire an excellent candidate for the position of Laravel Developer. 5). Are there any Validations in Laravel or Not? Validations are the most critical aspect of any program. The primary purpose of confirmations is to clean and code in the correct format. We can also say that the code structure also depends on the developer's assurances for Laravel App Development. That means a developer must be efficient in applying good warranties. You can find many ways to use validation in Laravel. 6). Had You Ever Work on Laravel Project or Not? One of the challenging questions for PHP Developers in Laravel Interview is your work experience on the Laravel Projects. Had you ever worked or not in this framework project is also the main requirement of the company. For Laravel Development Services, they need a perfect candidate for a company that had already work on this framework. Now the question is how to answer when you do not handle a single project in Laravel? You can also explain your PHP Experience and mention how passionate you are about developing Laravel Framework. Primary Industries and Social Media Platforms Developed in Laravel: There is no doubt that Laravel is a framework in the 21st century used in many various industries for development purposes. Gone were the days when developers have only one or two programming language skills. Nowadays, a developer has many programming language skills. Development of applications means never stop learning, and if you stop learning, then you may never be able to get success in this industry. Key-Reasons to go with Laravel Development Services: Here you also need to know about the key reason to go with Laravel Development Services. No matter you are technical or non-technical? Fast Web and Mobile Applications are the demand of industry and clients. Laravel provides this opportunity to developers for making excellent quality applications with fast speed. Training Programs Also Conducted By Companies: To skilled developers in various programming languages, companies also conducted skill training programs to switch development frameworks. As a PHP Developer, switching to Laravel is quite tricky initially. You may face so many problems in developing applications or frameworks but don't worry. This can only happen in the beginning time. Once time passes, you will know about things automatically in a short period. Many multinational companies focus on training and skill programs for the developers, and that's why they want to improve workforce skills by using training programs. Summary: Why do you need to know about these six questions as a PHP Developer when you are going to give a Laravel Developer Interview? To justify your knowledge and skill behind the recruiter, you need to know about these questions. Thus, we already state these questions briefly, and we hope this information is useful for your Laravel Developer Interview. You can also answer these questions according to your knowledge and opinion because we only give ideas here. Laravel is an excellent framework for people to establish a portal with advanced functionality and flexibility. Laravel also allows for an easy content management system for the users. These 6 questions are helpful for all those developers who are going to give interviews for Laravel Development.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】IPを判別するプログラム

最近PHPを始めたカラボンです。 今回は接続しているIPを判別する方法を教えていきたいと思います。 ↓コード↓ <?php if ($_SERVER["REMOTE_ADDR"] == 'ここにIPを入れる') { } else{ echo('表示は許可されていません'); exit; } ?> コードの解説 if ($_SERVER["REMOTE_ADDR"] == 'ここにIPを入れる') if文でIPを判別。{}の中身は空でもよし! else{ echo('表示は許可されていません'); exit; } exit;で処理を終了する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel リレーションのまとめ(一対多 多対多 )

リレーションについて毎回調べてしまっていることが多いので、内容整理として。 (随時更新) 前提 バージョン:Laravel6 一対多 ①一対多とは 一対多のリレーションとは、あるmodelインスタンス一つ(ModelA)に対して複数のmodelインスタンス(ModelB)が紐づく関係のこと。その「紐づき」にあたるものが外部キー。あるModelAインスタンスには複数のModelBインスタンスが紐づくが、あるModelBインスタンスは特定のModelAインスタンスにだけ紐づく。 ②migration create_model_a_table.php //ModelAマイグレーション public function up() { Schema::create('model_a', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('name'); $table->timestamps(); }); } create_model_b_table.php //ModelBマイグレーション public function up() { Schema::create('model_b', function (Blueprint $table) { $table->bigIncrements('id'); $table->unsignedBigInteger('model_a_id'); // 外部キー $table->string('name'); $table->timestamps(); // 外部キー制約 $table->foreign('model_a_id')->references('id')->on('model_a')->onDelete('cascade'); }); } 外部キー制約はforeign('外部キー')->references('参照先のカラム')->on('参照テーブル')と記述する。 onDelete('**')はデータ削除時の振舞いを指定するもの。 cascade:参照先が削除されると、共に削除される。 set null:参照先が削除されると、外部キーがnullになる。 restrict:参照先が削除されると、エラーを返す。 ③model ModelA.php // このインスタンスに紐づくModelBインスタンス public function modelBs() { return $this->hasMany(ModelB::class); } // このインスタンスに紐づくインスタンスの数を取得する。 public function loadRelationshipCounts() { $this->loadCount('modelBs'); } ModelB.php // このインスタンスに紐づくModelAインスタンス public function modelA() { return $this->belongsTo(ModelA::class); } 多対多 ①多対多とは 多対多のリレーションとは、あるmodelインスタンス群(ModelC)に対して複数のmodelインスタンス群(ModelD)が紐づく関係のこと。一対多とは異なり、あるModelCインスタンスが複数のModelDインスタンスと紐づくだけでなく、あるModelDインスタンスが複数のModelCインスタンスと紐づくことができる。 多対多を実現するには中間テーブルを用いる。 ②migration create_model_c_table.php //ModelCマイグレーション public function up() { Schema::create('model_c', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('name'); $table->timestamps(); }); } create_model_d_table.php //ModelDマイグレーション public function up() { Schema::create('model_d', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('name'); $table->timestamps(); }); } create_relay_c_d_table.php //中間テーブルマイグレーション public function up() { Schema::create('relay_c_d', function (Blueprint $table) { $table->bigIncrements('id'); $table->unsignedBigInteger('model_c_id'); // 外部キー $table->unsignedBigInteger('model_d_id'); // 外部キー $table->string('name'); $table->timestamps(); // 外部キー制約 $table->foreign('model_a_id')->references('id')->on('model_c')->onDelete('cascade'); $table->foreign('model_a_id')->references('id')->on('model_d')->onDelete('cascade'); }); } 外部キー制約はforeign('外部キー')->references('参照先のカラム')->on('参照テーブル')と記述する。 onDelete('**')はデータ削除時の振舞いを指定するもの。 cascade:参照先が削除されると、共に削除される。 set null:参照先が削除されると、外部キーがnullになる。 restrict:参照先が削除されると、エラーを返す。 ③model ModelC.php // このインスタンスに紐づくModelDインスタンス public function modelDs() { return $this->belongsToMany(ModelB::class, 'relay_c_d', 'model_c_id', 'model_d_id')->withTimestamps(); } // このインスタンスに紐づくインスタンスの数を取得する。 public function loadRelationshipCounts() { $this->loadCount('modelDs'); } ModelD.php // このインスタンスに紐づくModelCインスタンス public function modelCs() { return $this->belongsToMany(ModelC::class, 'relay_c_d', 'model_d_id', 'model_c_id')->withTimestamps(); } // このインスタンスに紐づくインスタンスの数を取得する。 public function loadRelationshipCounts() { $this->loadCount('modelCs'); }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPで読む「オブジェクト指向設計実践ガイド」

背景 オブジェクト指向設計実践ガイドを読んでみて、非常に良い内容でしたが、Rubyに馴染みがあまりないのでPHPでまとめました。 まとめ まずは以下の項目を意識づけるようにして、細かい点は後ほど学べば良さそう。 単一責任 依存オブジェクトの注入 デメテルの法則 ダックタイピング フックメソッド はじめに オブジェクト指向設計とは何か オブジェクト間の依存関係を管理すること オブジェクト指向設計のメリット オブジェクト指向設計の手法に従えば、 楽しくコーディングできる。 効率よくソフトウェアを生産できる 楽しくってのが個人的に好き。 なぜオブジェクト指向設計が必要か? ソフトウェア開発は、仕様の変更や拡張など、変更がつきもの。 依存関係を管理することで、変更が容易になる。テストもしやすくなる。 自転車の構造 今回、自転車のクラスをみていくので、分かりにくいパーツ名を簡単に説明しておく。 自転車に詳しくないのでざっくりと。 チェーンリング:ペダルをこぐと回るやつ コグ:チェーンを通じて後輪で回るやつ リム:タイヤをはめる金属の輪っか 単一責任のクラスをつくる 単一責任の原則。 英語で言うと、SRP (Single Responsibility Principle) なぜ単一責任が重要なのか 変更が簡単なアプリケーションをつくるには、クラスを再利用しやすくする必要がある。 クラスの責任を1つにすることで、クラスを再利用しやすくする。 どうやるか やることは3つ。 インスタンス変数の隠蔽 メソッド単位で単一責任にする 余計な責任を隔離する まずはダメなコードの例。これを修正していく。 <?php class Gear { /** * チェーンリングの直径 */ private $chainring; /** * コグの直径 */ private $cog; /** * リムの直径 */ private $rim; /** * タイヤの厚さ */ private $tire; public function __construct($chainring, $cog, $rim, $tire) { $this->chainring = $chainring; $this->cog = $cog; // rimとtireの情報をGearに聞くのはおかしい $this->rim = $rim; $this->tire = $tire; } public function ratio() { // アクセサメソッドで隠蔽するべき return $this->chainring / $this->cog; } // ペダルを1回こいだときに、どれだけ進むかの計算 public function gearInches() { // 直径の計算は他のメソッドに分離するべき return $this->ratio() * ($this->rim + ($this->tire * 2)); } } $gear = new Gear(52, 11, 26, 1.5); echo $gear->gearInches(); // -> 137.09090909091 インスタンス変数の隠蔽 インスタンス変数は、常にアクセサメソッドで包んで、直接参照しないようにする。 インスタンス変数が変更になった場合でも、変更箇所は1箇所だけ。 <?php class Gear { private $chainring; private $cog; private $rim; private $tire; public function __construct($chainring, $cog, $rim, $tire) { $this->chainring = $chainring; $this->cog = $cog; // rimとtireの情報をGearに聞くのはおかしい $this->rim = $rim; $this->tire = $tire; } public function getChainring() { return $this->chainring; } public function getCog() { return $this->cog; } public function getRim() { return $this->rim; } public function getTire() { return $this->tire; } public function ratio() { return $this->getChainring() / $this->getCog(); } public function gearInches() { // 直径の計算は他のメソッドに分離するべき return $this->ratio() * ($this->getRim() + ($this->getTire() * 2)); } } $gear = new Gear(52, 11, 26, 1.5); echo $gear->gearInches(); // -> 137.09090909091 ゲッターをいちいち定義するのが面倒な場合は、__callメソッドを定義すると良さそう。 メソッド単位で単一責任にする メソッド単位でも単一の責任を持つようにする。 以下のメソッドは、wheelsを繰り返し処理する、直径を計算するの2つの責任をもってしまっている。 public function diameters() { return array_map(function ($wheel) { return $wheel->getRim() + ($wheel->getTire() * 2); }, $wheels); } これを単一責任にすると、以下のようになる。 public function diameters() { return array_map(function ($wheel) { return $this->diameters($wheel); }, $wheels); } public function diamter($wheel) { $wheel->getRim() + ($wheel->getTire() * 2); } 余計な責任を隔離する タイヤの直径の計算をGearから隔離する。Wheelクラスをつくるとよい。 これで単一責任のクラスといえる。 <?php class Gear { private $chainring; private $cog; private $wheel; public function __construct($chainring, $cog, $wheel) { $this->chainring = $chainring; $this->cog = $cog; // wheelインスタンスを受け取る $this->wheel = $wheel; } public function getChainring() { return $this->chainring; } public function getCog() { return $this->cog; } public function getWheel() { return $this->wheel; } public function ratio() { return $this->getChainring() / $this->getCog(); } public function gearInches() { return $this->ratio() * $this->getWheel()->diameter(); } } class Wheel { private $rim; private $tire; public function __construct($rim, $tire) { $this->rim = $rim; $this->tire = $tire; } public function getRim() { return $this->rim; } public function getTire() { return $this->tire; } public function diameter() { return $this->getRim() + ($this->getTire() * 2); } } $wheel = new Wheel(26, 1.5); $gear = new Gear(52, 11, $wheel); echo $gear->gearInches(); // -> 137.09090909091 依存関係を管理する ダメな例から。 Gearが知っていることは4つある。 ほかのクラスの名前(Wheel) self以外に送ろうとするメッセージの名前(diameter) メッセージが要求する引数(rimとtire) それら引数の順番(rimとtireの順番) <?php class Gear { private $chainring; private $cog; private $rim; private $tire; public function __construct($chainring, $cog, $rim, $tire) { $this->chainring = $chainring; $this->cog = $cog; $this->rim = $rim; $this->tire = $tire; } public function gearInches() { // Gearが自分以外のことを多く知り過ぎている。 $wheel = new Wheel($this->getRim(), $this->getTire()); return $this->ratio() * $wheel->diameter(); } public function ratio() { return $this->getChainring() / $this->getCog(); } // ... } 依存オブジェクトの注入 引数にオブジェクトを渡すことで、依存を減らす。これがDI(Dependency Injection) 依存性注入とよく訳されるが、Dependencyとはオブジェクトのこと。 つまり、オブジェクトを注入する(->コンストラクタの引数にオブジェクトを渡す) <?php class Gear { private $chainring; private $cog; private $wheel; public function __construct($chainring, $cog, $wheel) { $this->chainring = $chainring; $this->cog = $cog; // wheelインスタンスを受け取る $this->wheel = $wheel; } public function gearInches() { // rim, tireやWheelクラスの名前は知らない。 // diameterに応答するものであれば良い。 return $this->ratio() * $this->getWheel()->diameter(); } public function ratio() { return $this->getChainring() / $this->getCog(); } // ... } 依存の隔離 現在、gearInchesメソッドはwheelに依存している。以下のように前後に計算が入ってくると、変更するのが大変になってくる。 public function gearInches() { // ... 複雑な計算が何行かある $this->ratio() * $this->getWheel()->diameter(); // ... 複雑な計算がさらに何行かある } 対策として、diameterメソッドを定義することで、gearInchesから依存を取り除く。 public function gearInches() { // ... 複雑な計算が何行かある $this->ratio() * $this->diameter(); // ... 複雑な計算がさらに何行かある } // 依存を隔離 public function diameter() { return $this->getWheel()->diameter(); } 依存方向の管理 今回のgearInchesメソッドは、GearクラスではなくWheelクラスがもっていても、問題はない。 自身より変更の少ないクラスに依存させることが重要。 変更が少ないクラスとは interfaceやabstractのような抽象的なもの intやstringなどの基本クラス 柔軟なインターフェースをつくる クラスに基づいた設計ではなく、メッセージに基づいた設計へ移行することが重要。 メッセージに基づいて、「なにを」頼むのかをポイントとして考えれば、必要となるインターフェースがみえてくる、ということだと解釈した。 デメテルの法則 最小知識の原則とも呼ばれる。 「直接の隣人にのみ話しかけよう」や「ドットは1つしか使わないようにしよう」と言うとわかりやすい。 つまり、以下のようなコードはやめようね、という話。 $customer->bicycle->wheel->tire; $customer->bicycle->wheel->tire; $customer->bicycle->wheel->rotate; ダックタイピングでコストを削減する 以下はダメな例。 prepareメソッドの引数のクラス名に応じて処理を振り分けている。変更するたびにクラス、処理の追加が必要となってしまっている。 <?php class Trip { private $bicycles; private $customers; private $vehicle; public function prepare($preparer) { switch (get_class($preparer)) { case Mechanic::class: $preparer->prepare_bicycles($bicycles); case TripCoordinator::class: $preparer->buy_food($customers); case Driver::class: $preparer->gas_up($vehicle); $preparer->fill_water_tank($vehicle); } } } こういう場合は、ダックタイプを使う(ポリモーフィズム) class Trip { public function prepare($preparer) { // ダックタイプ $preparer->prepareTrip($this); } } class Mechanic { public function prepareTrip($trip) { // ... } } class TripCoordinator { public function prepareTrip($trip) { // ... } } class Driver { public function prepareTrip($trip) { // ... } } 継承 フックメソッドを使ってサブクラスを疎結合にする 具象としてリカンベントバイクを実装するので、先にその説明。 以下の写真のようなもので、flagプロパティは文字通り旗のことを表している。 以下の例では、サブクラスがスーパークラスのコンストラクタを実行する必要があるが、実行し忘れている。 このようなバグが混入するおそれがあるため、結合を弱くする必要がある。 <?php abstract class Bicycle { private $size; private $chain; private $tireSize; public function __construct($args) { $this->size = $args['size']; $this->chain = $args['chain'] ?: $this->defaultChain(); $this->tireSize = $args['tireSize'] ?: $this->defaultTireSize(); } public function spares() { return [ 'tireSize' => $this->tireSize, 'chain' => $this->chain, ]; } public function defaultChain() { return '10-speed'; } abstract public function defaultTireSize(); } class RecumbentBike extends Bicycle { private $flag; public function __construct($args) { $this->flag = $args['flag']; // <- parent::__construct()を忘れている } public function spares() { return array_merge( parent::spares(), ['flag' => $this->flag]); } public function defaultChain() { return '9-speed'; } public function defaultTireSize() { return '28'; } } $bent = new RecumbentBike(['flag' => 'tall and orange']); echo json_encode($bent->spares()); // -> {"tireSize":null,"chain":null,"flag":"tall and orange"} フックメソッドの利用 ここで、フックメソッドを使用する。 フックメソッドとは、共通処理をスーパークラスに記述し、のこりの独自処理をサブクラスに記述したもの。 今回の例では、スーパークラスの__constructメソッドに共通処理を記述し、独自処理をサブクラスのpostConstructメソッドで定義する。 こうすることで、スーパークラスのコンストラクタを知らなくて済み、依存が弱くなる。 sparesメソッドも同様。 <?php abstract class Bicycle { private $size; private $chain; private $tireSize; public function __construct($args) { $this->size = $args['size']; $this->chain = $args['chain'] ?: $this->defaultChain(); $this->tireSize = $args['tireSize']?: $this->defaultTireSize(); $this->postConstruct($args); } public function spares() { return array_merge([ 'tireSize' => $this->tireSize, 'chain' => $this->chain, ], $this->localSpares()); } // フックメソッド public function postConstruct($args) { return; } // フックメソッド protected function localSpares() { return []; } public function defaultChain() { return '10-speed'; } abstract public function defaultTireSize(); } class RecumbentBike extends Bicycle { private $flag; public function postConstruct($args) { $this->flag = $args['flag']; } protected function localSpares() { return [ 'flag' => $this->flag, ]; } public function defaultChain() { return '9-speed'; } public function defaultTireSize() { return '28'; } } $bent = new RecumbentBike(['flag' => 'tall and orange']); echo json_encode($bent->spares()); // -> {"tireSize":"28","chain":"9-speed","flag":"tall and orange"}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelで簡単なCMSを作成(記事へのタグ付け)

Laravelで簡単なCMSを作成してみます。 環境構築から記事の登録・更新は下記の記事を参考にどうぞ。 今回は、作成した記事にタグを付けてみます。 リソースコントローラ作成 Laravelのartisanコマンドを使ってリソースの作成、読み取り、更新、または削除を処理するコントローラを作成できます。 Tagモデルをリソースとして、作成・読み取りができるように下記コマンドを実行してリソースコントローラーとモデルを作成します。 php artisan make:controller TagsController --resource --model=Tag php artisan make:model Tag TagsControllerを作成したらindex・create・storeメソッドを追加します。 app/Http/Controllers/TagsController.php <?php namespace App\Http\Controllers; use App\Models\Tag; use Illuminate\Http\Request; class TagsController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { $tags = Tag::all(); return view('tags.index', ['tags' => $tags]); } /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create() { return view('tags.add'); } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { $article = new Tag; $article->title = $request->title; $article->save(); return redirect('/tags'); } // その他のメソッド } リソースルートを登録します。 routes/web.php use App\Http\Controllers\TagsController; Route::resource('tags', TagsController::class); 以下のルートが自動的に作成されます。 動詞 URI アクション ルート名 GET /tags index tags.index GET /tags/create create tags.create POST /tags store tags.store GET /tags/{tag} show tags.show GET /tags/{tag}/edit edit tags.edit PUT/PATCH /tags/{tag} update tags.update DELETE /tags/{tag} destroy tags.destroy このうち今のところ、index・create・store以外は使用しないのでルートから除外します。 routes/web.php // Route::resource('tags', TagsController::class); Route::resource('tags', TagsController::class)->only([ 'index', 'create', 'store', ]); Viewを作成 add.blade.phpとindex.blade.phpを作成してtagの登録から一覧で確認できるようにします。 resources/views/tags/index.blade.php <x-app-layout> <x-slot name="header"> <h2 class="font-semibold text-xl text-gray-800 leading-tight"> {{ __('Tags') }} </h2> </x-slot> <div class="py-12"> <div class="max-w-7xl mx-auto sm:px-6 lg:px-8"> <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg"> <div class="p-6 bg-white border-b border-gray-200"> <table class="table-auto"> <thead> <tr> <th>Title</th> <th>Updated</th> </tr> </thead> <tbody> @foreach($tags as $tag) <tr> <td>{{$tag->title}}</td> <td>{{$tag->updated_at}}</td> </tr> @endforeach </tbody> </table> </div> </div> </div> </div> </x-app-layout> resources/views/tags/add.blade.php <x-app-layout> <x-slot name="header"> <h2 class="font-semibold text-xl text-gray-800 leading-tight"> {{ __('Tags') }} </h2> </x-slot> <div class="py-12"> <div class="max-w-7xl mx-auto sm:px-6 lg:px-8"> <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg"> <div class="p-6 bg-white border-b border-gray-200"> <form action="/tags" method="post"> @csrf <div class="grid grid-cols-1 gap-6"> <label class="block"> <span class="text-gray-700">Title</span> <input type="text" name="title" class="mt-1 block w-full"> </label> <input type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" value="send"> </div> </form> </div> </div> </div> </div> </x-app-layout> /tags/createにアクセスしてtagをいくつか登録します。 記事とタグを関連付ける 記事の登録と更新画面にタグを選択できるようにします。articles/add.blade.phpとarticles/edit.blade.phpに以下のようにtag用のチェックボックスを追加します。 <label class="block"> <span class="text-gray-700">Tag</span> <select class="mt-1 block w-full" rows="3" name="tags"> @foreach($tags as $tag) <option value="{{$tag->id}}">{{$tag->title}}</option> @endforeach </select> </label> モデルを関係付ける articlesテーブルとtagsテーブルを関連付けます。1記事に複数タグ付けすることができ、また1つのタグに対して複数の記事を関連付けることができるので、articlesテーブルとtagsテーブルは多対多の関係になります。多対多の関係はbelongsToManyメソッドで定義します。 ArticleModelのほうにbelongsToManyメソッドでtagsテーブルと関連付けます。 app/Models/ArticleModel.php use App\Models\Tags; public function tags() { return $this->belongsToMany(Tag::class); } 中間テーブルに登録する 多対多の関係であるarticlesテーブルとtagsテーブルを関連付けるために中間テーブルのarticle_tagテーブルを使用します。記事の作成・更新時に中間テーブルにデータを更新していきます。attachメソッドを使用することでリレーションの中間テーブルにデータを挿入できます。syncメソッドを使用すると、引数に渡されたIDが中間テーブルに登録され、引数に渡されなかったIDは中間テーブルから物理削除されます。 リレーションの更新処理をArticlesControllerに追加します。 app/Http/Controllers/ArticlesController.php public function create(Request $request) { $article = Article::create([ 'title' => $request->title, 'body' => $request->body, 'user_id' => Auth::id(), 'slug' => $request->title, ]); // 中間テーブルにtagを登録 $article->tags()->attach($request->tags); return redirect('/articles'); } public function update(Request $request, Article $article) { $article->fill($request->all())->save(); $article->tags()->sync($request->tags); return redirect('/articles'); } 中間テーブルへの登録時にtimestampを追加する場合は、withTimestampsメソッドを使用します。 app/Models/ArticleModel.php <?php namespace App\Models; use App\Models\Tag; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Article extends Model { use HasFactory; protected $fillable = [ 'title', 'body', 'slug', 'user_id', ]; protected $casts = [ 'user_id' => 'integer' ]; public function tags() { return $this->belongsToMany(Tag::class)->withTimestamps(); } } 一覧ページ修正 記事一覧ページに、記事に関連付けられているタグも表示させます。 resources/views/articles/index.blade.php <table class="table-fixed"> <thead> <tr> <th class="w-1/4">Title</th> <th class="w-1/4">Tag</th> <th class="w-1/4">Author</th> <th class="w-1/4">Updated</th> </tr> </thead> <tbody> @foreach($articles as $article) <tr> <td><a href="/article/edit/{{$article->id}}">{{$article->title}}</a></td> <td> @foreach($article->tags as $tag) <div> {{$tag->title}} </div> @endforeach </td> <td>{{$article->user_id}}</td> <td>{{$article->updated_at}}</td> </tr> @endforeach </tbody> </table> N + 1問題 laravelの動的プロパティは「遅延読み込み」なので、N+1問題が発生します。今回のように動的プロパティにアクセスすることが分かっている場合は、Eagerロードを使用してあらかじめモデルをロードすることでN+1問題を回避することができます。 記事一覧取得時にEagerロードを使用する場合と使用しない場合で発行されるSQLを比較してみます。まずEagerロードを使用しない場合、 app/Http/Controllers/ArticlesController.php public function index() { $articles = Article::all(); return view('articles.index', ['articles' => $articles]); } 発行されるSQLは、まず select * from "articles" で記事を全件取得しその後記事ごとに、 select "tags".*, "article_tag"."article_id" as "pivot_article_id", "article_tag"."tag_id" as "pivot_tag_id", "article_tag"."created_at" as "pivot_created_at", "article_tag"."updated_at" as "pivot_updated_at" from "tags" inner join "article_tag" on "tags"."id" = "article_tag"."tag_id" where "article_tag"."article_id" = article_id が発行されます。合計で記事の件数 + 1本のSQLが発行されます。 一方、Eagerロードを使用した場合、 app/Http/Controllers/ArticlesController.php public function index() { // $articles = Article::all(); $articles = Article::with('tags')->get(); return view('articles.index', ['articles' => $articles]); } 発行されるSQLは2本となり激減します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelで簡単なCMSを作成(記事を更新)

Laravelで簡単なCMSを作ってみます。 環境構築から記事の登録までは以下の記事を参考にどうぞ。 今回は記事の更新処理を処理を作ってみます。 Controllerを作成 ArticlesControllerにeditとupdateメソッドを追加します。 app/Http/Controllers/ArticlesController.php <?php namespace App\Http\Controllers; use App\Models\Article; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Gate; class ArticlesController extends Controller { // 記事の登録処理など public function edit(Article $article) { return view('articles.edit', ['article' => $article]); } public function update(Request $request, Article $article) { $article->fill($request->all())->save(); return redirect('/articles'); } } Viewを作成 更新ページを用意します。 resources/views/articles/edit.blade.php <x-app-layout> <x-slot name="header"> <h2 class="font-semibold text-xl text-gray-800 leading-tight"> {{ __('Articles') }} </h2> </x-slot> <div class="py-12"> <div class="max-w-7xl mx-auto sm:px-6 lg:px-8"> <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg"> <div class="p-6 bg-white border-b border-gray-200"> <form action="/article/update/{{$article->id}}" method="post"> @csrf <div class="grid grid-cols-1 gap-6"> <label class="block"> <span class="text-gray-700">Title</span> <input type="text" name="title" class="mt-1 block w-full" value="{{$article->title}}"> </label> <label class="block"> <span class="text-gray-700">Body</span> <textarea class="mt-1 block w-full" rows="3" name="body">{{$article->body}}</textarea> </label> <input type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" value="send"> </div> </form> </div> </div> </div> </div> </x-app-layout> ルートを登録 routes/web.php <?php use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | 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'); }); Route::get('/dashboard', function () { return view('dashboard'); })->middleware(['auth'])->name('dashboard'); Route::get('/articles', 'App\Http\Controllers\ArticlesController@index'); Route::get('/article/add', 'App\Http\Controllers\ArticlesController@add'); Route::post('/article/add', 'App\Http\Controllers\ArticlesController@create'); // 更新ページへのルートを登録 Route::get('/article/edit/{article}', 'App\Http\Controllers\ArticlesController@edit'); Route::post('/article/update/{article}', 'App\Http\Controllers\ArticlesController@update'); require __DIR__.'/auth.php'; これで、記事の登録に加えて更新もできるようになりました。記事を更新するためには/article/edit/1のようにarticle_idをURIに含めてリクエストしてください。ルートモデル結合を利用しているので、リクエストされたarticle_idに一致するArticleモデルが生成されサービスコンテナを通じてArticlesControllerのeditメソッドに自動的に注入されます。 作成したリソースは以下になります。 メソッド URI アクション GET /articles 一覧 GET /article/add 記事登録ページ表示 POST /article/add 記事登録 GET /article/edit/{article} 記事更新ページ表示 POST /article/edit/{article} 記事登録 作成した記事にタグを付ける機能を実装するには、下記の記事を参考に。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelで簡単なCMSを作成(認証)

Laravelで簡単なCMSを作成してみます。 環境構築とデータベースのマイグレーションは以下の記事を参考にどうぞ。 今回は認証機能を追加してみます。 スターターキットで認証機能を作成 素晴らしいことにLaravelには認証機能をサクッとつくれるようにスターターキットが用意されています。Laravel breezeを使用してシンプルな認証機能を作成します。 // パッケージをインストール composer require laravel/breeze --dev php artisan breeze:install npm install npm run dev php artisan migrate 上記のコマンドを実行するだけで、/loginにログイン画面が表示されます。ユーザーを登録するためには、/registerにアクセスします。 ログインページ ユーザー登録ページ 記事を登録する場合は、下記の記事を参考に。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelで簡単なCMSを作成(データベース作成)

Laravelで簡単なCMSを作ってみます。 環境構築は以下の記事を参考にどうぞ。 マイグレーションファイル作成 テーブル構成・定義はCakePHPのチュートリアルで紹介されているものを拝借し、3つテーブルを作成します。Laravelにはマイグレーションというデータベースの管理機能が提供されているので、これを使用します。 まずはartisanコマンドを使用してマイグレーションファイルを生成します。 php artisan make:migration create_articles_table php artisan make:migration create_tags_table php artisan make:migration create_article_tag_table テーブル定義作成 作成されたマイグレーションファイルに各テーブルの定義を加えていきます。 database/migratations/create_articles_table <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateArticlesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('articles', function (Blueprint $table) { $table->id(); $table->integer('user_id'); $table->string('title', 255); $table->string('slug', 191); $table->text('body'); $table->boolean('published')->default(false); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('articles'); } } database/migratations/create_tags_table <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateTagsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('tags', function (Blueprint $table) { $table->id(); $table->string('title', 191); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('tags'); } } database/migratations/create_article_tag_table <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateArticleTagTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('article_tag', function (Blueprint $table) { $table->integer('article_id'); $table->integer('tag_id'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('article_tag'); } } マイグレーションファイルが作成できたらmigrateArtisanコマンドを実行してデータベースに反映させます。 php artisan migrate これだけでテーブルを作成できます。 おまけ テーブル作成後にテストデータを追加したい場合は、Laravelのシーディングを利用します。 テストデータとしてusersテーブルにuserを作成してみます。 シーダーファイルを作成 マイグレーションと同様にartisanコマンドを実行してシーダファイルを作成します。コマンド実行後database/seedersにシーダファイルが生成されます。 php artisan make:seeder UserSeeder シーダファイルが生成されたら、データ挿入処理を追加します。 database/seeders/UserSeeder.php <?php namespace Database\Seeders; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; class UserSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('users')->insert([ 'name' => 'john', 'email' => 'john@gmail.com', 'password' => Hash::make('MyPassword'), ]); DB::table('users')->insert([ 'name' => 'jack', 'email' => 'jack@gmail.com', 'password' => Hash::make('MyPassword'), ]); } } シーダの呼び出し artisanコマンドを使用して個別にシーダファイルを実行することができますが、DatabaseSeederクラスのcallメソッドにシードクラスを登録することで、シーダファイルが複数になっても一括で実行できるようになります。 database/seeders/DatabaseSeeder.php <?php namespace Database\Seeders; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { // \App\Models\User::factory(10)->create(); $this->call([ UserSeeder::class, ]); } } シーダの実行 db:seed ArtisanコマンドでDatabase\Seeders\DatabaseSeederクラスを呼び出しシーディングを実行します。これによりテストデータが登録できます。 php artisan db:seed テーブルの中身を確認したいとき、vscodeの拡張機能を使用すれば簡単にテーブルデータを確認することができます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelで簡単なCMSを作成(環境構築)

Laravelの開発環境をdockerを使ってnginx + php + sqliteの構成で作ってみます。 docker-composeで管理 ディレクトリ構成は以下です。srcディレクトリ配下にはLaravelのコードがインストールされます。 . ├── docker │ ├── nginx │ │   └── default.conf │ └── php │ ├── Dockerfile │ └── php.ini │ ├── docker-compose.yml └── src docker-composeでコンテナを管理します。 docker-compose.yml version: '3' services: nginx: image: nginx ports: - 8080:80 volumes: - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf - ./src:/var/www depends_on: - php php: build: ./docker/php volumes: - ./src:/var/www nginxの設定 default.conf server { listen 80 default_server; listen [::]:80 default_server ipv6only=on; server_name localhost; root /var/www/public; index index.php index.html index.htm; location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php$ { try_files $uri /index.php =404; fastcgi_pass php:9000; fastcgi_index index.php; fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; #fixes timeouts fastcgi_read_timeout 600; include fastcgi_params; } } phpコンテナ Dockerfile FROM php:7.3-fpm ENV COMPOSER_ALLOW_SUPERUSER 1 ENV COMPOSER_HOME /composer ENV PATH $PATH:/composer/vendor/bin #Composer install COPY --from=composer /usr/bin/composer /usr/bin/composer RUN apt-get update &&\ apt-get install -y libzip-dev &&\ docker-php-ext-configure zip --with-libzip && \ # Install the zip extension docker-php-ext-install zip && \ docker-php-ext-install pdo_mysql COPY php.ini /usr/local/etc/php/ WORKDIR /var/www php.ini [Date] date.timezone = "Asia/Tokyo" [mbstring] mbstring.internal_encoding = "UTF-8" mbstring.language = "Japanese" コンテナ起動 docker-compose.ymlファイルが置いてある場所でdocker-compose upを実行するとコンテナが起動します。バックグラウンドで起動するために-dも付けて起動させます。 docker-compose up -d Laravelインストール コンテナが起動できたらLaravelをインストールするために、phpコンテナに入ります。 # docker-compose exec php bash phpコンテナに入れたらLaravelをインストールします。 # composer create-project laravel/laravel . インストールが完了したら、http://localhost:8080にアクセスして環境が正しく構築出来ているか確認します。下の画像のように表示できていればOKです。 SQLiteの設定 laravel/database 配下に database.sqlite というファイルを作成します。次に.envファイルをsqliteを使用するために修正します。 .env DB_CONNECTION=sqlite DB_HOST=127.0.0.1 DB_PORT=3306 # DB_DATABASE=laravel DB_USERNAME=root DB_PASSWORD= これで設定は終わりです。 作成したデータベースにデータを登録する場合は、以下の記事を参考に。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む