51
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

何故EloquentModelはクラス名を複数形にしたテーブルを自動で見に行けるのか

Posted at

前提の話

こんなモデルがあったとする

Post.php

<?php
class Post extends Eloquent
{
}

そこで全件取得の処理を書く

<?php
Post::all();

このモデルから呼ばれるテーブルは Posts テーブルとなる

ここで疑問

Modelに扱うテーブルを明示しなくてもテーブルを読んできてくれるのはナゼ
→モデル名を複数形にしたテーブルを勝手に読んできてくれるのはナゼ?

※ここ以降は完全に雑学です。途中を読むのがめんどくさい人は「まとめ」を読んでしまうことをオススメします。

Laravelの中身を追っかける

テーブルの取得処理

クラス

EloquentはModelクラス使ってる
https://github.com/laravel/framework/blob/5.1/src/Illuminate/Database/Eloquent/Model.php

テーブル名取得

その中を追うと、テーブル名の取得にはgetTable()を使ってることが分かる。

<?php
abstract class Model
{

// 省略
    /**
     * Get the table associated with the model.
     *
     * @return string
     */
    public function getTable()
    {
        if (isset($this->table)) {
            return $this->table;
        }
        return str_replace('\\', '', Str::snake(Str::plural(class_basename($this))));
    }
}

明示的に書かれていれば、そのテーブル名のテーブルを見るが
そうじゃない場合、
str_replace('\\', '', Str::snake(Str::plural(class_basename($this))))
ということをしてる

コレは何だというと
Str::snake()MyPostsという文字列をmy_postsってな感じに変換してくれる
Str::plural()は英単語の複数形を取得する
class_basename()Foo\Bar\Bazっていうクラスの呼ばれ方をしていたらBazだけ返すヘルパー

つまり、クラス名を取得して、その名前の複数形をスネーク表記にして返すというもの
(テーブル名はスネーク表記だから)

参考

Strについて
https://laravel.com/api/5.1/Illuminate/Support/Str.html#method_plural
class_basename()について
https://laravel.com/docs/5.1/helpers#method-class-basename

そして今回注目したいのは
英単語の複数形を取得するStr::plural()

複数形を習得する

Str::plural()は何をしている

Strクラスを見てみると

Str.php
<?php
class Str
{
    /**
     * Get the plural form of an English word.
     *
     * @param  string  $value
     * @param  int     $count
     * @return string
     */
    public static function plural($value, $count = 2)
    {
        return Pluralizer::plural($value, $count);
    }
}

Pluralizerというのを使っているらしい

Pluralizerとは

Laravelというフレームワーク自体の中で使われる
処理のサポートするクラスの1つで
単語の複数形への変換を行っている

実処理

<?php

class Pluralizer
{
    /**
     * Get the plural form of an English word.
     *
     * @param  string  $value
     * @param  int     $count
     * @return string
     */
    public static function plural($value, $count = 2)
    {
        if ((int) $count === 1 || static::uncountable($value)) {
            // 単数形を指定していたり、変換が不要の単語の場合はそのまま値を返す
            return $value;
        }
        // 複数形に変換できる場合はInflectorクラスのInflector()を呼び出す
        $plural = Inflector::pluralize($value);
                // 大文字、小文字を入力値に揃える
        return static::matchCase($plural, $value);
    }
}

Inflectorとは

単語の単数/複数形で文字列操作を行うことができるライブラリ

中枢部分(゚∀゚)キタコレ!!

<?php
class Inflector
{
    /**
     * Returns a word in plural form.
     *
     * @param string $word The word in singular form.
     *
     * @return string The word in plural form.
     */
    public static function pluralize($word)
    {
        if (isset(self::$cache['pluralize'][$word])) {
            return self::$cache['pluralize'][$word];
        }
        if (!isset(self::$plural['merged']['irregular'])) {
            self::$plural['merged']['irregular'] = self::$plural['irregular'];
        }
        if (!isset(self::$plural['merged']['uninflected'])) {
            self::$plural['merged']['uninflected'] = array_merge(self::$plural['uninflected'], self::$uninflected);
        }
        if (!isset(self::$plural['cacheUninflected']) || !isset(self::$plural['cacheIrregular'])) {
            self::$plural['cacheUninflected'] = '(?:' . implode('|', self::$plural['merged']['uninflected']) . ')';
            self::$plural['cacheIrregular']   = '(?:' . implode('|', array_keys(self::$plural['merged']['irregular'])) . ')';
        }
        if (preg_match('/(.*)\\b(' . self::$plural['cacheIrregular'] . ')$/i', $word, $regs)) {
            self::$cache['pluralize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$plural['merged']['irregular'][strtolower($regs[2])], 1);
            
            return self::$cache['pluralize'][$word];
        }
        if (preg_match('/^(' . self::$plural['cacheUninflected'] . ')$/i', $word, $regs)) {
            self::$cache['pluralize'][$word] = $word;
            return $word;
        }
        foreach (self::$plural['rules'] as $rule => $replacement) {
            if (preg_match($rule, $word)) {
                self::$cache['pluralize'][$word] = preg_replace($rule, $replacement, $word);
                return self::$cache['pluralize'][$word];
            }
        }
    }
}

なんか、たくさんif文が並んでるな−って感じですが
やってることとしては

不規則な複数形への変換があれば、変数に配列で書かれたマッチングで変換して
複数形と単数形の変化がないものは、そのままにしたり
典型的例に当てはまる語尾のものは、典型例に沿って複数形に変換したり(正規表現)

でした

まとめ

Modelがよしなにテーブルを読み込んできてくれるのは、「ベタな単語の照合表」や「正規表現」によって実現されていたことが分かった
確かに、それ以外に方法なんてあるのかという話で、
完全自動化とか考えると、言語処理のもはや研究の域に達するので、
まぁ予想はしてたけど、ベターな結果となった。

もし、オリジナルの単語を作ってしまい(やばそう)、
上手く変換できないときは、
ここが上手く対応できてないと思って良いのではないだろうか。

51
39
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
51
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?