前提の話
こんなモデルがあったとする
<?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クラスを見てみると
<?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がよしなにテーブルを読み込んできてくれるのは、「ベタな単語の照合表」や「正規表現」によって実現されていたことが分かった
確かに、それ以外に方法なんてあるのかという話で、
完全自動化とか考えると、言語処理のもはや研究の域に達するので、
まぁ予想はしてたけど、ベターな結果となった。
もし、オリジナルの単語を作ってしまい(やばそう)、
上手く変換できないときは、
ここが上手く対応できてないと思って良いのではないだろうか。