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

Could not open input file: composer.pharが出た場合の対処方法

macでcomposerインストール後にCakePHPのプロジェクトを作成しようとしてつまずいたのでメモ php composer.phar create-project --prefer-dist cakephp/app:4.* my_app_name を実行した場合に Could not open input file: composer.phar と表示されることがあります。 その場合は以下でプロジェクトを作成してみてください。 composer create-project --prefer-dist cakephp/app:4.* my_app_name
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP endroid / qr-codeを用いて、QRコードを表示

はじめに PHPライブラリであるendroid/qr-codeを用いて、webページに任意のQRコードを表示します。 https://github.com/endroid/qr-code 環境は、Mac、VSCodeです。 流れ ①composerをインストール ②QRコードを表示 ③html内の一部としてQRコードを表示する ④qr-code関連をqrcode.phpにまとめる composerをインストール こちらを参考に下記のコマンドを入力します。下記は2021年8月時点での最新版のコマンド。 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.pharは、PATHディレクトリに配置する必要があるため、下記コマンドを実行することで、composerを任意のディレクトリから簡単に呼び出すことができます。 $ sudo mv composer.phar /usr/local/bin/composer $ sudo chmod 755 /usr/local/bin/composer $ composer -V Composer version 2.0.12 2021-04-01 10:14:59 composerをインストールすることができました! QRコードを表示 以降は、https://github.com/endroid/qr-code を参考にします qr-codeディレクトリを作成後、index.phpを作成し、endroid/qr-codeをインストールします。 $ mkdir qr-code $ cd qr-code $ touch index.php $ composer require endroid/qr-code Usage: without using the builderのコードとUsage: working with resultsをindex.phpにコピペします index.php use Endroid\QrCode\Color\Color; use Endroid\QrCode\Encoding\Encoding; use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelLow; use Endroid\QrCode\QrCode; use Endroid\QrCode\Label\Label; use Endroid\QrCode\Logo\Logo; use Endroid\QrCode\RoundBlockSizeMode\RoundBlockSizeModeMargin; use Endroid\QrCode\Writer\PngWriter; $writer = new PngWriter(); // Create QR code $qrCode = QrCode::create('Data') ->setEncoding(new Encoding('UTF-8')) ->setErrorCorrectionLevel(new ErrorCorrectionLevelLow()) ->setSize(300) ->setMargin(10) ->setRoundBlockSizeMode(new RoundBlockSizeModeMargin()) ->setForegroundColor(new Color(0, 0, 0)) ->setBackgroundColor(new Color(255, 255, 255)); // Create generic logo $logo = Logo::create(__DIR__.'/assets/symfony.png') ->setResizeToWidth(50); // Create generic label $label = Label::create('Label') ->setTextColor(new Color(255, 0, 0)); $result = $writer->write($qrCode, $logo, $label); // Directly output the QR code header('Content-Type: '.$result->getMimeType()); echo $result->getString(); $dataUri = $result->getDataUri(); さらにindex.phpに以下の修正を加えます ・今回は、logoとlabelは使用しないため、コードを削除 ・最上部に、<?phpとrequire_once(__DIR__ . '/vendor/autoload.php');を追加 ・$qrCode = QrCode::create('Data')のDataをQrコードを作成したいURLに変更。今回はhttps://www.google.com/にします。 index.php <?php require_once(__DIR__ . '/vendor/autoload.php'); use Endroid\QrCode\Color\Color; use Endroid\QrCode\Encoding\Encoding; use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelLow; use Endroid\QrCode\QrCode; use Endroid\QrCode\RoundBlockSizeMode\RoundBlockSizeModeMargin; use Endroid\QrCode\Writer\PngWriter; $writer = new PngWriter(); // Create QR code $qrCode = QrCode::create('https://www.google.com/') ->setEncoding(new Encoding('UTF-8')) ->setErrorCorrectionLevel(new ErrorCorrectionLevelLow()) ->setSize(300) ->setMargin(10) ->setRoundBlockSizeMode(new RoundBlockSizeModeMargin()) ->setForegroundColor(new Color(0, 0, 0)) ->setBackgroundColor(new Color(255, 255, 255)); $result = $writer->write($qrCode); // Directly output the QR code header('Content-Type: '.$result->getMimeType()); echo $result->getString(); $dataUri = $result->getDataUri(); 確認のため、VSCのターミナルを用いて、PHPのビルトインWebサーバーを起動し、http://localhost:8000/index.phpにアクセスします。 $ php -S localhost:8000 QRコードが表示され、読み取るとgoogleに遷移しました! html内の一部としてQRコードを表示する index.phpに以下の修正を追加 ・下部のheader('Content-Type…以下を削除し、$img = base64_encode($result->getString());を追加 ・最下部に?>と<html>…、<img src="data:image/gif;base64,<?= $qrCodeImg ?>">を追加 index.php <?php require_once(__DIR__ . '/vendor/autoload.php'); use Endroid\QrCode\Color\Color; use Endroid\QrCode\Encoding\Encoding; use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelLow; use Endroid\QrCode\QrCode; use Endroid\QrCode\RoundBlockSizeMode\RoundBlockSizeModeMargin; use Endroid\QrCode\Writer\PngWriter; $writer = new PngWriter(); // Create QR code $qrCode = QrCode::create('https://www.google.com/') ->setEncoding(new Encoding('UTF-8')) ->setErrorCorrectionLevel(new ErrorCorrectionLevelLow()) ->setSize(300) ->setMargin(10) ->setRoundBlockSizeMode(new RoundBlockSizeModeMargin()) ->setForegroundColor(new Color(0, 0, 0)) ->setBackgroundColor(new Color(255, 255, 255)); $result = $writer->write($qrCode); $qrCodeImg = base64_encode($result->getString()); ?> <html> <body> <h2>QRコード</h2> <img src="data:image/gif;base64,<?= $qrCodeImg ?>"> </body> </html> webページの一部にQRコードを表示することができました! qr-code関連をqrcode.phpにまとめる index.php内にqr-codeのコードがあると保守性が悪いので、qrcode.phpを作成し、class名QrCodeGeneratorでまとめます。 $ pwd qr-code $ touch qrcode.php index.phpに以下の修正を加えます。 ・最上部に、include(dirname(__FILE__) . '/qrcode.php');を追加 ・imgタグ内を<?= QrCodeGenerator:: makeImg($url); ?> ・qrcode関係を削除 index.php <?php include(dirname(__FILE__) . '/qrcode.php'); ?> <html> <body> <h2>QRコード</h2> <?= QrCodeGenerator::makeImg("https://www.google.com/)"); ?> </body> </html> qrcode.phpに以下を加えます。 ・qrcode関係を移す ・classでまとめる qrcode.php <?php require_once(__DIR__ . '/vendor/autoload.php'); use Endroid\QrCode\Color\Color; use Endroid\QrCode\Encoding\Encoding; use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelLow; use Endroid\QrCode\QrCode; use Endroid\QrCode\RoundBlockSizeMode\RoundBlockSizeModeMargin; use Endroid\QrCode\Writer\PngWriter; class QrCodeGenerator { public static function makeImg($url){ $writer = new PngWriter(); $qrCode = QrCode::create($url) ->setEncoding(new Encoding('UTF-8')) ->setErrorCorrectionLevel(new ErrorCorrectionLevelLow()) ->setSize(300) ->setMargin(10) ->setRoundBlockSizeMode(new RoundBlockSizeModeMargin()) ->setForegroundColor(new Color(0, 0, 0)) ->setBackgroundColor(new Color(255, 255, 255)); $result = $writer->write($qrCode); $qrCodeImg = base64_encode($result->getString()); return '<img src="data:image/gif;base64,' . $qrCodeImg . '">'; } } qrcode関係をまとめることで、保守性のよいコードにすることができました! ちなみに $qrCode = QrCode::create($url) ->setEncoding(new Encoding('UTF-8')) ->setErrorCorrectionLevel(new ErrorCorrectionLevelLow()) // QRコードのサイス(単位:ピクセル) ->setSize(300) // QRコードの周囲の余白(単位:ピクセル) ->setMargin(10) ->setRoundBlockSizeMode(new RoundBlockSizeModeMargin()) // QRコードの色 ->setForegroundColor(new Color(0, 0, 0)) // 背景の色 ->setBackgroundColor(new Color(255, 255, 255)); // QRコードの中央に配置するロゴファイルのパス $qrCode->setLogoPath('logo.png'); // ロゴサイズ(単位:ピクセル) $qrCode->setLogoSize(20, 20); 参考文献 【PHP】endroid/qr-codeを使ってQRコードを生成してみる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】これしか使わない!リダイレクトのやり方2種類

HTTPリダイレクト リダイレクトレスポンスは、Illuminate\Http\RedirectResponseクラスのインスタンスで、ユーザーを他のURLへリダイレクトするために必要なしっかりとしたヘッダを含んでいる必要があります。RedirectResponseインスタンスを生成する方法はいくつかあります。 引用:HTTPリダイレクト 結論 通常のリダイレクト:redirectヘルパを使用する 前のページに戻るリダイレクト:backヘルパを使用する 1. redirectヘルパを使用する Laravelではredirectヘルパ関数がグローバルに定義されており、アプリケーションのどこからでも使用できます。 // URLへのリダイレクト return redirect('home/dashboard'); //名前付きルートへのリダイレクト return redirect()->route('login'); //名前付きルートへのリダイレクト(パラメータ付き) return redirect()->route('profile', ['id' => 1]); // コントローラアクションへのリダイレクト return redirect()->action('LoginController@login'); // コントローラアクションへのリダイレクト(パラメータ付き) return redirect()->action('LoginController@login', ['id' => 1]); // URLでへのリダイレクト(フラッシュメッセージ付き) return redirect('home')->with('status', 'ログインしました!'); // bladeでこんな感じに描けばフラッシュメッセージを表示できる @if (session('status')) <div class="alert alert-success"> {{ session('status') }} </div> @endif 2. backヘルパを使用する backヘルパ関数もグローバルに定義されており、アプリケーションのどこからでも使用できます。 // 前の画面に戻る return back(); // 前の画面に戻る(入力された値は保持する) return back()->withInput(); // 前の画面に戻る(エラーを設定する) return back()->withInput()->withErrors([ 'email' => '入力必須です。' ]); // こんな書き方も見たことあるけど、古い書き方で現在は上記のbackヘルパを使う模様 return redirect()->back(); まとめ 2種類のヘルパを使って簡単にリダイレクトができました。 内部的にはどちらもreturnの後のヘルパでIlluminate\Http\RedirectResponseクラスのインスタンスを作成して返しています。 Illuminate\Http\RedirectResponseクラスのインスタンスはリダイレクト先のURLやHTTPのステータスコードを保持します。 redirectヘルパでは引数で指定したURLを、backヘルパでは前画面のURLをそれぞれ設定しているようなイメージでしょうか。 return back()について内部的に何が起こっているか確認する記事も投稿しているので、よろしければこちらも併せて確認いただければと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】よく使うリダイレクトのやり方2種類

HTTPリダイレクト リダイレクトレスポンスは、Illuminate\Http\RedirectResponseクラスのインスタンスで、ユーザーを他のURLへリダイレクトするために必要なしっかりとしたヘッダを含んでいる必要があります。RedirectResponseインスタンスを生成する方法はいくつかあります。 引用:HTTPリダイレクト 結論 通常のリダイレクト:redirectヘルパを使用する 前のページに戻るリダイレクト:backヘルパを使用する 1. redirectヘルパを使用する Laravelではredirectヘルパ関数がグローバルに定義されており、アプリケーションのどこからでも使用できます。 // URLへのリダイレクト return redirect('home/dashboard'); //名前付きルートへのリダイレクト return redirect()->route('login'); //名前付きルートへのリダイレクト(パラメータ付き) return redirect()->route('profile', ['id' => 1]); // コントローラアクションへのリダイレクト return redirect()->action('LoginController@login'); // コントローラアクションへのリダイレクト(パラメータ付き) return redirect()->action('LoginController@login', ['id' => 1]); // URLでへのリダイレクト(フラッシュメッセージ付き) return redirect('home')->with('status', 'ログインしました!'); // bladeでこんな感じに描けばフラッシュメッセージを表示できる @if (session('status')) <div class="alert alert-success"> {{ session('status') }} </div> @endif 2. backヘルパを使用する backヘルパ関数もグローバルに定義されており、アプリケーションのどこからでも使用できます。 // 前の画面に戻る return back(); // 前の画面に戻る(入力された値は保持する) return back()->withInput(); // 前の画面に戻る(エラーを設定する) return back()->withInput()->withErrors([ 'email' => '入力必須です。' ]); // こんな書き方も見たことあるけど、古い書き方で現在は上記のbackヘルパを使う模様 return redirect()->back(); まとめ 2種類のヘルパを使って簡単にリダイレクトができました。 内部的にはどちらもreturnの後のヘルパでIlluminate\Http\RedirectResponseクラスのインスタンスを作成して返しています。 Illuminate\Http\RedirectResponseクラスのインスタンスはリダイレクト先のURLやHTTPのステータスコードを保持します。 redirectヘルパでは引数で指定したURLを、backヘルパでは前画面のURLをそれぞれ設定しているようなイメージでしょうか。 return back()について内部的に何が起こっているか確認する記事も投稿しているので、よろしければこちらも併せて確認いただければと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rectorphp/rector deep dive ~PHPStanと併用しプロダクションのPHPアプリケーションを大規模リファクタリングする方法~

一般的なサービスでは、DI やフレームワークを使用していると思います。その場合、デフォルトの設定でRector を入れただけではコードの追跡が不完全で、期待通りにリファクタリングできません。しかし PHPStanと併用し設定をしっかり記述すれば、力を最大限引き出せます。 弁護士ドットコムでは、Yii1 のプロダクションコードに PHP 7.4 Typed Properties のルールを適用し、530 のクラス変数に型を付けました。その過程で調べたことをまとめました。 またLaravelを例にどのような設定が必要か紹介します。(Yii1は需要ないと思うので) 3 文まとめ Rector を使えば、とても簡単に PHP コードを自動リファクタリングできる Rector は内部で PHPStan を利用している 依存関係の複雑なプロジェクトでも、PHPStan の設定を読み込ませれば Rector で綺麗にリファクタリングできる Rectorとは? Rector instantly upgrades and refactors the PHP code of your application. It can help you 2 major areas: Instant Upgrades Automated Refactoring 簡単に自動でPHPコードをリファクタリングしてくれるツールです。 すでに存在するたくさんのルールを柔軟に選択し、実行できます。もちろん拡張ルールも作成できます。とても簡単です。 どんなリファクタリングができるか 現在(2021/07/31)、500 弱の公式ルールが公開されています。非公式で有志が作ったルールもたくさんあります。 私が使用した PHP7.4 Typed Properties や、最近のルールだと下記のユニオンタイプを定義してくれる PHP 8.0 UnionTypesRector は需要がありそうです。 class SomeClass { - /** - * @param array|int $number - * @return bool|float - */ - public function go($number) + public function go(array|int $number): bool|float { } } PHPStanとは? PHPStan focuses on finding errors in your code without actually running it. It catches whole classes of bugs even before you write tests for the code. コードを実行することなく、バグを見つける静的解析ツールです。Rector は内部の型推論に PHPStan を使っています。 検知できるエラー 型の不整合、PHPDoc の不備、存在しない関数やデッドコードなどさまざまなエラーが検知できます。 Rector のインストールと実行 ローカルの場合 // install $ composer require rector/rector --dev // execute $ vendor/bin/rector process src --dry-run 公式 Docker イメージが提供されているので、Dockerでも可能です。 $ docker run --rm -v $(pwd):/project rector/rector:latest process src --dry-run Rector のオプション Rectorはどのルールを適用するか、実行ディレクトリなどのオプションを指定したファイルを置く必要があります。 use Rector\Php74\Rector\Property\TypedPropertyRector; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { $services = $containerConfigurator->services(); $services->set(TypedPropertyRector::class); }; Rector は宣言が見つからなくてもエラーを出さない Rector は、宣言(クラス、関数etc)が見つからなくてもエラーはでません。debug モードでも。私も最初気付かず「意外と動かないもんだな」と思いました。 たとえば、下記のディレクトリ構成でsrc 配下に対して実行します。 $ tree . ├── extensions │ └── TestService.php ├── rector.php └── src └── SomeClass.php src/SomeClass.php declare(strict_types=1); class SomeClass { /** * @var TestService $service */ private $service; private function __construct(TestService $service) { $this->service = $service; } } extensions/TestService.php declare(strict_types=1); class TestService { } rector.php use Rector\Php74\Rector\Property\TypedPropertyRector; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { $services = $containerConfigurator->services(); $services->set(TypedPropertyRector::class); }; $ docker run --rm -v $(pwd):/project rector/rector:latest process src --dry-run 0/1 [░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0% 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% [OK] Rector is done! ルールには PHP7.4 の Typed Properties を指定しており、SomeClass の$serviceに型がつくことを期待しています。 ですが、何も変化がありません。なぜでしょうか? 解析対象がsrc下のみで、TestService クラスが見つかっていないためです。TestService クラスをsrc 下に移動させると結果が変わります。 この例は非常にわかりやすいですが、コード量が多くなってくると一部解析に失敗していても意外と気づきません。きちんと依存クラスすべてを解析できるように準備してあげましょう。 $ docker run --rm -v $(pwd):/project rector/rector:latest process src --dry-run 0/2 [░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0% 1/2 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░] 50% 2/2 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% 1 file with changes =================== 1) src/SomeClass.php ---------- begin diff ---------- @@ @@ class SomeClass { - /** - * @var TestService $service - */ - private $service; + private \TestService $service; private function __construct(TestService $service) { ----------- end diff ----------- Applied rules: * TypedPropertyRector (https://wiki.php.net/rfc/typed_properties_v2#proposal) [OK] 1 file would have changed (dry-run) by Rector Rector & PHPStan のクラス解析の仕組み 0.10 以降、Rector は composer オートロードを使わず、さらに PHP コードを実行しないでプログラムを解析しています。PSR-4 のオートロード形式に従ったコードをBetterReflectionで定義から AST を抽出しクラスを見つけます。PHPStanも同じです。 結果、下記のような副作用とクラス/関数宣言が混在したコードでも問題ありません。 function doSomething(): void { // ... } doSomething(); うちのような composer ではない Yii1 のカスタムオートロードを使っていても、設定に記述すれば読み込めます。 rector.php use Rector\Core\Configuration\Option; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { $parameters = $containerConfigurator->parameters(); // 事前に読み込むファイルを追加できます。 $parameters->set(Option::AUTOLOAD_PATHS, [ // 特定のファイルで __DIR__ . '/file-with-functions.php', // ディレクトリすべて __DIR__ . '/project-without-composer', ]); また Rector 実行時に、起動ファイルを読み込むことも可能です。 rector.php $parameters->set(Option::BOOTSTRAP_FILES, [ __DIR__ . '/constants.php', __DIR__ . '/project/special/autoload.php', ]); Rector の PHPStan の設定 内部の型推論に PHPStan を使っているためPHPStanの設定を拡張できます。 個人的には、このオプションが一番重要だと思います。これによってrectorが全てのクラスを読み込み最適な形でリファクタリングできるからです。 Path to phpstan with extensions, that PHPSTan in Rector uses to determine types rector.php $parameters->set( Option::PHPSTAN_FOR_RECTOR_PATH, getcwd() . '/phpstan-for-config.neon' ); Laravelを例にPHPStan * Rector PHPStanは、ラッパーであるlarastanを使用しました。 Rectorとlarastanで、PHPStanのバージョン不整合が出るケースがあります。そのため、今回はrectorを先にインストールしました。すでにPHPStanが入ってるプロジェクトだと、多少調整が必要です。 あまりLaravelは詳しくないので、参考程度で。 リファクタリングルールは、PHP 7.4 Typed Propertiesです。 ソースコード全体 主要packageのバージョン composer.json "php": "^7.3|^8.0", "laravel/framework": "^8.40", "nunomaduro/larastan": "^0.7.12", "rector/rector": "^0.11.40" Rectorの設定 rector.php use Rector\Core\Configuration\Option; use Rector\Core\ValueObject\PhpVersion; use Rector\Php74\Rector\Property\TypedPropertyRector; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { $parameters = $containerConfigurator->parameters(); // リファクタリングディレクトリの指定 $parameters->set(Option::PATHS, [__DIR__ . '/app', __DIR__ . '/tests']); // php version指定 $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_74); // phpstan設定ファイルの読み込み $parameters->set(Option::PHPSTAN_FOR_RECTOR_PATH, getcwd() . '/phpstan.neon'); $parameters->set(Option::AUTOLOAD_PATHS, [ __DIR__ . '/config', ]); $parameters->set(Option::BOOTSTRAP_FILES, [ __DIR__ . '/bootstrap/app.php', __DIR__ . '/rector-bootstrap.php', ]); $services = $containerConfigurator->services(); $services->set(TypedPropertyRector::class); }; rector-bootstrap.php class_alias(Illuminate\Config\Repository::class, 'config'); classが見つからないエラーが出たので、class_aliasでごまかしました。 PHP Fatal error: Uncaught Error: Call to undefined method config::get() in /home/taki/develop/komtaki/blog/vendor/nunomaduro/larastan/src/ReturnTypes/GuardDynamicStaticMethodReturnTypeExtension.php:47 Stack trace: #0 phar:///home/taki/develop/komtaki/blog/vendor/rector/rector/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(3178): NunoMaduro\Larastan\ReturnTypes\GuardDynamicStaticMethodReturnTypeExtension->getTypeFromStaticMethodCall() #1 phar:///home/taki/develop/komtaki/blog/vendor/rector/rector/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1442): PHPStan\Analyser\MutatingScope->methodCallReturnType() #2 phar:///home/taki/develop/komtaki/blog/vendor/rector/rector/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1448): PHPStan\Analyser\MutatingScope->PHPStan\Analyser\{closure}() #3 phar:///home/taki/develop/komtaki/blog/vendor/rector/rector/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(440): PHPStan\Analyser\MutatingScope->resolveType() #4 /home/ta in /home/taki/develop/komtaki/blog/vendor/nunomaduro/larastan/src/ReturnTypes/GuardDynamicStaticMethodReturnTypeExtension.php on line 47 PHPStanの設定 larastonのトップにあるデフォルトそのままです。 igonoreErrorsで指定したエラーに該当するものがないと警告が出るので、reportUnmatchedIgnoredErrorsで無視する設定を追加しました。 phpstan.neon includes: - ./vendor/nunomaduro/larastan/extension.neon parameters: paths: - app level: 5 ignoreErrors: - '#Unsafe usage of new static#' excludePaths: - ./*/*/FileToBeExcluded.php checkMissingIterableValueType: false reportUnmatchedIgnoredErrors: false リファクタリングしたいファイル /app/Http/Controllers/TestController.php namespace App\Http\Controllers; use Illuminate\Http\Request; class TestController extends Controller { /** @var Request $request */ private $request; } 実行結果 $ ./vendor/bin/rector process app --dry-run 19/19 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% 1 file with changes =================== 1) app/Http/Controllers/TestController.php ---------- begin diff ---------- @@ @@ class TestController extends Controller { - /** @var Request $request */ - private $request; + private \Illuminate\Http\Request $request; } ----------- end diff ----------- Applied rules: * TypedPropertyRector (https://wiki.php.net/rfc/typed_properties_v2#proposal) [OK] 1 file would have changed (dry-run) by Rector まとめ Laravelのバージョンアップがしたい等であれば、rector-laravelを使うのがよいでしょう。CakePHPやSymfony もあり、フレームワーク固有の処理は非常になります。 それ以外でPHPの新しい言語機能を取り込みたい場合は、PHPStanとつなげると上手くリファクタリングできるでしょう。 Yii1だと何もなく、偶然PHPStan の拡張を自作し後だったため簡単に rector の力を引き出せました。 ただしrectorはリファクタリングがメインなので、修正後コードスタイルが崩れる可能性があります。その場合は、PhpStormのCode Cleanupや、PHP-CS-Fixerを使って分業するのがよいでしょう。 rectorを使いたいけど、うまく導入できていない方の参考になれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP で Zip ファイルをテンポラリディレクトリに展開する

概要 PHP で Zip ファイルをテンポラリディレクトリに展開して中身のファイルをごにょごにょするのが地味にめんどくさかったのでメモがてらまとめました。 テンポラリディレクトリに展開先を用意する Zip ファイルの展開先として一時的なディレクトリを使用したかったためsys_get_temp_dirメソッドを使いました。 /tmpなど OS が指定する一時的なファイルの置き場所へのパスを取得することができます。 加えてuniqidメソッドで取得する一意な値をディレクトリ名とするようにしてみました。 $tmpDir = sys_get_temp_dir() . '/' . uniqid(); mkdir($tmpDir); // ディレクトリを作成します このようにテンポラリディレクトリに展開するようにしておけば、展開後に処理が落ちてしまい削除が漏れしてしまった場合でも後で迷いなく削除することができます。 ※OS によっては一定期間経つとテンポラリディレクトリのファイルが削除される設定になっているものもあるようです Zip ファイルの展開 ZipArchiveという Zip を扱うクラスを使います。 ※環境によってはインストールが必要なようですが私の環境ではデフォルトで使えました $tmpDir = sys_get_temp_dir() . '/' . uniqid(); mkdir($tmpDir); $zip = new ZipArchive(); $zip->open($filePath); // 展開したい zip ファイルを指定します $zip->extractTo($tmpDir); // 展開先のディレクトリを指定します $zip->close(); このようにすればテンポラリディレクトリに Zip ファイルが展開されているはずですので後は煮るなり焼くなり好きにしましょう! おまけ 私が今回使ったファイル名を扱うメソッドを備忘録を兼ねて紹介しておきます。 PHP 公式のドキュメント見たほうが早そうですが参考までに ※ドキュメントへのリンクもつけておきます ファイル名取得 https://www.php.net/manual/ja/function.glob パターンに合ったファイルパスを取得します。フルパスが取得されます。 正規表現は使えませんが、以下のように書けばあるディレクトリの拡張子が txt のみのファイルのみ取得できるので便利です。 $files = glob("/hoge/*.txt"); 例えば以下のようなディレクトリの構造だった場合は、 /hoge/a.txt /hoge/b.txt /hoge/c.log 以下のような配列が取得されます。 $files = [ '/hoge/a.txt', '/hoge/b.txt', ]; 特定の拡張子のみ取得したい場合はたまにあるので使えそうです。 ファイルパスのパース globメソッドを使ってまとめてファイルパスを取得したものの以下のようなパターンもあると思います。 実際に使用するのはファイル名だけなのでパスは除いて抜き出したい 拡張子も削りたい そういうときはpathinfoメソッドがファイルパスをパースした連想配列を返してくれるので便利です。 $filePath = '/hoge/a.txt'; $pathinfo = pathinfo($filePath); // 以下のような連想配列が取得できます [ [dirname] => /hoge, [basename] => a.txt, [extension] => .txt, [filename] => a, ]; フラグを指定すれば連想配列ではなく、指定した要素だけを取得することも可能です。 $filePath = '/hoge/a.txt'; // 'a.txt' のみが返ります $pathinfo = pathinfo($filePath, PATHINFO_BASENAME); 上記と同じことをしてくれるbasenameメソッドがあるためお好みで。 感想 素の PHP のメソッドを使ってファイル操作をしましたがpathinfoのような気の利いたメソッドもあれば、ディレクトリが空でないと使えないrmdirなどもあり歴史を感じました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

2つの配列を1つのforeach処理でDBに格納する方法

やりたかったこと WordテーブルとNoteテーブルのカラムにformから送られてきた値を格納。 Noteテーブルのカラム ・user_id ・title Wordテーブルのカラム ・note_id ・question  ・answer create.blade.php <form action="{{route('Vocabularys.add')}}" method="POST"> @csrf <div> <h3>タイトル</h3> <input type="text" name='title'> </div> <div>      //name = 'question'だと最後の1個のデータしか渡せないがname='question[]'とすることで配列にして全部の値をコントローラーに渡すことができる <p>Q.<input type="text" name='question[]'></p> <p>A.<input type="text" name='answer[]'></p> <p>Q.<input type="text" name='question[]'></p> <p>A.<input type="text" name='answer[]'></p> <p>Q.<input type="text" name='question[]'></p> <p>A.<input type="text" name='answer[]'></p> <a href="">削除</a> </div> <div> <p> <a href="">追加</a> </p> <p class="create-btn"> <input type="submit" value="登録"> </p> </div> </form> contoroller public function add(Request $request){      //ログインしているユーザーのIDを取得 $userId = Auth::id(); //モデル名::createを使うとモデルと接続されているテーブルにカラムが格納される      //そのまま変数の中でcreateを行う。 $note = Note::create([ //上記でログインしているユーザーのID取得した$userIdをuser_idカラムに入れる。 'user_id'=>$userId //formから送られたtitleをリクエストで呼び出しtitleカラムに格納 'title'=>$request->title, ]); // create.bladeから配列で受け取った値をそれぞれ変数に代入 $questionItems = $request->question; $answerItems = $request->answer; // コールバック関数はないのでNull foreach (array_map(Null, $questionItems, $answerItems) as [$questionItem,$answerItem,]){ $word = Word::create([ 'note_id'=> $note->id, 'question' => $questionItem, 'answer' => $answerItem, ]); }; return redirect('/Vocabularys/index'); } 上記でうまく2つの配列を1つのforeach処理でDBに格納できた。 最初やろうとしたこと // create.bladeから配列で受け取った値をそれぞれ変数に代入 $questionItems = $request->question; $answerItems = $request->answer;      //①の処理 //$questionItemsの中の配列の値の分だけ回してWordテーブルに格納 foreach ($questionItems as $item){ $word = Word::create([ 'note_id'=> $note->id, 'question' => $item, ]); }; ②の処理 //$answerItemsの中の配列の値の分だけ回してWordテーブルに格納 foreach ($answerItems as $item){ $word = Word::create([ 'answer' => $item, ]); }; 実行すると ・①の処理でWordテーブルのanswerカラムがnullだよと怒られた。 同じ処理でquestionカラムとanswerカラムに値を入れないといけないだと!!え?どうしよう。 無知な僕はめちゃめちゃ悩んだ。ググるとarray_map関数に出会った。。 array_mapとは? https://techacademy.jp/magazine/29657 これでできるんじゃね? 実装するといけた。 使い方合ってるか知らんけどwww $配列1 = [1,2,3] function コールバック関数名($n) { //$nには$配列1が入る。 return $n + 1; } //hogeが呼ばれたら $hoge = array_map(コールバック関数名, $配列1) consol.log($hog); +1される [2,3,4] 自分のメモ用なのでわかりづらいと思いますが参考にしてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CakePHP4_migrationでdatetimeカラムが追加できない

まえがき CakePHPのmigrationを利用した際に発生したエラーの対処法となります。 バージョン CakePHP4 手順 ・Usersテーブルにmigrationでcreatedカラムを追加する 20210801024646_ChangeUsers.php public function change() { $table = $this->table('users'); $table->addColumn('created', 'datetime'); $table->update(); } ※上記のmigrationファイルでmigrate実行するとエラーが発生 bin\cake migrations migrate PDOException: SQLSTATE[22007]: Invalid datetime format: 1292 Incorrect datetime value: '0000-00-00 00:00:00' for column `proto_todo`.`users`.`created` at row 1 in C:\xampp\htdocs\cakephp_apps\todo\vendor\robmorgan\phinx\src\Phinx\Db\Adapter\PdoAdapter.php:194 設定情報はシンプルなものなのですが、何度実行しても失敗してしまいます。 原因を調査した結果、以下の内容に変更すると成功しました。 20210801024646_ChangeUsers.php public function change() { $table = $this->table('users'); $table->addColumn('created', 'datetime', [ 'null' => true // NULLを許容 ]); $table->update(); } bin\cake migrations migrate 原因 エラーが発生した原因としては、既にテーブル上に登録されているデータが悪さをしていたようでした。 addColumnsが処理される際、既存レコードのcreatedに対しては'0000-00-00 00:00:00'が登録される動きになるようです。 しかしcreatedカラムはnullを許容していなかった為エラーが発生してしまった様です。 上記を踏まえカラムをnull許容する事で正常にmigrationが行われました。 考えれば分かる事でした。。余計な時間を使ってしまった。。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CakePHP4_migrationでカラム位置を変更したい

まえがき CakePHPのmigrationを利用してカラム位置の修正を行う手順を記載します。 バージョン CakePHP4 手順 ・Usersテーブルをmigrationで作成 bin\cake bake migration CreateUsers login_id:string bin\cake migrations migrate ・Usersテーブル情報修正用のmigrationファイル生成 bin\cake bake migration ChangeUsers 20210801024646_ChangeUsers.php $table = $this->table('users'); $table->addColumn('loginId', 'string', [ 'after' => 'id' ]); $table->update(); ※上記のmigrationファイルでmigrate実行するとエラーが発生 bin\cake migrations migrate PDOException: SQLSTATE[42S21]: Column already exists: 1060 Duplicate column name 'loginId' in C:\xampp\htdocs\cakephp_apps\todo\vendor\robmorgan\phinx\src\Phinx\Db\Adapter\PdoAdapter.php:194 20210801024646_ChangeUsers.php $table = $this->table('users'); // addColumn -> changeColumnに変更 $table->changeColumn('loginId', 'string', [ 'after' => 'id' ]); $table->update(); bin\cake migrations migrate 今度は正しく反映されました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPの基本文法まとめ

PHPについて一通り学習して、今後また使う機会があるかもなので、備忘録として残しておきます。 基本形 phpを使う際は、<?php ... ?>の間に記述していく。 文末にセミコロンをつける。 <?php echo 'HelloWorld!'; ?> 変数に数字を足す 省略形を紹介 // 変数xに1を代入 $x = 1; // 四則演算の省略形 $x += 10; $x -= 10; $x *= 10; $X /= 10; $x %= 10; // 値が1のときだけの省略形 $x ++; $x --; 文字列の連結 文字列の連結は、ドット「.」を使う 「.=」で省略して書ける $name = '久保'; echo '私の名前は'.$name.'です'; // 結果: 私の名前は久保です // 省略形 $name .= '建英'; echo $name; // 結果: 久保建英 変数展開 ダブルクォーテーションで文字列を囲んだときのみ シングルクォーテーションで文字列を囲んだときは、そのまま文字列としてみなされる 変数を{ }で囲う $name = '久保建英'; echo "私の名前は、{$name}です"; // 結果: 私の名前は、久保建英です echo '私の名前は、{$name}です'; // 結果: 私の名前は、{$name}です if文 基本形はif(条件式){} else,elseifを使って条件分岐 条件の否定はif(!(条件式)){}のように「!」を使う $x = 10; if($x > 30){ echo '$xは30より大きい'; }elseif($x >= 30){ echo '$xは30以下20以上'; }else{ echo '$xは20より小さい'; } // 結果: $xは20より小さい // 条件の否定 $name = '久保建英'; if(!($name == 'リオネル・メッシ')){ echo '$nameはリオネル・メッシではない'; } // 結果: $nameはリオネル・メッシではない 条件式の条件 // 大小を比べる < <= > >= // 等しいか調べる == != // かつ、または && || switch文 if文で、条件分岐が多く複雑なときに、switch文に書き換えるとシンプルにできる breakを各ブロックの最後に記述 breakがないと、条件が一致しても次のブロックが実行される // if文で書いたコード if($coin == 0){ echo '表'; }elseif($coin == 1){ echo '裏'; }else{ echo 'エラー'; } // switch文で書いたコード swich($coin){ case 0: echo '表'; break; case 1: echo '裏'; break; default: echo 'エラー'; break; } 配列 インデックス番号は、0.1.2... 連想配列で、キーをつけられる $colors = array('赤','青','黃'); echo $colors[0]; // 結果: 赤 // 連想配列 $player = array( 'name' => 'リオネル・メッシ'; 'team' => 'バルセロナ'; 'sex' => '男性'; ) echo $player['name']; // 結果: リオネル・メッシ for文 for(初期化、ループの条件、変数の更新){繰り返す処理}が基本形で、ループの条件に合うまで繰り返し処理される // $iを1から100まで出力 for($i = 1; $i <= 100; $i++){ echo $i; } foreach文 基本形はforeach(配列as値変数){繰り返したい処理} キー名も出したければ、foreach(配列as $key => $value){繰り返したい処理} $colors = array('赤','青','黃'); foreach($colors as $color){ echo $color.' '; } // 結果: 赤 青 黃 // 連結配列 $colors = array( 'りんご' => '赤', 'ブルーベリー' => '青', 'バナナ' => '黃' ); foreach($colors as $key => $value){ echo $key.':'.$value.' '; } // 結果: りんご:赤 ブルーベリー:青 バナナ:黃 まとめ 今回はよく使うであろう基本形についてまとめておきました。 ぼく自身はRubyを学んでいたので、文法は少しずつ違うのですが、基本的にやってることは同じなので、理解が早かったように思います。 よく言う、2つ目の言語の習得は早いというのはこうゆうことなのでしょうか? まだまだPHPに関する理解は追いついていないため、これからも頑張っていきます!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelを用いてアプリを作る。

*本件はlaravelをインストールした状態から始める 1.アプリのインストール 1.アプリを作成する ターミナル composer create-project laravel/laravel アプリ名 下記のように出たらOK application keyはメモっとくのがいいらしいい ターミナル実行結果 php artisan key generate〜 application key〜  2.サーバーを起動(確認) ターミナル /ディレクトリは作成したアプリ名/ oredayoore@MacBook-Air-5 作成したアプリ$ php artisan serve 3.URL入力 リンク http://127.0.0.1:8000/ →画面が出る! 2.データベースを作成+設定する 1.環境変数を設定する アプリ名/.env 1.設定するデータベース名を記述(以下sqliteの場合) DB_CONNECTION = sqlite *他のDB_CONNECTION = データベース名が記述されていた場合、削除すること 2.データベースの『絶対』パスを指定すること(以下例) DB_DATABASE=/home/foobar/myproject/database/database.sqlite 2.データベースを作成する。 ターミナル 上述の『環境変数を設定する』2番で設定したパス通りにデータベースを作成すること touch /home/foobar/myproject/database/database.sqlite 3. デフォルトのデータベースを設定する。 config/database.php /* |-------------------------------------------------------------------------- | Default Database Connection Name |-------------------------------------------------------------------------- | | Here you may specify which of the database connections below you wish | to use as your default connection for all database work. Of course | you may use many connections at once using the Database library. | */ //下記のsqliteの所に自分の指定したいデータベースを入れる。上述のDB_CONNECTION = sqliteの環境変数と一致させること 'default' => env('DB_CONNECTION', 'sqlite'), 3.マイグレーションファイル作成 1.マイグレーションファイル作成 ターミナル oredayoore@MacBook-Air-5 作成したアプリ$php artisan make:migration create_books_table --create=books ターミナル 結果 created migration〜と出れば成功! →/database/migration/配下にファイルが作成される 2.マイグレーションファイルをいじる /database/migration/ public function up() { Schema::create('books', function (Blueprint $table) { //いじるのはここ!↓ 参考)$table->型(カラム名);                          $table->id(); $table->string('title'); $table->timestamps(); }); } 3.マイグレーションファイル更新(忘れがち) ターミナル oredayoore@MacBook-Air-5作成したアプリ$php artisan migrate 4.モデルの作成 1.モデルの作成 ターミナル oredayoore@MacBook-Air-5作成したアプリ$php artisan make:model Book(モデル名) //ここのモデル名の命名規則は頭文字は大文字、単数系 →/app/models/Book.phpにモデルファイルが作成される 5.ビューファイルを追加する 1.ビューファイルを追加 books.blade.php(自分でHTMLファイルを作成してみましょう) @extends('layouts.app') @section('content') <div class="container"> <div class="col-sm-offset-2 col-sm-8"> <div class="panel panel-default"> <div class="panel-heading"> 新たに書籍を追加する </div> <div class="panel-body"> <!-- Display Validation Errors --> @include('commons.errors') <!-- New Book Form --> <form action="/book" method="POST" class="form-horizontal"> {{ csrf_field() }} <!-- Book Name --> <div class="form-group"> <label for="task-name" class="col-sm-3 control-label">Book</label> <div class="col-sm-6"> <input type="text" name="name" id="book-name" class="form-control" value="{{ old('book') }}"> </div> </div> <!-- Add Book Button --> <div class="form-group"> <div class="col-sm-offset-3 col-sm-6"> <button type="submit" class="btn btn-default"> <i class="fa fa-plus"></i>本を追加する </button> </div> </div> </form> </div> </div> <!-- Books --> @if (count($books) > 0) <div class="panel panel-default"> <div class="panel-heading"> 書籍一覧 </div> <div class="panel-body"> <table class="table table-striped task-table"> <thead> <th>書籍タイトル</th> <th>&nbsp;</th> </thead> <tbody> @foreach ($books as $book) <tr> <td class="table-text"><div>{{ $book->title }}</div></td> <!-- Task Delete Button --> <td> <form action="/book/{{ $book->id }}" method="POST"> {{ csrf_field() }} {{ method_field('DELETE') }} <button type="submit" class="btn btn-danger"> <i class="fa fa-trash"></i>削除 </button> </form> </td> </tr> @endforeach </tbody> </table> </div> </div> @endif </div> </div> @endsection *注意 一番上の use App\models\Book; は、モデルのパスを表しているので 必ず一致されるようにしましょう! 主な語句の説明 @extends('layouts.app') @section('content') @include('commons.errors') @book→データベースから抽出したデータを表示されます。 そのためには後述の『ルーティング』で設定してあげる必要があります。 追加されたファイルを格納するパスの例) resources/views/layouts/ resources/views/layouts/ resources/views/layouts/ ルーティングの設定 WEBアプリケーションは必ずルーティングという見えない管を通じてVIEWを表示させたりデータベースを引っ張っていくので これを設定してあげなければVIEWも表示されない books.blade.php(自分でHTMLファイルを作成してみましょう) <?php use App\models\Book; use Illuminate\Http\Request; /* |-------------------------------------------------------------------------- | 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! | */ //画面遷移のルーティング $booksはビューに表記されている。 Bookというモデルクラスのallという関数によって、データベースのデータを全て呼び出して、 booksに代入している。 だからデータベースのデータをビューに表示することができる // Route::get('/', function () { $books = Book::all(); return view('books',['books' => $books]); }); //データ送信のルーティング 送信するには 送信先のパスが必要になる それが下記の例)post('/book' $validatorは、データ入力の制御(何でもかんでも受け入れる賭けにはいかない) また、データを保存するには、例のviewにある$bookという変数に、入力値を代入して、モデルまで送らなければならない。 その処理が $book = new Book; $book->title = $request ->name; $book->save(); である。 // Route::post('/book' , function(Request $request){ $validator = Validator::make($request->all(),[ 'name' => 'required|max255', ]); $book = new Book; $book->title = $request ->name; $book->save(); return redirect('/'); })->middleware('auth'); Route::delete('/book/{book}',function(Book $book){ $book->delete(); return redirect('/'); }); Auth::routes(); Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home'); 【解説】 ルーティングを作成するイメージとして、通信する度にルーティングの設定が必要になる 例) ページに移動→Route::get データを送信→Route::post 画像を削除→Route::delete、、などなど
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】一度設定したプロジェクト名を変更してみたら詰んだ話

Laravelプロジェクトの名前を変更したい 個人開発等を進めていて後から『プロジェクト名変えたいないぁ』と思う機会がありました。本来であれば要件定義、詳細設計等の段階でしっかりと決めておくべき内容でしたが、個人で学習用に開発を進めている場合だったので変更することとしました。 今回の記事では一度作成してしまったLaravelプロジェクト名の変更方法と注意点をまとめていきます。 本記事で対象となる方は以下の通りです ・個人開発で進めていたが途中からプロジェクト名を変えたくなった。 ・ポートフォリオ作成等で後からサービス名を変更する必要があった。 ・プロジェクト名が気に食わない。 ※僕自身まだ学習中のみであるため本記事によるエラーは自己責任でご対応ください。。。 【結論】なるべくおすすめはしません 例えば、以下のように元々sampleという名前のプロジェクトからsample2に変更する場合、そのまま修正するとエラーになり全く動かなくなります。 sample //変更前 sample2//変更後 その場合、その旧名の新フォルダを削除し、名前を元に戻せば、エラーは出なくなります。つまり、このフォルダ名自体を直接変更するというのは、出来ないと思ってください。 この後説明する内容も基本は新規にプロジェクトを作成しなおすという形になります。実際に試したところ結構面倒くさかったのでおすすめはしないです。 Laravelプロジェクト名の変更はGithubを使って行う ではどうするのかというと以下の流れで変更していきました。 ①Githubにバックアップを作成する ②ローカルに①でバックアップを保管した内容をクローンする ③環境構築を再設計する(docker等) ④ライブラリー関連を再インストールする(composer等) ①Githubにバックアップを作成する Githubにプッシュしてバックアップを作成します。コミット・プッシュが完了していなければ対応します。 ②ローカルに別ディレクトリを作成してGithubからクローンを作成する ローカルに新しく作成したいプロジェクト名のフォルダを作成します。 先程の例でいうと『sample2』 git cloneコマンドで①でバックアップした内容を反映します。 git clone http://リポジトリのurl/プロジェクト名.git ③新しく環境構築を設定しなおす ここからは各々の開発環境によるのであくまで参考程度にご覧ください。僕の場合はdocker(Laradock)を使った開発になります。 新しくdockerでの環境構築を行なっていきます。古いdockerコンテナ&イメージが走っているままだとエラーが起きるため一度停止&削除します。 ※僕の場合はLaravelインストールして少し実装を行なった程度の状態でした。古いプロジェクトを残す必要はなかったため、かつ既存プロジェクトのdockerファイルをそのまま転用するために上記対応を行いました。もし、既存プロジェクトを残した状態で複製する場合はDockerfileも既存適宜修正する必要があります。 ④Composerを再インストールする ここでかなり沼リました。 githubからプロジェクトを再インストールして、かつdockerでの環境構築も再設計しなおし、 『いざローカルホストにアクセス!!』したらこのありさま。 【事象】 【分析】 画像が表示されていない? ↓ intervention/image(PHPで画像を編集するためのライブラリー)が機能していない? ↓ 他の実装内容もチェックしていくとcomposerでインストールして実装した内容が軒並みエラーを起こしているよう。 ↓ vendorディレクトリがない・・・???!!! ↓ いやまってそもそもvendorディレクトリどっこいった。 ↓ エラーになにか書いてある。 /app/vendor/autoload.php : failed to open stream: No such file or directory in… 【対処】 composerを再インストールしたところうまくきました。 まとめ 今回は事象とエラー解決に絞って記載しました。 次回は『なぜcomposerの再インストールが必要なのか』『vendorディレクトリをGit管理下に置かない理由(gitignore指定)』について学んだことを書いてみようと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(Laravel)具体例付きでEloquentでリレーション先を見ながらデータ取得する操作の確認

はじめに 本記事作成の目的 私の同僚で過去にクエリビルダでjoinを駆使する方法をメインに使用していたためにEloquentに慣れていないという方にもっとEloquentを使ってもらいたいという目的で作成しております。 複数テーブルを跨いだ処理について、クエリビルダでjoinを駆使して実装することもできますが、 Eloquentを使ってLaravelに任せてしまったほうが簡単だしバグも発生しづらいということを目の当たりにして記事作成を決意しました。 基本は公式ドキュメントを見て状況に合った方法を確認するべきですが、まずはこの記事を理解の第一歩にしていただけると幸いです。 注意 ・想定する読者について マイグレーションやモデルの作成等、Laravelの基本的な操作をある程度把握している方を想定しています。 一通り使い方を学んだ方がデータ取得を実装するときに読んでいただけると理解しやすくなるのではと思います。 ・具体例について 本記事はイメージしやすくするため、予約サイトの機能を開発する状況を想定しています。 現実の予約サイトの開発では起こらない例もあるかと思いますがそのあたりはご容赦ください。 ・実行環境について php:7.4.8 Laravel Framework 6.20.23 Case1: データを取得してリレーション先のデータを表示 DB図 状況 全ての予約の中で"2021-08-01"のものだけを取得して画面に表示させたい。 画面には予約日と予約に紐づいた会員の名前、メールアドレスを表示させたい。 実装方法 ・予約モデルにリレーションを設定 app/Models/Reservation.php //...いろいろ省略 public function user() { return $this->belongsTo('App\User'); //App\Models\Userにするプロジェクトもあるかもしれません。 } ・コントローラでデータ取得を行ってビューに送る ※実際にはコントローラの肥大化を防ぐために別ファイルに実装するかもしれません。今回はコントローラに実装します app/Http/Controllers/ReservationController.php //...いろいろ省略 use App\Models\Reservation; //...いろいろ省略 public function search() { $reservations = Reservation::where('date', '2021-08-01')->get(); return view('index', compact('reservations')); } ・Eloquentで取得したデータをビューに表示する resources/views/index.blade.php <table> <tr> <th>予約日</th> <th>名前</th> <th>メールアドレス</th> <tr> @foreach($reservations as $reservation) <tr> <td>{{$reservation->date}}</td> <td>{{$reservation->user->name}}</td> <td>{{$reservation->user->email}}</td> </tr> @endforeach </table> Case2: リレーション先の値を条件にデータを検索したい DB図 状況 全ての予約の中で"山田太郎"さんのものだけを取得して画面に表示させたい。 画面には予約日、名前、メールアドレスを表示させたい。 実装方法 ・予約モデルにリレーションを設定 app/Models/Reservation.php //...いろいろ省略 public function user() { return $this->belongsTo('App\User'); //App\Models\Userにするプロジェクトもあるかもしれません。 } ・コントローラでデータ取得を行ってビューに送る ※いろいろ方法があるようですが、whereHasを使う形を紹介します。 app/Http/Controllers/ReservationController.php //...いろいろ省略 use App\Models\Reservation; //...いろいろ省略 public function search() { $userName = '山田太郎'; $reservations = Reservation::whereHas('users', function($query) use ($userName){ $query->where('name', $userName); //use ($userName)をよく忘れてエラーになるので注意。 })->get(); return view('index', compact('reservations')); } ・Eloquentで取得したデータをビューに表示する resources/views/index.blade.php <table> <tr> <th>予約日</th> <th>名前</th> <th>メールアドレス</th> <tr> @foreach($reservations as $reservation) <tr> <td>{{$reservation->date}}</td> <td>{{$reservation->user->name}}</td> <td>{{$reservation->user->email}}</td> </tr> @endforeach </table> Case3: リレーション2個分先の値を条件にデータを検索したい DB図 状況 全ての予約の中で"夏にこそ旅に出よう!20%OFF!"のクーポンを持つ会員のものだけを取得して画面に表示させたい。 画面には予約日、名前、メールアドレス、会員が所有する全クーポンを表示させたい。 実装方法 ・予約モデルにリレーションを設定 app/Models/Reservation.php //...いろいろ省略 public function user() { return $this->belongsTo('App\User'); //App\Models\Userにするプロジェクトもあるかもしれません。 } ・会員モデルにリレーションを設定 app/User.php //...いろいろ省略 public function coupons() { return $this->hasMany('App\Models\Coupon'); } ・コントローラでデータ取得を行ってビューに送る ※いろいろ方法があるようですが、whereHasを使う形を紹介します。 app/Http/Controllers/ReservationController.php //...いろいろ省略 use App\Models\Reservation; //...いろいろ省略 public function search() { $couponName = '夏にこそ旅に出よう!20%OFF!'; $reservations = Reservation::whereHas('users', function($query) use ($couponName){ $query->whereHas('coupons', function($query) use ($couponName) { $query->where('coupon_name', $couponName); }); })->get(); return view('index', compact('reservations')); } ・Eloquentで取得したデータをビューに表示する resources/views/index.blade.php <table> <tr> <th>予約日</th> <th>名前</th> <th>メールアドレス</th> <th>所有クーポン</th> <tr> @foreach($reservations as $reservation) <tr> <td>{{$reservation->date}}</td> <td>{{$reservation->user->name}}</td> <td>{{$reservation->user->email}}</td> <td> @foreach($reservation->user->coupons as $coupon) {{$coupon->coupon_name}}<br> @endforeach </td> </tr> @endforeach </table>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laravel Class 'ZipArchive' not foundエラー

laravelでExcelにあるデータをインポートしようとしたら 「Class 'ZipArchive' not foundエラー」が出ました。 内容的には「ZipArchiveというクラスが無いです!」と言っています。 しかし、 どこにZipArchiveというクラスをインストールしたら良いのか? などエラー解決に時間がかかったのでメモに残したいと思います。 開発環境 MacOSにLinuxの環境を作り、 Dockerを立ててlaravelを動かしている感じです。 実装 まずphp -mでモジュールがあるか確認してください。 ※php -m (モジュールの一覧を表示) ■MacOS ユーザー名@コンピュータ名 docker-on-vagrant %php -m ■Linux [vagrant@local-docker docker-laravel]$php -m ■Docker Webコンテナ内 [root@3ab4e8805629 html]# php -m 今回はDocker Webコンテナ内にzipモジュールをインストールすることで解決できました。 zipインストール [root@3ab4e8805629 html]# yum install --enablerepo=remi,remi-php72 php-pecl-zip DockerのWebコンテナ再起動 docker restart apache-php もう一度Docker Webコンテナ内に戻り php -mを試してください。 zipがインストールされていてエラーが解決できると思います。 ※docker-compose downにするとzipモジュール消えます 今回はこの方法でzipをインストールしましたが、docker-compose downすると毎回zipをインストールしないといけないので Dockerファイルにzipをインストールする記述を書いた方がいいとも感じました。 またおいおい書いていこうかと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む