Symfony Form Eventsの利用

はじめに

こんにちは。イメージ・マジック廣田です。
この頃は猛暑の上に、勉強することが多くて知恵熱が…(-д-;)
冗談ですが。

さて、業務でフォームを扱うことが多く、symfonyフォームの使い方を知ったので備忘がてら書いていきます。

Symfony Form について

PHPフレームワーク、SymfonyではHTMLフォームを扱いやすくするためにFormフォームが用意されています。

フォームを使う場合、入力された情報はDBに保存される場合が多いですよね。
例えば会員登録のフォームに入力したユーザ名やメールアドレス、パスワードなどはDBのUser
テーブルに保存される、といったような場合です。
SymfonyのFormバンドルでは、そういった仕組みのためにフォームをエンティティと結び付けてくれます。
基本的な使い方はSymfonyの公式ドキュメントをご参考に。

基本のその先

フォームに入力してそのままDB保存処理という流れの場合、
フォーム用意して…

・フォーム用意
Public function buildForm(FormBuilderInterface $builder, array $options)
{
	$builder->add(‘email’, TextType::class)
		->add(‘userName’, TextType::class)
		->add(‘password’, PasswordType::class)
	        ...
}

コントローラで表示処理とか保存処理とか…

・コントローラ
$user = $isNew ? new User() : $UserRepository->find($id);  //エンティティ用意
$form = $this->createForm(UserType::class, $user); //第二引数でエンティティを指定してフォームと連携
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { 
        // submit後の処理
        return $this->redirectToRoute('task_success');
    }

templateに表示…

・Twigでの表示
<div>
 <label>メールアドレス</label>
 {{ form_widget(form.email) }}
</div>
<div>
<label>ユーザ名</label>
{{ form_widget(form.userName) }}
</div>
……

という流れになると思います。

しかしながら、ユーザビリティを考慮して初期値を設定したり、submit後に入力値を調整したりしたい場合があります。

例えば、ユーザ登録フォームの場合。
メアドだけ変更したいけど、パスワード欄はpasswordTypeが設定されているため編集画面では常に空欄になってしまい、いちいちパスワード入力しないといけなくて面倒…。
パスワード欄を空欄のままにしても、送信後に自動的に既定値がセットされるようにしてバリデートエラーを避けたいなぁ…というようなシーンがあったとします。

そんな時に、コントローラ内で以下のようにすれば良いと思いました。

$form = $this->createForm(UsrMstEditType::class, $u);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
 送信後の処理
}
//以下でformのpassword欄の入力値を変更したい
$form->get("password")->setData($password);

しかし、これだと以下のエラーが出ます。
You cannot change the data of a submitted form.
送信されたデータを変えることはできないですよ、とのこと。

symfonyドキュメントを見直すと、こんなときはForm Eventsというのを使うと良いらしいです。

Form 送信フロー


こちらの送信フローを念頭にForm Eventsの使用例を書いていきます。

Form Eventsの利用

以下Form Eventを利用することで、Symfonyのフォームをより柔軟に動かすことができます。

FormEvents Constant Event’s Data
FormEvents::PRE_SET_DATA Model data
FormEvents::POST_SET_DATA Model data
FormEvents::PRE_SUBMIT Request data
FormEvents::SUBMIT Normalized data
FormEvents::POST_SUBMIT View data

1. Model Dataの修正

フォームの整形をする場合にはPOST_SET_DATAを利用します。

createFormの第二引数にEntityを入れるとそのEntityのフィールドの値が入りますが、フォームにそのEntityのフィールド以外の項目があった場合、そのままだと該当フォームは当然空欄のままで表示されます。
そこに初期値を設定したい時に利用したのがこのイベントです。

例えば以下のようなフォームを考えます。

$builder->add(‘email’, TextType::class)
		->add(‘userName’, TextType::class)
		->add(‘password’, PasswordType::class);
		->add(‘company’, EntityType::class, [
			‘Class’ => Company::class,
			'mapped' => false,
			'multiple' => true,
			'query_builder' => function (EntityRepository $er) {
				return $er->createQueryBuilder('c')
					->addOrderBy('c.id');}
		]);

companyの項目はUserエンティティのフィールドに存在しないものとします。
そこに以下の指定で初期値を設定します。

$entity = $builder->getData();//Userエンティティ取得
if($entity->getId()) {
	$builder->addEventListener(
		FormEvents::POST_SET_DATA,
		function(FormEvent $event) use ($entity) {
			$relations = $this->em->getRepository(UserCompany::class)->findCompaniesByUser($entity);
			$col = new ArrayCollection();
			foreach($relations as $r) {
				$col->add($r->getCompany());
			}
			$form = $event->getForm();
			$form->get(‘company’)->setData($col)
		});
}

これで初期値を設定できました!

2. Request dataの修正

リクエストデータの修正にはPRE_SUBMITイベントを利用します。

空欄のパスワード欄に規定値をセットする場合に利用したのがこのイベントでした。
実際のコードは以下のようになりました。

public function buildForm(FormBuilderInterface $builder, array $options)
{
	$builder->add(‘email’, TextType::class)
		->add(‘userName’, TextType::class)
		->add(‘password’, PasswordType::class);
	
	//パスワードが空欄でも既存値をFormにセットする処理
	$builder->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) {
		$data = $event->getData(); //入力したデータ取得
		$form = $event->getForm(); //フォームを取得
		if(is_array($data)) {
		  if(!$data[‘password’]) {
			$data[‘password’] = $form->getData()->getPassword();
		  }
		} else {
 		  if(!$data->getPassword()){
			$data->setPassword($form->getData()->getPassword());
		  }
		}
		$event->setData($data);
        });
      }

addEventListener以下の処理をすこし解説すると、
・$event->getData()でフォームに実際に入力されたデータを取得
・$event->getForm()でフォームを取得。
・if(!$data[‘password’]) 以下でパスワード欄の入力値の有無を判断し、空欄の場合にすでにエンティティが持っているパスワードをリクエストデータにセットしています。

$event->getForm()->getData()でcreateFormの第二引数に指定されたエンティティが取得できるので
$data[‘password’] = $form->getData()->getPassword();
とすればリクエストデータに既存値をセットできますよね。

※if (is_array($data))でリクエストデータが配列かどうか判定しているのは、送信ボタンを押すタイミングによって配列の時もあればオブジェクトの時もあったためです。原因よくわかってません。この辺、原因がわかれば追記したいと思います^^;

さいごに

他にもPRE_SET_DATAは初期値を設定したり、SUBMITはデータのvalidateなどができるようです。
使い方がわかると便利だな~としみじみしました。

Elastic Search基本まとめ@2018/07/30

はじめに

こんにちは。イメージマジックの宮川です!
最近は暑くて、アイスみたいに溶けそうですね。

ということで!記事第2弾です。今回は業務で使用したElasiticSearchについてです。
基本を改めて簡単にメモ的にまとめたいと思います。

ElasticSearchとは

https://www.elastic.co/jp/products/elasticsearch

Elastic社が開発しているオープンソースの全文検索エンジンです。(Java 7.X以上)
SQLの代わりに、RESTful インターフェースを使って独自のクエリを使って検索します。執筆時点の最新Versionは6.3.2になります。

ちなみに弊社では、Symfonyで使用するため、FOSElasticaBundleを使用しています。

ElasticaというPHP用にElasticsearchを使いやすくしたライブラリがありますが、これをSymfonyに合わせてBundle化したものがFOSElasticaBundle。

ElasticSearch特徴(RDBと比較して)

〇Good Point

・パフォーマンスがRDBと比較して速い(10倍速い場合もある)
・スキーマレス(ドキュメントで扱う)
・外部システムとの連携性〇(curlなどRESTful経由で操作できる)
・スケーラブル(クラスタ構成が前提で実装)

×Bad Point

・トランザクション使用不可

ElasticSearchの論理的構成

1. Index  ∋  2. Type  ∋  3. Filed

RDBで言うとIndex=Database、Type=Table、Document=Row

参照 https://dev.classmethod.jp/server-side/elasticsearch-getting-started-06/

・1. Documentの集合
・2. Indexに登録するDocumentを分類する型や定義を持つ
・3. データの最小単位。複数のフィールドから構成される

※注意 6.Xから1Indexに対して、1Typeのみ作成可能。

ElasticSearchの物理的構成

1. Cluster  ∋  2. Node   ∋  3. Shard

・1. 複数のNodeを一つのESとして動作させる。
・2. ESのインスタンス。ここにデータを格納する。イメージとしてはサーバ。
・3. Indexを分割したもの。primaryとreplicaがある。

primary: インデックスの書き込みと参照処理で使用。これをreplicaにコピー。
replica: インデックスと参照処理で使用。

ElasticSearchの全文検索の仕組み

1. Analyzer  ⊇  2. Char Filters, Tokenizer, Token Filters

・1. 形態素解析(※1)やN-gram(※2)等、データ格納・検索方法を指定。
・2. Char Filters → Tokenizer → Token Filtersでデータを検索する

Char Filters: 正規表現等で文章を検索。
Tokenizer: NGramやスペース等で単語を分ける。(=トークン化という)
Token Filters: 小文字に正規化したり、トークンをフィルタリングする。

※実際に全文検索で単語を分割していくイメージ

参照 https://blog.shibayu36.org/entry/2016/08/15/122913

※1 文章を意味を持つ最小限の単位(=単語)に分解する手法です。
例)
「私は、宮川という名前です。」
⇒ 私 / は / 、 / 宮川 / と / いう / 名前 です / 。

※2 指定文字数で文章を分割する手法です。
例(2gram))
「私は、宮川泰三という名前です。」
⇒ 私は / は宮 / 宮川 / 川と / とい / いう / う名 / 名前 / 前で / です / す。

コマンドサンプルたち

1. 状態確認のコマンド

#elasticsearchの状態
curl 'localhost:9200/'

# index 確認
curl 'localhost:9200/_cat/indices?v'

# node 確認
curl 'localhost:9200/_cat/nodes?v'

# cluster 確認
curl 'localhost:9200/_cat/health?v'

2. 作成系のコマンド


# index作成
curl -X PUT 'localhost:9200/newindex'

# type作成
# ?prettyをつけると結果をformatしてくれます。
curl -X PUT 'localhost:9200/newindex/newtype/1?pretty' -d '
{
  "name": "sample"
# elastic seach6.X系より以下が必須です。
}' -H 'Content-Type: application/json'

# insert or update
curl -X PUT 'localhost:9200/newindex/newtype/1?pretty' -d '
{
  "name": "John Doe"
}' -H 'Content-Type: application/json'

# delete
curl -X DELETE 'localhost:9200/newindex/newtype/1?pretty'

3. 検索系のコマンド

## query
curl -X POST 'localhost:9200/newindex/_search?pretty' -d '
{
  "query": {
	"bool": {
		"must": [
			{
				"match": {
					"name": {
						"query": "sample",
						"operator": "and"
					}
				}
			}
		]
	}
  }
}
' -H 'Content-Type: application/json'

参考:
ElasticSearch入門

Illustratorスクリプトのあれこれ

はじめに

こんにちは、イメージ・マジックのもあいです。
弊社開発部のメインはWeb(PHP/JavaScript/Css)なのですが、それ以外にも工場を効率的に稼働させるためにもいろいろ行っています。
今年になって、通常は人が手作業で行っているAdobe Illustratorの作業を自動化させようとして苦戦したことについて書いていきます。

オブジェクトを配置するための座標

ここは最後まで悩みの種でした。通常、PCやWebで座標の原点は左上となるのですが、Illustratorスクリプトでは左下となります。長年左上で考える癖がついていると座標計算には苦労しました。最終的には、Illustratorのアクションで真ん中に移動させることができるのと、それで問題が無いように処理する画像を作成することによって対処しました。

距離の単位がPt(ポイント)である

要求がmmやピクセルで表されるのですが、Illustratorではポイントで設定しなければいけないため、変換処理をいたるところに記述していたのですが、最終的には下記のような関数を定義して対処しました。

function convertUnit(num, before, after) {
    return new UnitValue(num, before).as(after);
}

当然ですが、印刷機の解像度(DPI)を考えないといけない場合は単純にはできません。

ドキュメントに載っていない、アクションを実行する

実際に動かして印刷機にデータを送信してみるものの、問題がたくさんでてきましたが、その中で一番困ったのはスクリプトではどうにもならなくてアクションだと問題無く処理ができるというとこ。
ドキュメントを見ているとアクションが実行できるようなメソッドが存在しません。(ただし、Photoshopにはドキュメントにあります)
いろいろ調べてみるとapp.doScriptというメソッドでアクションが実行できることがわかり、一部は対処可能となりました。

アクションでも完全に実行できるものと不完全に実行ができるものがある

アクションに登録すればapp.doScriptで実行はできるが、手動で実行したときとIllustratorスクリプトで実行したときに結果が異なることがありました。
これについては、解決できていないのですが、Illustrator以外で解決させるようにしました。

デバッグをする環境が非常に貧弱

Illustratorスクリプトをデバッグする環境として、Extendscript Toolkit CCというツールが一応存在します。ですが、導入するにはAdobe Createve Cloudアプリの設定で古いアプリケーションを表示にチェックを入れておかないと表示されないのと、app.documents.addでドキュメントを追加しようとすると動かなくなるという事象が発生してほとんど使い物になりませんでした。
従って、alertを入れてデバッグを進めるという非常に古典的な方法しか使えないのが大変でした。

最後に

工場を効率的に稼働させることができれば最終的には利益になるので、こういった作業はWelcomeです。使ったことのないものに触れるというのはそれなりに楽しいです。
今回初めてIllustratorスクリプトを触りましたが、デバッグしにくい等の問題も多くあり非常に大変でした。

Immutableという概念について

はじめに

こんにちは、イメージ・マジックの安藤です。

社会人歴は2年目に突入し、入社して1ヶ月ほどが過ぎました.

日々バグ対応を行っているのですが、弊社のシステムに関する私の理解がまだ浅いので苦戦することも多々あり、そこが仕事の面白みかなともまた思っている次第です。

今回はコードを書く上でのワンポイントとしてImmutableという概念について少し触れればと思います。

なお、以下サンプルコードはJavaScriptで記載します。

Immutableとは

ある値がImmutableであるとはどういうことでしょうか?

この記事では、「ある値の状態が変わることがない」ということをImmutableとしたいと思います。

普通に考えると、Immutable=不変なので値が変わらないということから定数のようなものを思い浮かべるかもしれませんが、その違いとは何でしょうか。

まずは単純に数字同士の足し算を考えてみましょう。

数字同士の足し算はすべてImmutableになり、以下の例ではnum1やnum2はconstなので直接書き換えることもできないためこれらの値の状態が変わることはありません。

const num1 = 1;
const num2 = 3;
const num3 = num1 + num2;
console.log(num1, num2, num3); // 1 3 4

では、値の状態が変わる(mutable)場合の例を見てみましょう。

以下ではarrはconstとなっていますが、sort関数で昇順にソートすることでarr自体が変更されていることがわかるでしょうか。

const arr = [1, 3, 2, 5, 4];
arr.sort((a, b) => a - b);
console.log(arr); // [1, 2, 3, 4, 5]

何故このようなことが起こるのかというと、少し難しい話になりますが、constはオブジェクトのインスタンスが変わらないことしか保証しないからです。

上記の例ではarrはArrayクラスのインスタンスになっており、オブジェクトの内部で状態が変更されています。

そして、arrが見ているインスタンス自体は変更されていないため、arrがconstであるかどうかに関わらず値の状態を変更することができてしまいます。

余談ですが、sortのような呼び出したオブジェクト自体が変化するメソッドを破壊的メソッドと呼びます。

なお、同じArrayクラスでも以下のように破壊的でないものもあります。JavaScriptに限らないですが、自分の使おうとしているメソッドが破壊的か否かはきちんと確認するべきでしょう。

const arr = [1, 2, 3, 4, 5];
const added = arr.map(n => n + 1);
console.log(arr, added); // [1, 2, 3, 4, 5] [2, 3, 4, 5, 6]

最後に、Immutableな配列への要素追加とmutableな配列への要素追加の対比の例を載せておきます。

const arr = [1, 2, 3, 4];
const arr2 = arr.concat(5); // immutable add
console.log(arr, arr2); // [1, 2, 3, 4] [1, 2, 3, 4, 5]
arr.push(5); // mutable add
console.log(arr, arr2); // [1, 2, 3, 4, 5] [1, 2, 3, 4, 5]

Immutableであるメリットとは

ここまでImmutableとは何かということに触れてきましたが、Immutableであることのメリットに触れていきたいと思います。

Immutableであることの最大のメリットはオブジェクトの破壊的変更を気にする必要がなくなるという点に尽きます。これはプログラムを書いていくうえで想定外の変更が起きにくくなり、バグを抑制することができるということでもあります。

また、Immutableを適切に意識することでプログラム上でどの値がどのように変更されていくのかが追いやすくなるという効果もあるでしょう。

Immutabeで注意すべき点

Immutableにしようとする値の状態が頻繁に変更される場合や変更可能なデータを多く持っている場合、Immutableは適切でないことが多いため注意が必要です。

また、非破壊的メソッドを使っているとメソッドチェーンを繋げやすくなりますが、過剰なメソッドチェーンは可読性を下げることになるので注意しましょう。

参考

Immutableの利便性、大きなメリットについて。

イミュータブル – Wikipedia

解像度について

こんにちは、イメージ・マジックのBONOです。

イメージ・マジックは、Webで入稿された画像をプリントしてお届けするWeb2Printと呼ばれるサービスを各種展開しています。そのため画像に関する知識や、プリンターに関する知識が開発する上で必要となるケースがあり、入社した方々がよく勘違いされるポイントを中心に、ざっくりと書いていきたいと思います。

今回は、解像度についてです。
解像度の単位で、よく耳にするのはDPI(Dot Per Inch)、PPI(Pixel Per Inch)、Density等使われますが、プリンターの解像度と画像ファイルの解像度は考え方が異なります。

◇画像の解像度について
1インチ(2.54cm)あたり、何ピクセルの画素を配置するかを定義するものです。解像度が高ければ高いほど細かい描写も表現でき、拡大しても比較的スムーズに見えるようになります。解像度が低いと昔のゲームのようにガタガタに見えます。1ピクセルはRGBやCMYK等で表現され、1ピクセルですべての色を表現します。

◇プリンターの解像度について
プリンターの仕様書によく書いてある解像度ですが、画像と比較するとかなり高解像度に感じると思います。ただ、同じ1ドットでもプリンターのドットはインク1滴分(1色分)のドットになるため、色を表現するためには数ドットを組み合わせる必要があるため、ある程度の面積が必要になります。

上記の違いから分かるように1200dpiの印刷解像度のプリンター(Tシャツ等へのプリンターだとこれぐらいの解像度)へ、1200dpiの画像を入力させても意味がありません。プリンターのインクはCMYKの4色で表現される多いため、1ピクセルを表現するには最低でも4ドット分必要になるため300dpi以上の画像を用意しても表現できないというわけです(実際には4ドットだけですべての色ができないのでもっと少ないdpiになるかと思います)。

ごくごく基礎的な話になってしまいますが、最初なのでこの辺で。

開発サイドから見るバグ報告の話

こんにちは株式会社イメージ・マジックのsoenoです。

私はフロントエンド側でバグを受け取る側にいます。
いざバグが上がってくると手一杯になり伝えられない報告について。
もらうと助かる報告とその理由などについて書いてみようかと思います。

まず不具合の発生時に報告を上げる側は以下のような思いを抱いているのではないかと思います。
・壊れているので直してほしい。
・クレーム来てるから早く動いてほしい。
・細かいことはわからないけどとにかく何とかして。

急いでほしい、早く結論が知りたい。
そんなときほど初期に上がる報告が重要です。
開発側で報告を受けたとき、いろいろと調べるポイントがあります。
素敵な報告をいただくと調べなくてはいけない箇所が減ったり、
うっすらと見当がついたり、もしくは重点的に確認するべき場所を決定できます。

ではもらってうれしい報告を手軽に書く方法について考えてみます。
あってほしい情報や、必要な項目について抜けなく上げるために
作文などでなじみのある5w1hに照らし合わせて考えました。
5w: いつ、どこで、だれが、何を、どうした。
1h: どのように

報告時に書く5w1hを言うと以下のようになります。

・いつ(When)
内容:バグ発生時刻(特にサーバー周りのバグの時に重要)
理由:記録を確認するときに膨大な情報の中から探す場合、時刻がわからないと探しきれないことがあります。

・どこで(Where)
内容:ネットワーク接続環境など。社内で接続しているかそれとも社外か。遅い回線ではないか。
理由:ネット回線のスピードによる挙動の場合そのスピードでないと再現しないことがあります。
対処する場所、対処可能な部署が違うこともあり得ます。

・だれが(Who)
内容:バグ発生時の環境、ブラウザ、ブラウザバージョン、端末(pc?smartphone?)、OS(windows?mac?linux?…)
理由:特定のブラウザのみで発生する動きがあるので、そのブラウザしか再現しないことがあります。

・なにを(What)
内容:ウェブでいうとどのページの(url等)どの要素に対して操作を行っていたか
理由:ページとどの要素かがはっきりしていると違うページの違う要素を延々調べなくて済むようになります。

・なぜ(Why)
内容:そもそも何のためにその操作をしたか。
理由:操作の意図ですが、想定した操作外での不具合の把握でしょうか。
バグ取りというよりはサイト改修、改善時に知っておきたいところです。

・どのように(How)
内容:バグ発生のための手順
理由:手元の環境で再現するかどうかはとても重要です。壊れていないものを直すためには
どうしたら壊れるのか、どこが壊れるかもしれないのかなど、上の5W総出で類推することになります。
特徴的な動作や、何か心当たりやひらめきがないと難しいということもあります。

追加でもう一つ5w1hでは足りなかった要素として”hope”を足します。

(英語的に正しいかの検証はしません。)

・どうなってほしかったか(Hope)
内容:正しく動いていたらどうなるか
理由:バグと仕様との判断と、一見正しく動いているように見える挙動の際に何が問題になっているかを特定します。

何か特殊な状態があればそれも追記してあるとなおいいです。
例として思いつくものを上げると
・同一画面を複数ブラウザで開いていた。
・ブラウザで戻るボタンを押してのフォーム入力。
等々

この報告を私が読む場合、下に書いた順番になります。
0、特殊な操作、状態の報告
1、どのように(How)
2、どうなってほしかったか(Hope)
3、なにを(What)
4、だれが(Who)
5、いつ(When)
6、どこで(Where)

順番は個人の手法、サーバー担当かフロント担当かで違ってくると思います。
特に抜けて困るのは1、2、3番。
サーバー側は加えて5番。(多分)

早く回答してほしい、今すぐ着手してほしい。
そういうときほど初期の報告が大切です。
そんなこと言ってもちゃんと書いても遅いじゃん。
という意見もあるかと思います。
ですが、ちゃんと書いていなければきっともっと遅かったはず!
というわけでもらってうれしい報告について書きました。

最後に、こちらの記事は一個人としての意見ですのでご注意ください。
フロント周りやってますのでサーバーあたりに抜けがあるのではないかと思います。
開発にもいろいろあります。自分のケースによりあうものを参考にするといいと思います。
(web上に既にきれいに書かれているものもあります。)

初心者がなやむ型の概念@2018/05/28

はじめに

こんばんわ!
今回担当の廣田です。

未経験入社で早3カ月たちました。
日々の業務のなかで得た知識などを書いていこうと思います。
高度な内容は書けませんが、同じように未経験からプログラミングを始めようと思ってらっしゃる方と共有できたり、学習の役に立てば良いなとおもいます。

今回は、型について備忘がてら書こうと思います。

phpの型指定

ベテランSEの方々には、当然の概念であろうデータ型。
恥ずかしながら、実業務のコードを見たときに一番初めにつまづいたところでした。

-型とは:整数型(int)や、文字列型(string)のようなデータ(値)の種類の分類のこと。

参考:PHPマニュアル

実業務のコードに出現した型指定はこんな以下のようなものでした。

public function findForListBox(Organization $org):array
{
	return $this->createQueryBuilder('m')
	->andwhere(m.organization = :org)
	->.....
}

メソッド名の後ろに「:(コロン)+ 型」で戻り値の型指定をするんですね。
このコロンが気になりすぎてネットで調べましたが、基礎的すぎるのか
型指定だと説明しているサイトはあまり無かったように思います。

プロジェクトで使っているフレームワーク、symfonyでは
アノテーションで記述したり。

/**
     * @param string $email
     * @param int $exclusiveId
     * @return bool
     */
    public function existsByEmail(string $email, int $exclusiveId): bool
    { ...

型キャスト

そのままだと戻り値が意図した型にならず、エラーが返ってくることがあります。
例えば

public function __toString()
    {
        return $this->getId();
    }

とすると以下のようなエラーがかえってきます。
「Return value of __toString() must be of the type string, integer returned」

先輩に聞いたところ、「キャストした形にして」とのこと。
キャスト?なにそれ楽しそう。ディズ〇ーランドのスタッフのことかしら、とか思ったのは秘密です。

型キャストを行うことで型の変換ができるんですね!

return (string)$this->getId();

とすると、string型に変換して返してくれます。

例:
<?php 
$foo = 10; // $foo はintegerです 
$bar = (boolean) $foo; // $bar はtrue(boolean)です 
?>

参考:PHPマニュアル,型の相互変換

フォームの値をコントローラで$Request->get()するときに凡ミスした点

フォームのチェックボックスがチェックされたらDB登録する機能を作りたかったある日。

html
<form>
	<input type="checkbox" name="isAgree" value="true">はい
	...
	<button type='submit'>保存</button>
</form>
controller

$isAgree = $request->get("isAgree")
if($isAgree){
	 $this->em->persist($user);
     $this->em->commit();
}

エラーになりました。

原因がわからず、しばらく考えこんでしまいましたが
とりあえずvar_dumo($isAgree)。

string(4)"true"

う~ん、値とれてるみたいだけど…
…ん?string?なぜ?

原因はhtml側で「value=”true”」としていたところでした。
bool値を返したいなら「value=true」としなくていけないんですね^^;;
勉強になりました。

まとめ

型について、完全には理解できていませんが、補完やバグ発見のためできるだけ戻り値を指定するべきとのことでした。
型を指定することで大きな問題になる前にエラーを特定できるなど
安全なプログラムを書くために有益なようです。

配列だと思ってたらオブジェクトだったり、
いまだにつまづくことが多い型ですが、よくわからなかったらとりあえずvar_dump()するようにしています。
そうすると該当のメソッドがどういう形で値を返してくるのかというメソッドへの理解や、初学者にはイメージしづらいオブジェクトの構造なんかも理解できたりします。

とりあえずエラーになったらvar_dump、
困ったらvar_dump、
暇になったらvar_dumpしてます。

入社から3カ月、
とりあえずvar_dumpとは友達になれたような気がします。

Vue.js 実務でハマったところ @2018/05/21

はじめに

こんにちは!
株式会社イメージ・マジックの技術ブログ、今回担当の宮川です。

本ブログでは、イメージ・マジックがどんな技術を扱っているのかを知ってもらうために、業務で得た知識のまとめやハマって解決したこと、技術選定理由、最新技術動向などや、社内の雰囲気が伝わる内容を書いていきたいと思います。

今回は、社内のプロジェクトで採用している、Vue.jsについて書きます。

Vue.js所感

Vue.js使い始めて2カ月ですが、所感として良いところは概ね以下かなと思ってます。

  • データバインディングが簡潔
  • 部品化しやすい
  • 機能の分類が細やかで配慮が行き届いている
  • だいたいライフサイクルがいい感じにやってくれる

Vue.js ハマったところ

以下、まとめました。

1. Arrayの更新は、以下の方法で更新しないとVue.jsが検知してくれない

// Vue.set
Vue.set(vm.items, indexOfItem, newValue)

// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

https://www.elastic.co/jp/products/elasticsearch

2. DOMレンダリングが完了したタイミングで処理したい

Vue.nextTick(function() {
  //処理
})

参考:いつ DOM が更新されますか?

 

3. 親子間のデータの受け渡し

// 親はプロパティを渡す
<children customprop="parent.child"></children>

// 子はcustompropを受け取る
Vue.component('my-component', {
   template: `<input type="text" value="customprop">`
})

 

4. 「input type=”file”」は、値をbindできない


// NG v-modelでbindできない
<input type="file" v-model="property.file"/>

// ファイルを取得する場合は、onChangeでイベントを受け取る
<input type="file" @onChange="storeFile($event)"/>

// js
...
storeFile: function(event) {
   const file = event.target.files[0]
},
...

 

5. $.emit()には、キャメルケースは使用できない

// NG(キャメルケースは不可)
$.emit('updateParent')

// OK(チェインケース(ハイフン区切り)は可。)
$.emit('update-parent')

 

6. Vue.jsのtemplateの最上位タグには v-forは使用不可

// NG
Vue.component('my-component', {
   template: `
<li v-for="item in items">
                {{ item.message }}
              </li>

`

})

// OK
// 最上位タグの子タグで、v-forを使用する。
Vue.component('my-component', {
  template: `
<ul>
               
<li v-for="item in items">
                 {{ item.message }}
               </li>

             </ul>

`
})

 

7. webpackで複数のjsファイルでそれぞれ定義したVueが読み込めない

webpackで分割すると、Vue自体のインスタンスが異なるためイベントが発生できない。importすれば問題無く動作する。

import {EventBus} from '../Util/EventBus';
import AutoCompleteInput from '../AutoCompleteInput.vue';
import AutoCompleteList from '../AutoCompleteList.vue';
...

 

8. Webpackで単一ファイルコンポーネントをcompileすると、cssがjs以下に出力

× public/js/...
○ public/css/...

 

9. Vue.jsの初期化後に、動的に追加したhtmlにVue.jsは適用できない

図のInitでEventsのAttach等行うため