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

駆け出しエンジニアがLaravelを理解するために意識したこと

はじめに

実務で PHP, Laravel を使用することになり、最低限の理解(土台つくる)をする上でやったこと・意識したことをまとめました。実務未経験の方や転職して間もないという方向けの記事になっております。

もちろん実務に入ってまだ約1ヶ月半なのでまだまだわからないことばかりですが、今まで学習してきた Ruby, Rails と違うけど大丈夫かなという不安はなくなってきましたので記事にまとめさせていただきました。

これから転職を考えている人もいるかと思います。個人の見解ですが、そのときに言語を特定のものに絞らなくても良いんじゃないかなあと思いましたので、迷われている方や実務で別の言語を触れるのか不安と思っている方の参考になったら幸いです。

実務にはいって

最初は今まで学習してきたものとの違いがあり、自分にできるかなあと思うこともありました。ただそれは振り返ってみれば、これからやっていけるかなあとか不安などから視野が狭くなっていたのが原因なのかなと思います。細かく具体的に見ていくとどの言語も別のものになってしまうと思いますが、少し抽象度を上げて考えるとこの部分は似ているなとか少し違うけどこれぐらいの差か、となったりします。

要はプログラミングをするということなのであまり大差はないはずです。(今までRuby, PHP, Pythonを扱っただけなので他の言語で全然違う意見があったらすみません。。)

「誰のどんな悩みを解決」するのか考え「成し遂げたいこと」「実装したいこと」がありそれを「どのように実現」していくか、それを 言語化 して実装していくというフローは一緒です。

とはいえ全く一緒ということはないので抵抗は少しありましたし、それを埋めるためにググる時間があるかもしれませんが、それも慣れなのかなと思います。簡単な例を出すと PHP だと変数名の前に $ をつけますが、 Ruby だと付けないみたいな感じです。まだ癖が抜けなくて $ を抜かしてしまうことがありますが、そのときはエラーが吐かれるのでデバッグをすれば解決です。

大事だと思ったこと

  • ブログ記事はたくさんありますが、補助程度に使用すること
  • チュートリアルに沿って要点を抑える
  • とにかく実践あるのみ、わからないことがあればとことん調べる
  • 公式ドキュメントを読む、英語から逃げない

チュートリアル

丁寧にインストールするところからサンプルコードから体系的に学習することができます。開発の流れだったりメインの機能だったりが書かれているのでとても取り組みやすいです。

公式ドキュメント

公式ドキュメントの大切さは実務に入り身にしみて実感しました。今までも大切だということは理解していたのですが、記事をメインに公式ドキュメントを補助という使い方になっていた気がします。公式ドキュメントにしっかり書いてあるのに、色々な記事を彷徨ってしまう、、これ本当に時間の無駄ですよね。

ドキュメントをしっかり読むのには時間がかかります。だったらブログ記事を読んでサクッとデバッグしてけばいいじゃん!って思うかもしれません。それで解決できるならまだ良いですが、解決できなかったりブログに書いてあることが間違いだった場合にまた違う挙動が起きたりしてしまうなんてことになるかもしれません。

はじめは時間をかけてしまうかもしれませんが、しっかりと着実に理解していくほうが後々効いてくると思います。どこに重要なことが書いてあるんだろうとかそういったことも数をこなして慣れていったり、基本英語で書いてあることが多いけどわからないことは翻訳しながら読むようにしています。(少しずつ英語から読み取れるようになった気がします。)

いろいろ書きましたが言いたいことは、公式ドキュメントを読むことでエラー解決のスピードや実装のスピードが格段に上がったと思います。

エラーが起きたときにググり偶然同じエラーが起きた人がたまたまブログ記事などでまとめてくれていればラッキーですが、同じようなエラーが起きていないときはすぐに解決することができなくなってしまいます。

ブログ記事などは本当にわかりやすくまとめてくれていますが、あくまで補助的にしようするほうが良いと思います。とはいえ、公式ドキュメントやマニュアルでわからなかったり難しい表現を使ったりしていることも少なくないのでそういったところはブログ記事を参考にさせていただいております。

ディレクトリ構造や役割を知る、慣れる

どのファイルで何をしているのか、どのような役割をもっているのか知ることは重要だと思います。エラーの原因なども特定しやすいですし、何より流れが追えてくるのでプログラミングの楽しさも増してきます。

わかりやすく全体像をまとめてくださっている記事がありましたのでこちらに載せておきます。

Ruby、Railsとの違い・共通点を考える

新しい言語だからと 「1から学ぶ」 と時間をものすごく浪費してしまう気がします。今まで学習してきたものがあるのであればそれと照らし合わせてこの言語だったらこういうふうにするんだよなあとか考えながらやると定着が早まると思います。また、このような学習の仕方をしていれば今後別の言語を扱ったときも同じようにスムーズに扱うことができるのではと思いました。

まとめ

  • 言語で選ぶのではなく「プログラミングとは」と少し抽象度を上げることによって見え方が変わる
  • チュートリアルは体系的にまとまっていて初学者でも取り組みやすい
  • 公式ドキュメントを読むことは実力をつけるプログラミングをしていく上では必須となる
  • ブログ記事はたくさんありわかりやすくまとめてくれているが、最終的にはその情報があっているのか判断できるように一時ソースを読む

おわりに

少し駆け足になってしまいましたが、実務未経験から転職して間もないという方向けに記事を書かせていただきました。ただ経験をいくら積んでも大事な点というのは変わらないと思います。より良い考え方や方法をアップデートなどしていけたらしていきます(別記事で違う視点からまとめる可能性も)。

参考文献

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

デザインパターンの学習を始める前にクラス図を読めるようになろう

デザインパターンを学習していると説明にクラス図が用いられることが多いですよね。
みなさんはクラス図を見て、概要を理解してからコードを読んでいますか?
クラス図の読み方を理解しておくと概要の把握と理解が捗るので、デザインパターンを始める前にクラス図を読めるようにしておくことをお勧めしています。
実際に自分もそうすることでデザインパターンを理解するまでのコストを抑えられたように感じました。

この記事では、クラス図を読む時に必要最低限の情報をまとめています。
よろしければ、参考になさってください。

図の読み方

スクリーンショット 2021-03-21 13.42.47.png
スクリーンショット 2021-03-21 13.56.11.png

図からソースコードを想像してみる

上で確認した読み方を参考に下のクラス図はどのようなソースコードになるか想像してみましょう。
サンプルコードはPHPで書いています。

クラス図

スクリーンショット 2021-03-21 16.11.42.png

ソースコード

ClassA.php
<?php
declare(strict_types=1);

abstract class ClassA
{
    private $attribute1;

    public abstract function operation1();

    public function operation2()
    {
        return true;
    }
}
ClassB.php
<?php
declare(strict_types=1);

class ClassB extends ClassA
{

    public function operation1()
    {
        // TODO: Implement operation1() method.
    }
}
InterfaceC.php
<?php
declare(strict_types=1);

interface InterfaceC
{
    public function operation3(ClassA $param);
}
ClassD.php
<?php
declare(strict_types=1);

class ClassD implements InterfaceC
{

    public function operation3(ClassA $param): int
    {
        // TODO: Implement operation3() method.
        $return_value = 0;

        return $return_value;
    }

    public static function operation4()
    {
        return new ClassE();
    }
}

いかがでしたでしょうか?
クラス図の読み方をマスターして、デザインパターン学習の役に立てたら幸いです。

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

Dockerのphp:XX-apacheでpdo_mysqlを使用したい時は

よくあるPHPのLAMP環境をDockerで再現しようとした時

  • Dockerfile
FROM php:7.2-apache
WORKDIR /var/www/html/
RUN echo "<?php" > index.php && echo "phpinfo();" >> index.php

でDocker起動

docker build -t phptest .
docker run -it -d --name phptest2021 -p 80:80 phptest:latest

localhostでphpinfoを見てみると
スクリーンショット 2021-03-21 16.07.47.png

ああ、sqliteしかないのか・・

MySQLやMariaDBを使用したいので、
https://www.php.net/manual/ja/ref.pdo-mysql.php
これが使いたいんですよね・・

そんな時は

docker-php-ext-install

を使う。

  • Dockerfile 書き直す。
FROM php:7.2-apache
RUN docker-php-ext-install pdo_mysql && docker-php-ext-enable pdo_mysql
WORKDIR /var/www/html/
RUN echo "<?php" > index.php && echo "phpinfo();" >> index.php

でもう一度Docker起動

docker build -t phptest .
docker run -it -d --name phptest2021 -p 80:80 phptest:latest

localhostでphpinfoを見てみると
スクリーンショット 2021-03-21 16.10.41.png

PDOが使用で切ることを確認。

  • おまけ
docker-php-ext-install

このコマンドを知らなかったときは
https://pentan.info/php/pdo_mysql_install.html
直接上記をやろうとして時間を浪費しました。
他にも

docker-php-ext-install mysqli mbstring gd iconv

色々インストールできます。

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

mixhostにcomposerをインストールする

引用元 https://noter.fugo.ml/2019-05-02-mixhost%E3%81%ABcomposer%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B

手順

sshでログインし、以下のコマンドを実行する

curl -sS https://getcomposer.org/installer | php

下記コマンドを実行し、composerのバージョンなどが表示されて入れば正常にインストールされている。

php composer.phar

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

laravel 姓 名が別カラムなので結合したり、キーワードが空白で区切られていても検索できるwhere節の作り方(全文検索を使わない)

laravelで、DBのカラムがfirst_nameとlast_nameに分かれている。
しかし検索キーワードはいろんなパターンに対応させるように検索させたい場合。
でも全文検索は使えない。

そんな時に役立つかもしれない記事です。

DBの例

id last_name fist_name
1 山田 太郎
2 田中 花子

ユーザーからの検索クエリ

  1. 山田 太郎
  2. 山田太郎
  3. 山田
  4. 太郎

という感じで、どんなパターンでも検索をヒットさせたい。
でも全文検索は使わない場合は、このように書けば解決できます。

解決コードのEuloquent Where節部分です

前提
usersには検索したいEuloquentモデルが入っているものとします。
keywordsには検索クエリが入っているものとします。

$users->Where(function ($query) use ($keywords) {
     $splited_keywords = extractKeywords($keywords);
     foreach($splited_keywords as $keyword){
        $query->orWhere('last_name', 'LIKE', "%{$keyword}%")
              ->orWhere('first_name', 'LIKE', "%{$keyword}%")
              ->orWhereRaw('CONCAT(last_name, "", first_name) LIKE ? ', '%' . $keyword . '%');
      }
});

という感じで、送られてきたkeywordを空白文字列で分割しながら、各列を検索し、かつ、CONCATで結合したカラムを検索します。どれかにHITすればOK。

空白文字列が含まれる配列を分割するのはこちらの記事を参考にしました。
https://qiita.com/mpyw/items/a704cb900dfda0fc0331

function extractKeywords(string $input, int $limit = -1): array
    {
        return array_values(array_unique(preg_split('/[\p{Z}\p{Cc}]++/u', $input, $limit, PREG_SPLIT_NO_EMPTY)));
    }

この記事があなたの生産性を少しでも上げることができたならば
LGTMボタンお願いします!

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

【Laravel】migrateコマンド一覧

概要

データベースにテーブルを作成するためのマイグレーションですが、テーブルの構造を定義、更新、削除するためのコマンドがいくつもあります。
ここではmigrateコマンドとして一覧でまとめてみました。

migrateコマンド一覧

  • マイグレーションファイルを作成する
$ php artisan make:migration create_テーブル名_table


  • マイグレーションファイルを実行する
$ php artisan migrate


  • migrationsテーブルを作成する
$ php artisan migrate:install


  • マイグレーションを再実行してテーブルを再構築する。テーブルとデータの初期化
$ php artisan migrate:refresh


  • すべてのマイグレーション操作を元に戻す(全削除)
$ php artisan migrate:reset


  • 1つ前のマイグレーション操作した情報に戻す
$ php artisan migrate:rollback


  • マイグレーションファイルと実行状態を確認できる
$ php artisan migrate:status
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

tinkerを使ってみて勉強になった話

MySQLの接続がうまくいかなくて、「解決できたよ!」って記事ではないのですが
「こんな方法あるんだ!」と思ったことがあったので備忘録のため残します。

tinker

artisan migrateコマンドがうまくいかず、エラーメッセージ的に接続が拒否されている内容かなと思ったのでその理由を調べたくてググっていたところtinkerでデバッグを調べられることが分かりました。

使い方は簡単でターミナルでコマンドを打つだけです。
Dockerの場合はコンテナに入った状態で行います。

php artisan tinker

そうするとそのまま対話シェルが起動してコマンドを打てるようになります。
以下が実際に入力してみた内容です。

>>>の後にそれぞれのコマンドを入力する事で設定内容が確認できます。

>>> config('database.default');
=> "mysql"
>>> config('database.connections.mysql.port');
=> "3306"
>>> config('database.connections.mysql.database');
=> "my_app"
>>> config('database.connections.mysql.password');
=> "pass"
>>> config('database.connections.mysql.host');
=> "127.0.0.1"

私の場合はconfig('database.connections.mysql.database');が.envやdatabase.phpと違ったので「あれ?」となってエラー解決の原因究明に役立ちました!

以上です。

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

SPAにおけるLaravelのCORS設定周辺でつまずいた

概要

筆者はエンジニア歴がまだ1年経っていないphpがメインのシステムエンジニアです。
普段の実務ではLaravelは使用しておりません。そのため、不備等がある場合は教えて頂けると幸いです。
今回は友人とSPA開発する際にCORSの設定周辺で苦労したので、今後の自分も含めて共有させていただきます。

そもそもCORSとは

調べれば詳細が記載されている記事がたくさん見つかると思うので大枠で説明すると、
同一生成元ではないドメインへリクエストの安全性を保証する仕組みです。
CORSに基づいた方法で実装すれば、同一生成元ではないところでもJavaScriptでアクセスすることが可能になると言うことです。
認識違いがあれば指摘していただきたいです。

環境

PHP 7.4.16
Laravel Framework 6.18.40

発生したエラー

SPAで画像をアップロードする機能を実装したところ、下記のエラーが出力されました...。

Access to XMLHttpRequest at 'http://localhost:8000/api/upload' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

CORSの存在は知っていたのですが、まず自分自身localhostの部分が同じであればポート番号が違くても同一生成元だと思っていました...。
お恥ずかしい...。
その他のクロスドメインの判定としては、下記が挙げられます。
- プロトコルが異なる。 httpsとhttpの違い
- ホスト名が異なる。

エラー解決までの過程

それでは上記のエラーを解決するのに、Laravel側でCORSの設定をしていきます。
自分はfruitcake/laravel-corsを利用しました。詳細は下記のリンクからご確認ください。
https://github.com/fruitcake/laravel-cors

# インストール
$ composer require fruitcake/laravel-cors
# メモリ周辺でエラーが発生する場合は下記のコマンドを実行。
$ COMPOSER_MEMORY_LIMIT=-1 composer require fruitcake/laravel-cors

# configファイルを作成。
$ php artisan vendor:publish --tag="cors"

configファイルは/config/cors.phpに作成されます。
自分は下記のように各設定をしました。

cors.php
<?php

return [

    // CORSを設定するURI
    'paths' => ['api/*', 'sanctum/csrf-cookie'],

    // 許可するリクエストメソッド
    'allowed_methods' => ['*'],

    // 許可するリクエストオリジンの設定
    'allowed_origins' => ['http://localhost:8080'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    // Access-Control-Allow-Credentialsヘッダーを設定する。
    'supports_credentials' => true,
];

そして、それらをグローバルミドルウェアに追加しました。

/app/Http/Kernel.php
protected $middleware = [
    \App\Http\Middleware\TrustProxies::class,
    \App\Http\Middleware\CheckForMaintenanceMode::class,
    \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    \App\Http\Middleware\TrimStrings::class,
    \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    \Fruitcake\Cors\HandleCors::class, // 追加
];

しかし、また別のエラーが出力されました...。

// コンソール
POST http://localhost:8000/api/upload 419 (unknown status)

// レスポンスメッセージ
"message": "CSRF token mismatch.",

どうしてー!自分の認識だと、api.phpに定義したルートはCSRFの保護は無効になると思っていました。
色々原因を探ったところ、以前実装したsanctum認証で設定したミドルウェアが原因だと判明しました。

/app/Http/Kernel.php
'api' => [
    EnsureFrontendRequestsAreStateful::class, // ← これ
    'throttle:api',
    \Illuminate\Routing\Middleware\SubstituteBindings::class
],

その中身を見ていると、下記の記述がありました。
だから、APIとして定義したルートでもCSRFのチェックがされていたのですね...。

EnsureFrontendRequestsAreStateful.php
public function handle($request, $next)
{
    $this->configureSecureCookieSessions();

    return (new Pipeline(app()))->send($request)->through(static::fromFrontend($request) ? [
        function ($request, $next) {
            $request->attributes->set('sanctum', true);

            return $next($request);
        },
        config('sanctum.middleware.encrypt_cookies', \Illuminate\Cookie\Middleware\EncryptCookies::class),
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        config('sanctum.middleware.verify_csrf_token', \Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class), // ここでCSRFの保護が有効になってる!!
    ] : [])->then(function ($request) use ($next) {
        return $next($request);
    });
}

簡単にまとめると、
1. sanctum認証で必要なミドルウェアをapi.phpのルート全てに反映されてしまっていた。
2. それによって、CSRFのチェックが不要なルートにもCSRFの保護が有効になっていた。
といった感じです。

エラー対策

上記の原因が判明したとなるとあとは簡単でした。
認証がいらないルートの場合はCSRFの保護を無効にすればいいので、/app/Http/Middleware/VerifyCsrfToken.phpexcept箇所で無効にするURIを設定します。

/app/Http/Middleware/VerifyCsrfToken.php
<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    /**
     * Indicates whether the XSRF-TOKEN cookie should be set on the response.
     *
     * @var bool
     */
    protected $addHttpCookie = true;

    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        // ここに無効にするURIを追加する。
        'api/login',
        'api/upload',
    ];
}

おわりに

今回SPAにおけるLaravel側のみのCORSの設定などを主に説明させていただきました。
また、筆者自身まだ未熟であるので説明不足であったり、理解不足などありましたらご指摘していただけると幸いです。

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

Atomで単語をダブルクリック時に$まで選択するようにする方法

概要

PHPなどを利用していると変数のプレフィックスが$なので、変数をダブルクリックした際には$を範囲に含みたいかと思います。
Atomを使っていたらいつの間にか選択範囲から$が除外されていたので変更方法をメモ。

スクリーンショット 2021-03-21 5.33.26.png
? こうしたい
スクリーンショット 2021-03-21 5.33.48.png

方法

[Atom] → [環境設定] もしくは ⌘+, → [エディタ設定] を開く
「単語の一部として扱わない文字」を以下のように変更

/\()"':,.;<>~!@#$%^&*|+=[]{}`?-…

?

/\()"':,.;<>~!@#%^&*|+=[]{}`?-…

$を消しただけですね。
逆に$を選択しないようにしたければ追加すればいいだけです。

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

【Laravel】Seedデータを作成する UserSeeder.php

Seedデータを作成する

$ dokcer-compose exec workspace php artisan make:seeder UserSeeder

laradockを使用しているので、laradockディレクトリに移動してから上記のコマンドを実行する。
ここでは、UserSeederというファイルを作成して、Userに関するデータを登録する。

database/seeds/UserSeeder.php
<?php

use Illuminate\Database\Seeder;

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

    }
}

すると、上記のようなファイルが作成される。
ここに登録したいデータに関する記述を加える。

database/seeds/UserSeeder.php
<?php

use Illuminate\Database\Seeder;

class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
      DB::table('users')->insert([
          [
              'name' => 'Tanaka Taro',
              'age' => 50
          ],
      ]);
    }
}
      DB::table('users')->insert([
          [
              'name' => 'Tanaka Taro',
              'age' => 50
          ],
      ]);

上記の記述で、Userテーブルにデータを登録できる。
あとは、Seederを実行するにはDatabaseSeederというクラスに作成したSeederクラスを登録する必要がある。

database/seeds/DatabaseSeeder.php
<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call(UserSeeder::class);
    }
}

これで準備完了。

あとは下記のコマンドを実行する。

$ docker-compose exec workspace php artisan db:seed

これでSeederに書いた情報がDBに登録される。

終わり

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

【Laravel】php artisan make:model ***で作成したマイグレーションファイルの中身

Laravelのmigrationファイルの中身

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateProductsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()  //upメソッドでマイグレーション時にこの中身が実行される。
    {
        Schema::create('products', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->timestamps();
        });
    }
//Schema::createでテーブルを作成するらしい。ここでは、productsテーブルが作成されるイメージ
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('products');
    }

}

Laravelでは、テーブルの中身のカラムとそのデータ型は以下のように記述するらしい。

$table->bigIncrements('id');
$table->timestamps();

これで、テーブル内のカラムとそのデータ型を指定してテーブルを作成する。
しかし、migrationファイルを作成した時点ではまだカラムの情報が不足している場合は、ここで追記が必要。

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