20201114のPHPに関する記事は11件です。

PHPファイルにbootstrapを導入する

install

composer を用いてインストール
https://getbootstrap.jp/docs/4.5/getting-started/download/

アプリのルートディレクトリに移動

composer require twbs/bootstrap:4.5.0

vendr/twbs/bootstrap が作成される。

bootstrap.cssとbootstrap.min.cssの違い。

bootstrap.min.cssは、圧縮されているため容量がコンパクト。ただし、それゆえ中身が見にくいので、
開発環境ではbootstrap.cssを用いた方が良い。

bootstrap.cssを読み込む

#読み込みファイルのheadに記載

<link rel ="stylesheet" href="vendor/twbs/bootstrap/dist/css/bootstrap.css">

Sassを用いる場合

Bootstrapをカスタマイズする際に、cssに手を加えていく。その際にSassを用いることにより、複雑な処理やソースの見通しをよくしたりすることができる。

Sassを読み込む際は、cssにコンパイルしてからhtmlで読み込む。

機能

ネスト:

style.css
nav ul{
  color:red;
}
nav li {
  color:blue;
}
style.scss
nav{
  nl {
    color:red;
  }
  li {
    color:blue;
  }

変数:

style.css
body{
  color:red;
  }
style.scss
$primary-color:red;

body{
  color:$primary-color;
  }

親クラスの指定:

style.css
a{
  color:red;
 }
a:hover{
  color:blue;
}
style.scss
a{
  color:red;
  &:hover{
    color:blue;
  }
}  

カスタマイズ手順

1 Sassをインストール

Sassは公式でcomposerに対応していない。今回は github上に公開されているscssphpというライブラリを用いる。

https://scssphp.github.io/scssphp/

composer.json
//composer.jsonのrequireに追記する。

{
    "require": {
        "scssphp/scssphp": "^1.3"
    }
}
composer update

vender/scssphp/scssphpが生成される。

2 Scss読み込む

Sassを使用するファイルでScssを読み込む。

sytle.scss
gi```

読み込んだファイルをcssにコンパイルする。

```ターミナル.
vendor/scssphp/scssphp/bin/pscss < sylesheets/scss/style.scss > stylesheet/css/style.css

# vendor/scssphp/scssphp/bin/pscss で sylesheets/scss/style.scssをコンパイルし、
その結果をstylesheet/css/style.cssに書き込んでいる。

# 変更がある度にコンパイルする。

3 作成したcssファイルをhtmlから読み込む

<head>
...
  <link rel ="stylesheet" href="stylesheet/css/style.css">
</head>


2〜3のイメージ

cssを使用する場合

【html】 →  → → → → → → → 読み込み → → → → → → → →   【bootstrap.css】

scssを使用する場合

【html】 → 読み込み → 【css】  ← コンパイル ← 【scss】 → 読み込み → 【bootstrap.css】
                           ↑  
                        ここをカスタマイズ

4 変数を定義する。

sytle.scss
$main-color: red;

@import 'vender/twbs/bootstrap/scss/bootstrap';

body{
  letter-spacing: 0.5em;
}

#変数は@import より上に書くことでデフォルト値が設定される。
bodyなどの定義されているものは、下に書く。下方にあるものほど、最終的に適用されるため。

修正後、コンパイルを実行。

変更をブラウザで確認するにはスーパーリロードを行う。
ブラウザのキャッシュを無視し、強制的にWebサーバーからファイルをダウンロードする方法。

mac:
cmd + shift + R

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

Laravel|同じ処理を複数のControllerで使う時に便利な共通関数を脳死で作成する方法

こんにちは、くりぱんです。

この記事で実現できること

  • 共通関数の作成&実装
  • コントローラーの簡略化
  • コントローラーのメンテナンス性向上

説明

今回実装するのは、Hello world!と出力する処理を複数のコントローラーで実装する時に、それぞれのコントローラーで記述するのではなく、共通関数を作成し同じ処理を何回も書かないようにする方法です。

私の場合、チャット機能実装の際に、チャットルームを作成するという処理を複数のコントローラーで記述しなければならなかったので、チャットルーム作成という機能を共通関数化し、それぞれのコントローラーで共通関数を使い回す手法を行いました。

その時の備忘録を今回簡単に残していこうと思います。

開発環境

  • OS: MacOS Catalina
  • PHP: 7.3.11
  • Laravel: 6

実装の流れ

  1. Laravelプロジェクト作成
  2. composer.jsonへ登録
  3. composer dump-autoload実行
  4. config/app.phpにエイリアスとして追加
  5. 共通関数作成
  6. 処理を利用したいコントローラでuseする
  7. routeの設定
  8. viewの設定

実装

Laravelプロジェクト作成

今回はMAMPのhtdocs内にLaravelのプロジェクトを作成していきます。
ターミナルで以下のコマンドを実行してください。

$ cd /Applications/MAMP/htdocs
$ composer create-project --prefer-dist laravel/laravel commonHelloWorld "6.*"

これでhtdocs配下にcommonHelloWorldというLaravelのプロジェクトが作成されました。
記事の中では、/Applications/MAMP/htdocs/commonHelloworldフォルダのことをプロジェクトフォルダと呼んでいきます。

composer.jsonへ登録

今回はcommonHelloworld/appCommonフォルダを作成し、そこに共通関数を格納していきます。
そのため、プロジェクトフォルダ配下にcomposer.jsonがあるので以下の通りに編集してください。

composer.json
{
    ー省略ー

    "autoload": {
        "psr-4": {
            "App\\": "app/"
        },
        "classmap": [
            "database/seeds",
            "database/factories", // 最後にコンマを追加
            "app/Common" // ここを追加
        ]
    },

    ー省略ー
}

composer dump-autoload実行

まずは、commonHelloworld/app配下にCommonフォルダを作成してから以下のコマンドを実行してください。

$ composer dump-autoload

下記のような感じになれば成功です。

Package manifest generated successfully.
Generated optimized autoload files containing 4255 classes

もし下記のようなエラーが出た時は、commonHelloWorld/appCommonフォルダを作成してくださいね!

  [RuntimeException]                                                                              
  Could not scan for classes inside "app/Common" which does not appear to be a file nor a folder

config/app.phpにエイリアスとして追加

commonHelloWorld/config/app.phpのaliasesに共通関数の登録を行っていきます。
今回は、クラス名をsayHelloにするので、下記のように記述してください。
※クラス名は自由です。
※共通関数と同じクラス名にしてください。

app.php
<?php

return [

    ー省略ー
    'aliases' => [

        ー省略ー

        'URL' => Illuminate\Support\Facades\URL::class,
        'Validator' => Illuminate\Support\Facades\Validator::class,
        'View' => Illuminate\Support\Facades\View::class,
        'sayHelloClass' => app\Common\sayHelloClas::class, //ここを追加
    ],

共通関数作成

今回は、単純にHello World!を出力するだけなので、commonHelloWorld/app/Common配下にsayHelloClass.phpを作成し、以下のように記述してください。

sayHelloClass.php
<?php

namespace app\Common;

class sayHelloClass
{
    public static function sayHello()
    {
        echo "Hello World!";
    }
}

処理を利用したいコントローラでuseする

まずは処理を利用したいコントローラーを作ります。
以下のコマンドをプロジェクトフォルダ配下で実行してください。

$ php artisan make:controller topContorller

これで、app/Http/Controllers配下にtopController.phpというコントローラーが作成できました。

では、以下のように編集してください。

topController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Common\sayHelloClass;

class topController extends Controller
{

    public function show()
    {
        sayHelloClass::sayHello();

        return view(
            'topShow'
        );
    }
}

これでtopShowというviewファイルにHello World!が出力されるようになります!
なお、topShowはこれから作成します。

routeの設定

次に、routeの設定をしていきます。
commonHelloWorld/routes/web.phpを下記のように編集してください。

web.php
<?php
Route::get('/', 'topController@show')->name('top.show');

これでrouteの設定は終わりました。

viewの設定

続いて、Viewファイルの作成です。
commonHelloWorld/resources/viewstopShow.blade.phpを作成し、下記のように編集してください。

topShow.blade.php
<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  これはトップだよ
</body>

</html>

これで、あとは下記のコマンドでサーバーを立ち上げて、画面を確認してみましょう!

$ php artisan serve

画面を表示して、Hello World! これはトップだよ! と出てくれば成功です!

最後に

こんな簡単にコントローラーから機能を切り分けられるんですね!
さらに、これを使い回せば同じ処理を何回も書かなくていいですし、メンテナンス性も向上するしで、一石二鳥ですね!
他にもファットコントローラーをスリムにするやり方があるので、まとめていきたいな!

何か間違ったことがあったり、質問・意見等あればコメントへお願いいたします!

それでは、当記事を最後まで見ていただき、ありがとうございました!

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

PHPでオブジェクト同士の関係を可視化する

はじめに

オブジェクト同士の関係が複雑になってくると関係を楽に把握したくなります。
BEAR.Sunday の生みの親である郡山さん(@koriym)が公開されている print_o を使用することでオブジェクト同士の関係を可視化できるので、関係の把握が楽にできるようになります。
使い方は簡単なのでReadmeを読めば理解できると思いますがやってみます。

インストール

composer require --dev koriym/printo

可視化対象

new A(
    new B(
        new E(
            new F(),
            new G(
                new H(
                    new L(),
                    new M(
                        new N()
                    )
                ),
                new I()
            ),
            new J(),
            new K()
        )
    ),
    new C(
        new D()
    )
);

オブジェクトグラフ生成

/** @var A $object */
$object = (new ObjectGraphService())->graph(); // 可視化対象を返す処理

$graph = (new Printo($object))
    ->setLinkDistance(200)
    ->setCharge(-2000);
 file_put_contents('/var/www/html/docs/graph.html', $graph);

可視化

出力されたhtmlを開きます。

objectgraph.png

オブジェクト同士の関係が可視化できました。
複雑なオブジェクトの関係を可視化したい時に使ってみるとよさそうですね。

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

OPcacheが有効になっているPHP-FPMをRead Onlyコンテナで動かす

環境

PHP7.3 opcache Docker php-fpm

前提

  • OPcache
    • キャッシュ有効化
    • file_cache_onlyが無効
    • lockfile_pathが/tmp
  • PHP-FPM
    • pidへのファイルパスなし
  • Docker
    • Read Only mode

方法

opcacheのロックファイルが作成される/tmpにvolumeをマウントします。

docker run -d -p 9000:9000 --read-only -v $(pwd)/tmp:/tmp php:7.3-fpm-alpine3.12

解説

なぜ/tmpにvolumeをマウントさせるのかを説明します。
opcacheは共有メモリにキャッシュするためにロックファイルが必要です。
zend_shared_alloc_create_lock関数内で、mkstempをコールして/tmpにロックファイルを作成します。
作成失敗の場合は、Unable to create lock fileというエラーメッセージを出力後に異常終了します。

ロックファイルの作成関数
void zend_shared_alloc_create_lock(char *lockfile_path)
{
    int val;

#ifdef ZTS
    zts_lock = tsrm_mutex_alloc();
#endif

    snprintf(lockfile_name, sizeof(lockfile_name), "%s/%sXXXXXX", lockfile_path, SEM_FILENAME_PREFIX);
    lock_file = mkstemp(lockfile_name);
    fchmod(lock_file, 0666);

    if (lock_file == -1) {
        zend_accel_error(ACCEL_LOG_FATAL, "Unable to create lock file: %s (%d)", strerror(errno), errno);
    }
    val = fcntl(lock_file, F_GETFD, 0);
    val |= FD_CLOEXEC;
    fcntl(lock_file, F_SETFD, val);

    unlink(lockfile_name);
}

zend_shared_alloc_create_lockはzend_shared_alloc_startupからコールされ、実行時設定のlockfile_pathを引数として渡します。

zend_shared_alloc_create_lockの呼び出し元
int zend_shared_alloc_startup(size_t requested_size)
{
/* 省略 */

#ifndef ZEND_WIN32
    zend_shared_alloc_create_lock(ZCG(accel_directives).lockfile_path);
#else
    zend_shared_alloc_create_lock();
#endif

Reference

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

セッションIDの取得と再発行

セッションID取得

session_id() = 現在のセッションIDを取得または設定する。

<?php
$start = session_start();
?>

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<body>

<?php
if( $start === true ){ 
  echo session_id(); // セッションスタートしていたら、セッションIDを取得して表示。
} else { 
  echo "セッションの作成に失敗しました!";  //セッションスタートされていなければ表示。
}

</body>
</html>

セッションIDの再発行

session_regenerate_id() = 現在のセッションIDを新しく生成したものと置き換える。

ページを読み込む度にセッションIDは更新されます。
ユーザーがログインして機密性の高い情報を$_SESSIONに格納する時に、新しいセッションIDを再発行することでセッションIDの漏洩によるデータの盗聴リスクを下げることができます。

<?php
session_start();  // セッション開始
session_regenerate_id(); // セッションID再発行
echo session_id();  // 再発行されたセッションIDを表示
?>

参考元はこちら
セッションID取得:(https://webkaru.net/php/function-session-id/)
         (https://wepicks.net/phpref-session/#0)

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

セッション開始と削除

先ずはセッションについて少しでも理解しておいた方がいいので、是非こちらの記事を参考にしてください。
セッション:https://qiita.com/yyy752/items/676cf3c3751c70aea7f2
クッキー:https://qiita.com/yyy752/items/9cc85104c3eba900ca3a

セッション開始

session_start() = 新しいセッションを開始、あるいは既存のセッションを再開する。

<?php
session_start(); 
?>

セッション削除

session_destroy() = セッションに登録されたデータを全て破棄する。
session_destroy()では変数「$_SESSION」のデータは削除されない為、
$_SESSION = array();で変数のデータも削除しましょう。`

<?php
session_start(); // セッション開始
?>

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<body>

<?php
$_SESSION['data'] = "こんにちは!"; // セッションにデータを格納
$_SESSION['name'] = "佐藤さん";

print_r($_SESSION); echo "<br />"; // $_SESSIONのデータ「こんにちは!佐藤さん」を表示

$_SESSION = array(); // $_SESSIONのデータを削除

session_destroy(); // セッションを破棄

?>

</body>
</html>

参考元はこちら
(https://webkaru.net/php/function-session-destroy/)

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

Laravelで日付を操作しよう

実務でLaravelを使って日付検索をしたことがあるので、ここで共有+アウトプットをしておく。

リクエストで文字列で渡ってきた日付を操作する必要があったのですが、簡単に調べてみるとdate関数とstrtotime関数を使えば簡単にできそう

date関数とstrtotime関数とは

公式より引用date関数
公式より引用strtotime関数

  • date関数・・・ ローカルの日付/時刻を書式化する。 指定された引数 timestamp を、与えられた フォーマット文字列によりフォーマットし、日付文字列を返します。 タイムスタンプが与えられない場合は、現在の時刻が使われます。
date ( string $format [, int $timestamp = time() ] ) : string

つまり第一引数のフォーマットにしたがって、第2引数のtimestampが日付文字列で変換される模様。

  • strtotime関数・・・英文形式の日付を Unix タイムスタンプに変換する この関数は英語の書式での日付を含む文字列が指定されることを期待しており、 now で与えられたその形式から Unix タイムスタンプ (1970 年 1 月 1 日 00:00:00 UTC からの経過秒数) への変換を試みます。 now が指定されていない場合は現在日時に変換します。
strtotime ( string $datetime [, int $now = time() ] ) : int

こちらは文字列型の日付をint型の時間で返す模様。

ここの第1引数で様々な操作ができます。
月、週、日付、時間など
ここではある日にちを第一引数としたものを1日追加してみましょう

$day = "2020-11-12";
echo strtotime($d . "+1 day"); //1605225600

という具合です。

なのでstrtotimeで文字列型の日付を一度操作して、date関数の第二引数に当てはめます。

$day = "2020-11-12";
echo date( "Y-m-d", strtotime($d . "+1 day")); //2020-11-13

という感じでリクエストで渡ってきた日付の文字列を操作できました。

使うこともあるかと思うので、是非参考にしてみてください。
ここでは紹介しませんがCarbonライブラリというのもあるみたいなので、そちらも調べてみるといいかもです。

最後まで読んでいただきありがとうございました。

参考文献

strtotime関数
date関数
qiita記事

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

PHPのtrait上で任意のモデルを`$this`として参照する

そもそもtraitとは

PHPは仕様として多重継承をサポートしていません。そのためある基底クラスを既に継承している場合に共通の振る舞いを拡張するため継承を追加する、といったことが行えないのです。。
しかし、traitを利用することで共通の振る舞いを切り出し、再利用することが可能となります。

PHP 5.4.0 以降では、コードを再利用するための「トレイト」という仕組みが導入されました。

トレイトは、PHP のような単一継承言語でコードを再利用するための仕組みのひとつです。 トレイトは、単一継承の制約を減らすために作られたもので、 いくつかのメソッド群を異なるクラス階層にある独立したクラスで再利用できるようにします。 トレイトとクラスを組み合わせた構文は複雑さを軽減させてくれ、 多重継承や Mixin に関連するありがちな問題を回避することもできます。

トレイトはクラスと似ていますが、トレイトは単にいくつかの機能をまとめるためだけのものです。 トレイト自身のインスタンスを作成することはできません。 昔ながらの継承に機能を加えて、振る舞いを水平方向で構成できるようになります。 つまり、継承しなくてもクラスのメンバーに追加できるようになります。

PHP: トレイト - Manual

使い方

通常の継承のようにextendするのではなく、traitを利用したいクラス内でuseして参照します。

class HogeModel() {
  use HogeTrait;
  .....
}

traitから$thisで呼び出し元を参照できない!?

traitはその性質上、共通処理の重複を防ぐなどの理由で複数のクラスから利用されることを想定されていることも多いと思います。

しかし、あるtraitが特定のクラスでのみ利用される設計のこともあると思います。
例えば弊社では設計にDDD(ドメイン駆動設計)を採用しているのですが、モデルの責務を超えてくるような責務についてはtraitに切り出し、モデル内から参照するようにしています(例えばFactoryパターンの実装などはtraitで行っています)。

そのような場合には$thisとして呼び出し元のモデルを参照する処理を実装したい場合もあると思いますが、それをtrait上で行ってみると。。。

class HogeModel() {
  use HogeTrait;
  .....
  public function getString(): string {
    return 'echo me';
  }
}

trait HogeTrait {
  private function hogeFunction(): void {
    $this->echoHogeModelString($this);
  }

  private function echoHogeModelString(HogeModel $model): void {
    echo $model->getString();
  }
}
Expected parameter of type 'HogeModel', 'HogeTrait' provided 

hogeFunction内でechoHogeModelString($this)として引き渡されている$thisHogeTraitとして認識されてしまう結果、「echoHogeModelStringの引数にはHogeModelを渡してね!」と注意されてしまう訳です。当然といえば当然ですね。

しかし、設計上単一のモデルからのみ参照されるtraitなのだから、$thisで呼び出し元モデルを参照したい!ということもあると思います(僕はありました。。)。

ここでは二つの対応方法をご紹介します。

1:phpdocの@varコメントで変数の型を指定する

下記のように型を指定した変数を作成し、それを参照することで$this参照を(間接的に)実現することができます。

trait HogeTrait {
  private function hogeFunction(): void {
    /** @var HogeModel $model */
    $model = $this;
    $this->echoHogeModelString($model);
  }
  .....

$model変数をHogeModel型であることを明示的に宣言することで、trait上でも警告なしで呼び出し元のモデルを直接参照することができます。

2:assertすることで$thisの型を保証する

下記のようにtrait上で型を保証するassertを記述することで$this参照を実現することができます。

trait HogeTrait {
  private function hogeFunction(): void {
    assert($this instanceof HogeModel)
    $this->echoHogeModelString($this);
  }
  .....

$thisHogeModel型であることをassertで明示的に保証することで、trait上でも警告なしで呼び出し元のモデルを直接参照することができます。

PHPでもやりようで型とは上手に付き合える(はず)

インタプリタ言語であるPHPですがdeclare(strict_types=1)の設定をすることで型指定に基づいた実装を行うことができます。より適切な型の扱いや実装を行えるよう、精進していきたいと思います(静的言語も業務でやりたい。。)。

お読みいただき、ありがとうございました。

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

未経験者がLaravelでポートフォリオを作成した話【学習開始〜AWSデプロイまで】

簡単に自己紹介

・東京で土木の構造設計をやっている30代前半
・働きつつ、これから転職活動するところ
・健康好きで趣味はアンチエイジング

どんなアプリ? 何ができる?

ひとことで言うと
「パレオダイエッター(という健康法をやってる人)向けのコミュニティ+計算機能ツール」
です。
画面キャプチャ

基本的なユースケースとしては、
 ・「ユーザー登録/ログイン/プロフィール編集」
 ・「文字や画像を投稿・編集/いいね/コメントで交流」
 ・「筋肉をつける・腹筋を割るための目標カロリー算出→マイページ登録」
という感じになります。

もう少し機能を細分化すると、
 ・ユーザー登録・ログイン機能・ゲストログイン機能
 ・プロフィール編集機能(自己紹介文とアバター画像)
 ・投稿作成機能(モーダル画面,文字数カウント,画像投稿)
 ・コメント機能
 ・投稿とコメントの編集・削除機能
 ・いいね機能(Ajax利用,リレーション数取得)
 ・効率よく筋肉をつけるための目標摂取カロリーおよび三大栄養素の計算機能(Ajax)
 ・最速で腹筋を割るための目標摂取カロリーおよび三大栄養素の計算機能(Ajax)
 ・投稿の検索機能とページネーション
といった具合です。

使用技術と環境は?

・言語:PHP7, JavaScript(jQuery), Bootstrap, HTML, CSS(Sass)
・フレームワーク:Laravel7
・DBMS:MySQL
・インフラ:AWS(EC2,RDS,ELB,S3,Route53,ACM)
・Webサーバー:Amazon Linux OS 2, Apache
・開発環境:ローカル環境(MacOS), VSCode
 (本当はDockerなども導入したかったですが..)

作った背景と目的

ポートフォリオを作った理由は、
 ①転職時に必要不可欠と考えるため
 ②アプリ開発を経験することで成長したいため
 ③必要な機能を実装して価値を提供したいため
の3つです。

①転職時に必要不可欠と考えるため

職務経歴や経験で充分にスキルを示せる場合は別ですが、そうでない場合は何かモノを作っていないとスキルを定量的に示すことができないかなぁと思います。
これは、もし自分が評価する側だったら「勉強してますが何も作ったことはありません」だと、その人がこれから活躍していく姿をうまくイメージするのは難しいと考えるためです。

②ゼロからアプリ開発を経験することで成長したいため

Progateなどの教材はとても分かりやすいですが、環境構築や設計、エラー解消といった場面に遭遇することはありません。
また書籍や動画の教材もそうですが何か問題に遭遇してもそこに答えのコードがあるので、実際にロジックを考えることはほぼ無いと言えます。(書いてあるコードを再現すれば動く)

やはり実際に「こんなものを作りたい」「そのために必要なことは?」「問題が発生」「試行錯誤」「解決!」といったプロセスを経験することで得られる学びは計り知れないほど大きいと思います。

③必要な機能を実装して価値を提供したいため

私自身、人に貢献することが好きなのでアプリ制作をするなら誰かに価値を提供できるものにしたいと思いました。

しかし実際に人に利用してもらえるサービスを提供するとなるとマーケティングや運用保守の要素がかなりに必要になりますし、初学の段階で「誰かに使ってもらうこと」に期待しすぎても学習の挫折要因になりかねないとも考えます。

そのため「自分でこんなのがあったら良いな」を少しずつ実現することを目的のひとつとしました。
具体的には「情報交換したいなぁ」「カロリー計算とかエクセルでやるの面倒だな」という課題ですね。

前提条件(学習期間)と開発期間は?

プログラミング学習を開始したのが2020年5月末。
仕事をしつつ6月は主にProgateで勉強し、10種類以上のコースを各1周。
HTML&CSS,Python,Java,PHP,SQL,Git,Sass,Rubyあたり。JavaScripとRailsも途中まで。

7月に小さなプログラミングスクールに入校。(コロナの影響でほぼオンライン)
内容は
・PHP/Laravelの3ヶ月コース。
・テキストベースのカリキュラムでHTML、PHP、Laravelを学びつつ課題をこなしていく形。
・開発環境はAWSのCloud9。
・週に1回オンラインで1時間のメンタリング。
という感じ。

8月上旬にはカリキュラムを終了し、
オリジナルアプリの制作に向けて企画/設計やER図作成を開始。
8月中旬から実際の開発作業に着手。
9月末でスクールは退会。
10月にAWSの学習とデプロイ作業。
という流れです。

開発期間としては、働きながら&勉強しながらとはいえ3ヶ月程度費やしていることになりますね。
時間をすべて記録していたわけではないので何とも言えませんが、
まず可処分時間は平均しておおよそ3時間/日程度を確保。
テレビはコンセントを抜き、Youtube等は撤廃、食事や大好きな読書も最小限にしました。

ITパスや基本情報技術者、AWSや各種技術書などの勉強も行っていたので、その半分の1.5時間をアプリ開発に充てたと考えると3ヶ月でだいたい150時間は費やしていることになります。
(6月からの学習時間や資格勉強などを全てトータルすると450時間ほどのプレイ時間になりますね..)

ここでやはり感じたのはエラーと戦ったり調べたりしている時間がほとんどで、
実際にコードを書いてる時間は少ないということですね。。

企画/設計はどんな感じで行った?

・アイディア出し、ペルソナ設定
・リーンキャンバスの作成
・エレベータピッチ、ユーザーストーリーマッピング、MVPの設定
・ワイヤーフレームの作成(手書き)
・ER図の作成(手書き)
という手順で行いました。

収益化が主目的ではないのでリーンキャンバスは埋められる範囲で、という感じですね。(KPIなど省略)
今思うとこのフェーズでもう少しアプリケーションのロジックの部分の設計ができていればなぁとも思いますが、「走りながら考える」というのもまた正解でもあるので、何とも言えないですね。

データベース設計(ER図)はどうなってる?

最初に手書きで簡単に作りつつも、開発しながら随時テーブルは更新/拡張していき、
結局はこのような形になりました。
ER図
実装してみるとやはりテーブルの関連付けの部分が難しく、キー制約や正規化などDBの奥の深さや面白さを感じました。どうしても開発前の構想とはやっているうちに変わっていくのでMigrationファイルがやや散らかってしまいました。。

インフラ構成はどうなってる??

すべてAWSで構成。主に使用したのはEC2、RDS、ELB、S3、Route53、ACMです。
インフラ構成図
本来はWebサーバーやDBサーバーは冗長化させて、S3もCloud Frontで..という想いはあるのですが、あくまで学習用。無料枠の中でということで断念しました。
ELBはロードバランサーというよりAmazon Certificate Managerで無料でhttps化(証明書発行)する目的で使用しています。(EC2インスタンスは1つで、ダミーのAZを設定)

またAWSを利用する上でルートユーザーはMFAでログイン、IAMユーザーに最小限の権限を持たせて作業、アクセスキーは厳重に管理、といったセキュリティ面は留意しました。(少し痛い目にあったので..)

アプリ制作する上で意識したことは?

ひとりチーム開発

まず、実務を想定した「ひとりチーム開発」を意識しました。
スクール等でチームを組んで共同開発..というのがひとつの理想ではありますが、個人開発なので「どうやったら実務でスムーズにキャッチアップできるか?」ということを考え、
 ・Git/GitHubによるソース管理
 ・issueを作成し、featureブランチを切って作業
 ・こまめにコミットし、プルリクエスト。
といった形で開発を進めました。

アプリ開発用にGitHubの新規アカウントを作成し、contributionsはこのようになりました。(これからも更新していきます)
githubの草

もちろん実務ではDockerでの開発環境、CI/CDパイプライン等いろいろと必要な事項があると思いますが「まずはアプリ自体を完成させなければ..」という想いから、このあたりの技術は転職活動〜入社の期間で少しずつ習得していこうと考えています。

わかりやすいコードにする

また「後から見た時に分かりやすいコード」を意識しました。
現職でも(構造設計の仕事してます)ドキュメントやメモをマメに残すようにしていて、自分で分かりやすいように意識してコメントは書いていたのですが、開発途中で書籍「リーダブルコード(O'RELLY社)」を読んで、リファクタリングを意識的に行うようになりました。
書籍の中では「そもそも最小限のコメントで分かるような変数名やロジックにする」というのが一番感銘を受けたポイントですね。。
変数やファイルの命名センスは大事。ロジックもコメントもセンスが大事。
(コードの美しさとは何か?みたいなテーマで飲みながら語らえるようになりたい)

インフラにAWSを使う

少し話が脱線してしまいましたが..
他には「インフラにAWSを使う」というのも実務を意識した点です。
なぜAWSか? については、
 ・教材でherokuにデプロイは経験していた(あとは読み込みも遅いとかドメイン名が長いとか)
 ・インフラ設計や構築について実際に手を動かして学びたかったから(分かってくるとインフラは楽しい)
 ・業務で最も使われているから(AzureやGCPも気になりますが)
です。

苦労した点は?

これはもう、とにかくたくさんあるのですが、、
・エラーや不具合にハマったとき(ほぼ毎日)
・投稿した画像を自動で加工する機能のあれこれ(InterventionImageを使いましたが色々問題発生)
・Ajaxでの計算機能,いいね機能の実装と理解(ControllerとViewとJSの処理のロジックが難しかった..)
・Gitでコンフリクトが生じた時(コミット打ち消しを行ったことなどに起因..)
・AWSの概念理解とデプロイ作業(https化も意外と大変だった..)
・環境構築の問題いろいろ(Composerメモリ足りない→php.iniとswapで対処等)
などなど、多岐にわたります。

ほとんど独習だったので「調べつつメンター契約して即時解決」というほうが良かったかも、、
ただ、苦労はしたけど"勉強が辛い"という感じはしなかったですね。(たぶん学習レベルだからですが)

全体的な感想

まずWebアプリを作ることは楽しいということ。
もちろん大変なことや覚えなければならないことは山ほどありますが、
 ・ロジックを考えてそれが実装できたとき
 ・エラーやデプロイの問題が解決できたとき
はガッツポーズしてしまうほどの達成感です。
また日々自分が成長していくことにもやりがいを感じます。
あとは同じ問題で困っている人に教えてあげることにも喜びを感じました。
実際にサービスを提供して誰かの課題を解決できたら最高かもしれないですね。

それと蛇足ですが..
プログラミングスクールは通う必要はないかなぁと個人的には思いました。
もちろん「大金を払っているので後戻りできない」とか「カリキュラムが系統的になっている」とか「質問できる環境」というメリットはありますが、、
今はもっと分かりやすい教材もありますし、google等で何でも調べられるので「独学で充分できるかも..」と後から感じています。

実際アプリケーション自体やインフラの構築は独学で学んだことがほとんどですしね。
(当時にもう少し情報の収集力があれば良かった..けど後からなら何でも言えますね)

今後の課題など

・Docker,CI/CDなど、開発環境,テスト,自動デプロイあたりについて学ぶ
・ユーザー登録時にメール認証させたりセキュリティ面もきちんと考える
・PHPを生の言語としてもっと深く学ぶ
・インフラ、ネットワーク、Linuxの基本をしっかり学ぶ

実際にサービスを運用するとなるとやるべきことはもっとあると思いますが、、少しずつ着実に力をつけていきたいなと考えております。


以上、最後までご覧いただきありがとうございました。
「おつかれ」「もっと頑張れよ」と思った方、LGTM!していただけたら今後の励みになります^^;

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

PHP7 正規表現 preg_match(): Compilation failed: invalid range in character class at offsetについて

preg_match関数で正規表現を用いた文字列検索でエラー

PHP7.3以降、正規表現を用いたpreg_match()関数

preg_match('|http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?|i',$line,$data)

こんな形でURL文字列検索を行ったら以下のようなエラーが、、、
preg_match(): Compilation failed: invalid range in character class at offset

解決法

正規表現の書き方自体は間違ってないはず(?)と思ってgoogle大先生に聞いてみると、
StackOverflowで以下のような回答を見つけました。
https://stackoverflow.com/questions/24764212/preg-match-compilation-failed-invalid-range-in-character-class-at-offset

まぁ要するに、PHP7.3以降挙動が変わったらしく、-(ハイフン)にはエスケープが必要になったらしい。
ということで、さっきのURL文字列の検索、正規表現を書き直すと
preg_match('|http(s)?://([\w\-]+\.)+[\w\-]+(/[\w\- ./?%&=]*)?|i',$line,$data)
こうなる。
なんか正規表現でエラー吐き出したら-(ハイフン)がエスケープされてるか確認してください。

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

Dockerのコンテナビルドでphp-radisのインストールに失敗する

Dockerでコンテナをビルドする中でphp-radisをインストールする際に下のエラーが出た
```
E: Failed to fetch http://ppa.launchpad.net/ondrej/php/ubuntu/pool/main/p/php-redis/php-redis_4.2.0-1+ubuntu16.04.1+deb.sury.org+1_amd64.deb 404 Not Found

E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?

php-redisのバージョンが新しくなり古いファイルが見つからないようだ
Dockerfileでアップデートしたものをインストールする場合は同じRUNの中でアップデートインストールしないいけないらしい
http://docs.docker.jp/engine/articles/dockerfile_best-practice.html
なのでDockerfileの記述は下のように
```doc
Dockerfile
apt-get update && apt-get install -yqq php-redis

これでビルド出来るようになった

以上

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