20200225のPHPに関する記事は13件です。

【Docker】チュートリアル(Python+php)

はじめに

Dockerを勉強するにあたり、素晴らしいチュートリアル動画があったのでそれを元に自分で手を動かして見ました。リンク先の動画は英語ですので、日本語で少し補足をしながら進めていきます。初心者のため認識の誤りがあるかもしれませんので、その際はコメント頂けると幸いです。

事前にdocker、docker-composeのインストールが必要です。

元動画

Docker Compose in 12 Minutes

最終的なフォルダ構成は以下のようになります。
スクリーンショット 2020-02-25 21.50.11.png

productディレクトリにはjsonを返すAPIをpythonで用意します。
websiteディレクトリには画面を描画するphpを用意します。

product

まずはdocker_tutorial配下にproductディレクトリを作成します。

$ mkdir product

そしてシンプルなPythonコードを作成します。flaskとflask_restfulというライブラリを使用します。

api.py
from 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.txt
Flask==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.yml
version: '3'
services:
    product-service:
        build: ./product
        volumes: 
            - ./product:/usr/src/app
        ports: 
            - 5001:80

version
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の環境ができることが分かります。

スクリーンショット 2020-02-25 21.18.20.png

この状態でapi.pyを以下のように変更し、保存します。

api.py
class Product(Resource):
    def get(self):
        return{
            'products': [
                'Ice cream',
                'Chocolate',
                'Fruit',
                'Eggs'
            ]
        }

再読み込みを行うと、表示内容も更新されます。コンテナを再起動しなくてもマウントしてくれます。

スクリーンショット 2020-02-25 21.21.04.png

ここまできたらコンソール上で「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.yml
version: '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-service

website
servicesにwebsiteというサービスを追加します。

image
imageを記載することで、Docker Hub上に用意されている既存のイメージを使用することができます。buildとimageは同時に指定することはできません。

depends_on
サービス同士の依存関係を記載します。今回の場合、websiteが実行されるより前にproduct-serviceが実行されるようになります。docker-compose.ymlにproduct-serviceがない場合はエラーになります。

再度Dockerを起動します。

$ docker-compose up

起動が完了したらlocalhost:5000にアクセスします。
以下のような画面が表示されていれば成功です。

スクリーンショット 2020-02-25 21.38.12.png

さいごに

Dockerを使用することで、簡単にPython+phpの環境を作ることができました。私もDocker初心者のためこれからも引き続き学習していきたいと思います。

おまけ

-dをつけることでバックグラウンドでコンテナを立ち上げることができます。

$ docker-compose up -d

Starting docker_tutorial_product-service_1 ... done
Starting docker_tutorial_website_1         ... done

psコマンドで起動中のコンテナの一覧が確認できます。

$ 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_1

stopでコンテナを停止することができます。

$ 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

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ユーザーアカウント登録内容の変更の手順

はじめに

laravelでAuthを実装すると簡単にユーザー登録が可能になりますが、登録内容の変更は別途実装する必要があります。
本投稿では名前、メールアドレスを変更する手順について記載します。
パスワードの変更手順については本投稿では触れませんが、下記サイトがかなり参考になります。
[Laravel 5.7]パスワード変更フォームの作り方

  • バージョン php: 7.2 laravel: 5.8
  • Authは実装済みであること

Controllerの編集

UserControllerの作成

$ php artisan make:controller Admin/UserController

UserControllerの編集

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>

下記画面のようになる
image.png

ユーザー情報の変更

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>

先ほどのユーザー登録内容の表示画面で「ユーザー登録内容の編集」ボタンをクリックすると下記画面に移る
image.png

Routingの設定

user/index.blade.php
 Route::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');
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ユーザーアカウント登録内容の変更の手順(パスワードは除く)

はじめに

laravelでAuthを実装すると簡単にユーザー登録が可能になりますが、登録内容の変更は別途実装する必要があります。
本投稿では名前とメールアドレスを変更する手順について記載します。パスワードは少し難しくなるので別途投稿する予定です。

  • バージョン php: 7.2 laravel: 5.8
  • Authは実装済みであること

Controllerの編集

UserControllerの作成

$ php artisan make:controller Admin/UserController

UserControllerの編集

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>

下記画面のようになる
image.png

ユーザー情報の変更

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>

先ほどのユーザー登録内容の表示画面で「ユーザー登録内容の編集」ボタンをクリックすると下記画面に移る
image.png

Routingの設定

user/index.blade.php
 Route::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');
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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についてはこちらでざっとまとめているので、是非!!

以上です!!!
ここまで読んでいただきありがとうございました!!
疑問、気になるところがございましたら、質問、コメントよろしくお願いします!!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel artisanコマンドのエラー Could not open input file: artisan

前回の記事の続きのような形で、MAMP環境でのlaravelの環境構築の記事でも書こうかと思っていたのですが、すでに良記事がゴロゴロ転がっているので必要ないかなと思ってるのですが、どうでしょう?
(訳 : 需要がありそうならMAMPのインストール、Laravelのプロジェクト作成、データベース周りの設定もやろうかと思います)

さて今日から

今日から記事の量産体制に入ろうかと思います。
Laravelを中心に小さいエラーの対処法から、覚えておくと便利なコードの書き方までガンガン書き記していきます。
読み物としては物足りないかもしれませんが、人は思わぬところで躓くものなので……。
これからの私の記事はそういった人達の一助になればと考えています。

今回の題

今回の題材は、タイトルの通りです

Could not open input file: artisan

という、たまーーに目にする小さなエラーです。

解決策

簡単なことで、

「artisan」コマンドを叩くディレクトリが間違っています。

「artisan」コマンドはLaravelのプロジェクトディレクトリで叩きます。

……解決。

終わりに

こんな感じの小さな記事を打ちまくります。
よろしくお願いします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPer脳の人がJavaScriptでループを回す

毎回for文書くたびに調べてるので

配列のforeach

phpだとこんな感じのやつ

$array = [1, 2, 3];

foreach($array as $a){
    echo $a;
}

// 1
// 2
// 3

jsだとこう

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 three

jsだとこう

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は正確には連想配列ではないけど

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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.php
Ex)  <a href="<?php echo $page_url; ?>">参照したいリンクのページの概要</a>

4.Foreach文の終了宣言をします

single.php
<?php 
}
?>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.php
strlen-要素の文字数を数える
<?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習得してポートフォリオを作成できたらと思います。
この記事を見ておかしいんじゃないのとかありましたらどんどん指摘してください、よろしくお願いします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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.php
namespace 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.yml
services:
    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を使ってみて、サービスコンテナ以外にも部品の再利用性が高く、疎統合な設計にしやすい点がメリットに感じられますね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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. 「管理者として実行」
2020-02-22 (3)_LI.jpg

以下のコマンドを実行

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プログラムに参加
  1. Windows Preview Programに参加
  2. Windows Insider の設定で「開始する」を押下

2020-02-22 (7)_LI.jpg

  1. 「アカウントリンク」より自身のアカウントと紐付ける
  2. 「スロー」を選択
  3. 「再起動」
  4. 「設定」「更新とセキュリティ」「更新プログラムのチェック」を押下
  5. すべてのインストール完了後「再起動」
  6. (「Windows 10、バージョン????の機能更新プログラム」がインストールされるまで6,7を繰り返す)

2020-02-22 (9).png

・バージョンの確認

「コマンドプロンプト(または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 2
WSL2に変換
C:¥> wsl --set-version WLinux 2
変換中です。この処理は数分かかることがあります...

もう一度状態を確認してみる。

C:¥> wsl -l -v
  NAME   STATE  VERSION
* WLinux  Stopped 2
  Ubuntu  Stopped 2

WSL2のリセット方法

「アプリと機能」のアプリの詳細から「リセット」するのが定番だったけど、
コマンド一つでできるようになるみたい。

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で走らせるとフォントがインストールされる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPで独自フレームワークを作ってサイトも作ってみた

Lalavelが重いと感じたのでPHP7でフレームワークを作ってサイトも作ってみました
サイトは https://mokuzist.com です

laravelが重いというところから始まったので無駄なものは出来るだけ入れないというスタンスです

Composerも名前空間も使っています
構造はDVECモデルとなっています

Controller、Entity Model、Data Model、Viewです
サイト自体は興味のある事をマッチング出来るサイトとなっています
サイトを使って頂いてバグ等あれば教えて頂けると幸いです

機能的にここがダメとかこれが欲しいという要望も受け付けております
これからの予定としては言語選択出来るようにして英語版も作る
人のフォロー機能もつける
もう少しスマホで使いやすいようにボタンを大きくする
スマホアプリ版も作る
といったような改修を予定しております

レビューありがとうございます
登録するとメールアドレスにhashがついたUrlが飛ぶのでそこにアクセスすると登録完了となります
ちなみにパスワードは某F社のように平文ではなくちゃんとhash化して保存してあり復元は不可能となっております

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPで独自フレームワーク作ってサイトも作ってみた

Lalavelが重いと感じたのでPHPで独自フレームワークを作ってそれでサイトも作ってみました。
https://www.mokuzist.com
というのがサイトです

フレームワーク自体はECDVという構造にしました
Controller、Entity Model、Data Model、viewという構造で余計なものを入れない超シンプル構造です
laravelが重いというところから始まっているので余計なものは一切入れてません

サイト、フレームワーク共にバグがないかテストして頂けると幸いです

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む