20210225のPHPに関する記事は14件です。

【PHP備忘録】ログインの流れ

ログインの大まかな流れ。

前提

・join/index.php(登録画面)
・join/checked.php(登録確認画面)
・login.php(ログイン画面)
・index.php(ログインされた画面)

formのactionは空にしておく

あと各フォルダの最初にこれを忘れない

session_start();
require('../dbconnect.php');

これで、つながる

$_SESSION['join'] = $_POST;

後これがPOSTにある値をセッションにさせたやつ

HTMLにて変数で代入する

例 この場合だと打たれた名前が表示されるようになる

check.php
        <dd>
        <?php print (htmlspecialchars($_SESSION['join']['name'],ENT_QUOTES));?>
        </dd>

これを各それぞれの値にやる
あとこれめちゃくちゃやる、もうほぼこれ。

DB接続->登録情報をINSERT

ここでドキドキのDB接続

check.php
if(!empty($_POST)) {
    $statement= $db->prepare('INSERT INTO members SET name=?, email=?, password=?, picture=?, created=NOW()');
$statement -> execute(array(
$_SESSION['join']['name'],
$_SESSION['join']['email'],
// 「sha1」というのは記号化してくれるもの(不可逆)
sha1($_SESSION['join']['password']),
$_SESSION['join']['image'],
));

終わったらデータが残らないように削除して完了。

check.php
unset ($_SESSION['join']);
header('Location: thanks.php');
exit();

これでDBにINSERTされた。

ログイン画面で呼び出し

さて…

login.php
  if($_POST['email']  !='' && $_POST['password']  !== '' ) {
    $login = $db ->prepare('SELECT * FROM members WHERE email=? AND password=?');
    $login ->execute(array(
      $_POST['email'],
      sha1 ($_POST['password'])
    ));

    $member = $login->fetch();


    if($member) {
      $_SESSION['id'] = $member['id'];
$_SESSION['time'] = time();
  header('Location: index.php');
      exit();

最初の段落から
POSTのEメール・パスワードがともに埋まってる時
→DB接続(memberのテーブルでEメール・パスワードの値が?をさがして!)
→POSTにあるこれさがして!


取得して$memberにわかりやすくして!

SESSIONとmemberのID一緒だったらindex.phpにいっていいよ!

って感じ?

違うIDの人が入らないように

index.php
if (isset($_SESSION['id'])  && $_SESSION['time'] +3600 > time() ) {
$_SESSION['time'] = time();

$members = $db->prepare('SELECT * FROM members WHERE id=?');
$members ->execute(array($_SESSION['id']));
$member= $members -> fetch();
} else {
  header('Location: login.php');
  exit();}

ん~、多分IDとパスワードの値が入ってたらそのログインした時間を現在の時間に上書きする。
そして、ログインしたユーザーの情報を持ってくることができた。

一通りは以上!

ps.用語意識して覚えてないから説明するの抽象的になってしまうなぁ

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

多次元配列を時間順に並び替える

概要

データベースの結果を多次元の配列に代入して時間順に並び替えたのでその方法をメモします。

結論

array_multisort( array_map( "strtotime", array_column( $data, "created_at" ) ), SORT_DESC, $array ) ;

一つずつ見ていきます。

実装

こんな感じの配列

$data=[
            {
                "post_id": 76,
                "user_id": 3,
                "description": "hogehoge",
                "created_at": "2021-02-08T09:59:52.000000Z",
            },
            {
                "post_id": 77,
                "user_id": 2,
                "description": "hogehoge",
                "created_at": "2021-02-11T12:08:44.000000Z",
            }
            {
                "post_id": 78,
                "user_id": 2,
                "description": "hogehoge",
                "created_at": "2021-01-26T21:50:47.000000Z",
            }
];

時間順に並べたいのでcreated_atから参照して並び替えます。

array_multisort( ソートしたい配列 , ソート順 ,ソート方法(省略可) , 追加の配列 )

今回ソートしたい配列は$dataですがcreated_atが英文日付なので、UNIXタイムスタンプ形式にする必要があります。
ですのでarray_map()関数を使います。

array_map(コールバック関数, $配列)

ですので、

array_map("strtotime",array_column( $data, "created_at" ) )

で日付を比較できるように変換します。

array_multisort( array_map( "strtotime", array_column( $data, "created_at" ) ), SORT_DESC, $array ) ;

結果

$data=[
            {
                "post_id": 78,
                "user_id": 2,
                "description": "hogehoge",
                "created_at": "2021-01-26T21:50:47.000000Z",
            },
            {
                "post_id": 76,
                "user_id": 3,
                "description": "hogehoge",
                "created_at": "2021-02-08T09:59:52.000000Z",
            },
            {
                "post_id": 77,
                "user_id": 2,
                "description": "hogehoge",
                "created_at": "2021-02-11T12:08:44.000000Z",
            }

];

無事時間順に並び替えることができました。

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

Laravel 8.x Seeder生成で怒られる

Seederでダミーデータを作成する際、グローバルヘルパや関数を使用する際の落とし穴があります。

php artisan make:seed UsersTableSeederで作成すると以下のSeederが生成されます。

UsersTableSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
      //
    }
}

ダミーデータをrunメソッド内に記述しSeederを実行するとヘルパや関数を使用している場合は怒られます。

UsersTableSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('users')->insert([
            'name'           => 'hoge',
            'password'       => Hash::make('hoge'),
            'remember_token' => Str::random(30),
        ]);
    }
}

対処法

ファザードやヘルパを使用する際は宣言してあげる必要があるらしいです。

UsersTableSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;       //最初からある
use Illuminate\Support\Facades\DB;    //DBファザード使用の為追加
use Illuminate\Support\Facades\Hash;  //Hashファザード使用の為追加
use Illuminate\Support\Str;           //strメソッド使用の為追加

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('users')->insert([
            'name'           => 'hoge',
            'password'       => Hash::make('hoge'),
            'remember_token' => Str::random(30),
        ]);
    }
}

参考

https://readouble.com/laravel/8.x/ja/helpers.html

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

VSCode で環境を汚さず好きなバージョンのPHP開発環境を作る手順

必要なもの

PC(Mac or Window or Linux)
Docker for Desktop(Toolkitではない方)
VSCode(Remote Development拡張機能が必要)
インターネット環境
ブラウザ(Chrome等)

VSCodeを起動

image.png

作業フォルダを開く

左上に縦に並んでいる中の Explorer ボタン
image.png
を押します。

Open Folderボタンを押し、作業するフォルダを選択します。ここでは空のphpフォルダを選択しました。
image.png
image.png

Dockerfile 作成

ファイル一覧の PHP(選択したフォルダ) の右側にあるファイル作成ボタン
image.png
を押します。

image.png

ファイル名部分に Dockerfile と入力します。(Dは大文字)
image.png

ファイルの内容は FROM php:5.4-apache と入力します。(5.4はPHPのバージョンで好きなバージョンを指定できます。)
image.png

CTRL+Sキーを押してファイルを保存します。

Docker開発環境を作成

左下の緑部分
image.png
をクリックします。
image.png

※緑部分がない場合は、Remote Development拡張機能が入っていないのでインストールしてください。
image.png

上部中央にリストが表示されるので Remote-Containers: Reopen in Container を選択します。
image.png

続けてリストが表示されるので、 From 'Dockerfile' を選択します。これで作成したDockerfileからDockerコンテナが起動します。
image.png

すると、.devcontainer フォルダ、 devcontainer.json が生成されます。このファイルでDocker環境を起動するときの設定ができます。
image.png

※これらのフォルダ、ファイルリストは Docker内のものが表示されています。
PHP[DEV CONTAINER:EXISTING DOCKERFILE] がそれを表しています。
ローカルのphpフォルダと内容が同期されるように設定されています。

devcontainer.json 設定変更

/var/www/html の内容が同期されるよう設定します。

以下の2行を一番外の{}内に追加します。

    "workspaceFolder": "/var/www/html",
    "workspaceMount": "type=bind,source=${localWorkspaceFolder},target=/var/www/html",
修正前
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.158.0/containers/docker-existing-dockerfile
{
    "name": "Existing Dockerfile",

    // Sets the run context to one level up instead of the .devcontainer folder.
    "context": "..",

    // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
    "dockerFile": "../Dockerfile",

    // Set *default* container specific settings.json values on container create.
    "settings": { 
        "terminal.integrated.shell.linux": null
    },
    // Add the IDs of extensions you want installed when the container is created.
    "extensions": []

    // Use 'forwardPorts' to make a list of ports inside the container available locally.
    // "forwardPorts": [],

    // Uncomment the next line to run commands after the container is created - for example installing curl.
    // "postCreateCommand": "apt-get update && apt-get install -y curl",

    // Uncomment when using a ptrace-based debugger like C++, Go, and Rust
    // "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ],

    // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker.
    // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ],

    // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root.
    // "remoteUser": "vscode"

}
修正後
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.158.0/containers/docker-existing-dockerfile
{
    "name": "Existing Dockerfile",

    // Sets the run context to one level up instead of the .devcontainer folder.
    "context": "..",

    // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
    "dockerFile": "../Dockerfile",

    // Set *default* container specific settings.json values on container create.
    "settings": { 
        "terminal.integrated.shell.linux": null
    },

    "workspaceFolder": "/var/www/html",
    "workspaceMount": "type=bind,source=${localWorkspaceFolder},target=/var/www/html",

    // Add the IDs of extensions you want installed when the container is created.
    "extensions": []

    // Use 'forwardPorts' to make a list of ports inside the container available locally.
    // "forwardPorts": [],

    // Uncomment the next line to run commands after the container is created - for example installing curl.
    // "postCreateCommand": "apt-get update && apt-get install -y curl",

    // Uncomment when using a ptrace-based debugger like C++, Go, and Rust
    // "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ],

    // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker.
    // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ],

    // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root.
    // "remoteUser": "vscode"

}

左下以下の部分をクリックし、Remote-Containers: Rebuild Container を選択します。
image.png
image.png
※Dockerfile や devcontainer.json を変更した時はこれで設定を反映させます。

ターミナルを確認すると /var/www/html が初期ディレクトリになり、
ここにファイルを作ればサーバで公開されブラウザで確認できるようになります。
image.png
image.png

phpファイル配置

ファイル一覧の HTML[DEV... の右側にあるファイル作成ボタン
image.png
を押します。
image.png

作成された枠の中にファイル名として index.php を入力します。
image.png

ファイルの内容に <?php phpinfo(); と入力します。
image.png

CTRL+Sでファイルを保存します。

動作確認

TERMINALを開いて、 apachectl start と入力し、Enter を押します。
image.png

以下のようになればサーバは起動しています。
image.png

左上に縦に並んでいるアイコンの中の Remote Expoloer ボタン
image.png
を押します。
image.png

PORTS部分に 80 →... の行があるので、右の Open in Browser ボタン
image.png
を押します。

image.png

phpinfoの情報が表示されれば確認OKです。
image.png

※56598の番号は環境によって変わります。

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

PHPでMySQLの接続確認をする

MySQLのクライアントが入っていないサーバーでMySQLアカウントの確認をする方法です。

ssh 対象のサーバ
vi test.php
-------------------------------------------------------------------------------------------------
<?php
define('HOSTNAME', 'hoge01');
define('DATABASE', 'db1');
define('USERNAME', 'test');
define('PASSWORD', 'hogehoge');

try {
  $db  = new PDO('mysql:host=' . HOSTNAME . ';dbname=' . DATABASE, USERNAME, PASSWORD);
  echo "OK\n";
} catch (PDOException $e) {
  echo "NG\n";
  echo $e->getMessage()."\n";
}
-------------------------------------------------------------------------------------------------
php test.php
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel DBレコードの存在判定

LaravelのクエリビルダでDBレコードの存在チェック

記事内容

Laravelでオリジナルアプリのバックエンドを実装していく際に、DBにレコードが存在すればtrueを返し(ページ遷移)、レコードが存在しなければfalseを返す(エラーメッセージを表示する)という機能を実装したので、備忘録として残したいと思います。

開発環境


Laravel 8.22.1
PHP:8.0.1
MySQL:8.0.23
MacOS:11.1 ( Big Sur )

DBレコードの存在チェック

○○Controller.php
DB::table('table名')->where(column, $data)->exists();

クエリビルダの基本的な書き方に、existsメソッドを繋げて記述することで簡単にレコードの存在を判定できます。

実際の記述

今回僕は、下記のように記述しました。

内容としては、ユーザーによるフォームからのリクエスト内容と一致するレコードが、存在するかどうかで条件分岐しています。

○○Controller.php
$user = User::all()->where('id', $request->id)->first();

$msg = ['msg' => '入力されたIDは存在しません'];

if (DB::table('users')->where('id', $request->id)->exists() && [$request->id === $users->id])
{
    return redirect()->route('home'); 
} else {
    return view('login', $msg);

ifのカッコ内の記述により、DB内にレコードが存在していて且つ、フォームから送信された内容と一致する場合は、homeビューが返され、DBにレコードが存在しない場合は$msg変数の内容とともにloginビューが返される。

参照:公式ドキュメント ~クエリビルダ~

あとがき

今回のアプリを作っていく過程で、フォームからDBにレコードが存在しない値を入力された際、
「Attempt to read property "id" on null」というエラーにつまづき、なかなか前へ進めなかったので、その解決策を模索していたところ、本記事に記載した対処法に至りました。

他にも解決策はあると思いますので、laravelの公式ドキュメントや他の記事を参考にして、引き続きアプリの実装をしていきたいと思います。

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

PHPでAWS S3からディレクトリ単位でダウンロード

前提条件

AWS SDK for PHP 3.x を利用

やってみた感想

CommandPool利用のために配列を作成する必要がなく
コードもシンプルになるのでよい

AWS S3 バケットを再帰的にダウンロード

同期転送

sample.php
<?php

use Aws\S3\S3Client;
use Aws\S3\Transfer;

$client = new S3Client([
    'region'  => '****',
    'version' => 'latest',
]);

// from
$source = 's3://bucket/foo';
// to ローカルディレクトリのパス
$dest   = '/path/to/destination/dir';

$manager = new Transfer(
    $client,
    $source,
    $dest,
);
$manager->transfer();

非同期転送

sample.php
<?php

use Aws\S3\S3Client;
use Aws\S3\Transfer;

$client = new S3Client([
    'region'  => '****',
    'version' => 'latest',
]);

// from
$source = 's3://bucket/foo';
// to ローカルディレクトリのパス
$dest   = '/path/to/destination/dir';

$manager = new Transfer(
    $client,
    $source,
    $dest,
);

$promise = $manager->promise();
$promise
    ->then(function () {
        echo 'Done!';
    })
    ->otherwise(function ($reason) {
        echo 'Transfer failed';
    });

参考

Amazon S3バージョン 3 での AWS SDK for PHP Transfer Manager

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

LaravelでStrategyパターン

Strategyパターンやってみました。
割と使い道があるんじゃないかと思います。

バージョン

Laravel 6.18.40
PHP 7.4.4

Strategyパターン

512px-StrategyPatternClassDiagram.svg.png

Strategyパターンはstrategy(戦略)という言葉の意味からわかるように、状況に応じて処理を動的に切り替えることを目的としたデザインパターンです。
それぞれの処理をクラスとして定義し、共通の呼び出し部分から呼び出して処理を代替できるようにします。(オブジェクト指向の文脈ではdelegationと言われるようです)

実装

仕様

例えば、ユーザーが単発の仕事に応募してその報酬をもらえるようなサイトがあるとします。仕事情報は管理画面からCSVでアップロードする仕様になっています。アップされたCSVの内容はDBに保存されます。
仕事にはいくつかの種類があり、扱うデータが異なります。なので管理者は様々なフォーマットのCSV(ヘッダー行が1行だったり2行だったり、列数が違っていたりなど)をアップロードします。

管理画面にあるCSVアップロードフォームは1つで、フォーム内にはファイル用inputとファイルのタイプを選択するselectが存在します。

<!--こんな感じ-->
<form action="{{route('route_name')}}" method="post">
    <input type="file" name="uploaded_file" />
    <select name="file_type">
        @foreach(config('values.file_type') as $label => $fileType)
            <option value="{{$fileType}}">{{$label}}</option>
        @endforeach
    </select>
</form>

考えること

フォームが送信されたらコントローラー内のメソッドでリクエスト値を受け取ります。それからCSVファイル内のデータを読み取って、そのデータをデータベースに保存します。
仕様にもある通り、アップロードされるCSVのフォーマットは何種類もあります。1つのメソッドにまとめてifなどで条件分岐しながらファイルを処理することは可能ですが、読みにくくて変更に弱い処理になってしまいそうです。
Strategyパターンを使えば、今回の場合だと、アップロードされるファイルのタイプごとにクラスを分けてその中で処理を行うことができます。

Dto

先にデータをやりとりするためのDtoクラスを定義しておきます。

use Illuminate\Http\UploadedFile;

class FileImportDto
{
    public UploadedFile $uploadedFile;
    public int $fileType;
    public string $fileName;
    public string $filePath;

    public function __construct(UploadedFile $uploadedFile, int $fileType, string $fileName, string $filePath)
    {
        $this->uploadedFile = $uploadedFile;
        $this->$fileType = $fileType;
        $this->$fileName = $fileName;
        $this->$filePath = $filePath;
    }
}

StrategyInterface

CSVファイルのタイプごとに処理が違うので、最終的にタイプごとにクラスを分ける前提です(上の図の各ConcreteStrategyに相当)。そのクラスの雛形となるInterfaceを作成します。
全てのクラスで必要な処理は

  • CSVファイルの中身の読み取り
  • データをDBに保存

この2つです。

use App\Models\FileImportDto;

interface StrategyInterface
{
    /**
     * @param FileImportDto $fileDto
     * @return array
     */
    public function readData(FileImportDto $fileDto): array;

    /**
     * @param array $fileData
     * @return mixed
     */
    public function storeData(array $fileData);
}

具体的な処理

StrategyInterfaceの実装クラスを作成します。何個でも作成できるのがStrategyパターンのメリットです。今回は一旦3種類にします。
上の仕様の例で仕事情報に種類があると書きましたが、"オフィスワーク"、"飲食"、"運送"の3種類あると仮定します。
具体的な処理内容は今回関係ないので省略します。

use App\Models\FileImportDto;

class OfficeWorkFile implements StrategyInterface
{
    public function readData(FileImportDto $fileDto): array
    {
        //CSVファイルの中身を読み取って配列に詰めて返す処理
        ()
    }

    public function storeData(array $fileData)
    {
        //配列を受け取って、中身をDBに保存する処理
        ()
    }
}
use App\Models\FileImportDto;

class RestaurantFile implements StrategyInterface
{
    public function readData(FileImportDto $fileDto): array
    {
        //CSVファイルの中身を読み取って配列に詰めて返す処理
        ()
    }

    public function storeData(array $fileData)
    {
        //配列を受け取って、中身をDBに保存する処理
        ()
    }
}
use App\Models\FileImportDto;

class DeliveryFile implements StrategyInterface
{
    public function readData(FileImportDto $fileDto): array
    {
        //CSVファイルの中身を読み取って配列に詰めて返す処理
        ()
    }

    public function storeData(array $fileData)
    {
        //配列を受け取って、中身をDBに保存する処理
        ()
    }
}

StrategyFactory

具体的な処理を行うクラスを実装できました。次に必要なのは、ファイルのタイプによってどのクラスをインスタンス化するか判別する処理です。
ここについては何通りか方法があると思いますが、今回はStrategyFactoryというクラスを作成してそこで行うことにします。

class StrategyFactory
{
    /**
     * @param int $fileType
     * @return StrategyInterface
     */
    public static function createStrategy(int $fileType): StrategyInterface
    {
        $strategy = null;

        switch ($fileType) {
            case config('values.file_type.office_work.value'):
                $strategy = new OfficeWorkFile();
                break;
            case config('values.file_type.restaurant.value'):
                $strategy = new RestaurantFile();
                break;
            case config('values.file_type.delivery.value'):
                $strategy = new DeliveryFile();
                break;
        }

        return $strategy;
    }

}

Context

具体的な処理を、上の図でいう各ConcreteStrategyにdelegationするクラスです。今回の例では場合に応じてOfficeWorkFileクラス、RestaurantFileクラス、もしくはDeliveryFileに処理を行ってもらうクラスということになります。
StrategyFactory::createStrategy()でどのクラスをインスタンス化するか判別し、executeで処理を実行します。

class Context
{
    private StrategyInterface $strategy;

    public function __construct(FileImportDto $fileDto)
    {
        $this->setStrategy($fileDto->fileType);
    }

    /**
     * @param int $fileType
     */
    public function setStrategy(int $fileType)
    {
        $this->strategy = StrategyFactory::createStrategy($fileType);
    }

    /**
     * @param FileImportDto $fileDto
     */
    public function execute(FileImportDto $fileDto)
    {
        $fileData = $this->strategy->readData($fileDto);
        $this->strategy->storeData($fileData);
    }
}

Contextクラスを動かすメソッド

Contextクラスの処理を動かすメソッドが必要です。これはとてもシンプルで、以下のようなものになります。

public function processFile(FileImportDto $fileDto)
{
    $context = new Context($fileDto);
    $context->execute($fileDto);
}

こちらの処理をどこに置くかは好みが分かれると思います。これぐらいならControllerに置いてもよさそうな気がしますが、ビジネスロジック用にServiceクラスというものを作成してそちらに配置することにします。

use App\Models\FileImportDto;
use App\Services\Strategies\Context;

class FileImportService
{
    public function processFile(FileImportDto $fileDto)
    {
        $context = new Context($fileDto);
        $context->execute($fileDto);
    }
}

FormRequest

あとはControllerからServiceクラスにあるprocessFileを呼び出せば完了です。
しかしその前に、リクエスト値をDtoに変換しなければなりません。これをそのままController内で行うのは少しfatな感じになりそうな気がします。
せっかくLaravelを使っているので、FormRequest内に変換処理を書くことにします。

class FileImportRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'uploaded_file' => /*バリデーションルール*/,
            'file_type' => /*バリデーションルール*/
        ];
    }

    /**
     * convert request values into FileImportDto
     *
     * @return FileImportDto
     */
    public function convertIntoDto(): FileImportDto
    {
        $uploadedFile =  $this->file('uploaded_file');

        return new FileImportDto(
            $uploadedFile,
            (int)$this->input('file_type'),
            $uploadedFile->getClientOriginalName(),
            $uploadedFile->path()
        );
    }
}

Controller

Serviceクラスにある処理を呼び出すのみです。
Dtoへの変換処理はFormRequestに定義してあるので、かなりスッキリしたものになりました。

class FileImportController extends Controller
{
    private $service;

    public function __construct(FileImportService $service)
    {
        $this->service = $service;
    }

    public function importFile(FileImportRequest $request)
    {
        $this->service->processFile($request->convertIntoDto());
        return redirect(route('route_name'));
    }
}

これでStrategyパターンは完成です?

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

フォームデータの送信

form要素

データを送信する方法を定義します。その属性すべてが、ユーザーが送信ボタンを押すと送信されるリクエストを調整できるように設計されています。

action 属性

action 属性は、どこにデータを送信するかを定義します。値は妥当な相対/絶対 URL でなければなりません。この属性が与えられなかった場合は、フォームが含まれているページの URL にデータが送信されます。

GET メソッド

GET メソッドは、サーバーに対して指定したリソースを返すよう求めるためにブラウザーが使用するメソッドです。

POST メソッド

POST メソッドは少し異なります。これは、HTTP リクエストの本文で提供したデータを考慮したレスポンスの要求を、ブラウザーがサーバーに送信するためのメソッドです。

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

計算と三項演算子でじゃんけん

[PHP]じゃんけんプログラムと、if文なしでじゃんけんを読んで自分でもやってみた。

まずは、じゃんけんの勝敗を表にする。
9パターンしかないので、そこに数値を入れると下のようになります。

グー(0) パー(1) チョキ(2)
グー(0) あいこ(0) 負け(2) 勝ち(1)
パー(1) 勝ち(1) あいこ(0) 負け(2)
チョキ(2) 負け(2) 勝ち(1) あいこ(0)

縦軸を$player_hand、横軸を$pc_handとすると、計算方法は下の通りです。

$result = ($player_hand - $pc_hand + 3) % 3;

これを邪悪な三項演算子に放り込む。

$judgment = $result == 0 ? "あいこ" : ($result == 1 ? "勝ち" : "負け");

特に目新しい物がないけど、こんな感じになる。

<?php

const RPS = ["グー", "パー", "チョキ"];

foreach (RPS as $player_hand) {
    foreach (RPS as $pc_hand) {
        $judgment = janken($player_hand, $pc_hand);
        echo $player_hand, ":", $pc_hand, ":", $judgment, PHP_EOL;
    }
}

function janken(string $player_hand, string $pc_hand): string {
    $result = (array_search($player_hand, RPS) - array_search($pc_hand, RPS) + 3) % 3;
    $judgment = $result == 0 ? "あいこ" : ($result == 1 ? "勝ち" : "負け");
    return $judgment;
}

普通すぎる結果になりました。

image.png

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

計算でじゃんけん

[PHP]じゃんけんプログラムと、if文なしでじゃんけんを読んで自分でもやってみた。

まずは、じゃんけんの勝敗を表にする。
9パターンしかないので、そこに数値を入れると下のようになります。

グー(0) パー(1) チョキ(2)
グー(0) あいこ(0) 負け(2) 勝ち(1)
パー(1) 勝ち(1) あいこ(0) 負け(2)
チョキ(2) 負け(2) 勝ち(1) あいこ(0)

縦軸を$player_hand、横軸を$pc_handとすると、計算方法は下の通りです。

$result = ($player_hand - $pc_hand + 3) % 3;

これを邪悪な三項演算子に放り込む。
配列だけ済むので修正しました。

$judgment = $result == 0 ? "あいこ" : ($result == 1 ? "勝ち" : "負け");

特に目新しい物がないけど、こんな感じになる。

<?php

const RPS = ["グー", "パー", "チョキ"];
const JUDGMENT = ["あいこ", "勝ち", "負け"];

foreach (RPS as $player_hand) {
    foreach (RPS as $pc_hand) {
        $judgment = janken($player_hand, $pc_hand);
        echo $player_hand, ":", $pc_hand, ":", $judgment, PHP_EOL;
    }
}

function janken(string $player_hand, string $pc_hand): string {
    $result = (array_search($player_hand, RPS) - array_search($pc_hand, RPS) + 3) % 3;
    return JUDGMENT[$result];
}

普通すぎる結果になりました。

image.png

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

【備忘録】写真のアップロード方法

最近、エラーが埋まってる埋まってないで考えると楽になるのかなと感じる

HTMLにて

これをすることにより、ファイルから選ぶことができる

join.php
input type="file"

PHPにて

理解はしていない…
上のPHPで名前(?)を決める

join.php
    $fileName = $_FILES['image']['name'];
    if(!empty($fileName)) {
$ext = substr($fileName, -3);
//写真のタイプを選定し、エラーを作る//
if($ext != 'jpg' && $ext !='gif') {
    $error['image'] = 'type';
} 
    }

PHPにて

上の2行でフォルダ内にアップロードできるようになる

join.php
    if(empty($error) ) {
//写真のアップロードの決まり文句らしい//
        $image = date('YmdHis') . $_FILES['image']['name'];
        move_uploaded_file($_FILES['image']['tmp_name'], '../member_picture/' . $image);

//ちょっとしたまとめ//
        $_SESSION['join'] = $_POST;
        $_SESSION['join']['image'] = $image;
        header('Location: check.php');
        exit();
    }

次の画面にて

これで表示OK

check.php
        <?php if($_SESSION['join']['image'] !==''):?>
        <img src="../member_picture/<?php print (htmlspecialchars($_SESSION['join']['image'],ENT_QUOTES));?>" >
            <?php endif ;?>

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

php require とrequire_oneceの違い , includeとの違い, パスの指定方法についてなど

includeとrequireの違いやパス指定のまとめ

違いについて

再読み込みを同じファイル内で行われた際の挙動が違う

参考:https://qiita.com/awesam86/items/3fa28e23c95ca74caddc

試してみた(index.phpでr.php, rone.phpを読み込み検証)

r.php

<?php 
$r = 'hoge'

rone.php

<?php 
$rone = 'hoge';

index.php

<?php 
require 'r.php';
require_once 'rone.php';

echo $r. "\n"; //hoge
echo $rone. "\n"; //hoge

$r = 'hoge_change'; 
$rone = 'hoge_change';

echo $r. "\n"; //hoge_change
echo $rone. "\n"; //hoge_change


require 'r.php'; 
require_once 'rone.php';

echo $r. "\n"; //hoge(requireではファイルの読み込みが再度行われるため値も$r = 'hoge'が再読み込みされている)
echo $rone. "\n"; //hoge_change(require_onceでは一度しか読み込まれないため表示がhoge_changeのまま)

includeとの違いは

エラー出た時requireは画面全体が消える。includeは警告の為消えずに他の画面が表示される。

参考:公式ドキュメント
include
スクリーンショット 2021-02-25 7.14.59.png

require
スクリーンショット 2021-02-25 7.14.51.png

パスの指定について

include時のパスの指定について
スクリーンショット 2021-02-25 7.14.25.png

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

if文なしでじゃんけん

[PHP]じゃんけんプログラム - Qiitaを読んで、自分ならどうするか考えてみた。

解答

複雑に考えればいくらでも複雑にできるけれども、とりあえずこのくらいにした。

<?php

const WIN_LOSE_TABLE = [
    'グー' => ['チョキ' => 1, 'パー' => -1],
    'チョキ' => ['パー' => 1, 'グー' => -1],
    'パー' => ['グー' => 1, 'チョキ' => -1],
];

$printer = fn(string $my_hand, string $your_hand, int $result): string =>
    sprintf("[%s vs %s] %s", $my_hand, $your_hand,
        match ($result) {
            -1 => 'あなたの負け',
            0 => 'あいこ',
            1 => 'あなたの勝ち',
        }
    );

foreach (['グー', 'チョキ', 'パー'] as $my_hand) {
    foreach (['グー', 'チョキ', 'パー'] as $your_hand) {
        echo battle($my_hand, $your_hand, $printer), PHP_EOL;
    }
}

/**
 * 尋常に勝負
 */
function battle(string $my_hand, string $your_hand, Closure $printer): string
{
    return $printer($my_hand, $your_hand, WIN_LOSE_TABLE[$my_hand][$your_hand] ?? 0);
}

大事なのはconst WIN_LOSE_TABLEと、それを取り出すところでWIN_LOSE_TABLE[$my_hand][$your_hand] ?? 0してるところ。match ($result)のところは勝ち・負け・あいこの状態を文字列に変換してるだけ。

これだけだと見映えがしないので

絵文字を使いましょう

<?php

namespace Janken;

use Closure;

const WIN_LOSE_TABLE = [
    'グー' => ['チョキ' => 1, 'パー' => -1],
    'チョキ' => ['パー' => 1, 'グー' => -1],
    'パー' => ['グー' => 1, 'チョキ' => -1],
];

const EMOJI_TABLE = [
    'グー' => '✊',
    'チョキ' => '✌',
    'パー' => '?',
];

$printer = fn(string $my_hand, string $your_hand, int $result): string =>
    sprintf("[%s vs %s] %s",
        EMOJI_TABLE[$my_hand],
        EMOJI_TABLE[$your_hand],
        match ($result) {
            -1 => 'あなたの負け',
            0 => 'あいこ',
            1 => 'あなたの勝ち',
        }
    );

foreach (['グー', 'チョキ', 'パー'] as $my_hand) {
    foreach (['グー', 'チョキ', 'パー'] as $your_hand) {
        echo battle($my_hand, $your_hand, $printer), PHP_EOL;
    }
}

function battle(string $my_hand, string $your_hand, Closure $printer): string
{
    return $printer($my_hand, $your_hand, WIN_LOSE_TABLE[$my_hand][$your_hand] ?? 0);
}

動作確認: https://3v4l.org/MFqsQ

スクリーンショット 2021-02-25 1.27.28.png

やりましたね!

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