20210124のPHPに関する記事は21件です。

【初心者】PHPエラー箇所の特定方法(MAMP)

①ターミナルでMAMP内のログファイルに移動

ApplenoMacBook-Air:~ ユーザー名$ cd/Applications/MAMP/logs

logsの中の、php_error.logファイルにてエラーが吐き出されている。

②tailコマンドを実行(-fは、ファイルが更新されても常に追い続けてくれる)

ApplenoMacBook-Air:~ ユーザー名$ $ tail -f php_error.log

③場所の特定が完了

ApplenoMacBook-Air:~ ユーザー名$ tail -f php_error.log
[24-Jan-2021 21:46:13 Asia/Tokyo] PHP Notice:  Undefined variable: statusee in /Applications/MAMP/htdocs/ディレクトリ/application/core/View.php on line 63
[24-Jan-2021 21:46:13 Asia/Tokyo] PHP Notice:  Undefined variable: statusee in /Applications/MAMP/htdocs/ディレクトリ/application/core/View.php on line 111
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】初中級者が解くべき過去問精選 100 問を解いてみた【6問目/100】

アルゴリズムのアウトプット

ということで、
レッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】@e869120さん

AtCoder で水色コーダー、つまりレーティング 1200 を少ない問題数で達成するために、茶色コーダー・緑コーダーにとって適切な教育的良問を 100 問集めました。

こちらの記事の初中級者が解くべき過去問精選 100 問
をPHPで解いていきます。

<?php

$s = trim(fgets(STDIN));
$number = trim(fgets(STDIN));
$numToArr = str_split($number);

$ansList = [];
$firstList = [];
$secondList = [];

for ($i = 0; $i < $s; $i++) {
    //1桁目の組み合わせが既に出てきている場合は飛ばす
    if(in_array($numToArr[$i], $firstList)) {continue;}
    $firstList[] = $numToArr[$i];
    for ($j = $i + 1; $j < $s; $j++) {
        //1桁目、2桁目の組み合わせが既に出てきている場合は飛ばす
        if ($i >= $j) {continue;}
        if(in_array($numToArr[$i] . $numToArr[$j], $secondList)) {continue;}
            $secondList[] = $numToArr[$i] . $numToArr[$j];
       for ($k = $j + 1; $k < $s; $k++) {
           if ($j >= $k) {continue;}
           $ansList[] = $numToArr[$i] . $numToArr[$j] . $numToArr[$k];
        }   
    }
}

既に出てきたものをチェックするために、配列を持っておくことで計算量をへらす。

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

Dockerコンテナ間でのFTPアップロードするためのdocker-compose.ymlの書き方

PHP+ApacheからFTPサーバーへデータをアップロードするアプリケーションを開発するために、まずはDockerで開発環境を作ることになりました。
その際に、コンテナ(Apache + php)とコンテナ(Pure-FTPd)の間の通信でハマったので、記事にまとめます。

バージョン情報

  • PHP 7.4
  • Apache 2.4
  • Pure-FTPd v1.0.47

結論

最終的なdocker-compose.ymlとPHPのプログラムの記述はこんな感じ。

docker-compose.yml

docker-compose.yml
version: '3.7'
services:
  app:
    build: .
    ports:
      - 443:443
    volumes:
      - .:/var/www/html
  ftp-server:
    image: stilliard/pure-ftpd:latest
    ports:
      - "21:21"
      - "30000-30009:30000-30009"
    volumes:
      - ./ftp/data:/home/ftpusers
    environment:
      - PUBLICHOST=ftp-server
      - FTP_USER_NAME=ftp_user
      - FTP_USER_PASS=ftp_password
      - FTP_USER_HOME=/home/ftpusers

FTPでアップロードするプログラム

参考にした記事

ftp_upload.php
$ftp_server = 'ftp-server';
$ftp_port = 21;
$ftp_user_name = 'ftp_user';
$ftp_user_pass = 'ftp_password';
$ftp_send_file = '/hoge/data.txt';
$ftp_remote_file = '/hoge/data.txt';

// FTPサーバへ接続する
$conn_id = ftp_connect($ftp_server, $ftp_port);
if($conn_id == false){
    echo "FTPサーバへの接続失敗"."\n";
    exit();
}

// ユーザー名とパスワードでログインする
$login_result = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass);
if($login_result == false){
    echo "FTPサーバへのログイン失敗"."\n";
    // 接続を閉じる
    ftp_close($conn_id);
    exit();
}

//パッシブモードに設定
ftp_pasv($conn_id, true);

// ファイルをアップロードする
if (ftp_put($conn_id, $ftp_remote_file, $ftp_send_file, FTP_BINARY)) {
    echo "UPLOAD 成功"."\n";
} else {
    echo "UPLOAD 失敗"."\n";
}

// 接続を閉じる
ftp_close($conn_id);

Dockerfileはあまり重要ではないので割愛します。

重要なところ

docker-compose.yml
version: '3.7'
services:
# 省略
  ftp-server:
    # 省略
    environment:
          - PUBLICHOST=ftp-server
ftp_upload.php
$ftp_server = 'ftp-server';

解説

参考記事
Dockerはコンテナ名を使用してコンテナ間の通信をするそう。
なので、

docker-compose.yml
version: '3.7'
services:
# 省略
    ftp-server:

ここで設定したサービス名(container_nameで明示的にしていないとサービス名がコンテナ名となる)と、

docker-compose.yml
# 省略
environment:
      - PUBLICHOST=ftp-server

このPUBLICHOSTを合わせておかないと、Docker間でのFTP通信ができない。(ホスト名が違うとエラーが出る)

docker-compose.yml内の項目を合わせたら、これでFTPサーバー側の設定はできたので、

ftp_upload.php
$ftp_server = 'ftp-server';

上記のようにアプリ側でコンテナ名(ホスト名)を指定して、プログラムを実行すれば無事アップロードが成功する。(はず)

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

【PHP】メッセージ通知数表示

学習内容を備忘録としてまとめます。
メッセージ通知数表示機能を実装しましたので、作成方法を記載します。
message_tsuuchisuu6.gif

※通知数の表示は赤枠で囲っています

メッセージ機能については、下の記事で説明しているので割愛します。

【PHP】メッセージ機能実装

実装方法

実装方法について記載していきます。

テーブル構成

image.png
messatge_relationテーブルmessage_countを追加します。
このカラムは、ユーザー間にメッセージのやり取りがあった際にインクリメントされ、
通知数を表示するときはmessage_countカラムから、値を取り出して表示させます。

全体の流れ

本機能を、関数とカラム値の遷移を踏まえて全体的な流れをみていきます。
image.png

通知数をカウント

では、メッセージのやり取りがある際にmessage_countカラムの値をインクリメントするよう実装します。

message_add.php
:
    $stmt->execute($data);
    $dbh = null;

    if (!check_relation_message($user_id, $destination_user_id)) {
        insert_message($user_id, $destination_user_id);
    }
    insert_message_count($user_id,$destination_user_id); //ココ
    set_flash('sucsess', 'メッセージを送信しました');
    header('Location:../message/message.php?user_id=' . $destination_user_id . '');
:

メッセージを送信するときに、insert_message_countで値をインクリメントしています。

insert_message_count($user_id,$destination_user_id);

引数には自分のユーザーID送信先のユーザーIDを渡しています。

function insert_message_count($user_id,$destination_user_id){
  try {
    $dsn='mysql:dbname=db;host=localhost;charset=utf8';
    $user='root';
    $password='';
    $dbh=new PDO($dsn,$user,$password);
    $sql = "UPDATE message_relation
            SET message_count = message_count + 1
            WHERE ((user_id = :user_id and destination_user_id = :destination_user_id) or (user_id = :destination_user_id and destination_user_id = :user_id)) and user_id = :user_id";
    $stmt = $dbh->prepare($sql);
    $stmt->execute(array(':user_id' => $user_id,
                         ':destination_user_id' => $destination_user_id));
    return $stmt->fetch();
  } catch (\Exception $e) {
    error_log('エラー発生:' . $e->getMessage());
    set_flash('error',ERR_MSG1);
  }
}

UPDATE文でmessage_countをインクリメントしています。
条件文(where)ではメッセージのやり取りをしているユーザー同士のIDから該当のmessage_relationの列を取得して、message_relation.user_idがログイン中のユーザーIDと一致するようにしています。

message_relation.user_idをログイン中のユーザーidとする理由としては、表示する通知数を取得する際にこちらのカラムを利用するからです。
後ほど、詳しく説明します。

通知数を表示

相手からメッセージが届いていた時に、通知数を表示する処理を実装します。

message_top.php
:
foreach ($message_relations as $message_relation):
if($message_relation['destination_user_id']==$current_user['id']){
$destination_user=get_user($message_relation['user_id']);
}else{
$destination_user=get_user($message_relation['destination_user_id']);
}
$bottom_message=get_bottom_message($current_user['id'],$destination_user['id']);
$new_message_count=current(new_message_count($current_user['id'],$destination_user['id'])); //ココ
?>
:
    <div class="message_notification">
        <span id="message_count">
            <?php if($new_message_count!=0){  //
            print''.$new_message_count.'';   //ココ
            }?>                               //
        </span>
    </div>
:

変数$new_message_countに通知数を渡します。

function new_message_count($user_id,$destination_user_id){
  try {
    $dsn='mysql:dbname=db;host=localhost;charset=utf8';
    $user='root';
    $password='';
    $dbh=new PDO($dsn,$user,$password);
    $sql = "SELECT message_count
            FROM message_relation
            WHERE ((user_id = :user_id and destination_user_id = :destination_user_id) or (user_id = :destination_user_id and destination_user_id = :user_id)) and user_id = :destination_user_id";
    $stmt = $dbh->prepare($sql);
    $stmt->execute(array(':user_id' => $user_id,
                         ':destination_user_id' => $destination_user_id));
    return $stmt->fetch();
  } catch (\Exception $e) {
    error_log('エラー発生:' . $e->getMessage());
    set_flash('error',ERR_MSG1);
  }
}

引数のユーザーIDから通知数を取得しており、
条件文(where)では自分と相手のIDからmessage_relationの列を探して、user_idが相手のIDのものを取得しています。
そうすることで、先ほどインクリメントしていたmessage_countカラムの値が取得できます。

message_top.php
:
    <div class="message_notification">
        <span id="message_count">
            <?php if($new_message_count!=0){  //
            print''.$new_message_count.'';   //ココ
            }?>                               //
        </span>
    </div>
:

あとは表示したい場所に$new_message_countを表示させます。
0のときは表示をしないようにします。

通知数をリセット

メッセージ画面を開いたときに通知数を0にします。
これをしないとメッセージトップ画面に戻っても、まだ通知数が表示されてしまうからです。

message.php
:
$messages = get_messages($current_user['id'], $destination_user['id']);
$bottom_message=get_bottom_message($current_user['id'],$destination_user['id']);
reset_message_count($current_user['id'],$destination_user['id']);  //ココ
?>

<body>
:

reset_message_count関数では該当のカラムに0を渡します。

function reset_message_count($user_id,$destination_user_id){
  try {
  $dsn='mysql:dbname=db;host=localhost;charset=utf8';
  $user='root';
  $password='';
  $dbh=new PDO($dsn,$user,$password);
  $dbh->beginTransaction();
  $sql = 'UPDATE message_relation SET message_count = 0 WHERE ((user_id = :user_id and destination_user_id = :destination_user_id) or (user_id = :destination_user_id and destination_user_id = :user_id)) and user_id = :destination_user_id';
  $stmt = $dbh->prepare($sql);
  $stmt->execute(array(':user_id' => $user_id,
                       ':destination_user_id' => $destination_user_id));
  $dbh->commit();
} catch (\Exception $e) {
  error_log('エラー発生:' . $e->getMessage());
  set_flash('error',ERR_MSG1);
  $dbh->rollback();
  reload();
}
}

先ほどのnew_message_count関数と同様の条件文(where)で列を取得して、0を渡します。

上記が実装できれば、トップの動作画面のようになると思います。

【おまけ】新規メッセージ件数の通知

message_tsuuchisuu5.gif
※通知数の表示は赤枠で囲っています

新しいメッセージがあると、ログイン時に件数を通知する機能を実装します。

フラッシュメッセージ機能については、下の記事で説明しているので割愛します。

【PHP】フラッシュメッセージの実装

新規メッセージ数の取得

ログイン時に新しいメッセージあるか、DBを確認します。

user_login_check.php
:
    $_SESSION['user_id']=$rec['id'];
    $_SESSION['user_name']=$rec['name'];
    if(current(message_count($_SESSION['user_id']))!=0){ //ココ
    set_flash('sucsess','ログインしました       メッセージが'.current(message_count($_SESSION['user_id'])).'件届いています'); //ココ
    }else{
    set_flash('sucsess','ログインしました');
    }
    header('Location:user_top.php?page_id='.$rec['id'].'&type=main');
:

新規メッセージがあれば、message_count関数を使用して件数をフラッシュメッセージで表示します。

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

【PHP】初中級者が解くべき過去問精選 100 問を解いてみた【5問目/100】

アルゴリズムのアウトプット

ということで、
レッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】@e869120さん

AtCoder で水色コーダー、つまりレーティング 1200 を少ない問題数で達成するために、茶色コーダー・緑コーダーにとって適切な教育的良問を 100 問集めました。

こちらの記事の初中級者が解くべき過去問精選 100 問
をPHPで解いていきます。

<?php

$s = trim(fgets(STDIN));
$s = str_replace(array("\r\n","\r","\n"), '', $s);
$s = explode(" ", $s);

$priceA = $s[0];
$priceB = $s[1];
$priceC = $s[2];
$needA = $s[3];
$needB = $s[4];

$priceList = [];

//AとBの少ない方の数分をABで買って足りないを買い足す場合
$buyA = 0;
$buyB = 0;
$buyC = min($needA, $needB) * 2;
if ($needA <= $needB) {
    $buyB = $needB - $buyC / 2;
} else {
    $buyA = $needA - $buyC / 2;
}
$priceList[] = $buyA * $priceA + $buyB * $priceB + $buyC * $priceC;

//Cを買わない場合
$priceList[] = $priceA * $needA + $priceB * $needB;

//Cのみ買う場合
$priceList[] = $priceC * 2 * max($needA, $needB);

echo min($priceList) . "\n";
}

全列挙するとオーバーフローするので、条件別に分けて計算量を減らす問題

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

【最新版】はじめてのLaravel!Laradockを用いたLaravel開発環境構築

  • 最終編集日(2021年1月)

環境構築だけ読みたい方は、Laravelの環境構築まで読み飛ばしてください。

概要

本記事は、PHPのフレームワークであるLaravelの環境構築を開設した記事です。

構築する Laravel 及び PHP のバージョンは以下になります。

  • Laravel 8.x
  • PHP 7.4

初めてLaravelを使う方、一度環境構築で挫折した方でもつまずかないことを心掛けて執筆しました。

何かわからないことことがありましたら、コメントでお知らせください!

環境

Docker for Desktopのインストールが必要になります

  • Mac
  • Windows
    • Windows 10 homeの方は、Windowsをバージョン2004,ビルド19041以上にアップデートした上でWSL 2のインストールが必要になります
    • Docker Toolboxでも環境構築をすることはできますが、一部コマンドが異なる場合があります
  • 本記事ではMySQLを使います。PostgreSQLでの環境構築も可能ですが、Laradockの起動で一部コマンドが異なります。

Laradock で使用できる PHP バージョン早見表

Laradockで使用できるPHP、Laravelのバージョンは以下の通りです。
※2021年1月現在公式サポートされているPHPのバージョンのみ記載しています。

PHPバージョン Laradock対応
7.3
7.4
8.0 ×

前提条件

  • gitをインストールしていること
  • Docker for Desktopをインストールしていること

Terminalで以下のコマンドを実行して正しく出力されるかを確認してください。

$ git --version
git version 2.9.0
$ docker --version
Docker version 19.03.1, build 74b1e89e8a 
$ docker-compose --version
docker-compose version 1.24.1, build 4667896b

※Dockerのバージョンは17.12以降である必要があります。
(参考) Getting Started

Laravelの環境構築

それでは実際にLaravelの環境構築を行っていきましょう。

プロジェクトディレクトリの作成

mkdirコマンドでLaradockをインストールするディレクトリを作成します。ここでは、laravel-workspaceとしています。

mkdir laravel-workspace

cdコマンドで作成したディレクトリに移動します。

cd laravel-workspace

Laradockのインストール

Laradockをインストールしていきます。

以下のコマンドを実行します。

$ git clone https://github.com/Laradock/laradock.git

インストールが終わると以下のように表示されます。

$ git clone https://github.com/Laradock/laradock.git
Cloning into 'laradock'...
remote: Enumerating objects: 11524, done.
remote: Total 11524 (delta 0), reused 0 (Rdelta 0), pack-reused 11524eceiving objects: 100% (11524/11524), 11.36 MiB | 3Receiving objects: 100% (11524/11524), 11.42 MiB | 346.00
 KiB/s, done.
Resolving deltas: 100% (6203/6203), done.
Checking connectivity... done.

laradockディレクトリが作成されていることがわかります。

$ ls
laradock/

Laradockのインストールは以上です

.env ファイルの編集

.envファイルを編集することで、プロジェクトの環境変数を定義していきます。使用するデータベースやPHPのバージョンなどもここで指定します。

cdコマンドを使って先ほど作成されたlaradockディレクトリに移動しましょう。

$ cd laradock

laradockディレクトリの中には、env-exampleという.envのサンプルがあります。これをコピーして使用します。以下のコマンドでコピーしてください。

cp env-example .env

.envを編集していきます。お好きなエディタで.envファイルを開いてください。

1.プロジェクトの相対パスの指定

プロジェクトを作成するにあたり、パスを指定します。
ここでは、laravel-projectというプロジェクトをlaradock同じ階層に作成します。

laravel-workspace
├── laradock
└── laravel-project

8行目を以下のように編集してください。

.env
- APP_CODE_PATH_HOST=../
+ APP_CODE_PATH_HOST=../laravel-project

2.データベースのディレクトリ変更

デフォルトでは、データベースなどがホームディレクトリに保存されるようになっています。
プロジェクトディレクトリ配下に作成されるように設定を変更しましょう。

17行目を以下のように編集してください。

.env
- DATA_PATH_HOST=~/.laradock/data
+ DATA_PATH_HOST=.laradock/data

3. コンテナ名の変更

ここでは、Dockerを起動するときのコンテナ名を指定していきます。「コンテナ?」という方はコピペしていただいて構いません。
※Dockerに関しては「いまさらだけどDockerに入門したので分かりやすくまとめてみた」の記事が非常にわかりやすいです。

36行目を以下のように編集してください。

.env
- COMPOSE_PROJECT_NAME=laradock_
+ COMPOSE_PROJECT_NAME=laravel-project

4. PHPのバージョン設定

PHPのバージョンを設定します。

42行目を使用したいPHPのバージョンに変更します。
ここでは、PHP 7.4を指定します。

.env
- PHP_VERSION=7.3
+ PHP_VERSION=7.4

2021年1月現在のLaradockがサポートしているPHPのバージョンに関してはこちらをご覧ください。

Laradockの起動

それではLaradockを起動していきます。

laradockディレクトリで以下のコマンドを実行してコンテナを起動します。

postgresを使用している場合は、mysqlの部分をpostgresに置き換えてください。

$ docker-compose up -d php-fpm nginx mysql workspace

以下のように表示されればOKです。

$ docker-compose up -d nginx mysql workspace
.
.
.
Creating laravel-project_docker-in-docker_1 ... done
Creating laravel-project_mysql_1            ... done
Creating laravel-project_workspace_1        ... done
Creating laravel-project_php-fpm_1          ... done
Creating laravel-project_nginx_1            ... done

Laravelアプリケーションの作成

それではLaravelのアプリケーションを作成していきます。以下のコマンドを実行してください。

$ docker-compose exec workspace bash

以下のような表示になれば正常にbashが起動できています。

$ docker-compose exec workspace bash
root@6095e16a845d:/var/www#

コマンドの最後には、Laravelのバージョンを指定しています。2021年現在、最新版はLaravel8.xです。ここでは8.*を指定していますが、コマンドの最後の数字を変えることで、別のバージョンのLaravelでアプリケーションを作成をすることができます。

# composer create-project --prefer-dist laravel/laravel="8.*" .

※アプリケーションの作成には少し時間がかかります。

Application key set successfully.と表示されればアプリケーションの作成は完了です。

以下のようにプロジェクトの相対パスの指定で指定した名前と同じフォルダが作成されています。

laravel-workspace
├── laradock
└── laravel-project

アプリケーションの確認

http://localhost にアクセスしてLaravelプロジェクトが作成されているか確認してみましょう。
image

上記のように表示されていれば正常にアプリケーションが作成されています。

コンテナの停止

コンテナを停止するときは以下のコマンドで停止することができます。

$ docker-compose stop

参考

※本記事はTechpitの教材を一部修正したものです。

最後まで読んでいただきありがとうございました。今後も、PHP・Laravelに関する記事を投稿していきますので、もしよろしければ、Techpitをフォローしてお待ちください!

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

Docker上のLaravelにJetstreamをインストールする。

https://qiita.com/ucan-lab/items/56c9dc3cf2e6762672f4
https://qiita.com/ucan-lab/items/5fc1281cd8076c8ac9f4
上記の記事を参考に構築したLaravel8に、Jetstreamをインストールします。

ComposerでJetstreamをインストールする

まず、Laravelをインストールしたコンテナに入ります。

$ docker-compose exec [コンテナ名] [コマンド名]

次にcomposerコマンドを実行します。

$ composer require laravel/jetstream

jetstream:installを実行

フロントエンドスタックとして、LivewireかInertiaを指定します。
Livewireはテンプレート言語にLaravelのBladeを利用します。
一方、InertiaはVue.jsを利用します。
どちらの場合も、--teamsをつけることでチームサポートを有効にできます。

$ php artisan jetstream:install livewire
$ php artisan jetstream:install livewire --teams
$ php artisan jetstream:install inertia
$ php artisan jetstream:install inertia --teams

npmの依存パッケージをインストール・構築

npmがインストールされているコンテナで実行します。

$ npm install && npm run dev

データベースをマイグレートする

こちらは再びLaravelのコンテナで実行します。

$ php artisan migrate

確認

Laravelのホーム画面を確認すると、右上にログイン画面、ユーザー登録画面へのリンクが表示されており、それぞれの画面へ遷移できます。

スクリーンショット 2021-01-24 17.17.48.png
スクリーンショット 2021-01-24 17.21.37.png
スクリーンショット 2021-01-24 17.21.53.png

composer require aravel/jetstream でメモリエラーが出た場合

php.iniファイルのmemory_limitを一時的に無制限にすることで対応可能です。
インストール後、memory_limitの値を元に戻しておいてください。

php.ini
memory_limit = 256M

php.ini
memory_limit = -1
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

共同開発講座を受講中にコツコツ書いた「詰まったメモ」をジャンル別に全て公開します【データベース(migration/seeding)編】

はじめに

タイトルの通り、共同開発講座を受講中に起きたトラブルや詰まりポイントをまとめたメモ(約50記事)をジャンル別に全て公開します。
ジャンルは以下の通りなので、気になるものがあればぜひご覧ください。

【ジャンル一覧】

GitHub関連
Laravel関連
データベース(migration/seeding)関連
環境関連
総まとめ集

下記の点を、ご了承ください。
・ジャンルで統一しているため、内容に関しては統一性はありません。
・メモの難易度もバラバラです。
・初学者向けの内容となっております。
・自分用のメモを転用しておりますので、表現が稚拙な部分があるかと思います。
・あくまで僕のメモです!!

・Laravelのカラム作成で使えるデータ型

  • increments('id') ... 「符号なしINT」を使用した自動増分ID(主キー)
  • binary('カラム名') ... バイナリデータカラム
  • boolean('カラム名') ... 真偽値カラム
  • char('カラム名', 長さ) ... 長さを指定する文字列カラム
  • date('カラム名') ... 日付カラム
  • time('カラム名') ... 時間カラム
  • dateTime(カラム名) ... 日時カラム
  • double('カラム名', 桁数, 小数点以下桁数) ... ○桁で小数点以下×桁の小数カラム
  • enum('カラム名', ['定数', '定数']) ... ENUMカラム
  • integer('カラム名') ... 数値データカラム
  • json('カラム名') ... JSONフィールドカラム
  • timestamp('カラム名') ... TIMESTAMPカラム
  • timestamps() ... created_atとupdate_atカラム
  • nullableTimestamps() ... NULL値を許す以外、timestamps()と同じ
  • string('カラム名') ... VARCHARカラム
  • string('カラム名', 長さ) ... 長さ指定のVARCHARカラム
  • text('カラム名') ... TEXTカラム

・migration時の外部キー制約エラー

外部キー制約をつけてマイグレーションをするとエラーが起きてしまう件について

まず外部キー制約について

外部キー制約の一例

$table->foreign('sale_status_id')->references('sale_status_id')->on('m_sales_statuses')->onDelete('cascade');

解説

子テーブルのカラムとして定義したsale_status_idを外部キーと指定し、それはm_sales_statusesというテーブルのsale_status_idというカラムと紐付くこととする

onDelete(‘cascade’)・・・親テーブルの行を削除したときに、子テーブルないの一致する行を自動的に削除されるようにするオプションのこと。例えばユーザーが消えるとそのユーザが持っている動画が消える等。

エラー内容を見ていきます

エラー内容

 Illuminate\Database\QueryException  : SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint (SQL: alter table `m_products` add constraint `m_products_sale_status_id_foreign` foreign key (`sale_status_id`) references `m_sales_statuses` (`sale_status_id`))

原因

①データの型が合っていない
Integer やstring、unsignedなど  
上記のカラム作成で使えるデータ型を確認する(これが割と間違ってることが多い)

②マイグレーションする順番が違う(作成順序が違う)
親テーブルと子テーブルがあるが、先に参照される側の親テーブルを先にマイグレーションしてテーブルを作成しておかないといけない。
親テーブルが存在して初めて、外部キー制約を記述したものをマイグレーションできる

・seeding時のエラー

migrationはできたけどデータの流し込み(seedingがうまくいかない)
seedingを行うとエラーが発生する

エラー内容

 Illuminate\Database\QueryException  : SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`laravel`.`m_products`, CONSTRAINT `m_products_sale_status_id_foreign` FOREIGN KEY (`sale_status_id`) REFERENCES `m_sales_statuses` (`sale_status_id`) ON DELETE CASCADE) (SQL: insert into `m_products` (`product_id`, `product_name`, `category_id`, `price`, `description`, `sale_status_id`, `product_status_id`, `resist_date`, `user_id`, `delete_flag`) values (1, 黒毛和牛サーロイン, 1, 8000, なめらかでとろける食感と、甘くコクがある上品な香りが特徴です, 1, 1, 2020-11-22 12:16:43, 1, ))

原因と解説

子テーブルに外部キー制約が存在する場合、seedingの順番にも気をつける必要がある。
親テーブルより先に子テーブルをmigrationした場合にエラーが出たのと同様の理由でエラーが出る。
seeding時も親テーブルにデータが入っていない状態で、外部キーがある子テーブルにデータを流し込むとエラーが発生する。
seedingに関しても親→子の順番でseedingを行わなければならない

・SQL 1364 エラー

ブラウザ上でフォームを飛ばした時に起こるエラーです

エラー内容

SQLSTATE[HY000]: General error: 1364 Field 'user_classification_id' doesn't have a default value

エラーの意味

「user_classification_idのデフォルト値がない」

つまり…
「データベースにデータを登録する際に、user_classification_idの値がないとダメですよ」
っていう意味。
デフォルト値をつけろと言われている。

解決策

今回はフォームにuser_classification_idの要素を入力する欄がなかったため、データを登録時に送ることができなかった。
でも、データベースのusersテーブルにはuser_classification_idが存在するため、どうにかしてデータを入れないといけない。
そこで、migrationの段階で手を打っておく必要がある。
具体的には、migrationでusersテーブルのカラム定義の際に、修飾子をつけること。
修飾子は ->nullable()  もしくは ->default(○○)
つまり存在しなくても良しとするか、デフォルト値を予め設定しておく。というような修飾子をつけて、マイグレーションすることでこのエラーは解決する。
ただ、可能な限り ->default(○○) を使ったほうがいい。

例)m_users.php(マイグレーションファイル)

public function up()
    {
        Schema::create('m_users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('password', 64);
            $table->string('last_name', 16);
            $table->string('first_name', 16);
            $table->integer('zipcode');
            $table->string('prefecture', 16);
            $table->string('municipality', 16);
            $table->string('address', 32);
            $table->string('apartments', 32);
            $table->string('email', 128);
            $table->string('phone_number', 16);
            $table->integer('user_classification_id')->unsigned()->default(1);//←ここ!! 
            //外部キー制約
            $table->foreign('user_classification_id')->references('id')->on('m_users_classifications')->onDelete('cascade');
            
            $table->string('company_name', 128)->nullable();//←ここ!! 
            $table->char('delete_flag', 1)->nullable();//←ここ!! 
            $table->rememberToken();
            $table->timestamps();
        });
    }

・マイグレーション実行コマンド

カラムの追加をしたいときのマイグレーションファイルの作成コマンド

$ php artisan make:migration add_title_to_users_tables  - -table=users

マイグレーション実行コマンド

php artisan migrate:reset

全てのテーブルにおいて今まで実行したマイグレーションを初期化。
再度migrationは実行されない。

php artisan migrate:refresh

全てのテーブルにおいて今まで実行したマイグレーションを初期化
再度migrationを実行する。

php artisan migrate:fresh

全てのテーブルを削除。
再度migrationを実行する。

php artisan migrate:rollback

最後に実行したマイグレーションを元に戻す。
Step オプションをつけると元に戻す回数を指定できる。

・migration と seeding の総まとめ

マイグレーションとは

ザックリ言うとテーブルを作成するための作業
マイグレーションファイルをコマンドで作成し、作りたいテーブルのカラムを記載していく。
記載したファイルをコマンドでマイグレーションすると、データベースにテーブルが作成される。
また、マイグレーションファイルにはカラムに対してデータ型や修飾子、FKを記載しておくとマイグレーション実行時に反映される。
ただ、FKの関係でマイグレーションする順番が重要になってくるので、マイグレーションファイルを作成する順番に気を付ける。(FKで指定されているテーブルがないと、エラーが起こるので、先に親テーブルからマイグレーションされるように作成順番を考える。→子テーブルを後に作成する)
今回はphpmyadminを使用しているので、ブラウザ上で視覚的に確認することができる。
Phpmyadmin上で、使用するデータベースを作成しておかないと、テーブルをその中に作ることができないので注意

マイグレーションファイルの作成(カラム、FK、その他修飾子の作成)

 php artisan make:migration ファイル名
でファイル作成。

ファイルの中身はこんな感じ

 public function up()
    {
        Schema::create('m_products', function (Blueprint $table) {
            $table->increments('id');
            $table->string('product_name', 64);
            $table->integer('category_id')->unsigned();
            $table->integer('price')->unsigned();
            $table->string('description', 256);
            $table->integer('sale_status_id')->unsigned();
            $table->integer('product_status_id')->unsigned(); 
            $table->timestamp('resist_date');
            $table->integer('user_id')->unsigned();
            $table->char('delete_flag', 1);

            $table->foreign('sale_status_id')->references('id')->on('m_sales_statuses')->onDelete('cascade');
            $table->foreign('product_status_id')->references('id')->on('m_products_statuses')->onDelete('cascade');
        });
    }

データ型について

上記のファイル内の記載のうち、string integer timestamp などがデータ型にあたる。
そのデータはどういう形式のデータなのか?文字なのか?数字なのか?その時の時間なのか?
を指定することができる。詳しくはGoogle先生に。

php artisan migrate の実行

このコマンドを実行すると、作成したマイグレーションファイルがテーブルとして作成される。
エラーになる場合は、他のエラーに関するメモを参照(上記記載)。

migrationエラーについて

多かったエラーは
①データ型があっていない場合
②migrationする順番が違い、外部キー制約がつかないために起こるエラー

シーディングとは

ザックリい言うと、テストデータを一気に作成することができる機能で、ファイルを作成してシーディングを行うことテーブル内にデータが挿入される。

テストデータの決定

シーディングを行うテーブルに適したデータを考える。
例えば、商品(肉とか魚)のテストデータを考える時は、それぞれのカラムに適応した具体的なデータ(商品名や値段)を考える。
何個でも一気に登録できるが、まぁ、テストなので5~10個程度あれば十分。

シーディングファイルの作成

 php artisan make:seeder ファイル名
上記コマンドでシーディングファイルが作成される。

中身には前項で決定したテストデータを記載していく。

実際の内容としてはこんな感じ

public function run()
    {
        DB::table('m_products')->insert([
           'id' => 1,
           'product_name' => '黒毛和牛サーロイン',
           'category_id' => 1,
           'price' => 8000,
           'description' => 'なめらかでとろける食感と、甘くコクがある上品な香りが特徴です' ,
           'sale_status_id' => 1,
           'product_status_id' => 1,
           'resist_date' => date('Y-m-d H:i:s'),
           'user_id' => 1,
           'delete_flag' => ''
        ]);
        DB::table('m_products')->insert([
            'id' => 2,
            'product_name' => 'A5ランク松坂牛',
            'category_id' => 1,
            'price' => 12000,
            'description' => '松坂牛は濃厚で上品な甘みが絶品!' ,
            'sale_status_id' => 1,
            'product_status_id' => 1,
            'resist_date' => date('Y-m-d H:i:s'),
            'user_id' => 1,
            'delete_flag' => ''
         ]);

例として2つだけここに書いたが、実際は何個でも良い。

php artisan db seed  の実行

このコマンドを実行することで、作成したシーディングファイル(テストデータ込み)がシーディングされて、当該テーブルにデータが挿入される。
このコマンドの実行時にエラーとしてよく起こるのは
①子テーブルと親テーブルの関係
②外部キー関連
が多いが、詳しくは別のメモにまとめてあるものを参照(上記記載)。

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

Docker + Laravel + Vue の環境構築 - Docker編

はじめに

  • DockerWindows10 Homeにも対応したということでDockerでの環境構築を始めてみました。
  • DockerVueは初めて触ります。Laravelのみ少し経験あり。
  • Xserverにデプロイするつもりで構築。
    • ドキュメントルートはプロジェクト直下の public_html
    • 本来はWebサーバーはApacheだけど、慣れたいのでnginx使ってます。

環境

  • Windows 10 Home(20H2)

Dockerのインストールについて

  • インストール
  • 発生したトラブル
    • Win10にすんなりインストールして使えるとはいかず、バージョンアップをしました。
    • Windowsアップデートがまだの場合、下記公式からダウンロード可能です。
  • 生成されたバージョン
    • Docker: 20.10.2
    • Docker Compose: 1.27.4

image.png

Docker

  • Docker単体でも使えるがDocker Composeを使うのが便利なようです。
  • docker-composeはymlファイルに複数のコンテナの定義を記述し、立ち上げができます。
  • WEBサーバーやDB等の1つのファイルで管理でき、コンテナ間の連携もできる大変便利なツールです。
  • 今回使うDocker Desktopのインストール内容にDocker Composeも含まれています。

Docker Composeの準備

  • 下記のymlを実行し、以下の環境が作られました。
    • php : 7.4.14
    • nginx : 1.19.6
    • MySql : 5.7.32
    • PhpMyAdmin : 5.0.4
    • npm : 6.12.1
    • node : 12.13.1
docker-compose.yml
version: '3'

services:
  php:
    build: ./docker/php
    volumes:
      - ./src:/var/www

  nginx:
    image: nginx
    ports:
      - 80:80
    volumes:
      - ./src:/var/www
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - php
    tty: true

    command: [nginx-debug, '-g', 'daemon off;']

  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: sample_project
      MYSQL_USER: docker
      MYSQL_PASSWORD: docker
      TZ: 'Asia/Tokyo'
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
      - ./docker/db/data:/var/lib/mysql
      - ./docker/db/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./docker/db/sql:/docker-entrypoint-initdb.d
    ports:
      - 3306:3306

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    environment:
      - PMA_ARBITRARY=1
      - PMA_HOST=db
      - PMA_USER=docker
      - PMA_PASSWORD=docker
    links:
      - db
    ports:
      - 4000:80
    volumes:
      - ./docker/phpmyadmin/sessions:/sessions

  node:
    image: node:12.13-alpine
    tty: true
    volumes:
      - ./src:/var/www
    working_dir: /var/www

php -> Dockefileの準備

Dockerfileに各コンテナの詳細情報を定義します。
これはphp用のDockerfileで、今回個別に用意するのはこれだけです。

FROM php:7.4-fpm
COPY php.ini /usr/local/etc/php/

# 1. 必要なツールをインストール
RUN apt-get update \
  && apt-get install -y zlib1g-dev mariadb-client vim libzip-dev \
  && docker-php-ext-install zip pdo_mysql \
  && git vim curl cron unzip

# 2. Composerインストール
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
RUN php composer-setup.php
RUN php -r "unlink('composer-setup.php');"
RUN mv composer.phar /usr/local/bin/composerd

ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME /composer
ENV PATH $PATH:/composer/vendor/bin

WORKDIR /var/www

# 3. laravelインストール
RUN composer global require "laravel/installer"

php -> php.iniの準備

php.ini
[Date]
date.timezone = "Asia/Tokyo"
[mbstring]
mbstring.internal_encoding = "UTF-8"
mbstring.language = "Japanese"

nginx -> default.conf

server {
  listen 80;
    index index.php index.html;
    root /var/www/public_html;

  location / {
    try_files $uri $uri/ /index.php?$query_string;
    }

  location ~ \.php$ {

    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass php:9000;
    fastcgi_index index.php;
    include fastcgi_params;
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      fastcgi_param PATH_INFO $fastcgi_path_info;
  }
}

ここまでのディレクトリ構成

適当な場所にプロジェクトフォルダを作り、下記のように配置してください。
[]で囲ってる箇所はディレクトリです。

[myLaravel]
  ├── [docker]
  │    ├── [nginx]
  │    │   └── default.conf
  │    └── [php]
  │        ├── Dockerfile
  │        └── php.ini
  └── [src]

Docker Composeの実行

  • コマンドプロンプト、またはGit Bash等を使ってプロジェクトフォルダへ移動してください。
  • 下記コマンドを実行するとDocker Composeの内容からコンテナが作成され、下記のように動作を確認できます。
~/myLaravel$ docker-compose up -d

Starting mylaravel_db_1         ... done
Starting mylaravel_node_1       ... done
Starting mylaravel_php_1        ... done
Starting mylaravel_phpmyadmin_1 ... done
Starting mylaravel_nginx_1      ... done

Docker Desktopを確認すると下記のように指定したプロジェクトと、コンテナが作成されていることが確認できます。
image.png

これで環境が整いました。

Docker Desktopからの操作

各コンテナにカーソルを合わせると、いくつかアイコンが表示されます。

image.png

左から

  • ブラウザを開く
    • WebサーバーやPhpMyAdmin等ブラウザ上で確認できるものにだけ、アイコンが表示されます。
  • コンソール画面を開く
    • コンソール上での操作を行います。
    • 他の記事だとコマンドdocker-compose exec php bashを使ってるものが多いです。
    • なぜか僕はこのコマンドでエラーになるので、デスクトップアプリ経由で使ってます。
      • docker-compose.ymlの各コンテナの設定にtty:trueがあればできるはずなんですが解決できなかったので、設定から外しています。
    • PHPコマンドやnpmを使う時はそれぞれのコンソール上からしか実行できないので、やりたいことに合わせたコンソールを開く必要があります。ちょっとめんどくさい。
  • コンテナの停止
  • コンテナの再起動
  • コンテナの削除

最終的にできた構成

db, phpmyadminディレクトリが作られました。中身は特に気にしなくていいので省略してます。

[myLaravel]
  ├── [docker]
  │    ├── [db]
  │    │   └── ...
  │    ├── [nginx]
  │    │   └── default.conf
  │    ├── [php]
  │    │   ├── Dockerfile
  │    │   └── php.ini
  │    └── [phpmyadmin]
  │        └── [sessions]
  └── [src]

次回

次回はLaravelとVueの環境構築を進めます。

参考にさせていただいた記事

https://qiita.com/phper_sugiyama/items/654f7a9171ae5ccf3e83
https://qiita.com/rockinruuula1227/items/83f3f1406f339083ef3f

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

【PHP】初中級者が解くべき過去問精選 100 問を解いてみた【4問目/100】

アルゴリズムのアウトプット

ということで、
レッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】@e869120さん

AtCoder で水色コーダー、つまりレーティング 1200 を少ない問題数で達成するために、茶色コーダー・緑コーダーにとって適切な教育的良問を 100 問集めました。

こちらの記事の初中級者が解くべき過去問精選 100 問
をPHPで解いていきます。

<?php
$input_line = trim(fgets(STDIN));
$input_line = str_replace(array("\r\n","\r","\n"), '', $input_line);
$input_line = explode(" ", $input_line);
$arr = [];
for ($i = 0; $i < $input_line[0]; $i++) {
    $s = trim(fgets(STDIN));
    $s = str_replace(array("\r\n","\r","\n"), '', $s);
    $s = explode(" ", $s);
    $arr[] = $s;
}
$sumArr = [];

for ($k = 0; $k < $input_line[1]; $k++) {
    for ($l = 0; $l < $input_line[1]; $l++) {
        if ($k <= $l) {continue;}
        $sum = 0;
        for ($j = 0; $j < $input_line[0]; $j++) {
            $sum = $sum + max($arr[$j][$k], $arr[$j][$l]);
        }
        $sumArr[] = $sum; 
    }
}
echo max($sumArr) . "\n";
}

配列でデータを持って条件に合わせて参照する問題。

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

【PHP】住所を都道府県/市区町村/それ以降に分ける

参考

この記事を作るにあたり、以下のサイトを大いに参考にしました。

なるべく短い正規表現で住所を「都道府県/市区町村/それ以降」に分けるエクストリームスポーツ
PHP: preg_match - Manual
[PHP] preg_match の正規表現の中で日本語(マルチバイト文字)を使う « Codaholic

コード

separate_address.php
<?php

/**
 * @param string $address
 * @return array
 */
function separate_address(string $address)
{
    if (preg_match('@^(.{2,3}?[都道府県])(.+?郡.+?[町村]|.+?市.+?区|.+?[市区町村])(.+)@u', $address, $matches) !== 1) {
        return [
            'state' => null,
            'city' => null,
            'other' => null
        ];
    }

    return [
        'state' => $matches[1],
        'city' => $matches[2],
        'other' => $matches[3],
    ];
}

var_dump(separate_address('京都府京都市東山区清水1丁目294'));
var_dump(separate_address('東京都府中市宮西町2丁目24番地'));

var_dump(separate_address('不正な住所'));

// 市より先がない
var_dump(separate_address('東京都府中市'));

// 市名に村が入ってるのでうまく分けられていない。今回は非対応
var_dump(separate_address('東京都東村山市本町1丁目2番地3'));

$ php separate_address.php
array(3) {
  ["state"]=>
  string(9) "京都府"
  ["city"]=>
  string(18) "京都市東山区"
  ["other"]=>
  string(16) "清水1丁目294"
}
array(3) {
  ["state"]=>
  string(9) "東京都"
  ["city"]=>
  string(9) "府中市"
  ["other"]=>
  string(24) "宮西町2丁目24番地"
}
array(3) {
  ["state"]=>
  NULL
  ["city"]=>
  NULL
  ["other"]=>
  NULL
}
array(3) {
  ["state"]=>
  NULL
  ["city"]=>
  NULL
  ["other"]=>
  NULL
}
array(3) {
  ["state"]=>
  string(9) "東京都"
  ["city"]=>
  string(6) "東村"
  ["other"]=>
  string(27) "山市本町1丁目2番地3"
}

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

phpMyAdmin 4.6.6 のWarning対処

同じような環境を何度も構築しては、phpMyAdminから警告がでるので、いい加減まとめる。
最新をソースからinstallすればいいんだけど、aptに慣れた体にはマジ無理・・。

環境

  • Ubuntu 18.04.5 LTS (Bionic Beaver)
  • Apache 2.4.29
  • MySQL 5.7.32-0ubuntu0.18.04.1 - (Ubuntu)
  • PHP 7.2.24-0ubuntu0.18.04.7
  • phpMyAdmin 4.6.6deb5ubuntu0.5

インポート/エクスポート回り2箇所でWarningがでるので対処。

Warning in ./libraries/plugin_interface.lib.php#551

Warning in ./libraries/plugin_interface.lib.php#551
 count(): Parameter must be an array or an object that implements Countable

php7.2から、count(null)に対して0ではなく、warningを返すようになったため。
/usr/hsare/phpmyadmin/libraries/plugin_interface.lib.php の551行目あたり

/usr/share/phpmyadmin/libraries/plugin_interface.lib.php
if ($options != null && count($options) > 0) {

を、

/usr/share/phpmyadmin/libraries/plugin_interface.lib.php
if ($options != null && count((array)$options) > 0) {

に修正。もう一箇所は、、

Warning in ./libraries/sql.lib.php#613

Warning in ./libraries/sql.lib.php#613
 count(): Parameter must be an array or an object that implements Countable

/usr/share/phpmyadmin/libraries/sql.lib.phpの、613行目あたり。

/usr/share/phpmyadmin/libraries/sql.lib.php
  || (count($analyzed_sql_results['select_expr'] == 1)

を、

/usr/share/phpmyadmin/libraries/sql.lib.php
|| ((count($analyzed_sql_results['select_expr']) == 1)

参考(ありがとう)

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

共同開発講座を受講中にコツコツ書いた「詰まったメモ」をジャンル別に全て公開します【Laravel編】

はじめに

タイトルの通り、共同開発講座を受講中に起きたトラブルや詰まりポイントをまとめたメモ(約50記事)をジャンル別に全て公開します。
ジャンルは以下の通りなので、気になるものがあればぜひご覧ください。

【ジャンル一覧】

・GitHub関連
・関連
・php関連
・データベース(migration/seeding)関連
・環境関連
・総まとめ集

下記の点を、ご了承ください。
ジャンルで統一しているため、内容に関しては統一性はありません。
なるべく有用性の高いメモの使用を心がけておりますが、あくまで初学者向けの内容となっております。
自分用のメモを転用しておりますので、表現が稚拙な部分があるかと思います。

・MVCの考え方(言葉で説明)

ユーザ登録機能を題材にMVCの実際の動きを確認します

ルーティング(Web.php)の

Route::get('signup', 'Auth\RegisterController@showRegistrationForm')->name('signup');

この意味は
signupと検索されればRegisterControllerにいって、showRegistrationFormアクションを発動させる

RegisterControllerに行くとshowRegistrationFormアクションはトレイトで表記されている。

ちなみに
トレイトの内容は

 return view(‘auth.register’)

となっていて
「Authフォルダにある、register.blade.phpというviewファイルを表示しなさい」という意味
つまり
Authフォルダを作って、その中にregister.blade.phpを作成してそれが呼び出されるようにする

・LaravelCollective導入時のエラー

Laravel Collectiveとは

Laravelには便利なライブラリがたくさんある。
Larval collective はその中の一つ
Laravel のビューでフォームを記述するには直接HTMLを記述する方法とヘルパー関数(HTML/Formヘルパー)を使う方法がある。
ヘルパー関数を使う場合はこのパッケージをcomposerでインストールする必要がある。
なお、LaravelCollective/htmlはLaravel4では標準で組み込まれていたが、Laravel5からは別パッケージになった。

導入時のエラーの詳細

Webコンテナに入る

[vagrant@local-docker lara-d]$ docker-compose exec web bash

紹介されたコマンド実行

以下のようなエラーが起こる

[root@1b08bc3fd4bf html]# composer require "laravelcollective/html":"5.5.*"
./composer.json has been updated
Running composer update laravelcollective/html
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - laravelcollective/html[v5.5, ..., 5.5.x-dev] require illuminate/http 5.5.* -> found illuminate/http[v5.5.0, ..., 5.5.x-dev] but it conflicts with another require.
    - laravelcollective/html v5.5.2 requires illuminate/http 5.6.* -> found illuminate/http[v5.6.0, ..., 5.6.x-dev] but it conflicts with another require.
    - Root composer.json requires laravelcollective/html 5.5.* -> satisfiable by laravelcollective/html[v5.5, ..., 5.5.x-dev].


Installation failed, reverting ./composer.json and ./composer.lock to their original content.

導入時のエラーの原因

原因①

PHPのバージョンとLaravelのバージョンが一致していなかったのが原因。
適応するバージョンにアップデートしたら無事導入できた。

原因②

インストール実行コマンドの仕様が違っていた。

紹介されていたコマンド

composer require "laravelcollective/html":"5.5.*"

調べてインストール成功したコマンド

composer require "laravelcollective/html":"^5.5"

※ちなみにLaravelCollectiveはプロジェクト毎にインストールしないといけないので注意

・セッションの使用方法

セッションの使用方法は大きく3つある

①Requestインスタンスを用いる方法
②グローバルSessionへルパ関数を利用する方法
(③ファサードを利用する方法)

今回は①のRequestインスタンスを利用する方法で実装する

セッションへのデータ保存(Requestインスタンス経由)

$request -> session () -> put ( ‘ key ’  , ’ value ’ ) ; 

ここのkeyは勝手に名前つけて良いの?→良い!
Valueは何を入れたら良いの?→保存したい値!

セッションデータの取得(Requestインスタンス経由)

$request->session () -> get( ‘ key ’ );

データの保存先

Laravelの
strage > framework > sessions  に格納されていく!

・フォームリクエストの作成方法

コントローラ作成する時みたいに
php artisan make:request CreateProductRequest
でリクエストを作成する

作成したファイルにバリデーションを記載していく
この時、authorize()はとりあえずtureにしておく(細かい設定もある)

CreateProductRequest
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;

//クラス名は作成時に指定したものになる
class CreateProductRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
        //細かい設定もあるようだけど、とりあえずtureにしとく
    }
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
  //ここにバリデーションを書いていく
    public function rules()
    {
        return [
            'product_name' => 'required|string|max:20',
            'category_id' => 'required|numeric|exists:m_categories,id',
            'price' => 'required|numeric',
            'sale_status_id' => 'required|numeric|exists:m_sales_statuses,id',
            'product_status_id' => 'required|numeric|exists:m_products_statuses,id',
            'description' => 'required|string|max:191',
        ];
    }
}

コントローラのupdateアクションで、さっき作成したフォームリクエスト(バリデーション)を用いて、リクエスト情報を更新する。

Controllerのupdateアクション
ここに
public function update(CreateProductRequest $request, $id)
    {
        $product = MProduct::with(['category', 'sale_status', 'product_status'])->find($id);
        // dd($request);
        $product->product_name = $request->product_name;
        $product->category_id = $request->category_id;
        $product->price = $request->price;
        $product->sale_status_id = $request->sale_status_id;
        $product->product_status_id = $request->product_status_id;
        $product->description = $request->description;
        $product->save();
        return redirect('seller/items');
    }

フォームリクエストを使用することで、更新時には、まずCreateProductRequestに記載したバリデーションを実行してくれて、それがtureであれば( authorize()の話 )コントローラの内容が実行されるという流れ。
もしバリデーションに引っかかった場合はエラー情報を持たせて、直前のページにリダイレクトしてくれる。

・リレーション先のデータを取得する

モデルにリレーションを貼って、リレーション先のデータを取得できるようにします

例えば...

Product テーブル
→商品に関するテーブルで登録された商品がたくさん入っている

Categories テーブル
→Productテーブルの親テーブルで、商品をカテゴリー別に分類するためのカテゴリーデータが入っている

この二つのテーブルにリレーションを貼ります

リレーションの貼り方

Product モデル

class Product extends Model
{
・・・
public function category()
    {
        return $this->belongsTo(Category::class);
    }
・・・
}

category メソッドを作成して
「このクラスはCategoryモデルに属するんだよ」
と記述してあげる。
→Productモデルの親はCategoryモデルであり、Productモデルの中身の一つである’カテゴリー’カラムのデータは、Categoryテーブルから持ってくる。
→ProductモデルはCategoryモデルに属する!!

Category モデル

class Category extends Model
{
・・・
    public function Products()
    {
        return $this->hasMany(Product::class);
    }
}

Products メソッドを作成して
「このクラスはProductモデルをたくさん保有しているよ」
と記述してあげる。
→Categoryモデルの子はProductモデルであり、Categoryモデルの中身の一つである’カテゴリー’カラムのデータを持つProductモデルはたくさんある。
→CategoryモデルはたくさんのProductモデルを保有する!!

上記二つのテーブル(モデル)間のリレーションを貼りたい場合は、モデル内に上記のような親子関係を考慮した記述を双方に対して記載してあげることでリレーションを張ることができる。

コントローラで取得の方法を確認していく↓

コントローラの記述

public function show($id)//Product $product ?
    {
     //Productモデルから$idの商品データを取得
        $pd_info = Product::find($id);
     //上記で定義した商品データからcategory_idを取得できるようになる
        $pd_category = Category::find($pd_info -> category_id);
      
        return view('iteminfo', 
        [
            'pd_info' => $pd_info,
            'pd_category' => $pd_category,
        ]);
    }

Productモデルから$idの商品データを取得し、その商品データからcategory_idを取得できるようになる。
つまり、
Categoryモデルからデータを取得している!ということ

・ルーティング prefix の使い方

Prefixとは
直訳すると「接頭語」という意味
ルーティングする上での意味はprefixで指定した文字を最初に設置するっという感じの意味合いになる(url のアクセスに関する話)

使用例

Route::group(["prefix"=> 'iteminfo'], function() {
    Route::get('/{id}', 'ProductController@show');
    Route::post('/add', 'ProductController@addCart')->name('addcart');
});

解説)

Route::group(["prefix"=> 'iteminfo']

Prefix => ‘iteminfo’  とすることで、urlのアクセス部分を統一することができる。
この場合iteminfoというurlにアクセスされるルーティングを一まとめ(グループ)にすることができる。

上記のグループの中身である

Route::get('/{id}', 'ProductController@show');
Route::post('/add', 'ProductController@addCart')->name('addcart');

この部分は iteminfo 以降のurl部分を記述していて、
一つ目は iteminfo/id にアクセスされた場合のルーティングとなる。

・新規プロジェクトの立ち上げ方法(バルスした時に新しいプロジェクトを立ち上げて再開する)

環境にvagrant-dockerを使用しています

最新のリモートをクローンしてくる

[vagrant@local-docker docker-laravel]$ git clone https://github.com・・・・/・・・・.git

クローンが終わればファイルがあるか確認(必要ならmvコマンドで名前を変更)
※ここではlara-dというファイル名で進めます

[vagrant@local-docker docker-laravel]$ ls
README.md   docker-compose.yml  lara-d
apache-php  index.php           

lara-d に移動

[vagrant@local-docker docker-laravel]$ cd lara-d

今どのブランチにいるかを確認

[vagrant@local-docker lara-d]$ git branch
* master

ここで、最新のリモートdevelop_alphaブランチをpullしてくる(ここではdevelop_alphaというリモートのメインブランチ)

[vagrant@local-docker lara-d]$ git pull origin develop_alpha
・・・・・
・・・・
・・
//pullが完了

Lara-dでdockerを再起動する

[vagrant@local-docker lara-d]$ docker-compose restart
Restarting apache-php   ... done
Restarting phpmyamin-la ... done
Restarting mysql5.7     ... done

webコンテナ内に入る

[vagrant@local-docker lara-d]$ docker-compose exec web bash

Composerが入っていないので新しくインストールする
これがおそらく新しくプロジェクトを始める際のに必要な手順?

[root@1b08bc3fd4bf html]# composer install
・・・・・
・・・・
・・
//composerのインストール完了

Git logを確認
→みんなのlogが確認できれば正しくpullできているということ(?)

[vagrant@local-docker lara-d]$ git log
・・・・・
・・・・
・・
//logが存在していれば成功

ブランチを確認→なぜかdevelop_alphaブランチがない

[vagrant@local-docker lara-d]$ git branch
* master

develop_alphaブランチに移動してみる

[vagrant@local-docker lara-d]$ git checkout develop_alpha
D   .env.example
Branch develop_alpha set up to track remote branch develop_alpha from origin.
Switched to a new branch 'develop_alpha'

あった!!

[vagrant@local-docker lara-d]$ git branch
* develop_alpha
  master

--ここから続きの開発を始めることができる--

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

谷藤賢一『いきなりはじめるPHPワクワク・ドキドキの入門教室』でPHPを勉強するのをやめた話

チャプター3まで終え、チャプター4『データベース』に進もうとしたら、mampの設定で挫折した。

以前【PHP】超優良入門書『いきなりはじめるPHP』をMAMPで完走するために知っておくべき5つのこと【Mac】

に「xamppではなくmampでも勉強できる」と記載したが、調べてみるとmampはmacbook向けのデータベースでwindowsには向かないと判明した。

【PHP初心者向け】MAMPでMySQLの文字コードを設定する方法(文字化け対策)【Mac】を参考に文字化け対策を行おうにも、コマンドプロンプトでエラーが出て進めない。対策方法はあるらしいが、初学者の私にはどうすればいいか見当もつかない。

というわけで、この本でphpの学習を続けるのは断念した。

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

DockerでPHP環境を作る(mac)

はじめに

勤め先でもPHPを使っており自宅でPHPを勉強するために、ローカルでPHP開発環境を整えることにしました。
MAMP(マンプ)でも良いかなと思いましたが、せっかくなのでDockerで作ってみます。
(開発中に色々詰まってしまったところがあるので、備忘録がわりのメモです。)

手順

  1. Dockerによる環境構築
  2. ファイル・ディレクトリの準備
  3. 各ファイルの作成
  4. コンテナを起動
  5. サイトにアクセス

1. Dockerによる環境構築

Dockerを使うには、「Docker for Mac」のインストールが必要です。
Qitaに素敵な記事がたくさんあるので、ここでの説明は省略します。

今回はDockerで以下のコンテナを使います。

  • nginx
  • PHP
  • MySQL
  • PHPMyAdmin

使用するコンテナが複数あるので、今回は「Docker Compose」を使って環境を作っていきます。

2. ディレクトリ・ファイルの準備

Dockerを起動する場所を作成しましょう。
まずはフォルダを1つ用意し、次のようなファイル構造となるようにしてください。
用意するフォルダは何でも良いのですが、今回は「php-test」という名前で作成します。

php-test
├── docker-compose.yml
├── mysql
│   └── data
├── nginx
│   └── nginx.conf
├── php
│   ├── Dockerfile
│   └── php.ini
└── www
   └── html
       └── index.php

3. 各ファイルの作成

用意したファイルの中身を作成していきます。
中身を作成するファイルは、docker-compose.ymlnginx.confDockerfilephp.iniindex.phpの5つです。

docker-compose.yml

version: '3'
services:
  nginx:
    image: nginx:latest
    ports:
      - 8080:80
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - ./www/html:/var/www/html
    depends_on:
      - php

  php:
    build: ./php
    volumes:
      - ./www/html:/var/www/html
    depends_on:
      - db

  db:
    image: mysql:5.7
    ports:
      - 13306:3306
    volumes:
      - ./mysql/data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret

  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    ports:
      - 8888:80
    depends_on:
      - db

nginx.conf

server {
  listen 80;
  server_name _;

  root  /var/www/html;
  index index.php index.html;

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;

  location / {
      try_files $uri $uri/ /index.php$is_args$args;
  }

  location ~ \.php$ {
      fastcgi_pass php:9000;
      fastcgi_index index.php;    
      fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
      include       fastcgi_params;
  }
}

Dockerfile

FROM php:7.2-fpm
COPY php.ini /usr/local/etc/php/
RUN docker-php-ext-install pdo_mysql

php.ini

date.timezone = "Asia/Tokyo"

index.php

設定情報を確認するためにphpinfo();としていますが、なんでもいいです。

<?php
phpinfo();
?>

4. コンテナを起動

いよいよコンテナ起動です。
ターミナルを開き、docker-compose.ymlがあるフォルダ(今回はphp-test)に移動します。

下記コマンドで、php-testフォルダに移動。

cd php-test

下記コマンドで、コンテナを起動。

docker-compose up -d

以下のような記述がターミナル内に表示されたら、起動は成功です。

Creating php_01_db_1 ... done
Creating php_01_phpmyadmin_1 ... done
Creating php_01_php_1        ... done
Creating php_01_nginx_1      ... done

なお下記コマンドで、コンテナを停止できます。

docker-compose stop

5. サイトにアクセス

最後にサイトにアクセスします。
それぞれ下記画像のような画面が表示されたら、成功です。
お疲れ様でした。

Webサイト:http://localhost:8080/
localhost_8080_config.php.png

管理画面:http://localhost:8888/
localhost_8888_ (1).png

ユーザー名:root
パスワード:secret

以上です。
最後までお読みいただき、ありがとうございました。


お世話になったサイト

【補足】Qitaに投稿されているDockerインストール記事

下記の中から自分に合いそうなものを1つ選んで、進めてみると良いと思います。

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

エンジニア未経験がWordPressでWEBアプリを制作する際につまずいたこと

まず初めにどのようなWEBアプリを作ったのか

AboutMyself」という、プロフィール専門SNSを製作しました。
※内容については、こちらの記事を参照。

AboutMyself」を製作する上でつまずいたこと

  1. SNSを製作する上でのプラグインの選択について困った

    →日本語の説明ブログなどもかなり投稿されていますので、これに関しては「BuddyPress」一択で大丈夫だと思います。

  2. ログイン・登録画面で自分のサイトのUIではなく、WordPressのログイン画面に遷移してしまう。

    →これもプラグインがおすすめです。「Theme My Login」を入れることで、自分のサイトのUIにあったログイン・登録画面に遷移されます。

  3. ログアウト後の画面にログインをしないと見れないメニューが表示される

    →以下のスクショのようにログアウト後の画面に、ログインをしなければ見れない画面が出てきてしまいました。(本来「グループ作成」メニューはログアウト後には必要ない)
    こちらもプラグインを導入しました。「If Menu」というログイン後とログアウト後のメニュー画面の表示を分けられる優れものです!

スクリーンショット 2021-01-24 12.18.32.png

今回WordPressを使ってわかったこと

WordPressを使用することで、プログラミング未経験者でも簡単にWEBアプリが作成できます!プラグインというのは本当に便利な機能ですよね。適宜、欲しい機能にあったプラグインを調べて導入することである程度自由なサイトを構築できます!

最後に「AboutMyself」のご利用もお願いいたします!

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

【Laravel】テーブル名が長い時に、Laravel7から導入された外部キー制約をつけるマイグレーションエイリアスを使う方法

久しぶりのQiitaへの投稿です。

未経験からエンジニアに転職をして、2年目に突入したバックエンドエンジニアです。
DBに外部キー制約を追加をしようと思い、Laravel7から使えるエイリアスでmigrationファイルを作成していました。
ただ、テーブル名が長い時にハマってしまい、できる人には当たり前すぎる内容かもしれませんが、振り返りの意味も込めて投稿します。

開発環境

  • 言語:PHP7.4.8
  • フレームワーク:Laravel7.28.3
  • DB:MySQL8.0.22

テーブル名が短い時

目標のテーブル構造

公式ガイドでもよく使われるような下記のようなテーブルを作り、postsテーブルのuser_idに外部キー制約をかけることを目指します。

image.png

Laravel7より前の時の記法

Laravel7より前のバージョンで外部キー制約をつけるためには、下記のようにmigrationファイルを作成をしていました。
一度user_idのカラムを作成してから別の行で外部キー制約をつけています。

        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('user_id');
            $table->foreign('user_id')->references('id')->on('users');
        });

image.png

そんなに難しくもないですが、少しだけ面倒です。

Laravel7以降で使えるエイリアスを使用

そこでLaravel7以降では下記のようなエイリアスができ、foreignIdconstrained()を使えば一行でも外部キー制約をかけることができるようになりました。
短くて楽ですね。

        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained();
            $table->timestamps();
        });

image.png

テーブル名が長い時

目標のテーブル構造

問題のパターンです。下記のようなテーブルを想定して試してみます。
名前は適当に僕が好きなスポーツからとってきましたが、とりあえず2つのテーブル名が長く文字数の合計が一定数を超えると同じ現象になります。

image.png

Laravel7以降で使えるエイリアスを使用

せっかく新しい記法があるということなので、foreignIdを使った新しいエイリアスを使ってやってみます。

        Schema::create('national_basketball_association_players', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->foreignId('national_basketball_association_team_id')->constrained();
            $table->timestamps();
        });

キー名が長すぎるようで下記のようなエラーが出てしまいました。

  SQLSTATE[42000]: Syntax error or access violation: 1059 Identifier name 'national_basketball_association_players_national_basketball_association_team_id_foreign' is too long (SQL: alter table `national_basketball_association_players` add constraint `national_basketball_association_players_national_basketball_association_team_id_foreign` foreign key (`national_basketball_association_team_id`) references `national_basketball_association_teams` (`id`))

通常であれば、Laravelのスキームビルダの制約命名規則に従い、自動的に外部キー制約の名前は変換されるため、外部キー制約名は下記のようになります。

(接続元テーブル名)_(外部キー名)_foreign

しかし、今回この命名規則に当てはめようとすると、national_basketball_association_players_national_basketball_association_team_id_foreignという名前になりますが、これは文字数が長すぎるということでエラーになってしまいました。

Laravel7以降で使えるエイリアスを使用(nameでキー名変更)

同じようなことが他の制約を作る時でも起こっていたのを思い出します。
nameメソッドを使えば直せたような気がするのでnameメソッドを使って試してみました。
外部キー制約名は省略した、basketball_players_team_id_foreignを設定します。

        Schema::create('national_basketball_association_players', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->foreignId('national_basketball_association_team_id')->constrained()->name('basketball_players_team_id_foreign');
            $table->timestamps();
        });

先程と違い、今度はエラーも起こらず、migrationファイルが無事に通りました。
念のためにdatabaseを確認してみます。

image.png

外部キー制約がついていません。。。
多分routeを作成する時に使うnameメソッドと勘違いしていたのですが、ここで結構ハマってしまいました。

migration関連でnameメソッドは用意されていなさそうでした。

エイリアスメソッドのコードを確認

改めて今回のエイリアスのforeignIdメソッドを確認してみます。

    public function foreignId($column)
    {
        $this->columns[] = $column = new ForeignIdColumnDefinition($this, [
            'type' => 'bigInteger',
            'name' => $column,
            'autoIncrement' => false,
            'unsigned' => true,
        ]);

        return $column;
    }

引数がカラム名のみで1つしか受け付けていないので、キー名を短くして渡そうとしても無理そうです。

また、foreignIdの後に使っているconstrainedも確認してみます。

    public function constrained($table = null, $column = 'id')
    {
        return $this->references($column)->on($table ?? Str::plural(Str::beforeLast($this->name, '_'.$column)));
    }

こちらも外部キー制約名を渡す引数がなく、無理そうです。

結果:Laravel7以降で使えるエイリアスを使用(indexでキー名変更)

下記のようにindexメソッドを使って、外部キー名を指定するとうまくいきました。

        Schema::create('national_basketball_association_players', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->foreignId('national_basketball_association_team_id')->constrained()->index('basketball_players_team_id_foreign');
            $table->timestamps();
        });

公式ガイドを見てみるとindexメソッドは「基本的なインデックス追加」とだけ書いてありましたが、外部キー制約の名前を作成をする際にも使えるようです。
uniqueキー作るときは、第2引数にインデックスキー名指定できるのに対して、外部キー制約の場合はindexメソッドを使わないといけなさそうなので書き方が結構違いますね。

image.png

https://readouble.com/laravel/7.x/ja/migrations.html

※何か誤解していたり、他にいい方法があれば教えていただけると助かります!!!

Laravel7より前の時の記法でやる方法

もちろん元々使っていた記法を使って、foreignメソッドの中の第2引数に外部キー制約名を指定してもうまくいきます。
このやり方は元々やったことあったのですが、ハマるくらいなら最初からこっち使っておけばよかったなと思いました、、、

        Schema::create('national_basketball_association_players', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->unsignedBigInteger('national_basketball_association_team_id');
            $table->foreign('national_basketball_association_team_id', 'basketball_players_team_id_foreign')->references('id')->on('national_basketball_association_teams');
            $table->timestamps();
        });

image.png

感想

チュートリアルで使うようなusersとかpostsとかのテーブルと違い、実務ではテーブル名が長くなるケースは命名規則によりけりだと思いますが、結構発生しそうなイメージなので意外と困ることもあるかなと思います。

また、今回少し深く調べてみて、フレームワークのコードをより深くみるようにしたのはよかったなと思いました。

それにしてもnameメソッドを使ったときに何故かmigrationファイル通るのに外部キー制約がついていないのは結構たちが悪かったです。。。

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

PHP ログイン認証の作成手順⑤ 〜ログイン機能作成②とセッションハイジャック〜

今回は
・emailと一致するユーザ検索
・passwordが一致するか検証
・ユーザ情報をセッションに格納してログイン
を実装する

パスワード照会の方法

ユーザ入力とDBの値を照会
しかし、パスワードはハッシュ化されている→照会できる関数がある
password_verify(パス、ハッシュ)
パスワードがハッシュにマッチするかを判定する
マッチしたらtrue、しなかったらfalseを返す

セッションハイジャック対策

セッションIDを盗まれて、なりすましをされること
対策:セッションIDを再作成する

手順

ログインロジック作成

前回までに作成したUserLogicクラスの中にログイン専用のメソッドを作成していく。
loginメソッドとする。
top.phpがログインフォーム後のページだから、ここでログインの処理を記述する。

top.php
<?php

require_once('../classes/UserLogic.php');

session_start();

// エラーメッセージ
$err = [];

// バリデーション
if(!$email = filter_input(INPUT_POST, 'email')){
    $err['email'] = 'メールアドレスを記入してください';
}

if(!$password = filter_input(INPUT_POST, 'password')){
    $err['password'] = 'パスワードを記入してください';
}

if(count($err) > 0){
    // エラーがあった場合は戻す
    $_SESSION = $err;
    header('Location: login.php');
    return;
}
// ログイン成功時の処理
$result = UserLogic::login($email, $password);
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ユーザ登録完了画面</title>
</head>
<body>
<?php if (count($err) > 0) : ?>
<!-- エラーがある場合にforeachで該当エラーを表示させる -->
    <?php foreach($err as $e) : ?>
    <p><?php echo $e ?></p>
    <?php endforeach; ?>
<!-- $err配列に何もない=エラーがない から完了画面を表示させる -->
<?php else: ?>
    <p>ユーザー登録が完了しました。</p>
<?php endif ; ?>
<a href="login.php">戻る</a>
</body>
</html>

$result = UserLogic::login($email, $password);の部分。

そして、そのloginメソッドを以下の通りに記述する。

UserLogic.php
/**
     * ログイン処理
     * @param string $email
     * @param string $password
     * @return bool $result
     */
    public static function login($email, $password)
    {
        // 結果
        $result = false;
        // ユーザをemailから検索して取得
        $user = self::getUserByEmail($email);
    }

emailからユーザ取得ロジック作成

loginメソッドとは別に、emailの照会のためのメソッドを作成する。

UserLogic.php
/**
     * ログイン処理
     * @param string $email
     * @param string $password
     * @return bool $result
     */
    public static function login($email, $password)
    {
        // 結果
        $result = false;
        // ユーザをemailから検索して取得
        $user = self::getUserByEmail($email);
        return $user;
    }

    /**
     * emailからユーザを取得
     * @param string $email
     * @return array|bool $user|false
     */
    public static function getUserByEmail($email)
    {
        // SQLの準備
        // SQLの実行
        // SQLの結果を返す
        $sql = 'SELECT * FROM users WHERE email = ?';

        // ユーザデータを配列に入れる
        $arr = [];
        $arr[] = $email;
        try{
            $stmt = connect()->prepare($sql);
            $stmt->execute($arr);
            //SQLの結果を返す
            $user = $stmt->fetch();
            return $user;
        }catch(\Exception $e){
            return false;
        }

    }

パスワード照会とセッションハイジャック対策

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

PHP ログイン認証の作成手順④ 〜ログイン機能作成①とセッションとキャッシュ〜

ログイン機能の作成の流れ

①emailとpasswordを受け取る
②emailと一致するユーザ検索
③passwordが一致するか検証
④ユーザ情報をセッションに格納してログイン

sessionとは

開始から終了までの期間のこと。
1ユーザのログインからログアウトまでの行動は1セッション。
1セッションはセッションIDで管理する。

通信(HTTP)はユーザを識別することができない。
そのため、識別するためにセッションが必要になる。
セッションIDを管理するのはCookie。

CookieはセッションIDをブラウザに保存する。

ユーザがログインすると、サーバ側からセッションIDが生成されてブラウザに送られ、Cookieに保存する。
そして、ログイン中は常にセッションIDを送って、サーバ側でユーザを識別してもらう。

sessionの使い方

関数

session_start()
→Cookie(ブラウザ)にセッションIDを入れる
→セッションファイルが生成される
→セッションファイルに自由に値を保存可能(現場ではセッションファイルを管理する用のデータベースを作ることが多い)

session_destroy()
→セッションファイルを削除する

グローバル変数

$_SESSION
→セッションへデータを連想配列で保存

ログインフォーム作成

login.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ログイン画面</title>
</head>
<body>
    <h2>ログインフォーム</h2>
    <form action="top.php" method="POST">
    <p>
        <label for="email">メールアドレス</label>
        <input type="email" name="email">
    </p>
    <p>
        <label for="password">パスワード</label>
        <input type="password" name="password">
    </p>
    <p>
        <input type="submit" value="ログイン">
    </p>
    </form>
    <a href="signup_form.php">新規登録はこちら</a>
</body>
</html>

バリデーションの作成

login.phpのsubmitでtop.phpに移動するのだが、移動した時のバリデーションを作成する

top.php
<?php

require_once('../classes/UserLogic.php');

// エラーメッセージ
$err = [];

// バリデーション
if(!$email = filter_input(INPUT_POST, 'email')){
    $err[] = 'メールアドレスを記入してください';
}

if(!$password = filter_input(INPUT_POST, 'password')){
    $err[] = 'パスワードを記入してください';
}

if(count($err) === 0){
    // ログインする処理
    echo 'ログインしました!';
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ユーザ登録完了画面</title>
</head>
<body>
<?php if (count($err) > 0) : ?>
<!-- エラーがある場合にforeachで該当エラーを表示させる -->
    <?php foreach($err as $e) : ?>
    <p><?php echo $e ?></p>
    <?php endforeach; ?>
<!-- $err配列に何もない=エラーがない から完了画面を表示させる -->
<?php else: ?>
    <p>ユーザー登録が完了しました。</p>
<?php endif ; ?>
<a href="login.php">戻る</a>
</body>
</html>

sessionでバリデーションを出す

上記のやり方だと、top.php上でのエラー表示になる。
本来はlogin.phpでエラー表示をさせる。
そのためには、
①logion.php → ログインフォームに入力して、ログインボタンを押す
②top.phpに移動して送られたデータのバリデーション行う
③header関数で強制的にlogin.phpへ戻して、sessionのデータを下にエラーを表示させる
という流れを作成する

保存されたsessionのデータはブラウザからも確認できるし、MAMP/tmp/phpフォルダの中にsessionファイルが作られており、その中にsessionの値が記述されている。

$_SESSION変数には連想配列として値が格納されることになる。

top.php
<?php

require_once('../classes/UserLogic.php');

session_start();

// エラーメッセージ
$err = [];

// バリデーション
if(!$email = filter_input(INPUT_POST, 'email')){
    $err['email'] = 'メールアドレスを記入してください';
}

if(!$password = filter_input(INPUT_POST, 'password')){
    $err['password'] = 'パスワードを記入してください';
}

if(count($err) > 0){
    // エラーがあった場合は戻す
    $_SESSION = $err;
    header('Location: login.php');
    return;
}
// ログイン成功時の処理
echo 'ログインしました!';
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ユーザ登録完了画面</title>
</head>
<body>
<?php if (count($err) > 0) : ?>
<!-- エラーがある場合にforeachで該当エラーを表示させる -->
    <?php foreach($err as $e) : ?>
    <p><?php echo $e ?></p>
    <?php endforeach; ?>
<!-- $err配列に何もない=エラーがない から完了画面を表示させる -->
<?php else: ?>
    <p>ユーザー登録が完了しました。</p>
<?php endif ; ?>
<a href="login.php">戻る</a>
</body>
</html>
login.php
<?php
session_start(); 

$err = $_SESSION;

// セッションを消す
$_SESSION = array();
session_destroy();
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ログイン画面</title>
</head>
<body>
    <h2>ログインフォーム</h2>
    <form action="top.php" method="POST">
    <p>
        <label for="email">メールアドレス:</label>
        <input type="email" name="email">
        <?php if(isset($err['email'])) : ?>
            <p><?php echo $err['email']; ?></p>
        <?php endif; ?>
    </p>
    <p>
        <label for="password">パスワード:</label>
        <input type="password" name="password">
        <?php if(isset($err['password'])) : ?>
            <p><?php echo $err['password']; ?></p>
        <?php endif; ?>
    </p>
    <p>
        <input type="submit" value="ログイン">
    </p>
    </form>
    <a href="signup_form.php">新規登録はこちら</a>
</body>
</html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

エンジニア未経験がWordPressを使ってWEBアプリを作ってみた

今回製作したWEBアプリについて

今回作成したWEBアプリは「AboutMyself」。プロフィールを質問形式で記載していくSNSのようなものです。15年程前に流行った「前略プロフ」をイメージしていただければ良いかと思います。IMG_7162.PNG

1. 開発経緯

インターンのテレワークをするなかで、新しく入ってきた人のこと(趣味や経歴)についてわからずなかなかコミュニケーションが取りづらいと思った。また、気難しいと思っていた上司の趣味がわかった途端、急に親近感が沸いた。これらのことから、ある程度事前に相手のことをわかっていればコミュニケーションが円滑になるのではないかと考え、「AboutMyself」を作ることになった。

2. 「AboutMyself」の想定利用ケース

人と深く関わりたい人の場合
* 人と会う機会が減ったなかで、ユニークなプロフィールを通して親交を深めたい。
* プロフィールを通して相手のことを知りたい。
* プロフィールを通して初対面の人と仲良くなるきっかけを作りたい。

そんなに深く関わりたくない人の場合
* プライベートで利用しているLINEやFacebookなどを教え合う仲ではないけど連絡は取りたい。
* グループも作成できるので、何かについてざっくばらんに議論や質問などをしたい。

スクリーンショット 2021-01-24 0.04.35.png
スクリーンショット 2021-01-24 0.04.44.png

3. 開発工程

  1. お名前.comからドメインを取得し、同時にレンタルサーバーも申し込み。
  2. レンタルサーバーのコントロールパネルからWordPressをインストール
  3. SNS作成にはもってこいのプラグイン「BuddyPress」を使用。テーマは「GeneratePress」を使用。
  4. その他ログイン、ログアウトに関するプラグインを使用。
  5. ある程度機能が備わったらデザインの微調整をし、自分でパソコン3台、スマホ2台を使いテスト。
  6. テストに問題がなければ、友人に使用してもらいフィードバックをもらう
  7. フィードバックを元に完成段階まで完了。 ここまで5日ほど。

4. 今後の「AboutMyself」について

今後は、多くの人に利用してもらうようマーケティングに重きを置き、フィードバックを元に改善していく予定。この記事をみた方もぜひ、アクセス、ユーザー登録していただきたいです!お願いします!

5. 最後に

今はユーザー0ですが、何か進捗があればGoogle Analyticsの画像と共に報告できればなと思います。
初めての執筆なので、めちゃくちゃな文章でしたが最後までご覧いただきありがとうございました!

AboutMyself

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

開発未経験がWordPressを使ってWEBアプリを作ってみた

今回製作したWEBアプリについて

今回作成したWEBアプリは「AboutMyself」。プロフィールを質問形式で記載していくSNSのようなものです。15年程前に流行った「前略プロフ」をイメージしていただければ良いかと思います。IMG_7162.PNG

1. 開発経緯

インターンのテレワークをするなかで、新しく入ってきた人のこと(趣味や経歴)についてわからずなかなかコミュニケーションが取りづらいと思った。また、気難しいと思っていた上司の趣味がわかった途端、急に親近感が沸いた。これらのことから、ある程度事前に相手のことをわかっていればコミュニケーションが円滑になるのではないかと考え、「AboutMyself」を作ることになった。

2. 「AboutMyself」の想定利用ケース

人と深く関わりたい人の場合
* 人と会う機会が減ったなかで、ユニークなプロフィールを通して親交を深めたい。
* プロフィールを通して相手のことを知りたい。
* プロフィールを通して初対面の人と仲良くなるきっかけを作りたい。

そんなに深く関わりたくない人の場合
* プライベートで利用しているLINEやFacebookなどを教え合う仲ではないけど連絡は取りたい。
* グループも作成できるので、何かについてざっくばらんに議論や質問などをしたい。

スクリーンショット 2021-01-24 0.04.35.png
スクリーンショット 2021-01-24 0.04.44.png

3. 開発工程

  1. お名前.comからドメインを取得し、同時にレンタルサーバーも申し込み。
  2. レンタルサーバーのコントロールパネルからWordPressをインストール
  3. SNS作成にはもってこいのプラグイン「BuddyPress」を使用。テーマは「GeneratePress」を使用。
  4. その他ログイン、ログアウトに関するプラグインを使用。
  5. ある程度機能が備わったらデザインの微調整をし、自分でパソコン3台、スマホ2台を使いテスト。
  6. テストに問題がなければ、友人に使用してもらいフィードバックをもらう
  7. フィードバックを元に完成段階まで完了。 ここまで5日ほど。

4. 今後の「AboutMyself」について

今後は、多くの人に利用してもらうようマーケティングに重きを置き、フィードバックを元に改善していく予定。この記事をみた方もぜひ、アクセス、ユーザー登録していただきたいです!お願いします!

5. 最後に

今はユーザー0ですが、何か進捗があればGoogle Analyticsの画像と共に報告できればなと思います。
初めての執筆なので、めちゃくちゃな文章でしたが最後までご覧いただきありがとうございました!

AboutMyself

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