アイコンを作ってみました 19/06/05

はじめに

東京もそろそろ梅雨に突入しそうな6月です。廣田です。

最近はあまりtechなことをしていないので、techじゃないブログになりそうですが先日アイコンを作る機会があったので、気がついたことを書こうと思います。

アンチエイリアスのいたずら

PC画面上では有用なデザインソフトの便利機能が、実際に印刷されると思わぬ結果となって現れることがあります。

そのひとつがアンチエイリアスだと思います。

以前はよくphotoshopで漫画を描いていたのですが、デジタルで原稿データを作成する場合、アンチエイリアスをかけたまま入稿してしまうとトーン部分(よく漫画にあるドット柄のこと)に予期せぬ幾何模様が浮き出てくる「モアレ」という現象が発生します。
そのため入稿時は必ず二値化を行ってアンチエイリアスを無効化していました。

似たようなことが弊社サービスのオリジナルプリントでも起こるようで、お客様が入稿されたデータにかかっていたアンチエイリアスのせいで予期しない色が出てしまったりもするようです。

こと印刷と相性が悪いアンチエイリアスは面倒な存在だったりしました。

いざアイコン作成

そんないつもの感覚でアイコンを作成して画像を差し替えてみました。
かなりジャギーが気になる結果に。。

ここで忘れ去っていたアンチエイリアスを強めにかけてみました。
滑らかになりました!
アイコンのような小さく表示される画像の時は特にジャギーが出てしまうようで、こんな時、アンチエイリアスは有効なのだなと今更ながら気づきました。

幅広い業務に関われます。

趣味のブログのようになってしまいましたが、今回作成したアイコンは弊社サービスのODPSのクライアントシステムのアイコンで使われていたりします。

開発本部では作成したバーコードやRFIDタグをリーダーでピコピコ読み込んでいる方がいたり、DAS(デジタルアソートシステム)を導入するために何やらライトを光らせている方がいたり、工場のプロセスを省力化するために幅広い業務を行っています。

おそらく普通のIT企業のプログラマーさんよりも、興味があれば色んな技術に関われるのがイメージマジックの面白さかもしれません。

気になった方は弊社求人情報を覗いてみてください!  

Doctrine オブジェクトレベルのトランザクション

はじめに

廣田です。東京は2月あたりのこの時期が一番寒いですね。(;´T`)ズズッ

この間、絶版した雑誌の記事がどうしても読みたくて国会図書館オンラインを初めて利用しました。
日本で出版された書籍を全て所蔵しているともいわれる国立国会図書館ですが、行かなくてもオンラインで記事(複写)を購入できるようになっているんですね。(知らなかった…)

使ってみた感想ですが図書館のサイトと侮るなかれ。
まるで大手通販サイトなみの使い勝手の良さに驚きました。
記事を「カートに入れる」んだぁ…💡

図書館らしいシンプルなデザインでありながら、わかりやすい操作性は個人的に大変勉強になるものでした。

創刊当初の週刊少年漫画なども所蔵しているので、機会があれば利用してみてはいかがでしょうか。
https://ndlonline.ndl.go.jp


さて今回はsymfonyで利用されているORMのDoctrineについて、
トランザクション中で、persistされたエンティティってflushされるまでどこに貯まるんだろうと疑問に思い調べたので、備忘としてメモしようと思います。

背景

以下のような感じでCSVファイルからデータを取り込んでDB登録している機能を作っていました。


 ・・・・
$file = $uploadForm->getData()['file'];
/** @var EntityManagerInterface $em */
$em->beginTransaction();
$errors = $csv->upload($file->getFileInfo(), $csvHeader, $callback);
     //↑ここで1行1行エンティティに変換してpersistしていたりする。
if (count($errors)) {
                $em->rollback();
                $this->addFlash('warning', 'エラーが発生したため登録していません。');
            } else {
                $em->commit();
                $this->addFlash('notice', '登録しました。');
            }
   ・・・・

CSVデータに内容がダブっている行があった時にそこはpersistしないようにしたいなぁ…
⇒ すでにpersistされているエンティティと今見ているCSVデータの内容を比較したいなぁ…
⇒ 同一トランザクション中ですでにpersistされているエンティティってどうやって取得すればいいんだろう??
というきっかけでした。

UnitOfWorkのサイクル

Doctrine公式のドキュメントによると、UnitOfWorkはオブジェクトレベルのトランザクションのようなものと表現されています。
https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html#working-with-objects

EntityManagerが生成された時、あるいはEntityManager->flushを発動した後に新しくUnitOfWOrkのサイクルが開始されるのだそう。
そのサイクルの中で変更をトラッキングし、データベースに正しい順序で書き込む役割を担う…とのことで
ここを見ればpersistされたエンティティを取得できそうと考えました。

UnitOfWorkへのアクセス

公式ドキュメントでは$em->getUnitOfWork();でUnitOfWorkにアクセスできるとしか書いてなかったので、 UnitOfWOrk配下の関数を一通り見ていると、それっぽい名前のものを発見。

$em->getUnitOfWork()->getScheduledEntityInsertions();//←これ


さっそく使ってみると、persistされているエンティティの一覧を配列で返してくれました。

他にもgetScheduledEntityUpdates()、cheduledEntityDeletions()などがあり、UnitOfWorkがトラッキングしているエンティティを状態ごとに取得できるようになっています。

さいごに

Doctrineってこういうライフサイクルなんだなーと、ほんっっっの少しだけ仕組みが理解できたような気がしました。

axiosで渡したパラメータをSymfonyで受け取る方法まとめ

はじめに

あっという間に年の瀬で、もうすぐ入社から10か月の廣田です。

入社時よりは多少知識はつきましたが、他の記事を読むとハイレベルだな~、まだまだだな~と感じ入ります。

とはいえ難しいことは語れないので(汗)、今回は毎回調べてしまうaxiosとSymfonyのやりとり(SymfonyのRequestクラスの使い方)について書こうと思います。

開発本部で使っているjavascriptフレームワーク、Vue.jsでサーバーとやり取りする際にaxiosを利用していますが、その際のaxiosの書き方やSymfonyでパラメータを受け取る時の方法が、いつもなんだっけ?となるので備忘がてらまとめました。


Routeにplaceholderを設定しているとき

SymfonyでRouteを以下の例のように設定している場合、GET/POST送信されたパラメータをコントローラの引数として取得できます。

単純な値を1、2個渡すだけならこの方法を使ったほうが良いと思います。(URLの見た目もスッキリします。)
▼javascript側
const url = Routing.generate('app_find_prdct', {id: this.id});
const res = await axios.get(url);
if (!res.data.success) {
   EventBus.$emit('simple-modal_alert', 'エラー', res.data.errorMessages, callback);
   return;
}
//うまくいったときの処理
・・・
  ▼PHP側
  /**
     * @Route("/findProduct/{id}", name="app_find_prdct", options={"expose"=true}, methods={"GET"})
     * @param ProductRepository $pRepo
     * @param int|null $id
     * @return JsonResponse
     */
    public function findProduct(ProductRepository $pRepo, ?int $id ): JsonResponse
    {
        $p = $pRepo->findById( $id );
        if( !$p ){
            return $this->json(['success' => false]);
        }
        return $this->json([
            'success'  => true,
            'product' => $p
        ]);
    }
Routeでプレースホルダーに{id}とすることでコントローラの引数$idでaxiosが渡したパラメータを取得しています。

SymfonyのRequestクラスを使う

プレースホルダーの指定をしないときはSymfonyのRequestクラスを使います。

use Symfony\Component\HttpFoundation\Request;
public funtion someFunction(Request $request) {
     ...............
//GETの時
$id = $request->query->get('id');
//POSTの時
$id = $request->get('id');

//JSON形式でPOSTされた値を取得する時など
$content = $request->getContent();
['id' => $id] = json_decode($content, true);

}
axiosで渡すパラメータの値がJSON形式の時や、パラメータが複数の時はRequestクラスのgetContentを使うと、POST/GETされたそのままのデータにアクセスできます。
さらに、その値をPHPのjson_decodeを利用して連想配列形式で取得したりできるので便利です。

※参考:Symfony Routing
    generate_url_javascript
    HttpFoundation Component

さいごに

初歩的なところですが、当然のように使っていたので文章にまとめてみると理解が深まったような気がします。
記事を書く順番が来るとネタを決めるまで悩みますが、振り返る良い機会になりますね^^

冬休みまで指折り数える毎日ですが、それまで体調管理に気を付けて頑張りましょう!
ではでは。

命名について:The Art of Readable Codeを読んで。

はじめに

今日も元気に働きバチ!こんにちは。廣田です。
弊社ではコミュニケーションツールとしてslackを使用していますが、たまに見るヘルプセンターの文面がユニークで仕事中にフフっとしてしまいます。

さて先日、先達の方々にコードを見ていただける機会がありまして
いろんなダメ出しがあったのですが、変数やメソッドの命名について多く指摘をうけました。

そこで結構前に紹介してもらっておきながら未だ読んでいなかった名著The Art of Readable Codeを読んだので、
命名について書いている2章から、学んだことを抜粋して紹介していこうと思います。

Packing Information into Names

名前はちょっとしたコメントのようなものです。
十分なスペースが無くとも、良い名前を選ぶことにより多くの情報を伝えることができるのです。

明確な単語を選ぶ

例えば ”get” という単語はとても不明確です。例えば以下のようなメソッドがあるとします。

def GetPage(url):
……

この場合、このメソッドはどこからpageを取得するのでしょうか。

ローカルキャッシュでしょうか? DB、インターネットからかもしれません。
もしインターネットからであれば、FetchPage()やDownloadPageといったほうがより具体的です。

同様によく使われる単語としてsizeがありますが、返り値が予測できるような、より具体的な命名を考えるべきです。
例えばツリー構造の高さを示すならTreeHeight、ノードの数ならNumNodes()、 メモリのサイズならMemoryBytes()など。

よく使われる単語について以下のように置き替えることで、より具体的な表現ができるでしょう。

不明確な単語 より明確な代替語
send deliver, dispatch, announce, distribute, route
find search, extract, locate, recover
start launch, create, begin, open
make create, set up, build, generate, compose, add, new

 

tmpやretvalのような総称的な命名を避ける

“Tmp”や”retval”, “foo”などはよく使われる命名ですが、そのものの持つ値や目的を描写するような名前を選ぶべきです。
以下のようなコードを例に見ていきましょう。

function (v) {
  var retval = 0;
  for (var i = 0; i < v.length; i ++ ) {
	retval += v[i] * v[i];
	}
  return Math.sqrt(retval);
}

変数retvalは“I am a returned value”という以上の情報を含んでいません。

より良い命名とは変数の目的やそのものの含む値を詳細に表すものです。

先述のコードでは変数の中身は”v”の2乗の累積でした。
なのでより良い命名は、”sum_squares”などでしょう。このような命名にすることで、バグも見付けやすくなります。

抽象的な名前よりも具体的な名前をつけるようにする

たとえばServerCanStart()という名前のメソッドがあるとします。
そして、このメソッドはサーバが規定の TCP/IPポートをListenできているかを検証するものだとします。

この場合、より具体的な名前はCanListenOnPort()などでしょう。
こちらのほうが、メソッドがなにをするものなのかを直接的に示しているからです。

抽象的な命名をした場合、チームに新しく加わったメンバーにとって、それが何であるのか、なぜ必要なのかということが分かりにくくなってしまいます

名前に追加の情報を付け加える

読み手が変数について知っておかなければならない重要な情報がある場合、
名前に追加の単語を付け加えることは価値のあることです。

たとえば16進数の文字列を含んだ変数があるとします。

  string id; // Example: "af84ef845cd8"

この場合、hex_id という名前のほうがより良いでしょう。
読み手がidのフォーマットは16進数だとわかるからです。

 変数名  より良い命名
暗号化されていないパスワード password ⇒ plaintext_password
UTF-8にエンコードされたhtmlバイト html ⇒ html_utf8

名前の長さはどの程度にするべきか?

変数名は長すぎてはよくありません。
名前が長くなればなるほど、覚えづらくなり、スペースも消費してしまいます。

一方、1単語、あるいは1文字の名前というのも考え物です。

短い名前は短いスコープで

複数ブロックにまたがるような変数に対し1,2文字の暗号のような名前を使ってはいけません。
そのような短い名前は2,3行の短い範囲で使用される変数には良いでしょう。

頭文字や省略形の使用

例えば”BackEndManager”のかわりに”BEManager”のような省略形を使うことがあります。
これは混乱を生じさせるでしょうか?

もしその省略形が、プロジェクトに特有のものであるときは使用を避けるべきです。
そのプロジェクトに新しく参加した人たちにとって暗号のようにしか見えないからです。
さらに言うと、そのコードを書いた人にさえ、ずっと後に見たときに暗号のように見えるはずです。

省略形を使えるかどうかの判断基準は
新しいチームメイトが、その名前が何を意味しているか理解できるか?
この1点です。

さいごに

と、ざっくりとまとめてしまいましたが、
初めてコードを見た人が「何を保持している変数なのか」、「具体的に何をする関数なのか」ということがわかる命名をするのが重要ということで、今後の業務で気を付けていこうと思います。

また、この内容はかなり私の意訳が入っているのと、実際の本にはもっとたくさんの例やコードが書いてあるので、そちらを読むことをお勧めします。
あと挿絵が面白いです(笑)

日本語の本もamazonで見かけました。

3章以降も学ぶ内容が多いので、自分のような初学者はまず読んだほうが良い内容だと思いました。
では今回はこの辺で。

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などができるようです。
使い方がわかると便利だな~としみじみしました。

初心者がなやむ型の概念@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とは友達になれたような気がします。