- 投稿日:2020-12-06T23:30:20+09:00
【2020年版】明日話せる!PHP Weekly News 注目記事をご紹介!
はじめに
皆さんこんにちは。MasaKuです。
ブラックフライデーでPCパーツを購入しホクホクしております。ラクスアドベントカレンダー 7日目です。
今回執筆する記事について
今年、PHP関連のビックニュースとしましては、以下のようなものがありました。
- PHP誕生25周年
- PHP 8
- Larvel 8
- Composer 2.0
- Xdebug 3.0
また、世間的には新型コロナウイルスの流行による働き方改革が世界的に実施され、それの付随するテーマも大きく話題になりました。
私の情報収集源としては、Twitterやその他メディアから仕入れることが多いですが、今年一番の情報源になった媒体は PHP: The Right Way でも紹介されている PHP Weekly News というPHP関連のメルマガです。
というわけで、今年 PHP Weekly News で発信された記事の中で感銘を受けたものをご紹介したいと思います。
PHP Weekly News について
記事は週1回メルマガ配信されており、構成は以下の通りです。
- 1. Articles
- いわゆる時事ネタ。PHPに限らずエンジニアに対する自己啓発的な内容もある。
- 2. Tutorials and Talks
- PHPに関連する基礎知識やツール、設計など技術寄りの雑多なネタ
- 3. News and Announcements
- フレームワークやツールのバージョンアップ関連のニュース
- 4. Podcasts and Vlogs
- ポッドキャストやVlogの更新紹介
- 5. Reading and Viewing
- 書籍関連のお知らせ
- 6. Jobs
- 求人情報
今回は以下のカテゴリのものから注目すべき記事をピックアップしていきたいと思います。
- 1. Articles
- 2. Tutorials and Talks
本題
さっそく気になったニュースを以下の通りご紹介いたします。
※1 執筆時期が12月上旬ですので、12月分の記事は現在少なめです。年内のうちに随時更新していきたいと思います。
※2 私がメルマガの購読を開始したのは 2020年6月 からなので、それ以前の内容についてはアーカイブより参照したものとなりますのでご了承ください。1月
- 8 Steps You Can Make Before Huge Upgrade to Make it Faster, Cheaper and More Stable
- 開発速度をアップさせるための方法ついて
- The Definitive PHP 5.6, 7.0, 7.1, 7.2, 7.3, and 7.4 Benchmarks (2020)
- PHPのバージョン比較によるテスト(このときはまだPHP8の検証は開始していない)
- Preloading benchmarks in PHP 7.4
- PHP7.4 のプリロード機能のベンチマーク
- My PhpStorm settings after 8 years of use
- 8年間 PHPStorm を利用した開発者の設定
2月
- Containerise Your PHP Application (Docker 101)
- Dockerの基礎的な知識およびPHP実行環境の作成
- How is your website impacting the planet?
- ウェブサイトの表示にどれだけの二酸化炭素を排出するか
- What's new in PHP 8
- PHP8 についてのニュース記事
3月
- How Many Days of Technical Debt Has your PHP Project
- 技術的負債の返済方法
- Deep Dive into PHP 8's JIT
- JITについての解説
- PHP 7 can do that ?
- PHP7からできるようになった機能で忘れがちなこと
- PHP: enter the Matrix
- PHPのメモリ管理、Zvals についてのお話
- Making the Best of a Less-Than-Ideal Remote Work Environment
- リモートワークを快適にするために必要なこと
- PHP is very much alive and doesn’t plan on dying
- PHPが今後も活躍できる理由について
- On the shoulders of giants: recent changes in Internet traffic
- コロナ事情によりインターネット経由の連絡手段がより重要になっているが、その影響もあってか、ネットワークトラフィックが圧迫しているというお話
- Why is === faster than == in PHP ?
- 「==」よりも「===」のほうが速い理由
4月
- Some cool Laravel 7 Blade components
- 便利なBladeコンポーネント紹介
- PHP Frameworks: Top 12 Factors to Consider
- PHPフレームワークに求められる12個の重要なこと
- The History of Remote Work, 1560-Present (with Infographic)
- リモートワークの歴史
- Fast. Faster. Composer 2.0
- Composer 2.0 の速度検証
- Advanced Level Tips To Boost Your Site Performance
- サイトのパフォーマンスを向上させるための上級テクニック
5月
- What drew you to PHP?
- PHPの何が好きかという問
- Build laravel development environment with docker
- Docker の Laravel 開発環境(ucanさんの記事)
- The Future of Code Is in Your Browser
- コーディングもブラウザ上で行う未来が来るかもしれないというお話
- Introduction to Objects and References in PHP Memory
- PHPのメモリ管理について概説
6月
- What’s New in PHP 8 (Features, Improvements and the JIT Compiler)
- PHP8 の新機能について紹介
- ECMAScript 4: The Missing Version
- ECMAScript 4 がリリースされないまま ECMAScript 5 がリリースされた理由について
- What Is Good Code?
- いいコードとはどんなコードなのか、ということをエンジニア視点とマーケター視点で捉えた話
- Happy 25th birthday PHP!
- PHP 25週年おめでとう!
- Don't write your own framework
- オレオレフレームワークが引き起こした恐怖の出来事
7月
- DOES PHP HAVE A FUTURE, OR ARE TWENTY FIVE YEARS ENOUGH?
- PHP のこれまでとこれからについて
- Refactoring to Livewire polling
- Livewire の polling を用いて既存の WebSockets の処理をリファクタリングした話
- Microsoft Announces that it will drop official support of PHP on Windows
- Microsoft が PHP の公式サポートを終了するというお話
- How Can PHP Recognize Voices in Audio
- PHPで音声認識させる方法(Microsoft AzureのAPIを利用)
- 10 Cool Features You Get after switching from YAML to PHP Configs
- YAMLからPHPに置き換えることで得られるメリットについて
8月
- Laravel 8 will be released on September 8th!
- Laravel 8 が 9月8日にリリースされるというお話
- Legacy to Laravel: How to Modernize an Aging PHP Application
- レガシーなシステムをLaravel に乗せ換えするアイディア
- Manager's Guide to PHP Testing
- PHPで実施すべきテスト項目およびそのツールについて紹介
9月
- Pre-release PHP 8.0 images now available
- PHP8 RC版のコンテナイメージが利用可能に
- Steps to Install Apache, MySQL and PHP in WSL 2 -Windows 10
- WSL2 に PHP等々を構築するための手順
- Refactoring PHP
- リファクタリングの本質である「機能を変えずにコードを変更、再構築するためのプロセス」を実現するためのTips
10月
- Xdebug Update
- Xdebug 3.0 や Xdebug Cloud といったリリース情報の詳細
- ClockWork, PHP Dev Tools in your Browser
- ブラウザで利用可能なPHP開発ツール
- Rob Pike's Rules of Programming
- Rob Pikeのプログラミング法則
- 「Data structures, not algorithms, are central to programming.(プログラムを支配するのはアルゴリズムではなく、データ構造である)」にしびれました
- PHP Online - The Story So Far
- PHP Online という海外のコミュニティの紹介および今後の動きについて
11月
- Common PHP 8.0 Compilation Error Messages
- PHP8 でエラーになる構文まとめ
- How to Use Your Programming Mistakes to Learn to be a Better Developer and the New PHP ElePHPant Models that Are Available Now
- 失敗から学び得ていくためのモチベーション
- On Exactitude in Technical Debt
- 技術的負債という言葉についての見解
- Sunsetting PHP Faker
- PHP Faker の公式サポート終了
- 今後は各国の言語版のリポジトリをForkしてメンテしていく方針
- Xdebug 2 vs Xdebug 3 Performance Comparison
- Xdebug 2 と Xdebug 3 のパフォーマンス比較
12月
- Programming languages: PHP 8.0 brings big updates. Here's what's new
- PHP 8 が11/26にリリースされたということでざっくりと注目機能を紹介
おわりに
いかがでしたでしょうか。
時系列で並べてみると、この時期だったかと今年一年を振り返ることができたように感じました。PHP界隈にとっては怒涛の1年だったのではないでしょうか。
特にPHP8の登場によってPHPが利用される範囲がより拡大される可能性が垣間見えた1年となったのではないでしょうか。皆さんのお役に立てる情報へリンクできていたら幸いです!
以上で私の記事は終了とさせていただきたいと思います。
次回は、EichiSandenさんの記事です。
お楽しみに!参考サイト
PHP Weekly News 公式HP
http://www.phpweekly.com/PHP Weekly News Twitter アカウント
https://twitter.com/PHPWeeklyNewsPHP: The Right Way
http://ja.phptherightway.com/
- 投稿日:2020-12-06T22:41:38+09:00
テンプレートエンジンを使ってコード生成
はじめに
如何にしてコーディングを楽にするかを考えることは重要です。
DBスキーマと一対一で作成されているModelみたいな、同じような構造のファイルが並んでいるのを見るとコード生成をしたくなりますね。
そんなとき、自分はスクリプト言語とテンプレートエンジンを使ってちゃっちゃとコード生成スクリプトを組んでいます。使用言語
色々なスクリプト言語はありますが、当記事では下記を使ったコード生成スクリプトについて記載しています。
- php
- twig(テンプレートエンジン)
Twigテンプレートエンジンとは
軽量・高速なPHPのテンプレートエンジンです。
もとはLaravelなどのフレームワークと組み合わせてHTMLを吐き出すのに使います。(MVCでいうViewの部分)
Viewに渡された変数をView側で変換できる、「フィルター」という機能が便利で、簡潔かつ直感的にテンプレートファイルを記載することができます。
例えば標準フィルターとして搭載されているupperはViewに渡された文字列をすべて大文字にします。{# フィルター機能はbashにおけるパイプ('|')のようなイメージ #} {# 変数の後ろの'|'で区切られているフィルター処理によって随時変換される #} {# 変数name内に例えば'apple'が渡された場合、下記の出力は'APPLE'になる #} {{ name|upper }}Twigには大量の便利な標準フィルターが搭載されています。
さらに独自のフィルターを作成することもできます。
実践
目的
例えば下記のようなCSVファイルがあるとして、物理名からモデルファイルを生成したい場合を考えます。
プロパティとして$id、$name、$numがあり、そのgetterとsetterが存在するphpクラスファイルを生成するイメージです。id,name,num 1,りんご,3 2,みかん,5 3,さくらんぼ,7準備
composerとtwigのインストールを行っておきます。
composerのインストール
twigのインストール
php composer.phar require "twig/twig:^3.0"
- 今回用いるディレクトリ構成は以下
. ├── composer.json ├── composer.lock ├── composer.phar ├── data │ └── test.csv ├── main.php ├── output ├── template │ └── model.tpl └── vendor └── ...コード
生成したいソースコードのテンプレートファイルを作成し、phpファイルに向けて書き出すことでコード生成を成立させます。
ucfirstは最初の文字を大文字にするカスタムフィルターです。(twigの標準フィルターには存在しなかったため作成しています。)
- main.php
<?php require __DIR__ . '/vendor/autoload.php'; use Twig\Environment; use Twig\Loader\FilesystemLoader; use Twig\TwigFilter; $loader = new FilesystemLoader(dirname(__FILE__) . '/template'); $twig = new Environment($loader); // カスタムフィルター定義 $twig->addFilter( new TwigFilter( 'ucfirst', function (string $str) { return ucfirst($str); } ) ); // csv読み込み(物理名取得) $fp = fopen("./data/test.csv", "r"); $columns = fgetcsv($fp); fclose($fp); // ソースコード吐き出し $template = $twig->load("model.tpl"); file_put_contents( "./output/test.php", $template->render(["className" => "test", "columns" => $columns]) );
- model.tpl
<?php namespace Path/To/Model; class {{className|ucfirst}} { {% for column in columns %} /** * @var string */ private string ${{column}}; {% endfor %} /** * {{className}} constructor. {% for column in columns %} * @param string ${{column}} {% endfor %} */ public function __construct( {% for column in columns %} string ${{column}}{% if not loop.last %},{% endif %} {% endfor %} ) { {% for column in columns %} $this->{{column}} = ${{column}}; {% endfor %} } {% for column in columns %} /** * @return string */ public function set{{column|ucfirst}}(): string { return $this->{{column}}; } {% endfor %} {% for column in columns %} /** * @return string */ public function get{{column|ucfirst}}(): string { return $this->{{column}}; } {% endfor %} }実行
main.phpを実行すると下記のようなファイルがoutputディレクトリに吐き出されます。
- test.php
<?php namespace Path/To/Model; class Test { /** * @var string */ private string $id; /** * @var string */ private string $name; /** * @var string */ private string $num; /** * test constructor. * @param string $id * @param string $name * @param string $num */ public function __construct( string $id, string $name, string $num ) { $this->id = $id; $this->name = $name; $this->num = $num; } /** * @return string */ public function setId(string $id): string { return $this->id = $id; } /** * @return string */ public function setName(string $name): string { return $this->name = $name; } /** * @return string */ public function setNum(string $num): string { return $this->num = $num; } /** * @return string */ public function getId(): string { return $this->id; } /** * @return string */ public function getName(): string { return $this->name; } /** * @return string */ public function getNum(): string { return $this->num; } }おわりに
CSVのスキーマから対応するコード生成を行う簡単なスクリプトを組みました。
テンプレートエンジンを使うことの利点は、構造情報取得ロジックの部分を変えずに、吐き出すソースコードの形を自由に変えられることだと思います。
ロジックとView(コードテンプレート)を切り分けることで、すばやくメンテのし易いコード生成スクリプトを作ることができるのではないでしょうか。
- 投稿日:2020-12-06T22:35:26+09:00
[自分用メモ]ローカルのXAMPP環境で複数サイトを制作する方法
0. やりたいこと
- PHP学習用にxamppをローカル環境に入れているが、デフォルトの設定だと複数のWebサイトの制作ができない。 そこで、複数サイト制作を可能にするために、「バーチャルホスト」の設定をして、複数のドメインを使う設定を行った。 本記事はその作業の際の忘備録として残しておく。
1. 環境
- Windows10
- xampp環境構築済み
2. そもそも「バーチャルホスト」って何?
バーチャルホストって何?
Apache バーチャルホスト説明書 https://httpd.apache.org/docs/2.4/ja/vhosts/
バーチャルホストという用語は、1 台のマシン上で (www.company1.com and www.company2.com のような) 二つ以上のウェブサイトを扱う運用方法のことを指します。test1.comやtest2.com等、複数のドメインを運用していた場合、それぞれドメインごとにサーバを建てるのではなく、1台のWebサーバで複数のドメインのサービスを構築しよう!ということが出来る。
図はこちら
【Apache】virtual hostでの複数URL運営
https://www.toyo104-memo.com/entry/apache-virtual-host
↓↓図の画像リンク
https://cdn-ak.f.st-hatena.com/images/fotolife/t/toyo--104/20180610/20180610001649.png早速、やってみよう。
3. 手順
「localhost」とは別の"2つ目のドメイン"用のルートフォルダを作成する。
- xamppのインストールフォルダが「C:\xampp」なので、「C:\xampp\htdocs」以下に新しいフォルダを作成する。
- 今回作成するドメイン名は「example.com」とします。
バーチャルホスト設定ファイル編集の有効化:「C:¥xampp¥apache¥conf¥httpd.conf」の編集
- バーチャルホストの編集ファイルが有効になっているか確認
- 「Include conf/extra/httpd-vhosts.conf」部分の先頭のコメントアウト「#」があったら削除しておく
# Virtual hosts Include conf/extra/httpd-vhosts.conf
- バーチャルホストを有効化:「C:\xampp\apache\conf\extra」配下の「httpd-vhosts.conf」を編集
- 20行目付近:
##NameVirtualHost *:80の##をコメントアウト- ファイルの一番最後に有効にしたいドメインの情報を記述
<VirtualHost *:80> DocumentRoot "C:\xampp\htdocs\example.com" ServerName example.com </VirtualHost>・DocumentRoot:ドメインの一番上のルートフォルダのパス
・ServerName:ルートフォルダに紐づけられるドメイン名
- hostsファイル<host>にドメインを追加
- 「C:\WINDOWS\system32\drivers\etc」下の「hosts」ファイルを編集し、新しく追加したドメインを記述
127.0.0.1 example.comXAMPPを再起動すると、ドメインが有効になる。
4. まとめ
複数のサービスを同時に開発することもあるので、この手順はよく使う。
自分用メモです。
- 投稿日:2020-12-06T22:34:31+09:00
laravelでゲストログイン機能を作成
今やポートフォリオには必須の機能とされているゲストログイン機能を作成しました
環境
PHP 7.3.11
Laravel Framework 7.29.3今回はLoginControllerをいじっていきます
LoginController.php//省略// public function guestLogin() { $name = 'ゲスト'; $password = 'guestpass'; if(Auth::attempt(['name' => $name, 'password' => $password])) { return redirect('/home'); } return redirect('/'); }Auth::attemptは引数に指定したレコードがDB内にあればtrue,そうでなければfalseを返す
今回は予めゲストログイン用のユーザーアカウントを作成しておき名前とパスワードをname,passwordに代入、
Auth::attemptメソッドの引数に指定すればもちろんtrueが帰ってくるので認証が成功し
/homeにリダイレクトされるといった感じです。web.phpRoute::get('/login/guest', 'Auth\LoginController@guestLogin');/login/guestを踏めばguestLoginメソッドが実行されます
guestLoginComponent.vue<template> <div> <a class="nav-link" @click="openModal">ゲストログイン</a> <div class="overlay" @click="closeModal" v-if="showContent"> <div class="dialog" @click="stopEvent"> <p class="borderbottom pt-2 pb-4">ゲストユーザーとしてこのサイトをお試しになれます</p> <p class="text-right pr-2"> <a @click="closeModal" class="mr-4 black">キャンセル</a> <a href="/login/guest" class="ml-auto">ログイン</a> </p> </div> </div> </div> </template> <script> export default { data() { return { showContent: false } }, methods:{ stopEvent(){ event.stopPropagation() }, openModal(){ this.showContent = true }, closeModal(){ this.showContent = false } } } </script>今回はゲストログインボタンを押したらダイアログが表示され「ログイン」をクリックしたらメソッドが発火するというデザインにしてみました。
- 投稿日:2020-12-06T22:16:37+09:00
googleアカウントでログインする機能を実装した時のメモ
googleアカウントによるログイン機能の実装について紹介します。
通常のログイン画面にグーグルアカウント連携ボタンを追加しました。
GCPへの登録やグーグルAPIの勉強から、既存のDBへの書き込み作業等のデバッグでかなり苦労したので、メモ書きとして記録しておきます。1.事前準備(GCPの登録)
まずGoogle Cloud Platformのページで左上の「Project」よりプロジェクトを作成を選択します。そのあと「OAuth同意画⾯の設定」に進み、「クライアントID」、「クライアントシークレット」、「URI」を設定します。
詳細は以下リンクを参照ください。
https://qiita.com/kmtym1998/items/768212fe92dbaa384c272.composerのインストール
※composerをインストールしておく必要があるので、composerの事前知識がない人は詳細を確認してください。1行目のコマンド(require '../../vendor/autoload.php'; )がgoogleAPIを読み込んでいて、今回のアカウント連携機能に必要になります。サーバー環境に接続して以下のコマンドを入力します。
(サーバーやローカル環境の接続方法は各自確認ください。)
php composer.phar require google/apiclientこれで事前準備は完了です。
3.redirect.phpの作成
つづいて、実装していきます。
作成するファイルは1つです。redirect.php<?php require '../../vendor/autoload.php'; session_start(); // init configuration $clientID = '***************************.apps.googleusercontent.com'; $clientSecret = '*************************_'; $redirectUri = '******************/redirect.php'; // create Client Request to access Google API $client = new Google_Client(); $client->setClientId($clientID); $client->setClientSecret($clientSecret); $client->setRedirectUri($redirectUri); $client->addScope("email"); $client->addScope("profile"); if (isset($_GET['code'])) { $token = $client->fetchAccessTokenWithAuthCode($_GET['code']); $client->setAccessToken($token['access_token']); // get profile info $google_oauth = new Google_Service_Oauth2($client); $google_account_info = $google_oauth->userinfo->get(); $email = $google_account_info->email; $name = $google_account_info->name; /* now you can get mail and account information*/ require_once("db.php"); $dbh = db_connect(); /* this is connection to exsiting MySQL*/ try { $statement = $dbh->prepare("SELECT * FROM {$tablename1} WHERE mail= :email and account= :account"); $statement->bindParam( ':email',$email, PDO::PARAM_STR); $statement->bindParam( ':account',$name, PDO::PARAM_STR); $statement->execute(); $count = $statement->rowCount(); /* CHECK EMAIL AND NAME IN DATABASE */ if ($count){ $_SESSION["NAME"] = $name; }else{ /* INSERT USER TO DATABASE */ $statement = $dbh->prepare("INSERT INTO {$tablename1} (account,mail) VALUES (:account,:mail)"); $statement->bindValue(':account', $name, PDO::PARAM_STR); $statement->bindValue(':mail', $email, PDO::PARAM_STR); $statement->execute(); $_SESSION["NAME"] = $name; $dbh->commit(); $dbh = null; } } catch (PDOException $e) { $errors['database'] = 'データベースエラー'; echo $e->getMessage(); } $_SESSION["NAME"] = $name; $move="******************************" ; header("Location: $move"); /* move to redirect page */ } ?> <html lang="en"> <body> <p><?php echo "<a href='".$client->createAuthUrl()."'>Google Login</a>" ?></p> </body> </html>解説します。
$clientID = '***************************.apps.googleusercontent.com'; $clientSecret = '*************************_'; $redirectUri = '******************/redirect.php';この部分でGCPで設定した項目を入力してください。
$redirectUri部分は、移動したいサイトのアドレスを入力するのが本来の使い方のようですが、どうもそれをすると、アカウント情報を処理する前にサイト移動してしまうので、ここでは同じページ(redirect.php)を指定しています。require_once("db.php"); $dbh = db_connect(); /* this is connection to exsiting MySQL*/この部分はMySQLへ接続するためのコードです。
/* CHECK EMAIL AND NAME IN DATABASE */ if ($count){ $_SESSION["NAME"] = $name; }この部分で既存のアカウントかどうかを判別しています。
既存のアカウントであれば、そのままログインします。else{ /* INSERT USER TO DATABASE */ $statement = $dbh->prepare("INSERT INTO {$tablename1} (account,mail) VALUES (:account,:mail)"); $statement->bindValue(':account', $name, PDO::PARAM_STR); $statement->bindValue(':mail', $email, PDO::PARAM_STR); $statement->execute();既存アカウントでない場合は、この部分でMySQLにアカウント登録しています。
$_SESSION["NAME"] = $name; $move="******************************" ; header("Location: $move"); /* move to redirect page */最後に、この部分でページ移動しています。
$move=の部分に移動先のページ(https:// )を記載します。実際に作ったページがこちらです。
https://estacionsuzuki.com/engineering/community/login/index.php
- 投稿日:2020-12-06T21:50:05+09:00
【初心者向け】20分でLaravel開発環境を爆速構築するDockerハンズオン
概要
docker(docker-compose)でLEMP環境(PHP/nginx/MySQL)を構築し、Laravelの新規プロジェクト作成、構築した環境を破棄してから環境の再構築までをハンズオン形式で行います。
ご注意
記事を寄稿したタイミングでは、補足の解説は特になくコマンドだけのご紹介だったので、コマンド部分のみコピペすれば20分で終わる内容でした。
しかし、継ぎ足しで解説を追記していった結果、普通に読みながら進めていくと2時間ぐらいのボリュームになってしまってます?ご了承下さい?♂️
更新履歴
- 2020/09/09 Laravel8(当時はLaravel6が出た頃)がリリースされ、こちらの記事も内容が古くなったので全て書き直しました。
目的
Dockerの細かい概念などはすっ飛ばして、
このハンズオンの通りに進めればDocker×Laravelの環境を構築できます。
習うより慣れろで行ってみましょう!ハンズオン終了時のGitHub
https://github.com/ucan-lab/docker-laravel-handson
ハンズオンイベント
【ぺちオブ】ゆーきゃんプレゼンツ!Laravel + Docker 環境構築ハンズオン
https://phper-oop.connpass.com/event/137152
あの有名なぺちオブのイベントとしてハンズオン講師をやらせていただきました!
ありがとうございます?レビュワー
- hiroさん @hirodragon
- かひろくん @kahirokunn
- もっぷ先生 @kobayashi-m42
- こはさん @smspp
スペシャルサンクス?♂️?
前提条件
- Mac基準ですが、Windowsでも動作することを確認済み
- コマンドは
[環境] $とprefixを付けてます。- 所要時間は事前準備部分を除いて20分を目安としてます。
- エディタはファイル編集時にお好みのもので開いて作業ください?
構築環境(Dockerイメージ、各種バージョン)
- php: 7.4-fpm-buster
- composer: 1.10
- nginx: 1.18-alpine
- mysql: 8.0
- Laravel: 8.x
dockerおすすめ書籍
冒頭で習うより慣れろと言いましたが、習っておくのもそれはそれで良いことなのでオススメの書籍や記事をご紹介します。私はこの辺りの記事や本を読んで勉強したのでオススメしてます。
マンガでわかるdockerシリーズ
@llminatoll さんがDockerについてマンガで分かりやすく説明されてます。
初めての方はこちらのマンガでDockerの概念を学んでから進めると理解しやすいです。dockerおすすめ記事・サイト
公式系
記事
スライド
- Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
- Dockerfileを改善するためのBest Practice 2019年版
- Dockerfile を書くためのベストプラクティス解説編
事前準備
Windowsユーザー
Git Bashのインストールをお願いします。(Linuxコマンドをそのまま打てるようになります)
Git Bash を起動した際は下記のコマンドを実行ください。
$ exec winpty bashGitHubアカウントの作成
Gitの初期設定
user.emailとuser.nameが設定されていればok!
Gitの初期設定を行ってない場合、下記の記事を参考に初期設定をお願いします。最終的に下記のように表示されればokです。
$ git config --list | grep user user.email=xxx@yyy.com user.name=zzzGitHub SSH接続設定
$ ssh -T github.com Warning: Permanently added 'github.com,52.69.186.44' (RSA) to the list of known hosts. Hi ucan-lab! You've successfully authenticated, but GitHub does not provide shell access.
successfully authenticatedの文字が出ればok!
Warningは気にしなくてok!GitHub SSH接続設定を行ってない場合、下記の記事を参考に初期設定をお願いします。
もしくはハンズオンをHTTPSに置き換えて進めてください。docker, docker-compose のインストール
[Mac] Docker for Macをインストール
[Windows] Docker for Windowsをインストール
イントール確認
[mac] $ docker --version Docker version 19.03.12, build 48a66213fe [mac] $ docker-compose --version docker-compose version 1.26.2, build eefe0d31Docker起動確認
インストールしたDockerを起動してください。
running状態になっていればokです。
ここまでで事前準備終了です。
今回のハンズオンのゴール
- 3層アーキテクチャのコンテナの構築
- ウェブサーバー(web)
- nginxで静的コンテンツ配信サーバを構築
- アプリケーションサーバー(app)
- nginxを経由してPHPを動作させるアプリケーションサーバを構築
- PHPパッケージ管理ツールComposerのインストール
- データベースサーバー(db)
- MySQLデータベースサーバーの構築
- Laravelをインストールしてwelcome画面の表示
- LaravelとMySQLを連携し、マイグレーションを実行
- Docker環境の破棄
- Docker環境をGitHubから再構築
?【初心者向け】20分でLaravel開発環境を爆速構築するDockerハンズオン?
- 所要時間: 20分目安
作業ディレクトリを作成
[mac] $ mkdir docker-laravel-handson [mac] $ cd docker-laravel-handson最終的なディレクトリ構成
. ├── README.md (この名前にするとGitHubで見た時にHTMLに変換して表示してくれる) ├── infra (*1) │ ├── mysql (*1) │ │ ├── Dockerfile │ │ └── my.cnf (*1) │ ├── nginx (*1) │ │ └── default.conf (*1) │ └── php (*1) │ ├── Dockerfile (この名前にするとファイル名の指定を省略できる) │ └── php.ini (*1) ├── docker-compose.yml (この名前にするとファイル名の指定を省略できる) └── backend (*1) └── Laravelをインストールするディレクトリこのディレクトリ構成を目指します。
(*1)任意の名前に変更してもokです。リモートリポジトリを作成
リポジトリ名
docker-laravel-handsonのGitHubリモートリポジトリを作成する。リモートリポジトリへ初回コミット&プッシュ
[mac] $ echo "# docker-laravel-handson" >> README.md [mac] $ git init [mac] $ git add README.md [mac] $ git commit -m "first commit" [mac] $ git branch -M main※ 2020/10/1 からデフォルトブランチがmasterからmainに変更されました。
リモートリポジトリを登録します。
リモートリポジトリ先は適宜置き換えてください。[mac] $ git remote add origin git@github.com:ucan-lab/docker-laravel-handson.gitリモートリポジトリ先が正しく設定されていることを確認してください。
[mac] $ git remote -v origin git@github.com:ucan-lab/docker-laravel-handson.git (fetch) origin git@github.com:ucan-lab/docker-laravel-handson.git (push) # もしリモートリポジトリ先を間違えた場合は下記のコマンドから変更できます。 [mac] $ git remote set-url origin <リモートリポジトリ>リモートリポジトリ先の名前を
originと付けられていること。
リモートリポジトリ先のURL等に間違いがないことを確認する。GitHubへpushします。
[mac] $ git push -u origin main
-uオプションは--set-upstreamの省略
- ローカルリポジトリの現在のブランチ(main)をリモートリポジトリ(origin)のmainにpush先を設定
- 以降は
git pushだけでgit push origin mainと同義GitHubのリモートリポジトリのmainブランチへpushされていることを確認します。
お好みのGUIエディタで開く
ymlファイルをCUIエディタで書いていくのは大変なのでお好みのGUIエディタ(今回はVSCode)で開きます。
codeコマンドがインストールされていれば、簡単にプロジェクトをVSCodeで開けます。[mac] $ code .また、
control+shift+@で統合ターミナルをVSCodeで開くことができます。
ファイルの編集もコマンドの実行もVSCodeだけで行えます。アプリケーションサーバ(app)コンテナを作る
PHPアプリケーションサーバコンテナを作成します。
公式のPHP-FPMイメージをベースイメージとしてカスタマイズします。ディレクトリ構成
. ├── infra │ └── php │ ├── Dockerfile │ └── php.ini # PHPの設定ファイル ├── backend # Laravelをインストールするディレクトリ └── docker-compose.ymldocker-compose.yml を作成する
touchコマンドで空ファイルを作成してますが、作成後はお好みのエディタで編集ください。
ファイル名のタイプミス防止のため空ファイルだけ作成してます。
当ハンズオンはこの流れで進めていきます。[mac] $ touch docker-compose.yml作成した
docker-compose.ymlを下記の通りに編集します。docker-compose.ymlversion: "3.8" services: app: build: ./infra/php volumes: - ./backend:/work
docker-compose.ymlファイルはインデント(半角スペース)が意味を持ちます。注意してコピペしてください。docker-compose.yml: 設定値の補足
docker-compose.ymlversion: "3.8"Docker Composeファイルのバージョンを指定しています。
https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/compose-versioning/
通常はメジャー番号とマイナー番号を両方指定します。
ちなみにマイナーバージョンを指定しない場合はデフォルト0が使用されます。docker-compose.yml# 下記は同じ指定になる version: "3" version: "3.0"docker-compose.ymlservices: app: # => サービス名は任意 build: ./infra/php volumes: - ./backend:/workサービス名に
app(アプリケーションサーバー)の名前を付けて定義しています。
サービス名は任意に決められます。
build:で指定しているのはビルドコンテキストを指定します。
ビルドコンテキストとは、docker buildを実行する際の現在の作業ディレクトリのことをビルドコンテキスト(build context)と呼びます。
Dockerfileが置かれている./infra/phpディレクトリをビルドコンテキストとして指定します。
Dockerビルドの際はDockerfileのファイルを探すので、ファイル名の指定は不要です。
volumes:ではホスト側のディレクトリや名前付きボリュームをコンテナ側へマウントしたい時に指定します。
今回はホスト側の./backendディレクトリをappサービスのコンテナ内/workへマウントしてます。./docker/php/Dockerfile を作成する
- Composerコマンドのインストール
- Laravelで必要なPHP拡張機能のインストール
- bcmath, pdo_mysql が不足しているので追加インストール
[mac] $ mkdir -p infra/php [mac] $ touch infra/php/Dockerfile下記のコードを丸ごとコピーして Dockerfile へ貼り付けてください。
infra/php/DockerfileFROM php:7.4-fpm-buster SHELL ["/bin/bash", "-oeux", "pipefail", "-c"] ENV COMPOSER_ALLOW_SUPERUSER=1 \ COMPOSER_HOME=/composer COPY --from=composer:1.10 /usr/bin/composer /usr/bin/composer RUN apt-get update && \ apt-get -y install git unzip libzip-dev libicu-dev libonig-dev && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ docker-php-ext-install intl pdo_mysql zip bcmath COPY ./php.ini /usr/local/etc/php/php.ini WORKDIR /work./docker/php/Dockerfile: 設定値の補足
Dockerfileはテキストファイルであり、Dockerイメージを作り上げるために実行する命令をこのファイルに含めることができます。
各種Docker命令を解説します。
FROM php:7.4-fpm-busterhttps://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#from
FROM命令はイメージビルドのためのベースイメージを設定します。
FROM イメージ名:タグ名で指定します。php | Docker Hub公式イメージをベースイメージとして利用します。
SHELL ["/bin/bash", "-oeux", "pipefail", "-c"]https://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#shell
SHELL命令は何も指定しない場合は
SHELL ["/bin/sh", "-c"]がデフォルト値となります(Linuxの場合)パイプ中のあらゆる段階でエラーがあれば失敗とするため、
pipefailオプションを明示的に指定してます。
-oオプションはオプションを設定するためのオプションです。
-eオプションを定義しておくと、そのシェルスクリプト内で何らかのエラーが発生した時点で、それ以降の処理を中断できます。
-uオプションを定義しておくと、未定義の変数に対して読み込み等を行おうとした際にエラーとなります。
-xオプションを定義しておくと、実行したコマンドを全て標準エラー出力に出してくれます。おまじないと思ってもらえればokです。SHELL命令は必須ではないです。
ENV COMPOSER_ALLOW_SUPERUSER=1 \ COMPOSER_HOME=/composerhttps://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#env
ENV命令はコンテナ内のサーバー環境変数を設定します。
COPY --from=composer:1.10 /usr/bin/composer /usr/bin/composerhttps://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#copy
RUN apt-get update && \ apt-get -y install git unzip libzip-dev libicu-dev libonig-dev && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ docker-php-ext-install intl pdo_mysql zip bcmathhttps://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#run
Debian系のパッケージ管理ツールは
apt-get,aptとありますが、Dockerfile内でaptを実行するとCLIインターフェース向けではないと警告が表示されるため、apt-getを使用します。
apt-get updateインストール可能なパッケージの「一覧」を更新します。
実際のパッケージのインストール、アップグレードなどは行いません。
apt-get -y install xxxLaravelのインストールに必要なパッケージをインストールします。
下記のパッケージをインストールしておけばokだと思います。
- https://packages.debian.org/ja/sid/git
- https://packages.debian.org/ja/sid/unzip
- https://packages.debian.org/ja/sid/libzip-dev
- https://packages.debian.org/ja/sid/libicu-dev
- https://packages.debian.org/ja/sid/libonig-dev
apt-get clean && rm -rf /var/lib/apt/lists/*ここはパッケージインストールで使用したキャッシュファイルを削除しています。phpの公式Dockerイメージには、
docker-php-ext-install,docker-php-ext-enable,docker-php-ext-configureのPHP拡張ライブラリを簡単に利用するための便利コマンドが予め用意されています。
docker-php-ext-install intl pdo_mysql zip bcmathPHPの拡張ライブラリをインストールしています。
- https://www.php.net/manual/ja/book.intl.php
- https://www.php.net/manual/ja/ref.pdo-mysql.php
- https://www.php.net/manual/ja/book.zip.php
- https://www.php.net/manual/ja/book.bc.php
COPY ./php.ini /usr/local/etc/php/php.inihttps://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#copy
WORKDIR /workhttps://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#workdir
./docker/php/php.ini を作成する
php.ini はPHPの設定ファイル
- PHPエラーメッセージの設定
- PHPエラーログの設定
- メモリ等の設定(お好みで)
- タイムゾーン設定
- 文字コード設定
[mac] $ touch infra/php/php.iniphp.inizend.exception_ignore_args = off expose_php = on max_execution_time = 30 max_input_vars = 1000 upload_max_filesize = 64M post_max_size = 128M memory_limit = 256M error_reporting = E_ALL display_errors = on display_startup_errors = on log_errors = on error_log = /dev/stderr default_charset = UTF-8 [Date] date.timezone = Asia/Tokyo [mysqlnd] mysqlnd.collect_memory_statistics = on [Assertion] zend.assertions = 1 [mbstring] mbstring.language = Japanesebuild & up
- appコンテナの作成
- PHPのバージョン確認
- Laravelで必要なPHP拡張機能の確認
[mac] $ docker-compose up -d --build
docker-composeコマンドはdocker-compose.ymlがあるディレクトリで実行します。docker-compose upはdocker-compose.ymlに定義したサービスを起動します。-d「デタッチド」モードでコンテナを起動します。
- デフォルトは「アタッチド」モードで全てのコンテナログを画面上に表示
- 「デタッチド」モードではバックグラウンドで動作
--buildコンテナの開始前にイメージを構築します
- 特に変更がない場合はキャッシュが使用されます。
[mac] $ docker-compose ps Name Command State Ports ------------------------------------------------------------------------------- docker-laravel-handson_app_1 docker-php-entrypoint php-fpm Up 9000/tcpdocker-laravel-handson_app_1 コンテナの State が Up になっていたら正常に起動している状態です。
docker-compose psコンテナ一覧を表示します。
- Name: コンテナ名
- Command: 最後に実行されたコマンド
- State: 状態(Up)はコンテナが起動している状態
- Ports: 9000/tcp(コンテナのポート)
- 9000番のホストポートは公開されていないので、コンテナの外からはアクセスできない
appコンテナ内ミドルウェアのバージョン確認(コンテナに入ってコマンド実行)
作成したappコンテナの中に入ってPHP, Composerのバージョン、インストール済みの拡張機能を確認します。
Laravel 7.xのサーバ要件に必要な拡張機能が入っていることを確認します。[mac] $ docker-compose exec app bash # PHPのバージョン確認 [app] # php -v PHP 7.4.6 (cli) (built: May 15 2020 13:01:21) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies # Composerのバージョン確認 [app] # composer -V Composer version 1.10.10 2020-08-03 11:35:19 # インストール済みの拡張機能の一覧 [app] # php -m [PHP Modules] bcmath Core ctype curl date dom fileinfo filter ftp hash iconv intl json libxml mbstring mysqlnd openssl pcre PDO pdo_mysql pdo_sqlite Phar posix readline Reflection session SimpleXML sodium SPL sqlite3 standard tokenizer xml xmlreader xmlwriter zip zlib [Zend Modules]
exitでコンテナの外に出ます。[app] $ exit
control+dでもコンテナから出られます。補足説明
appコンテナ内に入ってphpコマンドを実行しています。
docker-compose exec実行中のコンテナ内で、コマンドを実行します。appサービス名(コンテナ名)を指定します。PHPのバージョン確認(コンテナの外からコマンド実行)
コンテナの外から
phpコマンドを実行することもできます。
docker-compose exec [サービス名] [実行したいコマンド][mac] $ docker-compose exec app php -v PHP 7.4.6 (cli) (built: May 15 2020 13:01:21) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies結果にコミット
[mac] $ git status (use "git add <file>..." to include in what will be committed) docker-compose.yml infra/php/Dockerfile infra/php/php.ini [mac] $ git add . [mac] $ git status On branch main Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: docker-compose.yml new file: infra/php/Dockerfile new file: infra/php/php.ini [mac] $ git commit -m "feat create docker app container" [main f5a637f] feat create docker app container 3 files changed, 49 insertions(+) create mode 100644 docker-compose.yml create mode 100644 infra/php/Dockerfile create mode 100644 infra/php/php.ini [mac] $ git logウェブサーバー(web)コンテナを作る
nginxウェブサーバーコンテナを作成します。
nginxのベースイメージをそのまま利用します。ディレクトリ構成
. ├── infra │ └── nginx │ └── default.conf # nginxの設定ファイル ├── backend │ └── public # 動作確認用に作成 │ ├── index.html # HTML動作確認用 │ └── phpinfo.php # PHP動作確認用 └─── docker-compose.ymldocker-compose.yml へ追記する
- ポート転送の設定(今回は10080ポートにする)
- タイムゾーンの設定
version: "3.8" services: app: build: ./infra/php volumes: - ./backend:/work # 追記 web: image: nginx:1.18-alpine ports: - 10080:80 volumes: - ./backend:/work - ./infra/nginx/default.conf:/etc/nginx/conf.d/default.conf working_dir: /work
docker-compose.ymlファイルはインデント(半角スペース)が意味を持ちます。注意してコピペしてください。appコンテナの設定と同じインデントレベルで貼り付けます。docker-compose.yml: 設定値の解説
image: nginx:1.18-alpinehttps://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#image
コンテナを起動させるイメージを指定します。
nginx | Docker Hubを指定してます。
今回は公式のnginxイメージをそのまま利用しています。(Dockerfileは不要)ちなみにnginxは
1.10,1.12等の偶数のバージョンが安定バージョンになります。
特に理由がなければ偶数バージョンをご利用ください。ports: - 10080:80https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#ports
nginxへ外(ホスト側)からコンテナ内へアクセスさせるため公開用のポートを設定します。
ホスト側:コンテナ側と設定します。volumes: - ./backend:/work - ./infra/nginx/default.conf:/etc/nginx/conf.d/default.confhttps://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#volumes
ホスト側にあるディレクトリ、ファイルをコンテナ内へマウントさせています。
docker/nginx/default.conf を作成する
[mac] $ mkdir infra/nginx [mac] $ touch infra/nginx/default.confLaravel公式にnginxの設定例が用意されているので、こちらを流用します。
https://readouble.com/laravel/7.x/ja/deployment.htmldefault.confserver { listen 80; server_name example.com; root /work/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options "nosniff"; index index.php; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { fastcgi_pass app:9000; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } }
root,fastcgi_passドキュメントルート設定を書き換えてます。
nginxの設定を詳しく知りたい方は下記の記事がオススメです。build & up
[mac] $ docker-compose down [mac] $ docker-compose up -d --build
docker-composeコマンドはdocker-compose.ymlがあるディレクトリで実行します。[mac] $ docker-compose ps Name Command State Ports -------------------------------------------------------------------------------------------- docker-laravel-handson_app_1 docker-php-entrypoint php-fpm Up 9000/tcp docker-laravel-handson_web_1 nginx -g daemon off; Up 0.0.0.0:10080->80/tcpdocker-laravel-handson_web_1 コンテナの State が Up になっていたら正常に起動している状態です。
また、
Portsの項目が、appコンテナは9000/tcpでwebコンテナは0.0.0.0:10080->80/tcpと表示形式が異なってます。
これはホスト上の10080番ポートをコンテナの80番ポートへ割り当てています。nginxのバージョン確認
[mac] $ docker-compose exec web nginx -v nginx version: nginx/1.18.0webコンテナの確認
- webコンテナの動作確認
- HTMLとPHPが表示されるか
[mac] $ mkdir backend/public [mac] $ echo "Hello World" > backend/public/index.html [mac] $ echo "<?php phpinfo();" > backend/public/phpinfo.phphttp://127.0.0.1:10080/index.html
「Hello World」が表示されることを確認する。
http://127.0.0.1:10080/phpinfo.php
phpinfoの情報が表示されることを確認する。
[mac] $ rm -rf backend/*確認用に作成したHTML, PHPファイルは不要なので削除します。
結果にコミット
[mac] $ git status On branch main Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: docker-compose.yml modified: infra/php/Dockerfile Untracked files: (use "git add <file>..." to include in what will be committed) infra/nginx/default.conf [mac] $ git add . [mac] $ git status On branch main Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: docker-compose.yml new file: infra/nginx/default.conf modified: infra/php/Dockerfile [mac] $ git commit -m "feat create docker web container" [main 5e278c1] feat create docker web container 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 infra/nginx/default.conf [mac] $ git logLaravelをインストールする
- app コンテナに入り、Laravelをインストール
- welcomeページが表示されるか
[mac] $ docker-compose exec app bash [app] $ composer create-project --prefer-dist "laravel/laravel=8.*" . [app] $ php artisan -V Laravel Framework 8.0.0 [app] $ exit
composer create-project --prefer-dist
--prefer-distzipでダウンロードするため高速- composer の–prefer-distってよく使うけど何してる?
composer create-projectコマンドでメモリ足りない問題の対策artisanについて
- Laravelが用意しているコマンドラインインターフェイス
- https://readouble.com/laravel/7.0/ja/artisan.html
php artisan list使用可能なコマンド一覧を表示Laravel ウェルカム画面の表示
LaravelのWelcome画面が表示されることを確認する。
結果にコミット
[mac] $ git status [mac] $ git add . [mac] $ git status [mac] $ git commit -m "feat laravel install" [mac] $ git logデータベース(db)コンテナを作る
MySQLデータベースコンテナを作成します。
MySQLのベースイメージをそのまま利用します。参考記事
ディレクトリ構成
. ├── infra │ └── mysql │ ├── Dockerfile │ └── my.cnf # MySQLの設定ファイル └── docker-compose.ymldocker-compose.yml へ追記する
- データベース名やユーザー名等の接続情報とタイムゾーンの設定は環境変数で渡す
- トップレベルvolumeを使用してデータの永続化
version: "3.8" services: app: build: ./infra/php volumes: - ./backend:/work web: image: nginx:1.18-alpine ports: - 10080:80 volumes: - ./backend:/work - ./infra/nginx/default.conf:/etc/nginx/conf.d/default.conf working_dir: /work # 追記 db: build: ./infra/mysql volumes: - db-store:/var/lib/mysql volumes: db-store:
docker-compose.ymlファイルはインデント(半角スペース)が意味を持ちます。注意してコピペしてください。webコンテナの設定と同じインデントレベルでdbコンテナの設定を貼り付けます。volumesはトップレベル(servicesと同じレベル)に貼り付けます。- DockerのMySQLイメージ起動時に渡す環境変数
- Docker for Macのmount遅い問題まとめ
- Performance tuning for volume mounts (shared filesystems)
./docker/mysql/Dockerfile を作成する
[mac] $ mkdir infra/mysql [mac] $ touch infra/mysql/Dockerfile下記のコードを丸ごとコピーして Dockerfile へ貼り付けてください。
infra/mysql/DockerfileFROM mysql:8.0 ENV MYSQL_DATABASE=laravel_local \ MYSQL_USER=phper \ MYSQL_PASSWORD=secret \ MYSQL_ROOT_PASSWORD=secret \ TZ=Asia/Tokyo COPY ./my.cnf /etc/mysql/conf.d/my.cnf RUN chmod 644 /etc/mysql/conf.d/my.cnfコメントでいただいたのですが、Windows環境でボリュームマウントを行うと、ファイルパーミッションが777となるようです。
my.cnfに書き込み権限が付いてるとMySQLの起動時にエラーが発生します。
その対策としてボリュームマウントではなくDockerfileを作成してmy.cnfファイルコピー、読み取り専用に権限変更してます。docker/mysql/my.cnf を作成する
- 文字コードの設定
- タイムゾーンの設定
- ログ設定
[mac] $ mkdir infra/mysql [mac] $ touch infra/mysql/my.cnfmy.cnf[mysqld] # character set / collation character_set_server = utf8mb4 collation_server = utf8mb4_0900_ai_ci # timezone default-time-zone = SYSTEM log_timestamps = SYSTEM # Error Log log-error = mysql-error.log # Slow Query Log slow_query_log = 1 slow_query_log_file = mysql-slow.log long_query_time = 1.0 log_queries_not_using_indexes = 0 # General Log general_log = 1 general_log_file = mysql-general.log [mysql] default-character-set = utf8mb4 [client] default-character-set = utf8mb4build & up
[mac] $ docker-compose down [mac] $ docker-compose up -d --build
docker-composeコマンドはdocker-compose.ymlがあるディレクトリで実行します。[mac] $ docker-compose ps Name Command State Ports -------------------------------------------------------------------------------------------- docker-laravel-handson_app_1 docker-php-entrypoint php-fpm Up 9000/tcp docker-laravel-handson_db_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp docker-laravel-handson_web_1 nginx -g daemon off; Up 0.0.0.0:10080->80/tcpdocker-laravel-handson_db_1 コンテナの State が Up になっていたら正常に起動している状態です。
[mac] $ docker-compose exec db mysql -V mysql Ver 8.0.20 for Linux on x86_64 (MySQL Community Server - GPL)マイグレーション実行(エラーが発生します)
[mac] $ docker-compose exec app bash [app] $ php artisan migrate Illuminate\Database\QueryException SQLSTATE[HY000] [2002] Connection refused (SQL: select * from information_schema.tables where table_schema = laravel and table_name = migrations and table_type = 'BASE TABLE') at vendor/laravel/framework/src/Illuminate/Database/Connection.php:671 667▕ // If an exception occurs when attempting to run a query, we'll format the error 668▕ // message to include the bindings with SQL, which will make this exception a 669▕ // lot more helpful to the developer instead of just the database's errors. 670▕ catch (Exception $e) { ➜ 671▕ throw new QueryException( 672▕ $query, $this->prepareBindings($bindings), $e 673▕ ); 674▕ } 675▕ +37 vendor frames 38 artisan:37 Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput)) [app] $ exit
SQLSTATE[HY000] [2002] Connection refusedこのエラーはよく見るMySQLのエラーです。
MySQLに接続拒否されたエラーなので、この場合は大体MySQLへの接続設定に誤りがあります。
backend/.envのDB接続設定を修正する。[mac] $ vim backend/.env DB_CONNECTION=mysql DB_HOST=db DB_PORT=3306 DB_DATABASE=laravel_local DB_USERNAME=phper DB_PASSWORD=secret
backend/.env.exampleも同様にDB接続設定を修正しておきます。[mac] $ vim backend/.env.example DB_CONNECTION=mysql DB_HOST=db DB_PORT=3306 DB_DATABASE=laravel_local DB_USERNAME=phper DB_PASSWORD=secretマイグレーション実行(再実行)
[mac] $ docker-compose exec app bash [app] $ php artisan migrate Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (0.07 seconds) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (0.04 seconds) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (0.02 seconds)試しにデータを作ってみる
[mac] $ docker-compose exec app bash [app] php artisan tinker $user = new App\Models\User(); $user->name = 'phper'; $user->email = 'phper@example.com'; $user->password = Hash::make('secret'); $user->save();[mac] $ docker-compose exec db bash [db] $ mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE [mysql] > SELECT * FROM users; +----+--------------------+-----------------------------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+ | id | name | email | email_verified_at | password | remember_token | created_at | updated_at | +----+--------------------+-----------------------------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+ | 1 | phper | phper@example.com | NULL | $2y$10$/cmcAIdF7twOntcazkn4/e61YAM2F99hCrOo.qRUz7cUa/ZydrbVS | NULL | 2020-08-24 17:09:53 | 2020-08-24 17:09:53 | +----+--------------------+-----------------------------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+ 1 rows in set (0.00 sec)結果にコミット
[mac] $ git status [mac] $ git add . [mac] $ git status [mac] $ git commit -m "feat create docker db container" [mac] $ git logGitHubにpush
[mac] $ git pushリモートリポジトリへpushされていることを確認します。
Docker環境の再構築
Docker環境の破棄
コンテナの停止、ネットワーク・名前付きボリューム・コンテナイメージ、未定義コンテナを削除
[mac] $ docker-compose down --rmi all --volumes --remove-orphans作業ディレクトリの削除
プロジェクトを削除するので、GUIエディタは閉じておきましょう。
[mac] $ cd .. [mac] $ rm -rf docker-laravel-handsonVisual Studio Code等のGUIエディタで開いている場合は、一度エディタを終了しましょう。
環境の再構築
GitHubからリポジトリをクローン
** 自身のリポジトリ先に適宜変更してください **
[mac] $ git clone git@github.com:ucan-lab/docker-laravel-handson.git [mac] $ cd docker-laravel-handson [mac] $ docker-compose up -d --build
/work/public/../vendor/autoload.phpを開くのに失敗してエラーになっていることを確認します。
git cloneが終わった状態ではappコンテナ内に/work/vendorディレクトリが存在しないためです。Laravelインストール
app コンテナに入ります。
[mac] $ docker-compose exec app bash
vendorディレクトリへライブラリ群をインストールします。
composer.lockファイルを参照します。[app] $ composer install
composer install時は.env環境変数ファイルは作成されないので、.env.exampleを元にコピーして作成します。[app] $ cp .env.example .env
.envにAPP_KEY=の値がないとこのエラーが発生します。
このコマンドでアプリケーションキーを生成できます。[app] $ php artisan key:generateWelcome画面が表示されることを確認します。
[app] $ php artisan migrate最後にマイグレーションを実行して成功すればハンズオン終了です。
お疲れ様でした?? コンテナを停止して終了してください。[app] $ exit [mac] $ docker-compose downオマケ
MySQLに接続したい
[mac] $ docker-compose exec db bash -c 'mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} ${MYSQL_DATABASE}' mysql> show tables; +---------------------+ | Tables_in_homestead | +---------------------+ | migrations | | password_resets | | users | +---------------------+ 3 rows in set (0.00 sec) mysql> desc users; +-------------------+---------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------------+---------------------+------+-----+---------+----------------+ | id | bigint(20) unsigned | NO | PRI | NULL | auto_increment | | name | varchar(255) | NO | | NULL | | | email | varchar(255) | NO | UNI | NULL | | | email_verified_at | timestamp | YES | | NULL | | | password | varchar(255) | NO | | NULL | | | remember_token | varchar(100) | YES | | NULL | | | created_at | timestamp | YES | | NULL | | | updated_at | timestamp | YES | | NULL | | +-------------------+---------------------+------+-----+---------+----------------+ 8 rows in set (0.01 sec)マイグレーションが実行されるとLaravel側で最初から用意されている
users,password_resetsのテーブルが生成されています。Laravelのログをコンテナログに表示する
backend/.envを修正する。LOG_CHANNEL=stderr
backend/routes/web.phpRoute::get('/', function () { logger('welcome route.'); return view('welcome'); });$ docker-compose logs # -f でログウォッチ $ docker-compose logs -f # サービス名を指定してログを表示 $ docker-compose logs -f appMySQLクライアントツールで接続したい
docker-compose.ymlのdbサービスに下記設定を追記して、コンテナを再起動して設定を反映してください。ports: - 33060:3306dbコンテナへのポート転送設定がないとホストからアクセスが行えません。
MySQLクライアントツールは「Sequel Ace」がオススメです。Windowsエラーメモ
Error response from daemon
$ docker-compose up -d --build docker: Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers).解決策をいくつか
- Docker For Windowsを再起動する
- Dockerでpullやrunを行ったときイメージダウンロードができなくなった問題の解決法
- Proxy環境下のDockerトラブルシューティング
- さわって理解する Docker 入門
マイグレーションエラーの補足
Host '172.27.0.2' is not allowed to connect to this MySQL server
$ php artisan migrate Illuminate\Database\QueryException : SQLSTATE[HY000] [1130] Host '172.27.0.2' is not allowed to connect to this MySQL server (SQL: select * from information_schema.tables where table_schema = homestead and table_name = migrations and table_type = 'BASE TABLE') at /work/vendor/laravel/framework/src/Illuminate/Database/Connection.php:664 660| // If an exception occurs when attempting to run a query, we'll format the error 661| // message to include the bindings with SQL, which will make this exception a 662| // lot more helpful to the developer instead of just the database's errors. 663| catch (Exception $e) { > 664| throw new QueryException( 665| $query, $this->prepareBindings($bindings), $e 666| ); 667| } 668| Exception trace: 1 PDOException::("SQLSTATE[HY000] [1130] Host '172.27.0.2' is not allowed to connect to this MySQL server") /work/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70 2 PDO::__construct("mysql:host=db;port=3306;dbname=homestead", "homestead", "secret", []) /work/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70 Please use the argument -v to see more details.このエラーが発生した場合は
my.cnfを作成する前にdocker-compose up -dでビルドしてしまった可能性が高いです。$ docker-compose down --volumes --rmi all $ docker-compose up -d --build設定ファイルがない状態でMySQLの初期化が行われたでデータが永続化されてしまってるので一度ボリューム毎削除してビルドし直せばokです。
docker volumes の共有で Permission denied (SELinux) 問題
CentOS 等の場合 volumes でホストと共有したファイル(ディレクトリ)にアクセスできないことがあります。
書き込み権限を与える
$ chmod -R 777 logs $ chmod -R 777 src/storage/logsログディレクトリはホスト側からもコンテナ側からも書き込みするので、書き込み権限を付与しておきましょう。
応用
より実用的な構成のdocker構成の記事を書いてますので、よかったらこちらの記事も読んでもらえると嬉しいです。
- 投稿日:2020-12-06T19:07:01+09:00
PHPで、あいうえお配列の取得
入力
<?php $arr = str_split("あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわをん"); ?>結果
Array ( [0] => あ [1] => い [2] => う [3] => え [4] => お [5] => ... ... )
- 投稿日:2020-12-06T19:07:01+09:00
PHPで、あいうえお配列の取得 (簡単)
入力
(str_splitだとなぜか文字化けするので、このようにしないといけません。)<?php $arr = preg_split("//u", "あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわ"); var_dump($arr); // 表示のためのコード ?>結果
array(46) { [0]=> string(0) "" [1]=> string(3) "あ" [2]=> string(3) "い" [3]=> string(3) "う" [4]=> string(3) "え" [5]=> string(3) "お" [6]=> string(3) "か" [7]=> string(3) "き" [8]=> string(3) "く" [9]=> string(3) "け" [10]=> string(3) "こ" [11]=> string(3) "さ" [12]=> string(3) "し" [13]=> string(3) "す" [14]=> string(3) "せ" [15]=> string(3) "そ" [16]=> string(3) "た" [17]=> string(3) "ち" [18]=> string(3) "つ" [19]=> string(3) "て" [20]=> string(3) "と" [21]=> string(3) "な" [22]=> string(3) "に" [23]=> string(3) "ぬ" [24]=> string(3) "ね" [25]=> string(3) "の" [26]=> string(3) "は" [27]=> string(3) "ひ" [28]=> string(3) "ふ" [29]=> string(3) "へ" [30]=> string(3) "ほ" [31]=> string(3) "ま" [32]=> string(3) "み" [33]=> string(3) "む" [34]=> string(3) "め" [35]=> string(3) "も" [36]=> string(3) "や" [37]=> string(3) "ゆ" [38]=> string(3) "よ" [39]=> string(3) "ら" [40]=> string(3) "り" [41]=> string(3) "る" [42]=> string(3) "れ" [43]=> string(3) "ろ" [44]=> string(3) "わ" [45]=> string(0) "" }
- 投稿日:2020-12-06T17:56:07+09:00
サーバーサイドとクライアントサイド
この記事について
phpを勉強していた中で調べたサーバー、クライアントについて書きましたので私と同じような初心者の方の役に立てたら幸いです。
サーバーサイドとクライアントサイド
それぞれの違いはプログラムの実行場所がサーバーか端末かの違いです。プログラムの実行場所とはいわゆる、コードがどこで処理されるかということです。アクセスしたサーバー側でプログラムを実行した場合サーバーサイドのプログラムと言い、アクセスした端末でプログラムの処理を実行した場合クライアントサイドのプログラムと言います。
では、各々の特徴を見ていきましょう。サーバーサイド
- まず1つ目は、情報の保守性が上がることです。サーバー側で動作しているのでクライアント側からでは基本的にソースコードを見ることができません。なので動作内容がわからないというのは、扱う情報の安全性が守られることにつながります。
- 2つ目は、サーバー側で処理を行うのでデータベースに保存されているユーザー情報の取得や、新たに追加することが可能になるということです。これらができることにより、ユーザーのアカウントの新規登録や、ユーザーごとに表示する情報を変化させることができます。
これらの特徴があります。サーバーサイドで使われる主な言語としてPHPが挙げられます。
クライアントサイド
- 1つ目はサーバーにある情報を要求し受け取っていることです。普段使用しているPCやスマートフォンがそれにあたります。googleなどのブラウザを通じ、サーバーにアクセスを行いプログラムの結果を反映させていたりします。
- 2つ目はWEBの見た目を調整する際に使用されるということです。ページを表示した際に出てくる、デザインされた色やタイトルまたは、アニメーションなどは全てクライアントサイドで処理されたものです。
これらが特徴で挙げられます。そして、クライアントサイドの主な言語としてJava Scriptがあります。
まとめ
サーバーサイド、クライアントサイドについて見てきましたがそれぞれに特徴があり、メリットデメリットがあると思います。作成したいものにあったものが使えるように2つの判別はしっかりできるようになっておくとできることも広がると思います。
- 投稿日:2020-12-06T17:31:43+09:00
PHP基礎
Udemyで一通り学んだ内容のメモとして今回記載します。
画面に文を表示
<?php print('PHPです。'); ?><?php echo('PHPです。'); ?>echoとprintとの違い
・echoはカンマ区切りで複数の文字列を連結できるが、戻り値を返さない。
・printはカンマ区切りで複数の文字列を連結できずに1つの引数をとるが、戻り値を返せる。算術計算
<?php print(1+1); ?>画面に現在の時刻を表示
現在の時刻を表示
<?php print(date('G時 i分 s秒')); ?>date — 現在の日付/時刻を書式化します。
曜日を表示
<?php print(date('l')); ?>日本時間にする
<?php date_default_timezone_set('Asia/Tokyo'); print(date('G時 i分 s秒')); ?>※以下を参考
文字列を連結
<?php //.でつなげる print('現在は'.date('G時 i分 s秒').'です'); ?>オブジェクトを使って時刻を表示
<?php //DateTimeという日付や時間に関する様々な動作を持つオブジェクトを使用する $today=new DateTime(); print($today->format('G時i分s秒')); ?>変数を使って計算結果を表示する
<?php $sum=100+1050+200;?> <?php $tax=1.08;?> 合計金額は : <?php print($sum); ?>円です。 税込価格は : <?php print($sum*$tax); ?>円です。while構文
1から365までを表示する
<?php //初期化処理 $i=1; //while(繰り返す条件) while($i<=365){ //繰り返したい処理 print($i."\n"); //更新処理 $i=$i+1; } ?>for構文
<?php //for(初期化処理;繰り返す条件;更新処理) for($i=1;$i<=365;$i++){ //繰り返したい処理 print($i."\n"); } ?>1年後までのカレンダーを表示する
まずは実行した日の本日の日付を取得する
<?php time()は本日の日付を取得する、それをn/j(D)形式に変更。 print(date('n/j(D)',time() +60+60*24)); ?>strtotime
文字列で指定したものをtimestampに変換する
<?php //以下なら今日から二日後のtimestampを取得 strtotime('+2day') //出力 print(date('n/j(D)',strtotime('+2day'))); print "\n": //以下なら1543/1/31を取得 strtotime('1543/1/31') //出力 print(date('n/j(D)',strtotime('1543/1/31'))); ?>365日後までの日付を取得する
<?php for($i=1;$i<=365;$i++){ //文字列で指定したものをtimestamp(日付)に変換する。 $date=strtotime('+'.$i.'day'); print(date('n/j(D)',$date)); print"\n"; } //endforは何に対しての閉じなのか分かりやすくなる。 endfor: ?>whileの場合
<?php while(....): endwhile; ?>配列について
date('w')で月火水木金土日の数字を取得できる。
<?php //ブラケット[]の中に入れた値は,(カンマ)で区切る $week_name=['日','月','火','水','木','金','土']; //配列のままでは画面に表示できないので添え字の形式で表示する print($week_name[date('w')]); ?>連想配列とforeach構文
<?php //連想配列は要素を指定する前にキーを指定する $fruits=['apple'=>'りんご', 'grape'=>'ぶどう', 'lemon'=>'レモン' ]; //値のみを出力 foreach($fruits as $val){ print($val.':'"\n"); } ?><?php //連想配列は要素を指定する前にキーを指定する $fruits=['apple'=>'りんご', 'grape'=>'ぶどう', 'lemon'=>'レモン' ]; //$englishはインデックスが入り、$japaneseは値の要素が入る。 foreach($fruits as $english=>$japanese){ print($english.':'.$japanese."\n"); } ?>if構文
<?php //Gは時間を取得し、現在の時間が表示される。 if(date('G')<9){ printf('※ 現在受付時間外です。'); }else{ print('ようこそ'); } $x=1; if($x>0){ print('xは0ではありません'); } ?>小数を切り上げ、切り下げる方式
<?php //floorは小数が発生しても全て切り捨てる print (floor(100/3000*100)); ?>%引きです。 <?php //ceilは切り上げ print (ceil(100/3000*100)."\n"); ?> <?php //round(四捨五入) print (round(100/3000*100,1)); ?>sprintf
桁数、書式を合わせたいときに使用する
<?php //0002年1月23日と表示される。 $date=sprintf('%04s年 %02d月 %02d日' ,'2',1,23); print ($date); ?>ファイルに内容を書き込む場合
<?php //file_put_contentsはファイルに書き込むというファンクション。書き込みたいファイルのパスを指定する。ファイルには内容が書き込まれる。 $success=file_put_contents('../../newsdate/news.txt.','2018-06-01 ホームページをリニューアルしました。'); if($success){ print('ファイルへの書き込みに成功しました'); }else{ print('書き込みに失敗しました。フォルダの権限を追加してください'); } ?>ファイルを読み込む
<?php //読み込みたいファイルを指定する。 $news=file_get_contents('../../newsdate/news.txt.'); print($news); ?>変数を指定しないで読み込む場合
<?php //読み込みたいファイルを指定して表示できる。しかし、読み込んだファイルを再加工したりできない欠点がある。readfile('../../newsdate/news.txt.'); ?>ファイルに内容を追記する場合
<?php $news=file_get_contents('../../newsdate/news.txt.'); //$newsに内容を追加。 $news.="2018-06-03 ニュースを追加しました。\n"; file_put_contents('../../newsdate/news.txt.', $news); print($news); ?>XMLの情報を読み込む
XMLとは、拡張できるマークアップ言語のこと。
rssと呼ばれるものが一般的。
webページが更新されるとrssが更新されます。<?php //XML形式のファイルをロードする。 $xmlTree=simplexml_load_file('https://h2o-space.com/feed/'); foreach($xmlTree->channel->item as $item): ?> <a href="<?php print($item->url);?>"> <?php print($item->title);?></a> <?php endforeach; ?>JSONを読み込む
JSONとはJavaScript Object Notationの略。JavaScriptで扱えるオブジェクトとして使える形式。
以下のような記載です。//短くかける。 { "id":"https://h2o-space.com/2010/2/2", "url":"https//google.com", "title":"aaaa"; }各データの内容が分かりやすくなります。
<?php //jsonのデータを取得する $file=file_get_contents('https://h2o-space.com/feed/json/'); //jsonを処理した結果をいれる。 $json=json_decode($file); var_dump($json); foreach($json->items as $item): ?> <a href="<?php print($item->url); ?>"><?php print($item->title);?></a> <?php endforeach; ?>json_decode
jsonの内容をPHPで処理する。
フォームに入力した内容を取得する
ファイルを2つ用意します。
例
index.html<body> <header> <h1 class="font-weight-normal">PHP</h1> </header> <main> <h2>Practice</h2> <label for="my-name">お名前:</label> <!-- submit.phpのリンクを貼る --> <form action="submit.php" method="get"> <input type="text" id="my-name" name="my-name" maxlength="255" value=""> <input type="submit" value="送信する"> </form> </main> </body>submit.php<body> <header> <h1 class="font-weight-normal">PHP</h1> </header> <main> <h2>Practice</h2> <pre> お名前: <?php print(htmlspecialchars ($_REQUEST['my-name'],ENT_QUOTES));?> </pre> </main> </body>//index.htmlで記載した以下のnameの部分は$_REQUEST['my-name']の部分に当たる。 <input type="text" id="my-name" name="my-name" maxlength="255" value=""> //submit.php print(htmlspecialchars ($_REQUEST['my-name'],ENT_QUOTES));?>・$REQUEST(リクエスト変数)は、$_GET、$_POST、$_COOKIE の内容をまとめた配列(配列変数)
・htmlspecialcharsというファンクションを使い、htmlのタグなどで認識されるタグなどのセキュリティ対策を行う。情報を盗み取ったりすることを向こうにできる。また、パラメータは2つ必要である。
1つ目のパラメータは何を対象にするのか、2つ目はどう対処するのか。
・ENTQUOTESは2つ目のパラメータに基本的に使う。文字列ではなく数字なので``は必要ない。GETとPOSTの違い
GETはURLのところに?マークがつく。$_POSTの場合、URLの場合、裏側で送信できるのでURLに値はつかない。
チェックボックス、ラジオボタン、リストボックスの値を取得
index.html<body> <header> <h1 class="font-weight-normal">PHP</h1> </header> <main> <h2>Practice</h2> <form action="submit.php" method="post"> <p>性別: <input type="radio" name="gender" value="male">男性 / <input type="radio" name="gender" value="female">女性 </p> <input type="text" name="my_name" value="初期値"> <input type="checkbox" name="agree" value="on">同意する <select name="pref"> <option value="hokkaido">北海道</option> <option value="aomori">青森県</option> </select> <input type="submit" value="送信する"> </form> </main> </body>submit.php<body> <header> <h1 class="font-weight-normal">PHP</h1> </header> <main> <h2>Practice</h2> <pre> <?php print(htmlspecialchars($_POST['gender'], ENT_QUOTES)); ?> </pre> </main> </body>複数選択可能なチェックボックスの場合
index.html<body> <header> <h1 class="font-weight-normal">PHP</h1> </header> <main> <h2>Practice</h2> <form action="submit.php" method="post"> <p>ご予約希望日</p> <p> <!--PHPで複数選択可能なチェックボックスを受け取るためにはname属性(reserve)にブラケット[]を記載する--> <input type="checkbox" name="reserve[]" value="1/1">1月1日<br> <input type="checkbox" name="reserve[]" value="1/2">1月2日<br> <input type="checkbox" name="reserve[]" value="1/3">1月3日<br> </p> <input type="submit" value="送信する"> </form> </main> </body>submit.php<body> <header> <h1 class="font-weight-normal">PHP</h1> </header> <main> <h2>Practice</h2> <pre> <?php foreach($_POST['reserve'] as $reserve){ print(htmlspecialchars($reserve,ENT_QUOTES).' '); } ?> </pre> </main> </body>半角数字に直して数字であるかどうかのチェックをする
全角数字で誤って入力してしまう場合があるのでプログラム上で自動で半角に直せるようにする。
<?php $age='20'; //mb_convert_kanaで半角数字に直してくれる $age=mb_convert_kana($age,'n','UTF-8'); if(is_numeric($age)){ print($age.'歳'); }else{ print('※ 年齢が数字ではありません'); } ?>is_numeric
パラメーターが数字であるかどうかをチェックしてくれる。
mb_convert_kana
仮名を("全角かな"、"半角かな"等に)変換する。
mb_convert_kana($age,'n','UTF-8');で半角の数字に直すことができる。郵便番号を正規表現を使って表現する
<?php $zip='987-6543'; //小文字のaは英数字を半角のaに直すもの。-が入る場合があるから。 $zip=mb_convert_kana($zip,'a','UTF-8'); if (preg_match ("/\A\d{3}[-]\d{4}\z/" , $zip)) { print('郵便番号:〒' . $zip); }else{ print('※ 郵便番号を 123-4567の形式でご記入ください'); } ?>preg_match
正規表現によるマッチングを行う
\d{3}は数字が3回続くという意味。[-]\d{4}で-で区切って、数字を4つ並べる。\Aは文章の先頭、\zは文章の最後。つまり、数字が3つ始まって、4つで終わること。 preg_match ("/\A\d{3}[-]\d{4}\z/" , $zip)別のページにジャンプする
<?php //googleのページにジャンプする。headerというファンクションはwebブラウザに対してheaderというものを送信する。location:は別のページに遷移するという意味。 header('location:http/google.com'); //強制終了 exit();剰余算
計算結果の余りを求める。
例:テーブルの色を変える場合
<table> <?php for($i=1;$i<=10;$i++){ if($i%2){ print('<tr style="background-color:#ccc">'); }else{ print('<tr>'); } print('<td>' . $i . '行目</td>'); print('</tr>'); } ?> </table>Cookieに値を保存する
index.php<?php $value='変数に保存した値です。'; setcookie('save_message','Cookieに保存した値です',time()+60*60*24*14) ?> <!doctype html> <html lang="ja"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link rel="stylesheet" href="../css/style.css"> <title>PHP</title> </head> <body> <header> <h1 class="font-weight-normal">PHP</h1> </header> <main> <h2>Practice</h2> <pre> <a href="page02.php">Page02へ</a> </pre> </main> </body> </html>page02.php<?php cookie_start();?> <!doctype html> <html lang="ja"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link rel="stylesheet" href="../css/style.css"> <title>PHP</title> </head> <body> <header> <h1 class="font-weight-normal">PHP</h1> </header> <main> <h2>Practice</h2> <pre> <!-- 画面に文書を表示する --> 変数の値:<?php print($value);?> Cookieの値<?php print($_COOKIE['save_message']);?> </pre> </main> </body> </html>setcookie
最初のパラメータにcookieのキーを指定、次に保存したい内容、3番目のパラメータはいつまで保持していくのか指定する。
//14日間保持できる。 setcookie('save_message','Cookieに保存した値です',time()+60*60*24*14)//変数は表示した画面のみで使えるので以下はエラーとなる。 変数の値:<?php print($value);?> //保存したCookieの値を表示 <?php print($_COOKIE['save_message'])Cookieを使う際はセキュリティに注意する必要がある。
セッションに値を保存する
index.php<?php session_start(); $_SESSION['session_message']='値をセッションに保存しました'; ?> <!doctype html> <html lang="ja"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link rel="stylesheet" href="../css/style.css"> <title>PHP</title> </head> <body> <header> <h1 class="font-weight-normal">PHP</h1> </header> <main> <h2>Practice</h2> <pre> <!-- ここにプログラムを記述します --> <p>セッションに値を保存しました。次のページに移動してみましょう</p> &requo; <a href="page02.php">Page02へ</a> </pre> </main> </body> </html>page02.php<?php session_start(); $_SESSION['session_message']='値をセッションに保存しました'; ?> <!doctype html> <html lang="ja"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link rel="stylesheet" href="../css/style.css"> <title>PHP</title> </head> <body> <header> <h1 class="font-weight-normal">PHP</h1> </header> <main> <h2>Practice</h2> <pre> <!-- ここにプログラムを記述します --> <p>セッションに値を保存しました。次のページに移動してみましょう</p> &requo; <a href="page02.php">Page02へ</a> </pre> </main> </body> </html><!doctype html> <html lang="ja"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link rel="stylesheet" href="../css/style.css"> <title>PHP</title> </head> <body> <header> <h1 class="font-weight-normal">PHP</h1> </header> <main> <h2>Practice</h2> <pre> <!-- 画面に文書を表示する --> <?php print($_SESSION['session_message']);?> </pre> </main> </body> </html>
- 投稿日:2020-12-06T16:19:06+09:00
Vueソーシャルシェアリングのバージョン指定
- 投稿日:2020-12-06T16:05:10+09:00
更新処理は一回で!PHP ユーザー情報編集
どうも!Twitterの詩人でお馴染みのざんをですっ!
「PHPの画像処理ってなんであんなにややこしいんだろなぁ。ぴえん。。」
って思っているそこのあなた!
安心してください。履いてますよ!!
じゃなくて、、、今回はそんな不安を解消すべく私ざんをが一言コメントと画像処理を綺麗に場合分けして一回の更新処理で済ませる方法を紹介していきます笑info.php<?php session_start(); ~ $id = $_GET['user_id']; $db_user = "xxxxxx"; $db_pass = "xxxxxx"; $dsn = "xxxxxx"; try { $dbh = new PDO($dsn, $db_user, $db_pass); } catch (PDOException $e) { echo $e->getmessage(); exit; } $sql = "SELECT * FROM members WHERE id = :id"; $stmt = $dbh->prepare($sql); $stmt->execute(array(':id' => $id)); $value = $stmt->fetch(); $dbh = null; ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> </head> <body> <h1>ユーザー情報</h1> 〜 <p>画像: <?php if ($value['image'] === ''): ?> <p>未登録<p> <?php else: ?> <p><?php echo '<img src="./image/' . $value['image'] . '" width="190" height="145">'; ?></p> <?php endif; ?> <p>一言コメント:<?php echo $value['comment']; ?></p> <!-- ログイン者であり、そのログインユーザーの情報のみ編集リンクが現れる --> <?php if (isset($_SESSION['id']) && $_SESSION['id'] == $id): ?> <!-- aタグのgetでよるIDと一言コメントと画像の名前を送信 -->> <a href="info_edit.php?id=<?php echo $id; ?>&comment=<?php echo $value['comment']; ?>&image=<?php echo $value['image']; ?>">編集</a><br> <?php endif; ?> 〜 </body> </html>とユーザーのそれぞれの詳細表示画面がこれになります。
続いて編集画面info_edit.php<?php session_start(); if (!isset($_SESSION['id']) || !isset($_SESSION['name'])) { header('Location: index.php'); exit(); } $id = $_GET['id']; $comment = $_GET['comment']; $image = $_GET['image']; ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> </head> <body> <h1>ユーザー情報編集画面</h1> <form method="post" enctype="multipart/form-data" action="info_comp.php?id=<?php echo $id; ?>&image=<?php echo $image; ?>"> <p>画像</p> <input type="file" name="image"> <p>一言コメント</p> <textarea name="comment"><?php echo $comment; ?></textarea><br> <input type="submit" value="変更"><br> <a href="info.php?id=<?php echo $id; ?>">戻る</a> </form> </body> </html>formはpostなのにこうすることによってget送信も同時に出来るんです!
なんでかっていうのは次の処理の時にわざわざ画像データ検索したりしなきゃいけないからこうやって使いまわしているだけです!続いては編集の裏側の処理です!
info_comp.php<?php session_start(); // URLの直接入力を弾く if (!isset($_GET['id']) || !isset($_FILES['image']['name']) || !isset($_POST['comment'])) { header('Location: index.php'); exit; } $id = $_GET['id']; $comment = $_POST['comment']; $user = "xxxxxx"; $pass = "xxxxxx"; $dsn = "xxxxxx"; try { $dbh = new PDO($dsn, $user, $pass); } catch (PDOException $e) { echo $e->getmessage(); exit; } // 他のユーザーが変更できないようにする if ($_SESSION['id'] == $id) { // 画像の形式チェック $type = $_FILES['image']['type']; if ($_FILES['image']['error'] || $type === 'image/jpeg' || $type === 'image/png' || $type === 'image/gif') { // 画像が変更無しの場合の処理 if ($_FILES['image']['error']) { $image = $_GET['image']; } else { // ランダムな文字列(かぶる心配のないものならなんでもいい) $image = mt_rand(0, 9999999999); move_uploaded_file($_FILES['image']['tmp_name'], './image/' . $image); // データベースに画像がある場合 if ($_GET['image'] !== '') { unlink('./image/' . $_GET['image']); } } //コメントがなかった場合はnullを保存 if ($comment == '') { $comment = null; } $sql = "UPDATE members SET image = :image, comment = :comment WHERE id = :id"; $stmt = $dbh->prepare($sql); $data = array(':image' => $image, ':comment' => $comment, ':id' => $id); $stmt->execute($data); $msg = '変更されました'; } else { $msg = '画像のファイル形式が正しくありません。'; } } else { $msg = 'このプロフィールは編集できません。'; } $dbh = null; ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> </head> <body> <p><?php echo $msg; ?></p> <a href="info.php?id=<?php echo $id; ?>">戻る</a> </body> </html>これで最低限の処理でupdateできたかなって感じです笑
「これが無駄だなぁ」とか「これが足りてねえなあ」とかあれば教えていただけると幸いです!!
- 投稿日:2020-12-06T15:14:01+09:00
AtCoder Typical DP ContestのA - コンテストを絵で理解してみたい(PHP)
AtCoderのこちらの問題に挑戦してみました。
https://atcoder.jp/contests/tdpc/tasks/tdpc_contest入力例3 2 3 5PHP<?php fscanf(STDIN, "%d", $d); $ps = explode(" ", trim(fgets(STDIN))); $ans = array([0 => TRUE]); foreach ($ps as $p) { foreach ($ans as $k => $v) { $ans[$k + $p] = TRUE; } } echo count($ans) . "\n";foreachの中でforeachを回して、中のforeachの配列は処理中に中身が増えていきます。よりイメージしやすいように絵に描いてみました。
こちらの記事は自分用のメモですが、適宜追記していきます。
コメントあれば、ぜひよろしくお願い申し上げます。
- 投稿日:2020-12-06T14:57:06+09:00
AtCoderのRobot armsの問題を絵に描いて理解してみる(PHP)
区間スケジューリングというジャンルの問題らしいです。公式の解説だけでは、悲しいかな、僕には理解できなかったので、絵に書いて理解してみました。
問題はこちらです。
入力例
入力例4 2 4 4 3 9 3 100 5絵
こうやって絵に描いてみると「なんだそんなことか、確かにそりゃそうだよね」って感じになりました。
1番目の一番右端と2番目の一番左端を比べていって、2番目の左端が1番目の右端よりも数字が小さければ、その2番目を取り除いていけばいいだけですね。コード
コードにするとこんな感じ。
PHP<?php fscanf(STDIN, "%d", $n); for ($i = 0; $i < $n; $i++) { fscanf(STDIN, "%d %d", $x, $l); $ss[] = max(0, $x - $l); $ts[] = $x + $l; } array_multisort($ts, SORT_ASC, $ss); $t = 0; $count = 0; for ($i = 0; $i < $n; $i++) { if ($ss[$i] >= $x) { $count++; $t = $ts[$i]; } } echo $count . "\n";時間に余裕ができたら随時更新していきたいです。
コメントあれば、ぜひよろしくお願い申し上げます!
- 投稿日:2020-12-06T14:57:06+09:00
AtCoderのRobot armsの問題を絵に描いて理解してみる
区間スケジューリングというジャンルの問題らしいです。公式の解説だけでは、悲しいかな、僕には理解できなかったので、絵に書いて理解してみました。
問題はこちらです。
入力例
4 2 4 4 3 9 3 100 5絵
こうやって絵に描いてみると「なんだそんなことか、確かにそりゃそうだよね」って感じになりました。
1番目の一番右端と2番目の一番左端を比べていって、2番目の左端が1番目の右端よりも数字が小さければ、その2番目を取り除いていけばいいだけですね。コード
コードにするとこんな感じ。
<?php fscanf(STDIN, "%d", $n); for ($i = 0; $i < $n; $i++) { fscanf(STDIN, "%d %d", $x, $l); $ss[] = max(0, $x - $l); $ts[] = $x + $l; } array_multisort($ts, SORT_ASC, $ss); $t = 0; $count = 0; for ($i = 0; $i < $n; $i++) { if ($ss[$i] >= $x) { $count++; $t = $ts[$i]; } } echo $count . "\n";時間に余裕ができたら随時更新していきたいです。
コメントあれば、ぜひよろしくお願い申し上げます!
- 投稿日:2020-12-06T12:30:20+09:00
PHPの基本関数!切り分けたファイルを呼び出して使い回す方法
はじめに
同じコードをそれぞのページにベタ書きしてませんか?
もし、その共通の部分の修正や変更があった場合、
ページ分だけ修正の作業が増え手間です。その時に便利なPHPの関数があります。
共通の部分を別の「exammple.php」のようなファイルにして、
それを呼び出した方が完全に楽です。修正があっても
「exammple.php」ファイルを編集するだけで全ページに反映されます。PHP関数
下記の2通りの方法です。
<?php include("ファイルのパス"); ?><?php require("ファイルのパス"); ?>「include」か「require」関数を使います。
ファイルのパスのところは
ご自身の環境に合わせて下さい。このコードを書くファイルから見て
どこに共通のファイルを置いたかで確認すればわかります。
- 投稿日:2020-12-06T12:30:14+09:00
【サルが書く】Lazy Eager loadingでもキャストしたい
わきゃ!!
GIGアドベントカレンダー9日目になります!
GIGメンバーの投稿はこちら? から見てみてね!
https://qiita.com/organizations/gig-inc$test = Test::where(['test' => 'test'])->get(); $test->load(['table_name' => function ($query) { $query->orderByRaw('CAST(table_name.column_name AS signed) ASC'); }]);これでいけると思います。
- 投稿日:2020-12-06T12:17:21+09:00
PHPの無名関数を使ってみた
無名関数とは?
公式ドキュメントの引用です。
無名関数はクロージャとも呼ばれ、 関数名を指定せずに関数を作成できるようにするものです。 callable パラメータとして使う際に便利ですが、用途はそれにとどまりません。
https://www.php.net/manual/ja/functions.anonymous.php
callableパラメータ
callableパラメータとしての使い方を見てみます。
<?php // 使用方法1 // クロージャ $double = function($number) { return $number * 2; }; $numbers = [1, 2, 3, 4]; // 各値を2倍にしていく $new_numbers = array_map($double, $numbers); // 結果 [2, 4, 6, 8] // 使用方法2 $numbers = [1, 2, 3, 4]; $new_numbers = array_map(function($number) { return $number * 2; }, $numbers); // 結果 [2, 4, 6, 8]変数の使用
次に、外部から変数を使用する場合の使い方を見てみます。
エラー例
<?php $numbers = [1, 2, 3, 4]; // 倍率を指定する。 $magnification = 3; $new_numbers = array_map(function($number) { return $number * $magnification; }, $numbers); // 結果 PHP Notice: Undefined variable: magnification in......そのまま、変数を使用しようとするとエラーとなります。
そこで、useを使用します。<?php $numbers = [1, 2, 3, 4]; // 倍率を指定する $magnification = 3; $new_numbers = array_map(function($number) use ($magnification) { return $number * $magnification; }, $numbers); // 結果 [3, 6, 9, 12]もちろん参照渡しにすると、変数の値を変更できます。
<?php $numbers = [1, 2, 3, 4]; // 倍率を指定する $magnification = 3; $new_numbers = array_map(function($number) use (&$magnification) { // loopごとに倍率に1を足す $magnification++; return $number * $magnification; }, $numbers); // 結果 [4, 10, 18, 28]型指定
型を指定することができます。
指定していないからといってエラーにはならないですが、安全性、保守性の向上を考えると指定しておいたほうが良いです。
例としてLaravelのEloquentsを使用します。<?php $persons = App\Person::all(); array_filter($persons, function(Person $person) { // IDEによってはgenderに補完が効き、定義元へジャンプできるようになる。 return $person->gender == '男子'; });Laravel使用例
最後にLaravelのCollectionでよく使うので紹介します。
collect(['太郎', null, '次郎']) ->reject(function ($name) { // 名前がない要素を除外 return empty($name); }) ->map(function ($name) { // 末尾に「さん」をつける return $name . 'さん'; }) ->values();※
values()を使うことでキーをリセットしてくれます。公式ドキュメントのweb内検索で
function()と検索するとどのような場面でfunction()を使用しているかわかるので参考になります。
https://readouble.com/laravel/5.8/ja/collections.html
- 投稿日:2020-12-06T07:16:38+09:00
LaravelのBladeテンプレートにCSSやJavascriptを書かない理由はこれかな?
はじめに
この記事は、私個人の意見を文章にしているだけの記事です。
そのため、コードサンプルや、技術の説明のような情報はありません。
もし、コードサンプルや技術説明をお探しであれば、別の方の記事を参考にしてください。Bladeテンプレートでの問題点
以前、次のような記事を書きました。
LaravelのBladeテンプレートをVue.jsっぽく書いてみた
この手法だと、部品毎で分割しやすく、コードも見やすくなると思っていたんですよね。
しかし、実際に開発していて大きな問題が1つ発生しました。
それは、JavascriptのUnitテストができないことです。解決方法の検討
次のように解決方法を検討しました。
1. Javascriptを外部のJSファイルに記述する
これは、Vue.jsやReact等の、フロントエンドのフレームワークを利用しない場合、当たり前の記述方法だと思います。
Laravelでは、Bladeテンプレートファイルの中でCSSやJavascriptを使用することは推奨されていません。
しかし、明確に理由まで記載してある情報ソースを見つけることができなかったので、自分は「盲目的にルールを守ること」はしませんでした。
その結果、問題点が発覚したので、反省が必要ですね。
とはいえ、Bladeテンプレート内に書くJavascriptのコードはほとんどがDOM操作です。
なので、外部ファイルにして単体テストをしたところで、どこまで信頼できるものかわかりませんね。
※DOM操作は他のUIに依存するものが発生しやすいので、結局は判定ロジックや、算出ロジックだけのテストになるのかと。。2. フロントエンドフレームワークを利用する
これは、ある程度大きなプロダクトでは、当たり前の話です。
しかし、技術選定時は、「できるだけ多くの人が参画しやすいようにする」をテーマに、Laravelを選定しました。
というのも、私が参画させていただいている案件は、企業したてのスタートアップ企業様の案件です。
そのため、スペシャリストよりも、フルスタックな人が必要だったんですよね。経営を考慮しての技術選定だったので、現段階でLaravelを選定したことについては仕方なかったと思っています。
しかし、Unitテストができないというのは、品質を担保する指標が1つなくなること。
なので、後々はフロントエンドのスペシャリストに参画していただき、Vue.jsやReactを採用してUnitテストができる状態も必要なのかなとも思います。
その時は、売り上げが拡大されてきた時ですかね。
※余談ですが、VueやReactを使えばSPAも簡単に実現できますし、付加価値は付けやすいでしょう。3. Integrationテストの仕組み構築に尽力する
現段階では、これが一番現実的なのではないかと考えています。
ここまでは、Unitテストのことしか考えていませんでした。
しかし、Integrationテストでしっかりテストをする仕組みがあり、かつ、デバッグがしやすいのであれば問題ないかと思っています。
というのも、Integrationテストでバグが発生したらUnitテストからやり直しになることも多々あります。
また、網羅率100%のUnitテスト用コードを目指すと、テストをすることが目的になる可能性もあるんですよ。
そうなると、無駄なことに工数をかけてしまい、ビジネス的にはよろしくないですよね。
なので、今回はこの方向性が一番有効かと思います。最後に
この記事では、設計による問題の解決手法を検討した内容を記載しました。
今回の場合だと、予算が潤沢にある企業様だとしても「技術選定からやり直し」や、「Unitテストのためだけの設計変更」はするべきではないと思います。
というのも、出戻りばかりしていたら、リリースまでたどり着けないからです。
スタートアップの企業様ですと収益化できない期間が長くなるのは、より苦しいですよね。
そのため、今回は、「出戻りはしない」かつ、「品質は落とさない」のどちらにも配慮して検討してみました。
もし、本当に設計変更やフレームワークの導入をするのであれば、スケールして、予算に余裕が出てきた時で良いと思います。最後まで読んでいただき、ありがとうございます
私は、参画させていただいている企業様に心より感謝し、プロジェクトの成功に尽力致します。そして、自分のスキルアップに尽力することが、お世話になる企業の関係者様の利益になると信じています。
- 投稿日:2020-12-06T03:44:24+09:00
英語のテックジョークで使えそうなワード
良さげなワード
arrays(配列)-> a raise(昇給)
bugs(バグ) -> bugs(虫)
dark(IDEなどのダークモード) -> dark(暗闇)
table(DB table) -> table(机)
server(サーバ) -> server(レストラン店員)
cache(キャッシュ) -> cash(現金)
Java(プログラミング言語) -> java(コーヒー)今日の名言
I hope my death makes more cents than my life.(私はこの人生よりも硬貨な死を望む)
※ make cents(小金を稼ぐ)とmake sense(意味をなす)をかけている-アーサー(映画ジョーカーの主人公)
- 投稿日:2020-12-06T00:25:58+09:00
Symfony で composer install --no-dev が失敗する
業務でSymfonyを利用していますが、先日、運用環境のデプロイでエラーが発生したので、備忘録を兼ねて投稿します。
- Symfony 3.0.1
発生したエラー
$ composer install --no-dev Uncaught exception 'Symfony\Component\Debug\Exception\FatalErrorException' with message 'Error: Uncaught Symfony\Component\Debug\Exception\FatalThrowableError: Fatal error: Class 'Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle' not found in ...原因
Composer scripts に以下のような記載があり、
copmoser install時にcache:clearが実行されますが、デフォルトでは--env devで実行されます。... "scripts": { "post-install-cmd": [ "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::prepareDeploymentTarget" ], "post-update-cmd": [ "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::prepareDeploymentTarget" ] }, ...このため、存在しないバンドルに対してアクセスしようとしてしまい、エラーとなってしまいます。
対応方法
今回はデプロイコマンドで
composer installしていたので、SYMFONY_ENV=prodで実行してあげるだけでした。SYMFONY_ENV=prod copmoser install --no-dev手動でキャッシュクリアしている場合は、
cache:clearにも--env prodを指定してください。./bin/console cache:clear --env prod同じエラーで悩んでいる方がいましたら、ご参考いただければと思います。
参照














