20200525のPHPに関する記事は8件です。

PHP インターフェースの検証

インターフェースとはなんなのか、何も知らないところからスタートします。
公式ドキュメントから引用。

オブジェクトインターフェイスにより、あるクラスが実装する必要があるメソッドの種類を、
これらのメソッドの実装を定義することなく、指定するコードを作成できるようになります。

では、実際に書いていきます。

1.基本

理解するのに時間がかかりました。

test_sample.php
<?php

require_once(__DIR__ . '/classTest.php');

classSample::two();

?>
classTest.php
<?php

interface Test {
    public function one();
    public function two();
}

class classTest implements Test
{
    public function one(){
        echo 'TestOne';

    }

    public function two(){
        echo 'TestTwo';

    }

}

class classSample implements Test
{
    public function one(){
        echo 'SampleTwo';

    }

    public function two(){
        echo 'SampleTwo';

    }
}

?>
・実行結果
SampleTwo

ここまでは普通なんですけど、
インターフェースで指定したメソッドは最低限使わなくちゃいけないやつです。

上記ならoneメソッドとtwoメソッドは最低限使わなきゃいけない。

では、classSampleのoneメソッドをコメントアウトして実行したら以下↓

・実行結果
Fatal error: Class classSample contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Test::one) in /・・・・・・/classTest.php on line 22

2.メソッドが増えた場合

メソッドが増える文には問題ないかな?

test_sample.php
<?php

require_once(__DIR__ . '/classTest.php');

classSample::three();

?>
classTest.php
<?php

interface Test {
    public function one();
    public function two();
}

class classTest implements Test
{
    public function one(){
        echo 'TestOne';

    }

    public function two(){
        echo 'TestTwo';

    }

}

class classSample implements Test
{
    public function one(){
        echo 'SampleOne';

    }

    public function two(){
        echo 'SampleTwo';

    }


    public function three(){
        echo 'SampleThree';

    }

}

?>
・実行結果
SampleThree

増えたメソッドに関しては使える。

3.継承

継承はできるか・・・・・・

classTest.php
<?php

interface Test extends classTest {
    public function one();
    public function two();
}

class classTest
{
    public function one(){
        echo 'TestOne';

    }

    public function two(){
        echo 'TestTwo';

    }

}

class classSample implements Test
{
    public function one(){
        echo 'SampleOne';

    }

    public function two(){
        echo 'SampleTwo';

    }

}

?>
・実行結果
Fatal error: Test cannot implement classTest - it is not an interface in /・・・・・・・/classTest.php on line 3

これなら?

classTest.php
<?php

interface TestA {
    public function one();
}

interface TestB extends TestA{
    public function two();
}

class classTest
{
    public function one(){
        echo 'TestOne';

    }

    public function two(){
        echo 'TestTwo';

    }

}

class classSample implements TestA
{
    public function one(){
        echo 'SampleOne';

    }

    public function two(){
        echo 'SampleTwo';

    }

}

?>
・実行結果
SampleTwo

なるほど。
インターフェースとなら継承可能ですね。

ちな、カンマ区切りで複数継承可能。

classTest.php
<?php

interface TestA {
    public function one();
}

interface TestB{
    public function two();
}

interface TestC extends TestA, TestB{
    public function three();
}

class classSample implements TestC
{
    public function one(){
        echo 'SampleOne';

    }

    public function two(){
        echo 'SampleTwo';

    }

    public function three(){
        echo 'SampleThree';

    }

}

?>

4.interfaceとabstractの違い

下記の記事↓
https://qiita.com/yukiyohure0923/items/0e49a313b2b8c98d3a29
引用させて頂きます。

・interface
1.定数を定義できる
2.アクセス修飾子はpublicのみ指定可能
3.interfaceはクラスじゃないからインスタンス化できない
4.クラスで使うときはimplementsでinterface名を指定する
5.実装を伴うメソッドやプロパティの定義はできない(抽象メソッドのみ)
6.1つのクラスに対し複数のinterfaceを実装できる(多重継承みたいなことができる)
7.interfaceで定義したメソッドをクラス内で必ず使用(オーバーライド)しなければならない

・abstract
1.アクセス修飾子は自由につけられる
2.抽象プロパティだけ抽象クラスの中で定義できない
3.抽象クラスはクラスだけど直接インスタンス化できない
4.クラスで使うときは普通の継承のようにextendsを使う
5.普通のクラスみたいに中身があるメソッドやプロパティを定義できる
6.抽象クラスで「定義し、なおかつ未実装のメソッド」を継承したクラス内で必ず使用(オーバーライド)しなければならない

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

【PHP8.0】例外をcatchしたいけど何もしたくない

例外をcatchしたいけど何もしたくない。

try{
    foo();
}catch(Throwable $e){
    // 何もしない
}

何もしないのにわざわざ変数に受け取るのって無駄じゃありませんか?

というわけでnon-capturing catchesというRFCが提出されました。

PHP RFC: non-capturing catches

Introduction

今のところ、PHPは例外を明示的に変数でキャプチャする必要があります。

try {
    foo();
} catch (SomeException $ex) {
    die($ex->getMessage());
}

しかしながら、ときにはその変数を使わない場合もあります。

try {
    changeImportantData();
} catch (PermissionException $ex) {
    echo "You don't have permission to do this";
}

これを見た人は、プログラマが意図してこの変数を使わなかったのか、あるいはバグなのかがわかりません。

Proposal

例外を変数に受け取らずcatchできるようにします。

try {
    changeImportantData();
} catch (PermissionException) { // catchした変数を使わないという意図が明白
    echo "You don't have permission to do this";
}

Prior art

7年前にあった似たようなRFCは、以下のように例外名も省略可能にするという理由で否定意見が多数でした。

try {
    foo();
} catch {
    bar();
}

いっぽう、このRFCは肯定的な反応が多く、再検討の余地があります。

Backward Incompatible Changes

後方互換性を壊す変更はありません。

Proposed PHP Version(s)

PHP8.0

RFC Impact

特になし。

Vote

投票は2020/05/24まで、投票者の2/3の賛成で受理されます。
このRFCは賛成48反対1の圧倒的多数で受理されました。

Patches and Tests

https://github.com/php/php-src/pull/5345

References

メーリングリスト

感想

このRFCが意図しているところは決して例外の握り潰しなどではなく、例外処理のロジックに例外の中身を使わないときに省略できるというものです。
変数に受け取る処理がなくなるので速度も速まることでしょう。

しかしですな、こんな機能があったら絶対にこんなコードを書く奴が出てくるわけですよ。

PHP8
try{
    foo();
}catch(Exception){}

いやまあ、こんなの書いてしまう人は今でもやってると思いますけどね。

PHP7
try{
    foo();
}catch(Exception $e){}

なら別にあってもいいか。

劇的に便利になるというわけではないですが、ちょっと気が利く書き方ができるようになりますね。

最後にもう一度言いますが、冒頭のコードは悪い例なので決して真似してはいけません。

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

初心者がPHPを勉強する方法

まずは他の言語を始める

PHPは色々な言語をごちゃ混ぜにしたような言語である。
なので、勉強をするなら混ざる前のものから手を付ける方が理解しやすい。

C言語

C言語は文字列、構造体、ポインタが分かる程度のところまでやる。
関数ポインタまでやる必要はない。JavaやJavaScript等の他の言語をやったあとに戻ってくると何となく理解できるようになっている。

https://php.net/fopen
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/fopen.3.html

https://php.net/strcoll
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/strcoll.3.html

PHPはC言語からそのまま持ってきた関数が多いので、このようにC言語のマニュアルそのままの関数も多い。なので、何故このような関数になっているか?というような関数の成り立ちをPHPの中だけで考えても意味がない。PHPとしてファイル操作のためのfopenを定義したわけではなく、単にC言語のfopenをそのままPHPに持ってきただけなのだということを頭に隅に置きつつコードを書くべきである。
fopenrw+ は一見意味不明なインターフェースだが、C言語をやった後だと慣れで書ける。

PHPを作ったラスマス・ラードフ氏は
https://gihyo.jp/news/report/2015/12/1401?page=4

モジュール間での共通性には欠けるかもしれませんが,OCI拡張モジュールもMySQL拡張モジュールも,下で動いているC APIにほぼ1対1で対応している点において整合性があるといえます。MySQLドキュメントのC APIに関する部分を見てみれば,PHP開発者にとってはなじみ深く感じられるはずです。どの低レベル関数もPHP側の何かと対応しているからです。だからPHPは垂直方向に見れば整合性はあるのですが,多くの人から始終「PHPはまったく整合性がない」と言われます。整合性は確かにあるのです。ただ,人々が思っているような水平方向にはないというだけです。垂直方向に見れば,完璧な整合性があります。

このように言っている。

最近はバイナリセーフな関数が増えてきたとは言えるものの、まだバイナリセーフでない関数もある。初心者はそもそもバイナリセーフでないとはどういうことかと悩むところだが、C言語で文字列をやった後だと「あ〜Cのライブラリにそのまま渡してるからNULL終端なのね」ということがすぐに分かるだろう。
そのうち「あ、なんかC言語っぽい名前のマイナー関数だからバイナリセーフではないのかも?」と勘が働くようになるはずだ。

ポインタや構造体は必須ではないが、PHPの参照を理解する手助けになる。あと->の記法に慣れる。
その後、C言語のつもりで参照を無駄に使って余計にパフォーマンスが下がるというところまでが1セットである。

Java

PHPのオブジェクト指向はJava由来のものが多い。
デザインパターンやDIコンテナ、インターフェースの設計、DDD、これらクラスベースのオブジェクト指向に真面目に取り組むなら、PHPでやるよりもJavaでやった方が良い。
PHPのためのJavaと考えるならJava 1.4で十分なので、最近Javaが取り入れている関数型っぽい機能の部分はスルーしてオブジェクト指向に関するプログラムをやるのが良い。
静的型付け言語特有のAOPや汎用プログラムとしてのスレッド処理などもスルーして、クラスとインターフェースの基本的な部分をやる。

共変性と反変性やジェネリクスについては現在のPHPではまず出てこないのでJavaでやっておいた方が良い。
ジェネリクスはPHP本体の機能としては使えないが、PHPStanのような静的解析ツールを使うことを考えると必要になる。

それから、SPLの中では比較的よく使うイテレーター https://php.net/spl.iterators もJavaでやっておいた方が良い。

Perl

スクリプト言語での文字列処理と言えば正規表現であり、スクリプト言語での正規表現と言えばPerlである。
https://php.net/pcre
PCREとはPerl Compatible Regular Expressions、つまりPerl互換正規表現のことなので、歴史も例題もPerlで調べた方が早い。
そのためにはある程度は使えるようになっておく必要がある。

Ruby

RubyというよりもRuby on Railsをサンプルアプリケーションを作れる程度にやっておく。
PHPに限らずWebアプリケーション用のフレームワークはRailsに影響を受けて作られたフレームワークがいくつもあるので、本家の思想を知っておくと理解が早い。かつ、他の言語のRails風フレームワークを使うときにもすぐに慣れることが出来るようになるメリットがある。

JavaScript

Webアプリを作るにはどうせ必要になるのだから先にやっておく。
JavaScriptでクロージャに慣れておき、共通パターンの一部だけ変更するような処理をabstract classで実装するのかインターフェースを使うのかコールバックで実装するのかという選択肢を増やしておく。

ちなみにPHPのシンプルな無名関数はstatic function(){}である。static無しの場合は$thisがbindされる。
PHPの無名関数はbindToメソッドでthisの差し替えもできるので、JavaScriptも今風のライブラリは使わずに素のbindapplyに慣れておく。

=== 演算子にも慣れておきたいところだが、これはJavaScriptではあまり使われないのかもしれない。PHPほど厳密な比較云々という記事を見かけない。

その他

TypeScript, Haskell, Go, Rust等。

PHP8からunion型が使えるようになる、かもしれない。
静的型チェックをするなら現在でも使える。
そのためにunion型が使える言語をやる。

100100.0'100' を全部受け取りたい といったときに後ろ向きな気持ちで int|float|string と指定するのも有りと言えば有りだが、代数的データ型を使える言語で積極的に複数の型をアルゴリズムに組み込むのも良い。
public Left|Right $either; が型レベルで保証されると、手抜きもしつつ安心感も増す。

プログラムの習慣としてPHPで使えそうなものもある。
例えば

function do_something($i)
{
    if (!is_numeric($i)) return [null, 'invalid argument'];
    return [$i + 1, null];
}
[$ret, $err] = do_something($_GET['i']);

というようなエラー処理方法でも良いわけである。
使ったことが無ければどちらが良いかという判断もできないので、まずは他言語のパターンというものも知る必要がある。

traitに関しては、他の言語の経験がむしろ混乱の元になるかもしれない。
PHPでは珍しく正統派の(?)ものなので、mix-inとは若干使い方が異なる。
traitのサンプルはScalaではなくSmalltalkになるようだ。

PHPを始める

ここまで来れば、PHP自体を一度も見たことがなくても大抵のPHPプログラムは読めるようになっている。
むしろPHP初心者を既に脱している。

最近はmagic_quotes_gpcといった致命的な落とし穴も無いので、ハマることも少ない。
PHPの終了タグ?>は書かないであるとか、static変数はリクエスト毎に破棄されるとか、そんな程度である。
PHPというプログラミング言語特有の勉強が別途必要だということは無い。

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

PHP_CodeSniffer で “Missing file doc comment” エラーが消えない場合の対処法

問題

VSCode に phpcs プラグインを導入1して WordPress PHP コーディング規約に PHP コードが準拠しているかのチェックを行っているのだが、以下のようなコードで、“Missing file doc comment” エラーが消えない場合がある。

<?php
/**
 * ファイルのタイトル
 *
 * ファイルの説明.
 *
 * @category   Components
 * @package    WordPress
 * @subpackage テーマ名
 * @author     名前 <foo.bar@example.com>
 * @license    https://www.gnu.org/licenses/gpl-3.0.txt GNU/GPLv3
 * @link       https://example.com
 * @since      1.0.0
 */

require_once ABSPATH . 'wp-admin/includes/plugin.php';

// 以下略

確認してみると、どうやら require_once2 が原因となっているようである3

File doc comment 直後の関数宣言に doc comment がない場合にも “Missing file doc comment” エラーが出るようで4require_once が関数宣言に類するものとして扱われているのではないかと思われる5

当然ながら require include include_once でも同様の問題が発生する。

対処法 1(PHP 終了タグ & 開始タグを挿入)

File doc Comment と require once の間に PHP 終了タグと開始タグを挿入することで、エラーが出なくなる模様6

<?php
/**
 * ファイルのタイトル
 *
 * ファイルの説明.
 *
 * @category   Components
 * @package    WordPress
 * @subpackage テーマ名
 * @author     名前 <foo.bar@example.com>
 * @license    https://www.gnu.org/licenses/gpl-3.0.txt GNU/GPLv3
 * @link       https://example.com
 * @since      1.0.0
 */

?>
<?
require_once ABSPATH . 'wp-admin/includes/plugin.php';

// 以下略

対処法 2(なんらかのコメント挿入)

上記のように、(関数宣言に類するものとみなされているらしき)require_once の前に doc comment がないことが原因なので、なんらかのdoc comment を file doc comment と require_once の間に挿入することでも、エラーは解消される。

<?php
/**
 * ファイルのタイトル
 *
 * ファイルの説明.
 *
 * @category   Components
 * @package    WordPress
 * @subpackage テーマ名
 * @author     名前 <foo.bar@example.com>
 * @license    https://www.gnu.org/licenses/gpl-3.0.txt GNU/GPLv3
 * @link       https://example.com
 * @since      1.0.0
 */

/**
 * なんらかのコメント
*/
require_once ABSPATH . 'wp-admin/includes/plugin.php';

// 以下略

対処法 3(require_once の記述位置を変更する)

require するファイルが file doc comment の直後で必要でないのなら、必要な箇所の直前に記述位置を変更することでも対処可能。

<?php
/**
 * ファイルのタイトル
 *
 * ファイルの説明.
 *
 * @category   Components
 * @package    WordPress
 * @subpackage テーマ名
 * @author     名前 <foo.bar@example.com>
 * @license    https://www.gnu.org/licenses/gpl-3.0.txt GNU/GPLv3
 * @link       https://example.com
 * @since      1.0.0
 */

// なんらかのコード

require_once ABSPATH . 'wp-admin/includes/plugin.php';
if ( is_plugin_active( 'some-plugin/some-plugin.php' ) ) {
    // 略
}

// 以下略

対処法 4(require_once を即時関数で囲む)

冒頭で require したくて、とくにコメントも思いつかないなら、即時関数で囲むことでもエラーが回避できるし、問題なく動いた。いっそ場合によっては、file doc comment 以下すべてを即時関数で囲んでやってもいいかもしれない。

<?php
/**
 * ファイルのタイトル
 *
 * ファイルの説明.
 *
 * @category   Components
 * @package    WordPress
 * @subpackage テーマ名
 * @author     名前 <foo.bar@example.com>
 * @license    https://www.gnu.org/licenses/gpl-3.0.txt GNU/GPLv3
 * @link       https://example.com
 * @since      1.0.0
 */

( function() {
    require_once ABSPATH . 'wp-admin/includes/plugin.php';
} )();

// 以下略

  1. Cf. VSCodeで「WordPress Coding Standards」に準拠してPHPの自動フォーマットする方法 | HPcode 

  2. Cf. PHP: require_once - Manual 

  3. 今回この問題が発生した例では、プラグインが導入されているか否かを is_plugin_active()7 で確認するために、pluguin.php を読み込んでいる。 

  4. Cf. drupal - Missing file doc comment? - Stack Overflow 

  5. 以下のいずれの対処法でも、関数宣言の doc comment 欠如にかんしても “Missing file doc comment” が消えたため(当然 “Missing doc comment for function” エラーは出るが)、この推測はおそらく正しい。 

  6. Cf. Missing file doc comment · Issue #693 · WordPress/WordPress-Coding-Standards 

  7. Cf: 関数リファレンス/wp enqueue script - WordPress Codex 日本語版 

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

PHPで型宣言

PHP7以降型宣言充実しており
7.4でかなり良くなり、型宣言する機会が増えたので、まとめ

1.引数の型(タイプヒンティング)

// int型のみ
function hoge(int $num)
{
    echo $num;
}
// デフォルト値
function hoge(int $num = 0)
{
    echo $num;
}
// null許容型
function hoge(?int $num)// int $num = nullも可
{
    echo $num;
}

// オブジェクト(ex.Laravel)
function hoge(Request $request)
{
    echo $request->hoge;
}

返り値(戻り値)

// int型のみ
function hoge(int $num): int
{
    return $num;
}

// null許容
function hoge(?int $num): ?int
{
    return $num;
}

// オブジェクト(ex.Laravel)
function hoge(Request $request): Request
{
    return $request;
}

// void(returnしないメソッド)
function hoge(Request $request): void
{
    echo $request->hoge;
}

プロパティ(クラス変数)

class User
{
    // intのみ
    protected int $age;
    // デフォルト
    protected string $name = 'hoge';
    // null許容
    protected ?string $address;
    // オブジェクト
    protected Request $request;
}

せっかく7.4使ってるのにいまだにタイプヒントすらないところも多々あるので積極的に使っていきたいところ

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

PHP で列挙型を作りたい

PHP で列挙型を作りたかったのでがんばって作ってみました。

以下が使用例とソースです。

使用例
<?php

class Size extends Enum
{

    protected string $name;
    protected string $abbr;

    protected function __construct(string $name, string $abbr)
    {
        parent::__construct();
        $this->name = $name;
        $this->abbr = $abbr;
    }

    public function getName(): string { return $this->name; }
    public function getAbbr(): string { return $this->abbr; }

    /**
     * @Enum
     */
    public static function LARGE(): Size { return new static("大", "L"); }

    /**
     * @Enum
     */
    public static function MEDIUM(): Size { return new static("中", "M"); }

    /**
     * @Enum
     */
    public static function SMALL(): Size { return new static("小", "S"); }

    public static function TEST(): int
    {
        return 1;
    }
}

class Size2 extends Size {

    /**
     * @Enum
     */
    public static function EXTRA_LARGE(): Size { return new static("超大", "XL"); }
}

var_dump(
    Size::values(), // 3つ
    Size2::values(), // 4つ
    Size::MEDIUM(), // Size.MEDIUM
    Size::MEDIUM()->name(), // "MEDIUM"
    Size::MEDIUM()->ordinal(), // 1
    Size::valueOf("MEDIUM"), // Size.MEDIUM
    Size2::EXTRA_LARGE(), // Size2.EXTRA_LARGE
    Size2::EXTRA_LARGE()->name(), // "EXTRA_LARGE"
    Size2::EXTRA_LARGE()->ordinal(), // 0
    Size2::valueOf("EXTRA_LARGE"),  // "EXTRA_LARGE"
    Size::MEDIUM()->equals(Size::MEDIUM()), // true
    Size::MEDIUM()->equals(Size2::MEDIUM()), // false
);
ソース
<?php

interface IEnum
{
    public function name(): string;
    public function equals(IEnum $other): bool;
    public function ordinal(): int;
    public static function values(): array;
    public static function valueOf(string $name): IEnum;
}

abstract class Enum implements IEnum
{

    /** @var \ArrayObject<string, \ArrayObject<int, string>> */
    private static array $methodNameMemo = [];

    /** @var \ArrayObject<string, \ArrayObject<string, Enum>> */
    private static array $enumMemo = [];

    private int $ordinal;

    protected function __construct()
    {
        $this->init();
    }

    /**
     * @return string
     */
    public function name(): string
    {
        self::memorize();

        return (string) array_search($this->ordinal, self::$methodNameMemo[static::class]);
    }

    /**
     * @param self $other
     * @return bool
     */
    public function equals(IEnum $other): bool
    {
        return is_subclass_of($other, Enum::class) 
            && (static::class === get_class($other))
            && $this->ordinal === $other->ordinal();
    }

    /**
     * @return int
     */
    public function ordinal(): int
    {
        return $this->ordinal;
    }

    private function init(): void
    {
        self::memorize(false);

        $caller = debug_backtrace()[3]["function"];

        if (!isset(self::$methodNameMemo[static::class][$caller])) {
            throw new \Exception();
        }

        $this->ordinal = self::$methodNameMemo[static::class][$caller];
    }

    /**
     * @return \ArrayObject<Enum>
     */
    public static function values(): array
    {
        self::memorize();

        return array_values(self::$enumMemo[static::class]);
    }

    /**
     * @param string $name
     * @return Enum
     * @throws \InvalidArgumentException
     */
    public static function valueOf(string $name): Enum
    {
        self::memorize();

        if (!array_key_exists($name, self::$enumMemo[static::class])) {
            throw new \InvalidArgumentException();
        }

        return self::$enumMemo[static::class][$name];
    }

    private static function memorize($memolizeEnumerators = true): void
    {

        $isMethodNameMemorized = !empty(self::$methodNameMemo[static::class]);
        $isEnumMemorized = !empty(self::$enumMemo[static::class]);

        if ($memolizeEnumerators && $isMethodNameMemorized && $isEnumMemorized) {
            return;
        }

        $class = get_called_class();
        $reflectionClass = new \ReflectionClass($class);
        $reflectionMethods = $reflectionClass->getMethods(\ReflectionMethod::IS_STATIC);
        $ordinal = 0;

        foreach ($reflectionMethods as $reflectionMethod) {
            $docComment = $reflectionMethod->getDocComment();

            if (!self::hasEnumAnnotation($docComment)) {
                continue;
            }

            $methodName = $reflectionMethod->getName();

            if (!$isMethodNameMemorized) {
                self::$methodNameMemo[static::class][$methodName] = $ordinal;
                $ordinal++;
            }

            if ($memolizeEnumerators) {
                self::$enumMemo[static::class][$methodName] = static::$methodName();
            }
        }
    }

    private static function hasEnumAnnotation($docComment): bool
    {
        if (!is_string($docComment)) {
            return false;
        }
        foreach (preg_split("/(\r?\n)/", $docComment) as $line) {
            if (!preg_match("/^(?=\s+?\*[^\/])(.+)/", $line, $matches)) {
                continue;
            }
            $info = $matches[1];
            $info = trim($info);
            $info = preg_replace('/^(\*\s+?)/', '', $info);

            if (trim($info) === "@Enum") {
                return true;
            }
        }

        return false;
    }
}



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

【Laravel】 ネストしたEagerLoadを書いてみる

やりたいこと

以下のテーブル構成があったときに、
マンションテーブルの情報から郵便番号テーブルの住所名を取得したいときに、
ネストしたEagerLoadを使用することができたので、残します。

マンションテーブル(apartments)

項目名 カラム名 外部キー
id マンションID

マンションの基本情報テーブル(apartment_infos)

項目名 カラム名 外部キー
id マンションの基本情報のID
apartment_id マンションテーブルのID apartments.id
postal_code 郵便番号 postal_codes.postal_code

郵便番号テーブル(postal_codes)

項目名 カラム名 外部キー
postal_code 郵便番号
name 住所名(愛媛県松山市...)

公式の説明

Laravel 5.4 Eloquent:リレーション

初歩の初歩なのか、あっさりと書かれていて、
私はぱっと見ただけでは理解出来ませんでした。

実際のコード

モデル
//マンションモデル
class Apartment extends Model{
    public function apartmentInfo()
    {
        //マンションの基本情報テーブルとの関係を明示
        return $this->hasOne(ApartmentInfo::class,"id" , "id")->withDefault();
    }
}

//マンションの基本情報モデル
class ApartmentInfo extends Model{
    public function postalCode()
    {
        //郵便番号テーブルとの関係を明示
        return $this->hasOne(PostalCode::class,"postal_code" , "postal_code")->withDefault();
    }
}

//郵便番号のモデル
class PostalCode extends Model{
}

コントローラ
    public function sample(){
        $apartments = Apartment::with(
            ["apartmentInfo", "apartmentInfo.postalCode"])->get();

        var_dump($apartments->ApartmentInfo->PostalCode->name);
    }

感想

最初は、郵便番号のモデルに、findするようなメソッドを用意していたが、
N+1クエリ問題が発生してしまうので、どうにかならないかなあと思っていたところ、改善できてよかった。

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

php(関数と引数)

関数と引数について理解したことを備忘録として記録します。

●ユーザー定義関数の型
function 関数名(引数1、引数2、…){
//処理内容
return値;
}

例)
<?php
function heikin($num1, $num2){
$result = ($num1 + $num2) / 2;
print $num1.'と'.$num2.'の平均は'.$result.'です';
}

heikin(10, 8);
heikin(3, 23);

・出力結果
PHPのテストです。

10と8の平均は9です
3と23の平均は13です

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