- 投稿日:2020-02-25T23:10:13+09:00
【Docker】チュートリアル(Python+php)
はじめに
Dockerを勉強するにあたり、素晴らしいチュートリアル動画があったのでそれを元に自分で手を動かして見ました。リンク先の動画は英語ですので、日本語で少し補足をしながら進めていきます。初心者のため認識の誤りがあるかもしれませんので、その際はコメント頂けると幸いです。
事前にdocker、docker-composeのインストールが必要です。
元動画
productディレクトリにはjsonを返すAPIをpythonで用意します。
websiteディレクトリには画面を描画するphpを用意します。product
まずはdocker_tutorial配下にproductディレクトリを作成します。
$ mkdir productそしてシンプルなPythonコードを作成します。flaskとflask_restfulというライブラリを使用します。
api.pyfrom flask import Flask from flask_restful import Resource, Api app = Flask(__name__) api = Api(app) class Product(Resource): def get(self): return{ 'products': [ 'Ice cream', 'Chocolate', 'Fruit' ] } api.add_resource(Product, '/') if __name__ == '__main__': app.run(host='0.0.0.0', port=80, debug=True)ローカル環境にpythonをインストールしている場合は、pip installなどでライブラリをインストールしますが、Dockerを使用した場合は個別にインストールする必要はありません。その代わりにコンテナ起動時に必要となるライブラリなどを以下のテキストファイルに記載するか、後述のDockerfile内にpip installのコマンドを記載します。バージョンはライブラリの公式サイトなどを参照して使用したいバージョンを記載します。
requirements.txtFlask==0.12 flask-restful==0.3.5次にDockerfileを作成します。
DockerfileはDocker上で動作させるコンテナの構成情報を記述するためのファイルです。FROM python:3-onbuild COPY . /usr/src/app CMD ["python", "api.py"]FROM
FROMにはベースとするDockerイメージを指定します。また「onbuild」を記載することで、Dockerfile を使って docker build をするとき、Dockerfile 内で pip install を書かなくても Dockerfile と同じディレクトリに requirements.txt があると自動的にインストールをするようになっています。COPY
COPYにはローカルファイルをDockerイメージのどこにコピーするかを記載します。今回の場合はproductディレクトリをDocker上の/usr/src/app内にコピーします。CMD
CMDにはコンテナ起動時に実行するコマンドを記載します。次にdocker-compose.ymlを作成します。docker-composeには
- Dockerイメージをビルドするための情報(使用するDockerfile、イメージ名など)
- コンテナ起動するための情報(ホストとの共有ディレクトリ設定やポートフォワードなどの起動オプションなど)
- 使用するDockerネットワーク
などを記載します。
docker-compose.ymlversion: '3' services: product-service: build: ./product volumes: - ./product:/usr/src/app ports: - 5001:80version
docker-composeで使用するバージョンを記載します。現在はバージョン3が最新ですので、3を指定します。services
アプリケーションを動かすための各要素をServiceと読んでいます。各サービスをネストしてこちらに記載します。今回の場合は「product-service」というサービスを起動します。build
指定したディレクトリにあるDockerfileでコンテナを起動します。今回の場合はproductディレクトリのDockerfileでコンテナを起動します。volumes
ローカリのディレクトをコンテナのディレクトにマウントします。今回の場合はproductディレクトリをコンテナ上の/usr/src/appディレクトリにマウントします。ports
Dockerを立ち上げるポート番号を記載します。今回は5001番ポートで立ち上げます。それではDockerを起動
$ docker-compose up起動が完了したら、localhost:5001にアクセスします。
以下のような画面が表示されたら問題なく起動しています。
ここまでで、簡単にPython、Flaskの環境ができることが分かります。この状態でapi.pyを以下のように変更し、保存します。
api.pyclass Product(Resource): def get(self): return{ 'products': [ 'Ice cream', 'Chocolate', 'Fruit', 'Eggs' ] }再読み込みを行うと、表示内容も更新されます。コンテナを再起動しなくてもマウントしてくれます。
ここまできたらコンソール上で「control+C」を押して、dockerを一旦停止します。
website
次はdocker_tutorial配下にwebsiteというディレクトリを作成します。こちらにはphpのファイルを作成します。
http://product-serviceの結果をループして表示する処理です。
ここでいうproduct-serviceはdocker-composeに記載したサービス名が該当します。index.php<html> <head> <title>My Shop</title> </head> <body> <h1>Welcome to my shop</h1> <ul> <?php $json = file_get_contents('http://product-service'); $obj = json_decode($json); $products = $obj->products; foreach($products as $product) { echo "<li>$product</li>"; } ?> </ul> </body>次にdocker-compose.ymlを以下のように変更します。
docker-compose.ymlversion: '3' services: product-service: build: ./product volumes: - ./product:/usr/src/app ports: - 5001:80 website: image: php:apache volumes: - ./website:/var/www/html ports: - 5000:80 depends_on: - product-servicewebsite
servicesにwebsiteというサービスを追加します。image
imageを記載することで、Docker Hub上に用意されている既存のイメージを使用することができます。buildとimageは同時に指定することはできません。depends_on
サービス同士の依存関係を記載します。今回の場合、websiteが実行されるより前にproduct-serviceが実行されるようになります。docker-compose.ymlにproduct-serviceがない場合はエラーになります。再度Dockerを起動します。
$ docker-compose up起動が完了したらlocalhost:5000にアクセスします。
以下のような画面が表示されていれば成功です。さいごに
Dockerを使用することで、簡単にPython+phpの環境を作ることができました。私もDocker初心者のためこれからも引き続き学習していきたいと思います。
おまけ
-dをつけることでバックグラウンドでコンテナを立ち上げることができます。
$ docker-compose up -d Starting docker_tutorial_product-service_1 ... done Starting docker_tutorial_website_1 ... donepsコマンドで起動中のコンテナの一覧が確認できます。
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 24de043f94d9 php:apache "docker-php-entrypoi…" 10 minutes ago Up About a minute 0.0.0.0:5000->80/tcp docker_tutorial_website_1 da62924c1154 docker_tutorial_product-service "python api.py" 32 minutes ago Up About a minute 0.0.0.0:5001->80/tcp docker_tutorial_product-service_1stopでコンテナを停止することができます。
$ docker-compose stop Stopping docker_tutorial_website_1 ... done Stopping docker_tutorial_product-service_1 ... done $ docker ps ONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES参考
- 投稿日:2020-02-25T22:53:49+09:00
Laravelのフォーム、バリデーション・入力値の保持(Blade書き方メモ)
Laravelのフォームについての個人的なメモです。
Bootstrapのフォーム
・まとめたい要素form-control(label、option、selectなど)は
form-gruop
にまとめる。バリデーション
・
@error
ディレクティブを使う。引数にはチェックする項目を。
・フォームタグ内にrequired
属性を追加することで、ブラウザによるバリデーションを行う。入力値の保持
・
old()
と三項演算子でテーブルに登録されている値を入れる。
・autocomplete
には自動入力する値を設定。input
<div class="form-gruop"> <label for="title">Title</label> <input id="title" class="form-control @error('title') is-invalid @enderror" name="title" required autocomplete="title" value="{{ old('title') ? : $articles->title }}"> @error('title') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div>textarea
<div class="form-gruop"> <label for="Text">Text</label> <textarea class="form-control @error('text') is-invalid @enderror" name="text" required autocomplete="text" rows="4">{{ old('text') ? : $articles->text }}</textarea> @error('text') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div>select
<div class="form-gruop"> <label for="num">Number</label> <select name="num" id="genre" class="form-control @error('num') is-invalid @enderror" required> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> <option value="4">4</option> </select> @error('num') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div>
- 投稿日:2020-02-25T20:33:57+09:00
ユーザーアカウント登録内容の変更の手順
はじめに
laravelでAuthを実装すると簡単にユーザー登録が可能になりますが、登録内容の変更は別途実装する必要があります。
本投稿では名前、メールアドレスを変更する手順について記載します。
パスワードの変更手順については本投稿では触れませんが、下記サイトがかなり参考になります。
[Laravel 5.7]パスワード変更フォームの作り方
- バージョン php: 7.2 laravel: 5.8
- Authは実装済みであること
Controllerの編集
UserControllerの作成
$ php artisan make:controller Admin/UserControllerUserControllerの編集
User.Controller.php<?php namespace App\Http\Controllers\Admin; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Auth; //追加 class UserController extends Controller { //下記を追加 //userデータの取得 public function index() { return view('user.index', ['user' => Auth::user() ]); } //userデータの編集 public function edit() { return view('user.edit', ['user' => Auth::user() ]); } //userデータの保存 public function update(Request $request) { $user_form = $request->all(); $user = Auth::user(); //不要な「_token」の削除 unset($user_form['_token']); //保存 $user->fill($user_form)->save(); //リダイレクト return redirect('user/index'); } }Viewの作成と編集
ユーザー登録内容の表示
user/index.blade.php<div class="container m-5"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">ユーザー登録内容</div> <div class="card-body"> <div class="form-group"> <label for="name"> 名前 </label> <div> <input class="form-control" value="{{ $user->name }}"> </div> </div> <div class="form-group"> <label for="email"> email </label> <div> <input class="form-control" value="{{ $user->email }}"> </div> </div> <a href="{{ action('Admin\UserController@edit') }}"><button class="user-btn">ユーザー登録内容の編集</button></a> </div> </div> </div> </div> </div>ユーザー情報の変更
user/edit.blade.php<div class="container m-5"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">ユーザー登録内容の変更</div> <div class="card-body"> <form method="POST" action="{{ action('Admin\UserController@update') }}"> <div class="form-group"> <label for="name"> 名前 </label> <div> <input type="text" name="name" class="form-control" value="{{ $user->name }}"> </div> </div> <div class="form-group"> <label for="email"> email </label> <div> <input type="text" name="email" class="form-control" value="{{ $user->email }}"> </div> <button type="submit" class="user-btn">変更</button> {{ csrf_field() }} </form> </div> </div> </div> </div> </div>先ほどのユーザー登録内容の表示画面で「ユーザー登録内容の編集」ボタンをクリックすると下記画面に移る
Routingの設定
user/index.blade.phpRoute::group(['middleware' => 'auth:user'], function() { Route::get('user/index', 'Admin\UserController@index'); Route::get('user/edit', 'Admin\UserController@edit'); Route::post('user/edit', 'Admin\UserController@update'); });
- 投稿日:2020-02-25T20:33:57+09:00
ユーザーアカウント登録内容の変更の手順(パスワードは除く)
はじめに
laravelでAuthを実装すると簡単にユーザー登録が可能になりますが、登録内容の変更は別途実装する必要があります。
本投稿では名前とメールアドレスを変更する手順について記載します。パスワードは少し難しくなるので別途投稿する予定です。
- バージョン php: 7.2 laravel: 5.8
- Authは実装済みであること
Controllerの編集
UserControllerの作成
$ php artisan make:controller Admin/UserControllerUserControllerの編集
User.Controller.php<?php namespace App\Http\Controllers\Admin; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Auth; //追加 class UserController extends Controller { //下記を追加 //userデータの取得 public function index() { return view('user.index', ['user' => Auth::user() ]); } //userデータの編集 public function edit() { return view('user.edit', ['user' => Auth::user() ]); } //userデータの保存 public function update(Request $request) { $user_form = $request->all(); $user = Auth::user(); //不要な「_token」の削除 unset($user_form['_token']); //保存 $user->fill($user_form)->save(); //リダイレクト return redirect('user/index'); } }Viewの作成と編集
ユーザー登録内容の表示
user/index.blade.php<div class="container m-5"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">ユーザー登録内容</div> <div class="card-body"> <div class="form-group"> <label for="name"> 名前 </label> <div> <input class="form-control" value="{{ $user->name }}"> </div> </div> <div class="form-group"> <label for="email"> email </label> <div> <input class="form-control" value="{{ $user->email }}"> </div> </div> <a href="{{ action('Admin\UserController@edit') }}"><button class="user-btn">ユーザー登録内容の編集</button></a> </div> </div> </div> </div> </div>ユーザー情報の変更
user/edit.blade.php<div class="container m-5"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">ユーザー登録内容の変更</div> <div class="card-body"> <form method="POST" action="{{ action('Admin\UserController@update') }}"> <div class="form-group"> <label for="name"> 名前 </label> <div> <input type="text" name="name" class="form-control" value="{{ $user->name }}"> </div> </div> <div class="form-group"> <label for="email"> email </label> <div> <input type="text" name="email" class="form-control" value="{{ $user->email }}"> </div> <button type="submit" class="user-btn">変更</button> {{ csrf_field() }} </form> </div> </div> </div> </div> </div>先ほどのユーザー登録内容の表示画面で「ユーザー登録内容の編集」ボタンをクリックすると下記画面に移る
Routingの設定
user/index.blade.phpRoute::group(['middleware' => 'auth:user'], function() { Route::get('user/index', 'Admin\UserController@index'); Route::get('user/edit', 'Admin\UserController@edit'); Route::post('user/edit', 'Admin\UserController@update'); });
- 投稿日:2020-02-25T20:16:01+09:00
【Laravel】アクセサ、ミューテタの基本のキ
はじめに
今回はアクセサとミューテタの基本について解説します。
そもそもミューテタとは?
そもそもミューテタってなんなんでしょうか?
簡単にいうとデータをデータベースに保存する時に走る一定の処理のことです。そもそもアクセサとは?
そもそもアクセサとはなんでしょうか?
簡単にいうとデータをデータベースから取得する時に走る一定の処理のことです。使用例
例えば、都道府県をデータベースに保存する時を考えます。
そのまま文字データとして都道府県を保存することもありますが、多くの場合は整数に置き換えて保存します。(1が北海道、2が青森など都道府県それぞれに対応させた整数を設定する。こうする理由は文字データより圧倒的に整数の方がデータ容量が少ないからデータベースを圧迫しないからです。)
しかし、データベースにデータを保存する時に変換して、取得した時も変換してと毎回処理を書くのはめんどくさい。。。無駄なコードが増えるから邪魔!!!
できれば、アプリケーションで自動に処理してほしいですね。
そこで、活躍するのがアクセサとミューテタです!!
今回は基本のキということで簡単な例を扱っていきます。
最低限必要なエッセンスだけ説明します。
では、スターーーーート!!アクセサとミューテタでなにができるの?
Laravelを使ったことがある人であれば、実はミューテタとアクセサはもう使っているんです!!
Laravelではcreated_at、updated_atは自動的にCarbonインスタンスに変換されて使うようになっています。
だから、日付に関しては簡単に実装することができます。(日付については最後に記載しています)
しかし、今回は自分で独自にミューテタ、アクセサの実装する方法について説明します。アクセサの実装方法
アクセサはモデル内に定義します。
アクセサの命名規則はgetFooAttribute
アクセサを設定したいカラム名が「キャメルケース」でHogeの場合、カラムがあるモデルクラスにgetHogeAttributeメソッドを作成します。
アクセサ定義例:first_nameカラムにアクセサ定義
Userモデルクラスに下記を追加
下記のコードでfirst_nameカラムを取得する時にgetHogeAttribute
(頭文字を大文字に変える)が実行されます。app/User.php<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { public function getFirstNameAttribute($value) { return ucfirst($value); } }
getFirstNameAttribute
の引数$value
がデータベースから取得したカラムの中身になります。
ちなみにucfirst($value)
が頭文字を大文字にするメソッドですアクセサを実行する
上記のコードが正しく動いていれば、下記のように単純に
first_name
カラムを引っ張ってくるだけで自動でgetFirstNameAttribute
が実行されます。$user = App\User::find(1); $firstName = $user->first_name;以上がアクセサの使い方です。
ミューテタの実装方法
ミューテタもアクセサと同様にモデル内に定義します。
ミューテタの命名規則
これもアクセサと同様に、アクセスしたいカラム名が「キャメルケース」でFooの場合、カラムがあるモデルクラス内にsetFooAttributeメソッドを作成します。
ミューテタ定義例:first_nameカラムにミューテタ定義
下記のコードで
first_name
カラムの保存時に自動でsetFirstNameAttribute
が実行されます。app/User.php<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { public function setFirstNameAttribute($value) { $this->attributes['first_name'] = strtolower($value); } }
value
が設定しようとしている中身になります。
strtolower
が全て小文字に変えるメソッドです。
setFirstNameAttribute
の引数$value
が設定しようとしている値です。上記のメソッドはUserインスタンスのattributes
プロパティのfirst_name
に小文字にした$value
が代入されるようになっています。ミューテタを実行してみる
下記の場合2行目の
first_name
カラムにSallay
を設定しようとした時に、setFirstNameAttribute
メソッドが呼び出され、Sallay
の値が渡され、ミューテタが実行されます。
実は保存時にミューテタが実行されるわけではなく、インスタンスに値を設定する時にミューテタは実行されることに注意です$user = App\User::find(1); $user->first_name = 'Sally';おまけ:日付ミューテタ
おまけに日付ミューテタについて記述します。
自分で作成した日付のカラムをCarbonインスタンスで扱い時、設定は簡単です。
定義する場所はミューテタと同じモデルの中に定義します。
その中にデフォルトである$dates
をオーバーライドしますapp/User.php<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { protected $dates = [ 'created_at', 'updated_at', 'deleted_at' ]; }これにより、データベースから
delete_at
を取得すると自動でCarbonインスタンスに変換されているため、
自由にCarbonにあるメソッドを使うことができます。
Carbonについてはこちらでざっとまとめているので、是非!!以上です!!!
ここまで読んでいただきありがとうございました!!
疑問、気になるところがございましたら、質問、コメントよろしくお願いします!!!
- 投稿日:2020-02-25T18:06:56+09:00
Laravel artisanコマンドのエラー Could not open input file: artisan
前回の記事の続きのような形で、MAMP環境でのlaravelの環境構築の記事でも書こうかと思っていたのですが、すでに良記事がゴロゴロ転がっているので必要ないかなと思ってるのですが、どうでしょう?
(訳 : 需要がありそうならMAMPのインストール、Laravelのプロジェクト作成、データベース周りの設定もやろうかと思います)さて今日から
今日から記事の量産体制に入ろうかと思います。
Laravelを中心に小さいエラーの対処法から、覚えておくと便利なコードの書き方までガンガン書き記していきます。
読み物としては物足りないかもしれませんが、人は思わぬところで躓くものなので……。
これからの私の記事はそういった人達の一助になればと考えています。今回の題
今回の題材は、タイトルの通りです
Could not open input file: artisan
という、たまーーに目にする小さなエラーです。
解決策
簡単なことで、
「artisan」コマンドを叩くディレクトリが間違っています。
「artisan」コマンドはLaravelのプロジェクトディレクトリで叩きます。
……解決。
終わりに
こんな感じの小さな記事を打ちまくります。
よろしくお願いします。
- 投稿日:2020-02-25T16:12:48+09:00
PHPer脳の人がJavaScriptでループを回す
毎回for文書くたびに調べてるので
配列のforeach
phpだとこんな感じのやつ
$array = [1, 2, 3]; foreach($array as $a){ echo $a; } // 1 // 2 // 3jsだとこう
const array = [1, 2, 3]; for(let a of array){ console.log(a); } // 1 // 2 // 3連想配列のforeach
phpだとこんな感じのやつ
$array_assoc = [ 1 => 'one', 2 => 'two', 3 => 'three' ]; foreach($array_assoc as $k => $v){ echo $k . ' is ' . $v . "\n"; } // 1 is one // 2 is two // 3 is threejsだとこう
const array_assoc = { 1 : 'one', 2 : 'two', 3 : 'three' }; for(let [k, v] of Object.entries(array_assoc)){ console.log(k + ' is ' + v + "\n"); } // 1 is one // 2 is two // 3 is three※まあ、jsのObjectは正確には連想配列ではないけど
- 投稿日:2020-02-25T14:26:26+09:00
【Wordpress】繰り返しフィールドでコンテンツを表示させる
ACFの繰り返しフィールドでコンテンツを表示するには
なお、当記事では以下のように各々命名するとします
フィールド名:
detailsサブフィールド:
A.画像(ここではimgと名付ける)
B.タイトル(ここではtitleと名付ける)
C.詳細ページへのURL(ここではpage_urlと名付ける)1.まずフィールド名からフィールド内のコンテンツを取得します
single.php(例)<?php $details = get_field('details'); ?>2.次に各サブフィールドコンテンツを変数に代入します
single.php<?php foreach ($details as $detail) { $img=$detail['img']; $title=$detail['title']; $page_url=$detail['page_url']; ?>3.それぞれ埋め込みたい先に変数を使ってphpを記述します
single.phpEx) <a href="<?php echo $page_url; ?>">参照したいリンクのページの概要</a>4.Foreach文の終了宣言をします
single.php<?php } ?>
- 投稿日:2020-02-25T13:26:42+09:00
PHPの基礎
はじめに
最近、phpの勉強をしています。progateでphpの学習を一通り終えましたが、あまり理解できていなかったので、記事にして、覚えにくかった場所を自分なりにまとめてみようと思います。
基本のきの部分ですので、phpこれから勉強しようかなと思われている方の役に立てばと思います。配列
sample.phpノーマルの配列 <?php //$scoresという配列を定義 $scores = array('70', '90', '80'); //$colorsの一番目の要素を出力 echo $scores[0]; //結果、70を出力 //$scoresに要素を追加 $scores[] = '100'; //$scoresに追加した要素を出力 echo $scores[3]; //結果、100を出力 ?> 連想配列(要素にインデックス以外にキーを持たせる) <?php //$scoresという連想配列を定義 $scores = array('数学' => '70', '英語' => '90', '国語' => '80'); //インデックスではなくキーで要素を出力 echo $scores['英語']; //結果、90を出力 //国語(80)に5を足し合わせる $scores['国語'] += 5; echo $scores['国語'];//結果、85を出力 ?>繰り返し(ループ)処理
for文
sample.php<?php for(初期値を定義; ループの条件; 変数の更新){ 繰り返す処理; } ?> 例文 <?php //1~100までの数字を出力させる。 for($i = 1; $i <= 100; $i ++){ echo $i.'<br>'; //'<br>'は、改行を意味する } ?>while文
sample.php<?php 初期値を定義; while(ループの条件){ 繰り返す処理; 変数の更新; } ?> 例文 <?php //2~100までの数字で2の倍数のみを出力させる。 $i = 2; while($i <=100){ echo $i.'<br>'; $i +=2; } ?>break文(ループを強制的に中断する)
sample.php<?php //1~5までの数字を出力させる。 for($i = 1; $i <= 10; $i ++){ if($i > 5){ break; //$iの値が6になった時点でループを強制的に中断する命令 } echo $i; } ?>continue文(ある条件だけスキップし、ループそのものは継続)
sample.php<?php //3の倍数以外の数字(100まで)を出力させる。 for($i = 1; $i <= 100; $i ++){ if($i % 3 == 0){ continue; //$iの値が3の倍数になった時だけスキップ ループそのものは、継続 } echo $i; } ?>foreach文
sample.php<?php 配列の定義; foreach(配列もしくは連想配列 as 値変数){ 繰り返す処理; } ?> 例文 <?php //配列$dataを定義 $data = array('東京','大阪','名古屋'); //先頭のデータから順に繰り返し出力させる。 foreach($data as $value){ echo $value; //結果、東京、大阪、名古屋を出力 } ?>関数
代表的な関数
sample.phpstrlen-要素の文字数を数える <?php //変数$strを定義 $str = 'hello'; //変数$strの文字数を出力 echo strlen($str); //結果、5を出力 ?> count-配列の要素の数を数える <?php //配列$arrayを定義 $array = array('HTML', 'CSS', 'PHP'); //変数$arrayの要素数を出力 echo count($array); //結果、3を出力 ?> rand-ランダムな整数を出力 <?php //10~15のランダムな整数を出力 echo rand(10,15); //結果、12を出力 ?>自作の関数
sample.phpシンプルな関数 <?php function $関数名(){ 処理; } ?> 例文 <?php //関数hello()を定義 function hello(){ echo' Hello, world'; } //関数hello()を呼び出す hello(); //結果、Hello, worldを出力 ?> 戻り値と引数を用いて <?php function 関数名(引数){ return 処理; } ?> 例文 <?php //関数getCircleArea()を定義 function getCircleArea($radius){ return $radius * $radius * 3; //今回は、5 * 5 * 3になってます。 } //関数getCircleArea()を呼び出し、$circleAreaに代入 $circleArea = getCircleArea(5); //$circleAreaを出力 echo $circleArea; //結果、75を出力 ?>最後に
今回は、構文ばっかり並べてみました。まとめながら書くことは、頭の整理に繋がり、いい感じです。
理解が遅いので早く理解できるようになりたいです。
また、早くLaravel習得してポートフォリオを作成できたらと思います。
この記事を見ておかしいんじゃないのとかありましたらどんどん指摘してください、よろしくお願いします。
- 投稿日:2020-02-25T11:35:48+09:00
【Symfony】サービスコンテナを実装する
サービスとは?
サービスコンテナは、オブジェクト間の依存関係についてDependency Injection(依存性注入) するための仕組みです。
サービスにはロジックを実装します。
サービスを必要とするオブジェクトに対して、外から渡すことにとって、疎統合な設計を実現します。...と、サービスの概念だけを語っても抽象的でなんだかよくわからないと思うので、実際にサービスコンテナを実装する流れを追っていきます。
サービスコンテナを実装する
はじめに、あるECサイトを仮定しましょう。
このサイトでは、毎年2月はバレンタインセールを行うので、現在がその期間内か判定するロジックを実装することになりました。
以下のコードはそのままコントローラに実装した例です。
namespace App\Controller; use App\Entity\Product; use Symfony\Component\Routing\Annotation\Route; class HelloController extends AbstractController { /** * @Route("/", name="hello_index") */ public function indexAction() { $em = $this->getDoctrine()->getManager(); $products = $em->getRepository(Product::class)->findAll(); // ここが期間内が確認するロジック $startDate = new \Datetime('02-01'); $endDate = new \Datetime('02-28'); $now = new \Datetime(); $isSaleTerm = ($now >= $startDate $$ $now <= $endData); return $this->render('hello/index.html.twig', [ 'products' => $products, 'isSaleTerm' => $isSaleTerm ]; }最も単純に実装するとなると、このような形になると思います。
コントローラにドメインロジックを実装する問題点
今がセール期間中が否かというロジックは、ドメイン知識に相当します。
コントローラの責務は、URLをマッピングし、リクエストおよびレスポンスをハンドリングすることであり、ドメイン知識には関心がありません。どのような状況がセール期間にあたるのか小難しいことは考える必要はなく、とにかくいまがセール期間であるという情報さえ得られればよいのです。
つまるところ、ロジックを抽象化するということです。では実際に、コントローラがドメインの知識を所有しているとどのような問題点が存在するのかみていきます。
変更が難しくなる
セール期間かどうか判定するロジックは、indexページ限らず、おそらくサイト上のあらゆる箇所で登場すると考えられます。
また、コントローラーに限らずFormTypeでも使用したくなるかもしれません。そのためのもっとも簡単な方法は、ロジックをコピペして使い回すことでしょう。
// コピペで解決された実装 newAction() { $startDate = new \Datetime('02-01'); $endDate = new \Datetime('02-28'); $now = new \Datetime(); $isSaleTerm = ($now >= $startDate $$ $now <= $endData); } editAction() { $startDate = new \Datetime('02-01'); $endDate = new \Datetime('02-28'); $now = new \Datetime(); $isSaleTerm = ($now >= $startDate $$ $now <= $endData); } deletAction() { $startDate = new \Datetime('02-01'); $endDate = new \Datetime('02-28'); $now = new \Datetime(); $isSaleTerm = ($now >= $startDate $$ $now <= $endData); }もうすでにお気づきの方もいらっしゃるかもしれませんが、いまコピペで実装したことで大きな問題が生まれました。
例えば、セール期間が2月から3月に変更になることは容易に想像できます。
その場合には、ロジックが記述された箇所を漏れなく変更する必要があります。実際にすべての修正が完了したかどうかの判断は難しく、バグ発生の温床となります。
日付の変更程度の修正ならまだ定数を使うなどで対応できるかもしれませんが、例えばセールの対象はプレミアム会員限定にする、なんて変更が生じたらその手は利用できません。
// セールの対象はプレミアム会員のみ $startDate = new \Datetime('02-01'); $endDate = new \Datetime('02-28'); $now = new \Datetime(); $isSaleTerm = ($now >= $startDate $$ $now <= $endData); $isSale = ($isSaleTerm && $this->getUser()->isPremium());テストが書きにくい
コントローラにロジックが入り込むと、テストが書きにくい実装となってしまいます。
現在の日付をコントローラ内でnewして保持しているため、本当に2月にtrueを返すかどうかのテストを書くためには、2月になるまで待たなければいけません。
そうでなくてもコントローラの中のテストを単体テストするのはどうしても難しく、機能テストになってしまいます。
サービスコンテナの実装
それでは実際に、コントローラからロジックを抜き出して、サービスコンテナを実装します。
サービスクラスの作成
src/service/
フォルダを作成して、その配下にSalesService.php
ファイルを作成します。// src/service/SalesService.php namespace App\Service; class SalesSerivce { }メソッドを実装しましょう。先程のロジックを使用しますが、現在日付は外部から受け取るようにします。
src/service/SalesService.phpnamespace App\Service; class SalesSerivce { public function isSaleTerm(\Datetime $now): bool { $startDate = new \Datetime('02-01'); $endDate = new \Datetime('02-28'); return ($new >= $startDate && $new <= $endDate); }設定ファイルの作成
今作成したサービスを利用するためには、コンテナに登録する必要があります。
これを
Resouces/config/service.yml
に記述します。Resouces/config/service.ymlservices: app.sales: class: src\Services\SalesService arguments: [ ]コントローラからサービスを呼び出す
それでは、作成したサービスをコントローラから呼び出してみましょう。
コントローラではサービスコンテナを呼び出すことができます。
namespace App\Controller; use App\Entity\Product; use Symfony\Component\Routing\Annotation\Route; class HelloController extends AbstractController { /** * @Route("/", name="hello_index") */ public function indexAction() { $em = $this->getDoctrine()->getManager(); $products = $em->getRepository(Product::class)->findAll(); // ここが期間内が確認するロジック - $startDate = new \Datetime('02-01'); - $endDate = new \Datetime('02-28'); - $now = new \Datetime(); - - $isSaleTerm = ($now >= $startDate $$ $now <= $endData); + $SalesService = $this->container->get('app.sales'); + $isSaleTerm = $SalesService->isSaleTerm(new \Datetime()); return $this->render('hello/index.html.twig', [ 'products' => $products, 'isSaleTerm' => $isSaleTerm ]; }サービスコンテナを利用するメリット
疎統合になる
ロジックをサービスとして抜き出したことによって、疎統合になりました。
もし今後セール期間が変更になった場合には、SalesService
だけ変更すれば修正が完了するという確証が得られます。テストがしやすくなる
疎統合になったことで、テストが書きやすくなりました。
実際にサービスに対するテストを記述してみましょう。Tests/Service/SalesServiceTest.php<?php namespace app\Tests\Controller; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class SaleseServiceTest extends WebTestCase { private $saleService; // ここでサービスコンテナからサービスを取得 public function setUp():void { $kernel = self::createKernel(); $kernel->boot(); $container = $kernel->getContainer(); $this->salseService = $container->get('app.sales'); } public function test期間内ならtrueを返す() { $date = new \DateTime('2020-02-01'); $this->assertTrue($this->SalesService->isSaleTerm($date)); } public function test期間外ならfalseを返す() { $date = new \Datetime('2020-03-01'); $this->assertFalse($this->SalesService->isSaleTerm($date)); } }このように、現在日付を外部から渡すことによって、任意の日付のテストを記述することができました。
また、テストの観点は一つのロジックに着目したもとのして記述できていることがわかります。
おわりに
ざっくりとSymfonyのサービスコンテナについて書いてみました。
Symfonyを使ってみて、サービスコンテナ以外にも部品の再利用性が高く、疎統合な設計にしやすい点がメリットに感じられますね。
- 投稿日:2020-02-25T09:57:29+09:00
WSL2+docker+PHPのWindows開発環境構築(1) WSL2編
本記事はWindows Insider Previewを使っています。
ご利用は計画的にヾ(・ω<)ノ" 三三三● ⅱⅲ コロコロ♪
------------------- ↓ 余談はここから ↓-------------------
関連記事
* Bash on Ubuntu on Windowsを使う(1)
* Bash on Ubuntu on Windowsを使う(2):初期設定
* Bash on Ubuntu on Windowsを使う(3):WindowsからLinuxを使う
* WSL2+docker+PHPのWindows開発環境構築(1) WSL2編
* WSL2+docker+PHPのWindows開発環境構築(2) docker編仕事でdockerを使う機会が増えた。
なぜか知らんがMacを使わされるので大変面倒くさい。
(どこが便利なんだ?これ。)現在の仕事はPCを持ち込みができるらしいので、
この際Windows上で開発環境を作ろうと思う。数年前に登場したWSLというものがあって、
それについて記事も書いているが、
順調に進化してこのたびver2となる。
(リリースは令和2年5月頃)
そのときを見据えて今からPreview版を使って環境を作ってみよう。注意
- Preview版がStableになっても記事を書き換えたりしないのでご注意を。
- 記事が長くなったのでdocker編は別記事で。
------------------- ↓ 本題はここから ↓-------------------
事前準備
・Windows Update
前提としてWindowsを最新にしておく。
(https://www.microsoft.com/ja-jp/software-download/windows10
(執筆時November Update 2019)・WSLを有効にする
PowerShellを管理者モードで起動
1. 「スタート」「Windows PowerShell」「Windows PowerShell」を右クリック
2. 「管理者として実行」
以下のコマンドを実行
PS> dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart PS> dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /noresttそしてPCを再起動! (ここ重要)
Windows Insider Previewをインストール
・Previewプログラムに参加
- Windows Preview Programに参加
- Windows Insider の設定で「開始する」を押下
- 「アカウントリンク」より自身のアカウントと紐付ける
- 「スロー」を選択
- 「再起動」
- 「設定」「更新とセキュリティ」「更新プログラムのチェック」を押下
- すべてのインストール完了後「再起動」
- (「Windows 10、バージョン????の機能更新プログラム」がインストールされるまで6,7を繰り返す)
・バージョンの確認
「コマンドプロンプト(またはPowerShell)」で以下のコマンドを実行
$ ver Microsoft Windows [Version 10.0.19041.84]数字が10.0.18917以上なら成功
WSL2のインストール
*デフォルト設定をWSL2にする
wslコマンドのデフォルト設定をWSL2になるようにしておく。
前置きが長くなったがここからが本番。C:¥> wsl --set-default-version 2・Linuxを取得
windows Storeで好きなLinux Distributionをダウンロードしておく
ここではベタなUbuntuを選択。
https://www.microsoft.com/ja-jp/p/ubuntu/9nblggh4msv6
(私はPengwin派)状態確認
C:¥> wsl -l -v NAME STATE VERSION * Ubuntu Stopped 2あとは普通にUbuntuを使うだけ。
C:¥> wsl $ cat /etc/os-release NAME="Ubuntu" VERSION= 〜〜〜〜〜〜さて、次はdockerだ。
------------------- ↓ 後書はここから ↓-------------------
WSL1からWSL2に変換する
すでにに手持ちのLinuxを使っていて、
WSL2に変えたいときがあるだろう。
変換する手段はあるので慌てずに。状態の確認
C:¥> wsl -l -v NAME STATE VERSION * WLinux Stopped 1 Ubuntu Stopped 2WSL2に変換
C:¥> wsl --set-version WLinux 2 変換中です。この処理は数分かかることがあります...もう一度状態を確認してみる。
C:¥> wsl -l -v NAME STATE VERSION * WLinux Stopped 2 Ubuntu Stopped 2WSL2のリセット方法
「アプリと機能」のアプリの詳細から「リセット」するのが定番だったけど、
コマンド一つでできるようになるみたい。C:¥> wsl --unregister ubuntuコマンドの後、「スタート」からLinuxを起動すると初期状態になっている。
ターミナルをWindows Terminalに変える
個人的におすすめしたいターミナルがWindows Termminal。
https://www.microsoft.com/ja-jp/p/windows-terminal-preview/9n0dx20hk701
タブ機能が標準で付いている。powerline対応フォントを入れる
powerlineというPython製のシェルがあるのだが、
それに対応したフォントがある。
https://github.com/powerline/fonts
install.ps1シェルをpowershellで走らせるとフォントがインストールされる
- 投稿日:2020-02-25T02:09:11+09:00
PHPで独自フレームワークを作ってサイトも作ってみた
Lalavelが重いと感じたのでPHP7でフレームワークを作ってサイトも作ってみました
サイトは https://mokuzist.com ですlaravelが重いというところから始まったので無駄なものは出来るだけ入れないというスタンスです
Composerも名前空間も使っています
構造はDVECモデルとなっていますController、Entity Model、Data Model、Viewです
サイト自体は興味のある事をマッチング出来るサイトとなっています
サイトを使って頂いてバグ等あれば教えて頂けると幸いです機能的にここがダメとかこれが欲しいという要望も受け付けております
これからの予定としては言語選択出来るようにして英語版も作る
人のフォロー機能もつける
もう少しスマホで使いやすいようにボタンを大きくする
スマホアプリ版も作る
といったような改修を予定しておりますレビューありがとうございます
登録するとメールアドレスにhashがついたUrlが飛ぶのでそこにアクセスすると登録完了となります
ちなみにパスワードは某F社のように平文ではなくちゃんとhash化して保存してあり復元は不可能となっております
- 投稿日:2020-02-25T01:56:17+09:00
PHPで独自フレームワーク作ってサイトも作ってみた
Lalavelが重いと感じたのでPHPで独自フレームワークを作ってそれでサイトも作ってみました。
https://www.mokuzist.com
というのがサイトですフレームワーク自体はECDVという構造にしました
Controller、Entity Model、Data Model、viewという構造で余計なものを入れない超シンプル構造です
laravelが重いというところから始まっているので余計なものは一切入れてませんサイト、フレームワーク共にバグがないかテストして頂けると幸いです