Doctrine\DBAL\DBALException : Unknown database type enum requested.

概要

Doctrine\DBALを インストールしてカラムに変更を加えるマイグレーションを実行しようとすると発生するエラーとその回避の方法についてです。

参考

エラーの内容

migrationでテーブルに変更を加えるときには Doctrine\DBAL をインストールする必要がある。

enum型のあるテーブルでカラムの変更を加えようとすると下記のエラーがでる。

Doctrine\DBAL\DBALException  : Unknown database type enum requested, Doctrine\DBAL\Platforms\MySqlPlatform may not support it.

Doctrine\DBALがenumをサポートしてないから。

Note: Renaming columns in a table with a enum column is not currently supported.

このエラーは変更を加えようとしているカラムがenumでなくても同じテーブルにenum型があれば発生する。

回避の方法

Doctrine\DBALがenumに引っかからないようにすれば解決するようだ。

対象のマイグレーションファイルに初期化のメソッドを追加してDoctrine\DBALがenumをstringにマップするようにする。

public function __construct()
{
    DB::getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
}

これでenum型を持ったままカラム変更のマイグレーションが成功した。

Laravel Migration(カラム属性変更)

概要

Laravelでカラムに属性を変更したい時のマイグレーション

参考

Laravel 5.6 データベース:マイグレーション

ファイル生成コマンド

$ php artisan make:migration ファイル名(create_users_table) オプション(--create=users)
  • オプション
    • 生成するテーブル名を宣言(--create)。
    • 変更を加えるテーブルを宣言(--table)。
    • ファイルのパスを指定(--path)。

ファイルの構造

upとdownの二種類のメソッドがある。

  • up
    • 新しいテーブル、カラム、インデックスをdbに追加するために使用する(カラム属性の変更もupメソッド)。
  • down
    • upが行った操作を元に戻す。

実行

$ php artisan migrate

カラム変更

laravelはデフォルトではカラムの変更を行うことができない。composerでdoctrine/dbalを追加する必要がある。

$ composer require doctrine/dbal

無いと下のエラーが出る。上のコマンドはappのホームディレクトリで実行する。

[RuntimeException]                                                                   
  Changing columns for table "books" requires Doctrine DBAL; install "doctrine/dbal".  

カラムの属性変更

$ php artisan make:migration change_books_table --table books
<?php

public function up()
{
    Schema::table('books', function (Blueprint $table) {
        $table->datetime('published')->change();
    });
}

確認

mysql> desc books;

パーフェクトPHPで学習中(chapter5)

参考

パーフェクトPHP

chapter5

クラス

継承

PHPでは継承は下記のように行う。

class クラス名 extends 親クラス名{
}

また多重継承は許されておらず、一つのクラスしか継承することができない。

オーバーライド

親クラスで定義されたプロパティやメソッドを子クラスで同じ名前で定義すると子クラスでそれを上書きすることができる。これをオーバーライドと呼ぶ。オーバーライドしたメソッドは親クラスと異なる数の引数を持ってはいけない。ただし、デフォルト値をもつ引数を追加することは許されている。また、コンストラクタも例外的に異なる引数を持つことができる。

parentキーワード

クラスコンテキストの中ではparentは親クラスを意味するキーワードになる。 使い方はselfと同じ。 キーワードは変数ではないので$thisのように$はつけない。 下記のようにすると親のメソッドを呼び出すことができる。 これによって子クラスで重複する定義をなくすことができる。

parent::メソッド(引数)

インスタンスの生成

phpではnew演算子を使ってオブジェクトをインスタンス化する。 それを変数に代入するとオブジェクトへの参照が変数に渡されるようになる。 オブジェクトにラベルが貼られるようなイメージ。 なので下記のようにすると同じ一つのインスタンスを二つの変数が参照するようになる。

$test = new Test();
$test1 = $test;

もしも別のインスタンスを複製したい場合は下記のようにする。 これでインスタンスが二つ作られてそれぞれが別の変数に参照されるようになる。

$test1 = clone $test;

phpではメソッドの呼び出しに->(アロー演算子)が使われる。

アクセス修飾子

オブジェクトのアクセス修飾子とはメソッドやプロパティがどこからアクセス可能かを定義するのに使用する。3つのアクセス修飾子がある。

  • public
    • どこからでも参照・呼び出しが可能。
  • private
    • クラス内でのみ参照・呼び出しが可能。
  • protected
    • クラス内とそのクラスを継承したクラス内でのみ参照・呼び出しが可能。

protectedを宣言すると継承した先でプロパティやメソッドを使い回すことができる。

this

オブジェクトがインスタンス化されると$thisという変数が自動的に定義される。 $thisは自分自身のオブジェクトへの参照である。 オブジェクトは自分自身のプロパティやメソッドへは$thisを経由してアクセスするようになる。

staticプロパティ

staticプロパティはクラスの持っているプロパティ。 クラス定義の際下記のように定義する。 これで初期値'static property'の設定も行われている。

private static $property = 'static property'

staticメソッド

staticメソッドはクラスメソッドのようなもの。 インスタンスに対しては使えない。 クラス定義の際下記のように定義する。

<?php
public static function getStaticProperty(){
  return self::$staticProperty;
}

selfキーワードはクラスコンテキストの内部でクラス自体にアクセスする時に利用する。 self::で自分のクラス自体にアクセスすることができるようになる。 selfは変数ではなくキーワードなのでそれ自体に$をつけず::の後のプロパティには$をつける。

名前空間

概要

名前空間とはクラスや関数の使える名前の集合を限定することで名前の衝突を防ぎ、意味を明瞭にするための機能。 名前空間ディレクトリシステムに似ている。

定義

名前空間の区切りには\を使う。 同じ名前空間の中では名前空間の指定を省略することができる。 別の別の名前空間から他の名前空間のクラスや関数を呼び出すときは常に完全修飾名(絶対パスのようなもの)を指定しないといけない。 同じ理由から名前空間を定義しグローバルなクラスや関数を用いる際にはその頭に\をつけないといけない。 グローバルな空間にいる場合は装飾名(相対パスのようなもの)からの呼び出しが可能。

namespace 名前空間\サブ名前空間;

上記のように宣言する。 名前空間の宣言よりも前に出力や文が存在してはいけない。 名前空間の影響を受けるのは下記の3種類。

  • クラス
  • 関数
  • 定数(constで定義したもの。define()では影響を受けない。)

一つのファイルに複数の名前空間を作る時は下記のように宣言する。

<?php

namespace Project\Module {
  class Directory{}
}

namespace Project\Module2 {
  class Directory{}
}

この場合、namespaceのブロックの外に文や出力があってはいけない。 一つのときと同じようにnamespaceの外に文や出力があってはいけない。

インポート

useを使って別の名前空間のクラスをインポートすることができるが関数や定数を使うことはできない。

<?php
use \クラス名;
use 名前空間;
use 名前空間 as 別名;
use 名前空間\クラス名;

asを使った場合別名でインポートすることができる。 使わなかった場合は最後の被修飾名でインポートされる。

<?php
use Foo\Bar\Baz;
$baz = new Baz();

パーフェクトPHPで学習中(chapter4)

参考

パーフェクトPHP

chapter4

制御構造

制御構造はプログラムの実行の流れを制御する役割をもつ構文のこと。 繰り返しや条件分岐のこと。

文(statement)とグループ文

phpスクリプトは一連の文の集まり。 文は;で終了する。 複数の文を{}で囲むことによってグループ化しグループ文(ブロック文)を作ることができる。 文をグループ化することでグループ文は一つの文として扱われる。 制御構造・括弧・文からなるphpスクリプトで文が実行される箇所はグループ文に置き換え可能。 また、制御構造自体も一つの文とみなされている。制御構造である文に対して制御構造を用いることも可能。つまり例えばif文をネストしたりすることができるということ。

式(expression)

phpにおける式は値を持つ全てのもののこと。評価されうる全てのものと言い換えることもできる。rubyでの式は返り値を持つ全てのものだった。またphpでは式と文は異なる。正解にいうと式は文の一部で文の全てが式ではない。例えば下記は文だけど評価はできないので式ではない。なので制御構文の条件式などには適応できない。

 echo $test;

これに対して下記は式であり文でもある。

$var = 1;

上記の文には三つの式が含まれている。=は右辺の式の返り値を(評価して)左辺に代入する演算子である。=の値は右辺の値になる。この場合=の値は1。

($var = 1);

そして上記のように複数の式を()で囲むと一つの式とみなされ最後に評価された値が()内全体の値とみなされるようになる。 なのでこの式の値は1になる。

制御構文に関する別の構文

phpphpブロック内のみをphpスクリプトとして認識し実行するが一つのファイル内であれば、複数のphpブロックをまたぐブロック文を定義することができる。これによって制御構文内に例えばhtmlが入る場合でもブロック文を継続することができるようになる。 このような使い方をする場合は可読性のためブロックの始まりの{を:で表し終わりの}を(例えばif構文であれば)endifと表すことができる。

do-while

<?php
$i = 0;
do {
  echo $i, PHP_EOL;
  $i++;
}while($i < 10);

上記のようにするとwhile構文内の条件式に入る前に一度ブロック文を実行する。その後条件式を評価するので初期値が条件にあったものではない場合にも0周目だけはブロック文が実行されることになる。

for

<?php
for(初期化式; 条件式; 反復式;)

上記のような構文で繰り返しができる。jsのfor文と同じ。下記の順番で処理する。

  1. 初期化式を評価。
  2. 条件式を評価し、trueなら文に入る。falseならfor構文を終了。
  3. 文を実行。
  4. 反復式を評価。
  5. 2に戻る。

評価対象のそれぞれの式は下記のようにカンマで区切って複数の式を指定することもできる。ただし条件式と反復式での複数の式の指定は一般的には行われない。

<?php
$ary = [1, 2, 3, 4, 5];
for($i = 0, $end = count($ary); $i < $end; ++$i){
  echo $ary[$i], PHP_EOL;
}

関数

関数の呼び出し

関数を呼び出すときは基本的に関数名に()をつけて呼び出す。

コールバック関数

PHPでも引数に関数を指定することができ、対象となる関数のことをコールバック関数と呼ぶ。

無名関数

無名関数とは名前をつけて定義する関数ではなく、コールバックの対象にしたり、変数に代入したりするときに使う関数のこと。

パーフェクトPHPで学習中(chapter3)

参考

パーフェクトPHP

chapter3

論理型

PHP 型の比較表 phpでは文字列"0"などでもfalseになる。

演算子

可算子・減算子(インクリメント・デクリメント)

インクリメント(++)とデクリメント(--)は対象の変数に1を足す/引くという演算を行い結果を変数に代入する。前置か後置かで返り値が変わる。値を返す処理と演算の処理の順番が変わり、前置の場合は先に演算を行い、後置の場合はあとで演算を行う。

<?php
$hoge = 0;
var_dump ($hoge == ++$hoge); //bool(true)
var_dump ($hoge <= $hoge++); //bool(false)
$hoge = 0;
var_dump ($hoge++);          //int(0)

演算子

演算子(instanceof)は左辺のインスタンスが右辺の型である場合にtrueを返す演算子。 当てはまらない場合はfalseを返す。

<?php
if($a instanceof Some){
  ehco '$a is instance of Some class.'
}

パーフェクトPHPで学習中(chapter2)

参考

パーフェクトPHP

chapter2

関数とメソッド

PHPでは関数とメソッドには必ず()をつける。 ()がないとプロパティ扱いになる。

PHPではそれぞれの文の終わりに;をつける。 一文しかないPHPブロック内では;は不要。

PHP_EOL

PHP_EOLは改行を意味する定数。

変数

概要

PHPは変数を$と識別子で区別する。変数の頭には必ず$をつけなければならない。 識別子は大文字と小文字で区別される。 isset()で引数の名前の変数がセットされているかどうかを調べることができる。

可変変数

$$で宣言する。 評価する変数名を変数にすることができる。 内側の$から順に評価していくイメージ。 参照する変数を動的に変更するときや文字列から変数名を組み立てたいときに役立つ。

変数のスコープ

グローバルスコープ

PHPブロックの中はグローバルスコープになる。 他のPHPブロックやファイルの中でも使うことができる。 またPHPにはブロックスコープがない。 制御構文中のブロックを抜けた後でもブロック内の変数が残る。 要注意。

ローカルスコープ

関数やクラスのメソッド内がローカルスコープになる。 ローカルスコープとグローバルスコープの変数は互いに行き来することができない。

定義済み変数

phpには実行時に自動的に初期化されている変数がある。 全ての変数が書き換え可能であるため誤って上書きしないように注意する必要がある。

スーパーグローバル変数

前述の定義済み変数の他に全てのスコープから参照可能な変数がある。これも実行時に自動的に定義されている。実行環境情報やHTTPのparams、セッション変数やクッキーなどが入っている。

定数

一度定義したらスクリプト終了まで変更できない。定数には$はいらない。

定数定義

<?php
define('NAME', 'yshr');
const NAME = 'yshr';

上記の二つはどちらも定数定義の方法だが、constで定義したものだけ名前空間の影響を受ける。

rails でRDBにツリー構造を持たせるには

参考

概要

railsでデータにツリー構造を持たせたい必要がでたので調べてみた。RDBでツリー構造を表現する方法は

  1. 隣接リストモデル
  2. 経路列挙モデル
  3. 入れ子集合モデル
  4. 閉包テーブルモデル

など、いくつかメジャーな方法があるようです。

今回は入れ子集合モデルを持つawesome_nested_setというgemを使いました。

入れ子集合モデルとは、あえてすごい簡略化して説明すると、ツリー構造の図を一筆書きで反時計回りに囲うようになぞった時に各要素の左端と右端に出会った順番に番号を振っていくことでツリー構造をテーブル上に表現する方法のことです。この説明は適当すぎですが、SQLで木と階層構造のデータを扱う(1)―― 入れ子集合モデルでわかりやすく説明されています。

model

class PostIt < ApplicationRecord
  acts_as_nested_set counter_cache: :children_count
end

DB

class ChangePostIts < ActiveRecord::Migration[5.2]
  def change
    change_table :post_its do |t|
      t.integer :parent_id, index: true
      t.integer :lft, null: false, index: true
      t.integer :rgt, null: false, index: true
      t.integer :depth, null: false, default: 0
      t.integer :children_count, null: false, default: 0
    end
  end
end

It is highly recommended that you add an index to the rgt column on your models. Every insertion requires finding the next rgt value to use and this can be slow for large tables without an index. It is probably best to index the other fields as well (parent_id, lft, depth). awesome_nested_setより

なぜindexをつけると速くなるのかはよくわからない。また今度調べてみたいと思う。

method

@root  = PostIt.new(memo: ''@root.save
@child = @root.children.new(memo: '')
@child.save

こんな感じで children メソッドというのが使えるようになる。saveの時に上記で説明した一筆書きを計算するためのたくさんのSQLが生成されるのがわかります。要素が増えるほど大量のSQLが必要になるのでこの計算の際にindexをつけておくことが推奨されているのだと思う。