こんにちは。
最近、暖かくもなってきたし登山に復帰しようかと思ってる武田です。
前回に引き続き、Symfony2管理画面構築バンドル[Sonata Admin]を触っていこうと思います。
前の物を拡張する形で以下の機能を追加していきます。
- 項目「タグ」の追加
- 画像のアップロード
- バリデーション
- 日本語化
項目「タグ」の追加
これだけだとブログとしてはちょっと寂しいですね。
タグ(任意項目)の2項目を追加したいと思います。
Entityの更新
DBの設定を調整するためEntityを調整します。
以下を追記
vi src/AppBundle/Entity/BlogPost.php
...[略]
/**
* @var string
*
* @ORM\Column(name="tag", type="string", length=255, nullable=true)
*/
// 今回は未入力を許可してるので [nullable=true]を設定
private $tag;
/**
* Set tag
*
* @param string $tag
* @return BlogPost
*/
public function setTag($tag)
{
$this->tag = $tag;
return $this;
}
/**
* Get tag
*
* @return string
*/
public function getTag()
{
return $this->tag;
}
...[略]
EntityをDBに反映
chema:updateを実施
[adjust@localhost adminSample]$ php app/console doctrine:schema:update --force
Updating database schema...
Database schema updated successfully! "1" queries were executed
blog_postテーブルを確認して、追加した項目があることを確認
mysql> desc blog_post;
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| title | varchar(255) | NO | | NULL | |
| body | longtext | NO | | NULL | |
| draft | tinyint(1) | NO | | NULL | |
| category_id | int(11) | YES | MUL | NULL | |
| tag | varchar(255) | YES | | NULL | |
+-------------+--------------+------+-----+---------+----------------+
ADMIN CLASSへ項目の追加
タグの項目を追加します。
以下を追記
vi src/AppBundle/Admin/BlogPostAdmin.php
...[略]
$formMapper
->add('title', TextType::class)
->add('body', TextareaType::class)
->add('category', EntityType::class, array(
'class' => 'AppBundle\Entity\Category',
'property' => 'name',
))
->add('tag', TextType::class) // [追加]
;
...[略]
登録画面を見てみると項目が追加されてますね。
問題なければきちんと登録できるかと思います
画像のアップロード
やっぱり画像くらい登録したいですよね。
アップロード機能を作りましょう
ADMIN CLASSへ項目の追加
とりあえず動き云々の前に画面上に項目だけでも増やしましょう。
vi src/AppBundle/Admin/BlogPostAdmin.php
...[略]
use Symfony\Component\Form\Extension\Core\Type\FileType; // [追加]
{
// 登録更新で利用する入力フォームの定義
protected function configureFormFields(FormMapper $formMapper)
{
$fileOptions = ['mapped' => false];
// [追加 START]
// create / updateかの判定に使用するため今の記事情報を取得
$object = $this->getSubject();
if ($this->id($object)) {
// 登録済みの場合は画像もあるのでそれを組み立てて表示
$fileOptions['help'] = sprintf("<img src='%s%s.jpg'/>", $this->getRequest()->getUriForPath('/'), $object->getId());
}
// [追加 END]
$formMapper
->add('title', TextType::class)
->add('body', TextareaType::class)
->add('category', EntityType::class, array(
'class' => 'AppBundle\Entity\Category',
'property' => 'name',
))
->add('tag', TextType::class)
->add('file', FileType::class, $fileOptions) // [追加]
;
}
...[略]
ファイルアップロードのボタンが追加されたかと思います。
あわせて編集画面では登録済みの画像を表示するようにhelpオプションに画像表示のソースを引き渡してます。
画像の保存処理
アップロードした画像をきっちり保存しないと意味ないですね。
想定の位置へ保存するような処理を加えます。
sonata admin では 登録/更新/削除時などに読み出せるフックがすでに備わってます。
それを使いましょう。(公式)
今回は登録後(postPersist)/更新後(postUpdate)のフックを利用してその際に画像を所定の位置へ保存させます。
vi src/AppBundle/Admin/BlogPostAdmin.php
...[略]
public function postPersist($object)
{
$this->saveFile($object);
}
public function postUpdate($object)
{
$this->saveFile($object);
}
// 画像を保存させる
private function saveFile($object)
{
// symfony2ではよく使うcontainerを読み出す
$container = $this->getConfigurationPool()->getContainer();
// 画像保存のディレクトリを作成(今回はweb直下)
$path = $container->getParameter('kernel.root_dir').'/../web/';
// 画像名はDBの自動採番IDにしましょう
$imageName = sprintf("%s.jpg", $object->getId());
// getForm()にファイルとしてデータがあるのでそれを一旦変数へ
$file = $this->getForm()['file']->getData();
// 上で定義したディレクトリ/画像名で保存
$file->move($path, $imageName);
}
...[略]
画像が登録できるか試しましょう。
ちゃんと画像が保存できましたね。
画像の削除
これだと登録/更新時は保存されますが削除の際に画像だけが残ってしまいます。
そこもフックを使って消すようにしましょう。
vi src/AppBundle/Admin/BlogPostAdmin.php
...[略]
// 今回はpreを利用 postだとIDが消えちゃうので。。。
public function preRemove($object)
{
$this->deleteFile($object);
}
private function deleteFile($object)
{
$container = $this->getConfigurationPool()->getContainer();
$filePath = sprintf("%s%s.jpg", $container->getParameter('kernel.root_dir').'/../web/', $object->getId());
if(file_exists($filePath))
{
return unlink( $filePath );
}
}
...[略]
バリデーション
けっこう形になってきましたね。
ただ、現状だと値の確認をしてないので不安があります。
バリデーションを入れてみましょう。
カテゴリ管理画面
まずは項目の少ないカテゴリ管理画面から実施
nameに対して最大文字数を40とします。
Symfonyのバリデーション機能を読み出してそれを適応するように設定します。
ちなみに自前でバリデーションを定義すればそれを読み出す事も可能です。
vi src/AppBundle/Admin/CategoryAdmin.php
...[略]
use Symfony\Component\Validator\Constraints\Length; // [追加]
class CategoryAdmin extends AbstractAdmin
{
// 登録,更新で利用する入力フォームの定義
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper->add(
'name',
TextType::class,
// [追加 START]
[
'constraints' => [ new Length(['max' => 40]) ]
]
// [追加 END]
);
}
...[略]
動かしてみてみると、定義どおりのバリデーションが動作してるようですね
ブログ記事管理画面
引き続き、ブログ記事もやってきます。
以下のようなバリデーションを加えます。
Title
文字数40文字
Body
文字数1000文字
Category
なし
Tag
英数字のみ
File
jpgファイルのみを許可
vi src/AppBundle/Admin/BlogPostAdmin.php
...[略]
use Symfony\Component\Validator\Constraints\NotBlank; // [追加]
use Symfony\Component\Validator\Constraints\Length; // [追加]
use Symfony\Component\Validator\Constraints\File; // [追加]
use Symfony\Component\Validator\Constraints\Regex; // [追加]
class BlogPostAdmin extends AbstractAdmin
{
// 登録更新で利用する入力フォームの定義
protected function configureFormFields(FormMapper $formMapper)
{
// [追加 START]
$fileOptions = [
'constraints' => [ new File(['mimeTypes' => ['image/jpg'] ]) ],
'mapped' => false
];
$object = $this->getSubject();
if ($this->id($object)) {
$fileOptions['help'] = sprintf("<img src='%s%s.jpg'/>", $this->getRequest()->getUriForPath('/'), $object->getId());
}
$formMapper
->add(
'title',
TextType::class,
[ 'constraints' => [ new Length(['max' => 40]) ] ]
)
->add(
'body',
TextareaType::class,
[
'constraints' => [ new Length(['max' => 1000]) ]
]
)
->add('category',EntityType::class, [
'class' => 'AppBundle\Entity\Category',
'property' => 'name'
])
->add(
'tag',
TextType::class,
[
'constraints' => [
new Regex(['pattern' => '/^[a-zA-Z0-9]+$/'])
]
]
)
->add('file', FileType::class, $fileOptions)
;
// [追加 END]
}
...[略]
こちらもきっちりバリデーションが動いてますね!
日本語化
概ね機能としては完成しました!
ただ見た目が英語のままなのでちょっととっつきにくいですね。
日本語化しましょう。
サイトの言語を日本語に設定
vi app/config/config.yml
...[略]
parameters:
locale: ja
...[略]
これでsonata adminは最初から日本語に対応してくれます。
画面をみてもらうとエラーメッセージや共通パーツなどは日本語化されていることがわかります。
当たり前ですが自分で定義して項目やメニュー名などは翻訳する言葉がないのでそのままです。
こちらも対応しましょう。
翻訳ファイルを作成
vi app/Resources/translations/translations.ja.yml
Category : カテゴリ
Category List : ブログ記事一覧
Category Create : ブログ記事作成
Name : 名前
Blog post : ブログ記事
Blog Post List : ブログ記事一覧
Blog Post Create : ブログ記事作成
Title : タイトル
Body : 本文
Tag : タグ
File : 画像ファイル
翻訳ファイルを適用するように調整
vi app/config/services.yml
services:
admin.category:
class: AppBundle\Admin\CategoryAdmin
arguments: [~, AppBundle\Entity\Category, ~]
tags:
- { name: sonata.admin, manager_type: orm, label: Category }
# [追加]
calls:
- [ setTranslationDomain, [translations]]
admin.blog_post:
class: AppBundle\Admin\BlogPostAdmin
arguments: [~, AppBundle\Entity\BlogPost, ~]
tags:
- { name: sonata.admin, manager_type: orm, label: Blog post }
# [追加]
calls:
- [ setTranslationDomain, [translations]]
これで定義した項目に日本語が適応されました。
まとめ
sonata admin bundle いかがでしょうか?
型にはハマったCRUDを大量生産といったシチュエーションならかなり便利です。
symforny2で管理画面が必要な開発の場合、検討してもいいんじゃないかと思いますよ!
コメントを残す