Laravel・PHP入門

PHPer初心者

【PHP】初心者がアルゴリズムの基礎勉強した時のメモ

フレームワークから入ってしまったので、
コーディングやプログラミングの基礎が足りない、と
先輩に課題をもらって再度学習をはじめました。

順番としては以下。

1. 今のディレクトリ以下のファイルを全て表示する(2時間)
2. ソート関数をPHPの組み込み関数を使用せず実装する(2時間)
3. カレンダーを表示する(月別、曜日は日〜土まで)(5時間)
4. デジタル時計を表示する(JavaScript)(5時間)
5. ◯✖️ゲーム(3 *3 )を実装する(3日間)
6. オセロゲームを実装する(5日間)

完全な実装は目指す必要がない、
第一に動くものであること、今回は学習目標であること。

実装あるやつだけ
3のカレンダー
gist.github.com

5.6の◯×とオセロゲーム(そういえばマイグレ用意し忘れた)
github.com

特に6番目のオセロゲームは、クラス設計などの勉強になりました。
何を1つのクラスとして持つか、どこが共通化できるかなどなど
そして人間はものすごく一瞬でいろんな判断をしているんだなーと感動しました。
せっかくなので忘れないよう自分で作ったのとFBメモしておきます。

・ゲームは前回などに影響しないため、Session保持
・エントリーポイントは全てコントローラ
・ひっくり返すかどうかなど判定系のロジックはServiceクラス
・実際に保持するデータに影響する箇所はRepositoryクラス

作り直すときはUseCaseとかで作り直してみたいですね。。。

いただいたFBとしては、
何をオブジェクトとして捉えるか、でかなり設計が変わってきますと
例えばオセロの場合は、オセロという一つのものがあったとして
その中にリセットするたびに変わる順番(これが何回めのゲームなのか)だったり、
コマをのせる盤だったり、コマ自体だったり。

それを考えるのがオブジェクト指向だねーと。
例えばどのコマをひっくり返すか判定するメソッドで、ひっくり返して次も判定、、と
同じ引数、同じ条件で繰り返す必要があるので
それらを切り分けてもよかった、

あとは共通クラス(右とか左とか)系は、完全に共通箇所が多いので
Interfaceとしてルール定義してあげた方が良いよね、

などでした。

ちょうど前回静的メソッド勉強したてだったので、
静的メソッド使いまくる病だったのですが
これが動的メソッド使いまくり→静的メソッド使いまくり→いい感じのバランス
に落ち着いていくらしいです。いつ落ち着くのやら…


にしても楽しかった〜
制限のあるプログラミングってすごく楽しい〜
Viewとの接続とか面倒だったのでLaravel使っちゃいましたがLaravelやっぱり楽させてくれる…

初心者で文系で、アルゴリズム系ぶっ飛ばした方はこういう風にアルゴリズム系を
楽しく復習したら良いと思います。楽しいです。

以上

【PHP】静的と動的メソッドとは

先輩が初心者むけにあまりに分かり易すぎる解説をしてくださったので
忘れないうちに。と自分の解釈もメモ。

PHPでよくみるstaticですが、ずっと個人的にも謎でした。

サンプル

まずは例から。かなり簡易化して先輩がかいてくださいました。

今回は計算機を例にします。
計算機クラスは、誰が実行するか、実行者というプロパティを持っています。
そしてこの計算機を使おうとするたびに、実行者を教えてあげる必要があります。

早速使ってみます。

class 計算機
{
   private 実行者;

   private function __construnct(実行者)
   {
        $this->実行者 = 実行者;
   }

   public static function 足す($a, $b) 
   {
        return $a + $b;
   }

   public function 掛ける(a,b)
   {
         if ($this->実行者 == 田中さん) {
             return $a + $b; // 田中さんは掛け算が苦手なので、足した数字を返してしまいます
         }
         return $a * $b;
    }
 }
静的メソッド足し算

足し算は簡単で全員計算することができます。
実際にこのクラスを使ってみると、、

田中さんの答え = (new 計算機(田中))->足す(1,3) // 4
佐藤さんの答え = (new 計算機(佐藤))->足す(1,3) // 4
山田さんの答え = (new 計算機(山田))->足す(1,3) // 4

実行者、つまりはインスタンスにより変わらないですね。
なので、この「足す」というメソッドは静的メソッドとして定義することができます。

計算機::足す(1,3) //4

実行者にも依存しない、インスタンスにも依存しないので、
実態のないインスタンス化していない状態で、このメソッドを呼び出すことができるのですね。
今回結果を変えることができるのは、引数だけですね。

動的メソッド掛け算

さて、次に掛け算をしてみます。
上に書いている通り、田中さんは掛け算が苦手で、ついつい足してしまいます。

田中さんの答え = (new 計算機(田中))->掛ける(1,3) // 4
佐藤さんの答え = (new 計算機(佐藤))->掛ける(1,3) // 3
山田さんの答え = (new 計算機(山田))->掛ける(1,3) // 3
)

佐藤さんは掛け算が苦手で、ついつい足してしまいました。
インスタンス、実行者により結果が変わってしまいました。
このため、この「掛ける」メソッドは動的にしか定義できません。

計算機::掛ける(1,3) // エラー(誰が実行するかで結果が変わるのに実行者がわからないから、結果が出せませんよ!

静的メソッドの書き方をしても、エラーが出ます。



イメージ補足

個人的なイメージだと、
そのクラスが保持するプロパティがそのメソッドの結果に

ざっくり比較するとこんな感じです。

静的メソッド:インスタンスに影響を受けない
動的メソッド:インスタンスに依存する

静的メソッド:PostModel::join('comments', 'posts.id', '=', 'comments.post_id');
動的メソッド:(new PostModel(1))->comments();

例えば、joinはpostsテーブルに全てのcommentsテーブルを結合しているだけなので
idが1のPostでも、2のPostでも、結果は変わらないですね。

ですが、PostModelがidが1のPostModelをインスタンス化した時に、
リレーションで定義しているcommentsメソッドを呼び出すと
idが1のPostに紐づくcommentsが全て取得できます。
しかし、これはidが2のPostに紐づくcommentsと一緒ではないですよね。

これが、インスタンスに依存するかしないか、という話です。


わかりやすくいうと、そのメソッドのなかで、メソッドを定義しているクラスのプロパティが
影響しているかどうかで判断できますね。
クエリビルだとかだと、発行したクエリの中に、クラスのプロパティが含まれていれば動的とわかりますし、
引数からしか影響を受けていなければ静的です。

それでは、Laravel、Modelのwhereメソッドは静的・動的どちらでしょうか?




正解は静的です。where句の中身にプロパティを含めていない限り、
発行するクエリにプロパティは影響しないためです。

なぜ静的・動的メソッドを使うのか

クラスは、いろんな箇所で実体化して使うことが多いですよね。
保守の時も、いろんな箇所で改修が入ります。

仮に、staticと定義せず皆があの「足し算」を使っていたらどうでしょう?
皆、足して返してくれると思っています。
ですが、ある開発者が「あ、田中さん足し算じゃなくて引き算してしまうことにしよう」と
足し算でもif文で実行者による分岐をいれたとします。

staticにしていると、この段階でエラーが出ますが、むやみに動的にしている場合、エラーが出ません。
いろんな箇所で足してくれるものと思っているのに、
結果が変わってしまうと皆困ってしまいますし、バグの元になります。

そのため、結果がプロパティに依存しない場合はstaticを使うのも一つの良い手なんだと思います。


以上

【環境構築】【AWS / Laravel】Amazon Linux2 + Laravelで環境構築

割とつまづいたのでメモ。

構築する環境

更新

$ sudo su // 更新するので権限変更
# yum update // 全部アップデート

日付設定

# cp /usr/share/zoneinfo/Japan /etc/localtime
# yum install ntp
# vi /etc/sysconfig/clock

ZONE="UTC"
UTC=true

ZONE="Asia/Tokyo"
UTC=false

# vi /etc/sysconfig/ntpd

OPTIONS="-g"

OPTIONS="-g -x"

# systemctl restart ntpd.service
# date
2018年 7月 15日 日曜日 21:02:54 JST // 現在時刻が出ればok

Apacheをインストールする

# yum install httpd
# /usr/sbin/httpd -v

Server version: Apache/2.4.33 ()
Server built: Jury 15 2018 21:37:52 // インストールできればok

# systemctl start httpd.service // CentOS7でcommand変わったので注意
# systemctl stop httpd.service

Apache設定

// オリジナルコピー
# cp /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.`date +%Y%m%d`

// httpd.confをいじります
# vi /etc/httpd/conf/httpd.conf

## ドキュメントルート
DocumentRoot "/var/www/html"

DocumentRoot "/var/www/sample/public" // 今回もしsampleというディレクトリ名なら

## アクセス上書き

AllowOverride None

AllowOverride All

## Apacheのバージョン秘匿
(下の方に追加)
# Hide Apache Version
ServerTokens Prod

#service httpd configtest // 編集後テスト
Syntax OK // OKが出ればok

PHPインストール

# amazon-linux-extras install php7.2 // AWS公式の通り
# php -v // 7.2が出てくるはず
# yum -y install php-devel php-pdo php-mbstring php-mcrypt php-mysqlnd php-xml php-gd php-opcache php-pecl-zip
// PHPに必要なパッケージ色々はyumでインストール

Composerインストール

# curl -sS https://getcomposer.org/installer | php
# mv composer.phar /usr/bin/composer // パスが通る場所へ移動
# composer -v
めっちゃComposer!!って出ればok

Gitインストール

# yum install git
# git --version
git version 2.7.5 // 出ればok

npmとnodeとnvmとyarnインストール

# curl --silent --location https://rpm.nodesource.com/setup_8.x | bash -
# yum install nodejs npm
# npm -v
# node -v
# curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
# source ~/.nvm/nvm.sh // パスを通す
# nvm install 6.* // nodeが6系しかいうこと聞いてくれないので6系へ合わせる
// nodeとnvmのインストール順変えても良いかも

# sudo wget https://dl.yarnpkg.com/rpm/yarn.repo -O /etc/yum.repos.d/yarn.repo
# sudo yum install yarn
# yarn --version

Githubへ接続

# cd ~/.ssh
# ssh-keygen -t rsa -C sample@aaa.jp(enter 3回) // 自分のGithubアカウントアドレス
# vim ~/.ssh/config

Host github.com
HostName github.com
IdentityFile ~/.ssh/id_rsa
User Githubのアカウント名

# cat ~/.ssh/id_rsa.pub
※ ブラウザからgihubのSSH keysに追加
# cd /var/www/
# git clone git@github.com:sample/aa.git // 既存リポジトリをクローンする

リポジトリから色々インストールと権限

# cd sample/
# composer install
# yarn install
# yarn run dev
# chmod -R 777 storage
# chmod -R 777 bootstrap/cache

起動する

# systemctl restart sysstat.service // Apache
# cp .env.example .env
# php artisan key:generate

これで環境構築は完了です!

あとはAWSのアドレスへアクセスすればアクセスできるはず

【PHPUnit】【Laravel】定数ファイル管理

やりたいこと

  • 定数管理ファイルの場所変更
  • テストの時も定数管理

定数ファイルの場所を変更したい

今回の場合はbootstrap下へ作成したい。

bootstrap下でautoload.phpを作成する。

bootstrap/autoload.php

<?php

/*
|--------------------------------------------------------------------------
| Register The Composer Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any our classes "manually". Feels great to relax.
|
*/
require __DIR__.'/../vendor/autoload.php';

$filelist = glob(__DIR__ . '/constants/*.php');
foreach ($filelist as $file) {
    if (is_file($file)) {
        require_once $file;
    }
}
/*
|--------------------------------------------------------------------------
| Include The Compiled Class File
|--------------------------------------------------------------------------
|
| To dramatically increase your application's performance, you may use a
| compiled class file which contains all of the classes commonly used
| by a request. The Artisan "optimize" is used to create this file.
|
*/
$compiledPath = __DIR__.'/cache/compiled.php';
if (file_exists($compiledPath)) {
    require $compiledPath;
}
オートローダー呼び出し元ソース変更

通常はindex.phpでオートローダーを呼び出すのでこちらも変更
public/index.php

変更前:require __DIR__.'/../vendor/autoload.php';
変更後:require __DIR__.'/../bootstrap/autoload.php';
定数ファイルの作成

bootstrap下に早速作成する
定数なのでconstantsディレクトリを作成

bootstrap/constants/common.php

<?php
const FLAG_ON = 1;
const FLAG_OFF = 0;

フラグ作ってみました。

アプリケーションで使用

Post呼び出しの時に、表示するもの、しないものを分けてみます
例えばshow_flagカラムを追加し、flagがonの場合のみ表示したい

app/Repositories/Post.php

<?php
//  一部抜粋
    public function getAllPosts()
    {
        return $this->post_model
            ->where('show_flag', FLAG_ON) // ここで定数使用
            ->get();
    }

これでflagがonのpostのみがかえるようになりましたね

いざテスト

エラーが出る

./vendor/bin/phpunit tests/Feature/PostsTest.php

f:id:fresh_engineer:20180712230841p:plain

storage下のlogをみると、FLAG_ONなんて定数知らねーよ!!!!って
怒られます。。。

テストは指定している定数ファイルを読み込んでくれません。
なので読み込んでくれるようにベースのテストクラスで呼び出してあげます

ついでにベースのテストクラスも作る

<?php

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication;

    public function setUp()
    {
        parent::setUp();
        $this->readConstants();
    }

    /**
     * テスト用に定数ファイル読み込み
     */
    private function readConstants()
    {
        $file_list = glob('./bootstrap/constants/*.php'); // 定数ファイルを全部取得
        foreach ($file_list as $file) {
            if (is_file($file)) {
                require_once $file; // 各ファイル1度だけ読み込む
            }
        }
    }
}

こうして読み込んであげると、、、

./vendor/bin/phpunit tests/Feature/PostsTest.php


f:id:fresh_engineer:20180712231106p:plain

テスト通過!

これで定数も使いながらテストが実行できるようになりました。

以上

【Laravel】【PHPUnit】コレクションや配列のテスト

今日はLaravelでよく出てくるCollectionデータのテストコードを書いていて
ハマったのでまとめます。


サンプルはこちら。
Post一覧取得APIのテストコード · aihara005/laravelprot@ba2e4b8 · GitHub
PHPUnitテスト by aihara005 · Pull Request #3 · aihara005/laravelprot · GitHub

PHPUnitの準備

まずはローカルでphpunitを使うための準備。
$ vi .env

APP_URL = 自分のURL
php artisan serveで立ち上げてる時は
http://127.0.0.1:8000

サンプルテストを作る

早速簡単なテストを作って通過するかチェックします。
前回の記事でちょうどGETリクエストでレスポンスを返すAPIを作成していたので
それを使って200レスポンスがかえるかテストします。

/tests/Feature/ExampleTest.php

<?php

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testBasicTest()
    {
        $response = $this->get('/posts');

        $response->assertStatus(200);
    }
}

サンプルテストを実行する

$ ./vendor/bin/phpunit tests/Feature/ExampleTest.php //コマンドでphpunit叩く


f:id:fresh_engineer:20180712002447p:plain

成功しました!

コレクションのテストを作る

それでは前回作成したpostsのAPIについて、
postsコレクションがjsonで返却されるので
テストを作ってみます。

tests/Feature/PostsTest.php

<?php

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class PostsTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testGetPosts()
    {
        $response = $this->get('/posts');

        $response->assertStatus(200)
                ->assertJsonStructure([ // Json形式を指定
                    'data' => [
                        // コレクション系は * でキーを指定すればok
                        '*' => [ 
                            'id',
                            'title',
                            'body',
                             // リレーションも1対多なのでネストした状態で * でキーを指定すればok
                            'comments' => [
                                '*' => [
                                    'id',
                                    'post_id',
                                    'body'
                                ]
                            ]
                        ]
                    ]
                ]);
    }
}

「*」 を使う形式でokでした・・・!

テストを実行する

$ ./vendor/bin/phpunit tests/Feature/PostsTest.php

f:id:fresh_engineer:20180712003836p:plain

コレクションなどでモデルが配列になって返ってくるタイプはこれで解決しそうです。
結構はまりました。。。

エラー

失敗パターンもちゃんと指摘してくれます。
例えば上記のpost_idをpost_aaaidなどに変換すると

f:id:fresh_engineer:20180712004115p:plain

そんなidないよって怒ってくれますね。素敵です。



以上

【Laravel】APIリソースを使う(Json/Resource)

LaravelのAPIリソースを使ってとても便利だと思ったのでまとめ。

https://readouble.com/laravel/5.5/ja/eloquent-resources.html

APIリソースとは(日本訳ドキュメント引用)

API構築時、Eloquentモデルと、アプリケーションユーザーに対して実際に返信するJSONリスポンスとの間に、
トランスレーション層を設置することが必要となります。
Laravelのリソースクラスは、モデルやモデルコレクションを記述しやすく簡単に、JSONへと変換してくれます。

ちなみにLaravel5.5で登場したそうです。最高です。

実装するもの

  • /posts でPost一覧を取得するAPIで返却するリソース
  • /posts/{id}でIDが1のPostを取得するAPIで返却するリソース

完成形

  • 一覧

f:id:fresh_engineer:20180706235357p:plain

  • ID指定

f:id:fresh_engineer:20180706235431p:plain

こんな感じです。

前準備

  • 環境構築(Laravelプロジェクト)
  • Controllerの作成
  • Modelの作成
  • Route通す
  • DBにPostとCommentを準備

用意したデータ

  • postsテーブル

f:id:fresh_engineer:20180707001326p:plain

  • commentsテーブル

f:id:fresh_engineer:20180707001353p:plain

ディレクトリ構成

App
┗Http
  ┝Controllers
    ┝BaseController.php
    ┝PostController.php
    ┗CommentController.php
  ┝Resources
    ┝BaseResource.php
    ┝Post.php
    ┗Comment.php
  ┝Models
    ┝BaseModel.php
    ┝Post.php
    ┗Comment.php
  ┝Repositories
    ┝BasePost.php
    ┝Post.php
    ┗Comment.php
  ┗Services
    ┝BaseService.php
    ┝Post.php
    ┗Comment.php

深くなりましたが
APIリソースに深く関わるのは
・取得するデータのロジックを記載するRepository
インスタンス化するService
・レスポンスを定義するResourceクラス
この3つです。

イメージを明確にするために、補足でController/ Route/ Modelは画像のみ貼っておきます。

Route

f:id:fresh_engineer:20180707000006p:plain

Controller

f:id:fresh_engineer:20180707000018p:plain

Model

※PostにひもづくCommentも取得したいのでリレーション定義
f:id:fresh_engineer:20180707000457p:plain

リソースに渡すモデルを取得

今回はRepositoryクラスに実装します。
f:id:fresh_engineer:20180707000057p:plain

リソースの作成

php artisan make:resource PostResource

まずはリソースクラスを作成します。

  • Postリソース

今回はPost一覧を求められた場合、
Postのもつid,title,body,とそのPostにひもづくCommentを取得したい。
渡したい情報をそれぞれキー(クラスにより自由です)、
そして渡したモデルの誰がそのデータを持っているか渡してあげます。

CommentはPostと1対多の関係、つまりは1Postが複数のCommentを持ちうるので
Commentリソースについてcollectionメソッドを用います。

このように、APIリソースはそれらのリレーションで紐付く情報も
プロパティから呼び出して別のリソースを整形して同時に返却することができます。

<?php
namespace App\Http\Resources;

use App\Http\Resources\Comment as CommentResource;
use Illuminate\Http\Resources\Json\Resource;

class Post extends Resource
{
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'body' => $this->body,
            'comments' => CommentResource::collection($this->comments)
        ];
    }
}
  • Commentリソース

コメントではid,post_id,bodyを返却したいのでそれぞれ定義して返します。

<?php
namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\Resource;

class Comment extends Resource
{
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'post_id' => $this->post_id,
            'body' => $this->body
        ];
    }
}

リソースをインスタンス化する

リソースを実際に使用するときは、DBから取得したデータを
引数として渡しつつ、インスタンス化します。

リソースが1つの時は newでインスタンス化すれば良いですが、
複数のコレクションの場合はcollectionメソッドを用います。

<?php
namespace App\Services;

use App\Http\Resources\Post as PostResource;
use App\Repositories\Post as PostRepository;

class Post extends BaseService
{
    private $post_repository;

    public function __construct(PostRepository $post_repository)
    {
        $this->post_repository = $post_repository;
    }

    /**
     * 全てのPostを取得し成型してJsonで返す
     *
     * @return mixed
     */
    public function getAllPosts()
    {
         return PostResource::collection($this->post_repository->getAllPosts());
    }

    /**
     * IDからPostを取得し成型してJsonで返す
     *
     * @param int $id
     * @return mixed
     */
    public function getPostById(int $id)
    {
        return new PostResource($this->post_repository->getPostById($id));
    }
}


これで、URLを叩くと
f:id:fresh_engineer:20180706235431p:plain
f:id:fresh_engineer:20180706235357p:plain
データが返ってきましたね!

リソースはいくつも階層重ねることもできるので、
ドメイン的な整形の仕方にももってこいですね。
積極的に使っていきたいです

以上

【Linux】パーミッション設定についての基本

Permission Deniedよく引っかかりますよね。
詳しく紹介している記事はたくさんあるので
最低限ざっくりわかればと思います。

なんとなく使ってたchmod 777 〜ファイル名が今日解決したのでメモ。

パーミッションを確認する

ls -l ディレクトリ名

f:id:fresh_engineer:20180704233655p:plain

9個d、w、r、- などが並んでいますが、
これは1 + 3つずつに分解できます。

例えば

drwxr-xr-x の場合。

d / rwx / r-x / r-x

これに分けることができます。

初めのdはディレクトリかどうか。

その次は3つずつ、権限のことを示しています。
権限はざっくり3種類。

r 読み込み
w 書き込み
x 実行

順番はそのファイルやディレクトリの所有関係によります。
なので、10個並んでいた連番は下記のようなイメージですね。

ファイルの種類 / 所有ユーザの権限 / 所有グループの権限 / その他のユーザの権限

権限を付与する

そしてここまでわかればchmodで与えてあげるだけです。

例えば

chmod -R 777 ディレクトリ名

例えばこれはディレクトリ配下になんでもできる権限を与えています。

777は、先ほどの3種類のユーザの権限を
順番に指定しています。

rwx 4 + 2 + 1 = 7
rw- 4 + 2 + 0 = 6
r-x 4 + 0 + 1 = 5
r-- 4 + 0 + 0 = 4
-wx 0 + 2 + 1 = 3
-w- 0 + 2 + 0 = 2
--x 0 + 0 + 1 = 1
全てを許可しない 0 + 0 + 0 = 0

今日とてもスッキリしました!
ざっくり理解したい人はこれで使えると思います。

細かい内容はググってください

以上