スポンサーサイト
一定期間更新がないため広告を表示しています
- 2014.12.23 Tuesday
- -
- -
- -
- -
- -
- by スポンサードリンク
一人で開発
開発機のOSはWindows7 professional(メモリが64GBというモンスターマシン^p^)
gitはmsysgit利用
主に使うフレームワークはsymfony1又はSymfony2 たまにwordpressとかcakeとか
visual c++等の自力でphp及びその拡張モジュールをビルドできる環境は無い
新規開発案件(PHP5.4/5.5)と保守案件(PHP5.3)を使い分けたい
開発に使うIDEはphpstorm
原則としてTDDするのでphpunitを使いたい(symfony1の場合はlime使う)
*プロジェクトルートをsynced_folderにする
*コードはホスト側のphpstormで書き、テストはゲスト側のコンソールで手動実行(作業中は、phpstormとmsysgitのGit Bashとvagrant sshの三窓を行ったり来たり)
*Symfony2のassets:installは常に--symlinkなしで使う
(*symfony1のplugin:publish-assetsはホスト側で実行する)
*composer installはゲスト側で実行(synced_folderでホスト側にも共有されてくるので、IDEはそれを参照できる)
*本番とのphpバージョン差異に悩まされることがなくなった
*windows版php特有の問題(strftimeのフォーマット文字列とか)に悩まされることがなくなった
*よく言われる速度問題はホスト側の超性能でカバーできてた(と、思う。特に速度で不満感じたことなし)
*jsやcssを更新したときにassets:installを手動実行するのが面倒 *シンボリックリンクが必須のバンドルが使えない
*プロジェクトルートをtype: "rsync"でsynced_folderとして指定
*ホスト側のwindowsはcwrsyncを利用してrsyncコマンドを使えるようにしておく
*.git .idea app/cache/* app/log/* bin vendor web/bundlesを:rsync__excludeに指定(assetic使う場合はweb/assetic web/js web/cssも除外した方が良いかも)
*利用するboxにもよるが、hashicorp/precise64の場合は、初回vagrant up時にいったんVagrantfile上のsynced_folder設定とprovision設定をコメントアウトし、ゲスト側rsyncのバージョンをホスト側rsyncのバージョンと合わせてから、synced_folder設定とprovision設定を再度有効化してvagrant reload --provisionを実行する
*作業中に必要なウィンドウがphpstormとmsysgitのGit Bashとvagrant sshの三窓から、rsync-auto専用のGit Bashも加えた四窓に増えた
*composer installはゲスト側のプロジェクトルートで実行後、--no-scriptつきでゲスト側の/vagrantでも実行する(IDEで補完を利用するために必要)
*シンボリックリンクが必須の一部のバンドルを使っていてもwindowsホストで問題なく開発できるようになった
*rsync-autoにしておくと、excludeにしているにも関わらず時々ゲスト側だけにあってホスト側にないファイル・ディレクトリ(vendorなど)が削除されてしまうことがある
Vagrant1.5.2以下でrsyncしたディレクトリのパーミッションがおかしい場合→https://github.com/mitchellh/vagrant/issues/3256
Vagrant1.5.2以下でrsyncがNo such file or directoryで失敗する場合→https://github.com/mitchellh/vagrant/issues/3230(4/21 12:40訂正1.5.3でも発生してました)
- "symfony/symfony": "2.3.*" + "symfony/symfony": "~2.3"
$ composer update symfony/symfonyあとは、自分の書いたバンドルについて2.4でエラーが出るようなら修正しましょう。
Argument 1 passed to Knp¥Component¥Pager¥Paginator::__construct() must be an instance of Symfony¥Component¥EventDispatcher¥EventDispatcher, instance of Symfony¥Component¥HttpKernel¥Debug¥TraceableEventDispatcher given,
- "knplabs/knp-paginator-bundle": "2.3.*", + "knplabs/knp-components": "~1.2.4", + "knplabs/knp-paginator-bundle": "dev-master",
- "minimum-stability": "stable", + "minimum-stability": "dev", + "prefer-stable": true,
@77web 試していないのですが、symfony/symfonyのcomposer.jsonを見る限りでは要求するsymfony/icuは1.0以上で、symfony/icuを1.0にすればlib-icuの4.4は要求されないようです。
― Hidenori Goto (@hidenorigoto) September 10, 2013
結果、無事にicu4.2の環境でSymfony2.3のシステムを動かすことに成功しました!(略) "symfony/icu" : "1.0.*", "symfony/symfony": "2.3.*", (略)
// src/Acme/DemoBundle/Controller/BaseController.php public function barAction(Request $request) { //何かの処理 return $this->render('AcmeDemoBundle:Foo:bar.html.twig', array('name' => $name) ); } // src/Acme/DemoBundle/Controller/FooController.php public function barAction(Request $request) { //parent::barAction($request)だとResponseが返って来ちゃうから… //何かの処理(BaseController::barAction()と同じ内容コピペ) //追加の処理(カスタマイズ) return $this->render('AcmeDemoBundle:Foo:bar.html.twig', array('name' => $name, 'tel' => $tel) ); }
// src/Acme/DemoBundle/Controller/BaseController.php /** * @Template() */ public function barAction(Request $request) { //何かの処理 return array('name' => $name); } // src/Acme/DemoBundle/Controller/FooController.php /** * @Template() */ public function barAction(Request $request) { //返ってくるのは配列だからparent::barAction()使ってOK $vars = parent::barAction($request); //追加の処理(カスタマイズ) $vars['tel'] = $tel; return $vars; }
//src/Acme/DemoBundle/Entity/Customer.php class Customer { /** * @var integer * * @ORM¥Column(name="id", type="integer") * @ORM¥Id * @ORM¥GeneratedValue(strategy="AUTO") */ private $id; /** * @var string * * @ORM¥Column(name="name", type="string", length=255) */ private $name; /** * @var integer * * @ORM¥Column(name="seibetsu", type="integer") */ private $seibetsu; }
// src/Acme/DemoBundle/Admin/CustomerAdmin.php use Sonata¥AdminBundle¥Admin¥Admin; use Sonata¥AdminBundle¥Datagrid¥DatagridMapper; use Sonata¥AdminBundle¥Datagrid¥ListMapper; use Sonata¥AdminBundle¥Form¥FormMapper; class CustomerAdmin extends Admin { protected $baseRouteName = 'admin_customer'; /** * @param FormMapper $formMapper */ protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('name') ->add('seibetsu') ; } /** * @param DatagridMapper $datagridMapper */ protected function configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper ->add('name') ->add('seibetsu') ; } /** * @param ListMapper $listMapper */ protected function configureListFields(ListMapper $listMapper) { $listMapper ->addIdentifier('name') ->add('seibetsu') ; } }
/** * @param FormMapper $formMapper */ protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('name') ->add('seibetsu', 'gender') ; } /** * @param DatagridMapper $datagridMapper */ protected function configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper ->add('name') ->add('seibetsu', 'gender') ; }
専用TwigExtensionはサービスとして登録しておきます。// src/Acme/DemoBundle/Twig/Extension/GenderNameExtension.php class GenderNameExtension extends ¥Twig_Extension { public function getName() { return 'acme_demo_gender_name'; } public function getFunctions() { return array( 'gender_name' => new ¥Twig_Function_Method($this, 'getGenderName'), ); } /** * @param string $value * @return string */ public function getGenderName($value) { $list = array( 1 => '男', 2 => '女', 3 => 'その他', ); return isset($list[$value]) ? $list[$value] : '-'; } }
{# src/Acme/DemoBundle/Resources/views/sonata_customer_list_gender.html.twig #} {% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %} {% block field %} {% spaceless %} {% if value is empty %} {% else %} {{ gender_name(value) }} {% endif %} {% endspaceless %} {% endblock %}
/** * @param ListMapper $listMapper */ protected function configureListFields(ListMapper $listMapper) { $listMapper ->addIdentifier('name') ->add('seibetsu', 'string', array( 'template' => 'AcmeDemoBundle::sonata_customer_list_gender.html.twig', )) ; }
$this->get('session')->setFlash('notice', '更新しました。');
{% if app.session.hasFlash('data') %}
{# dataというflashがある場合の処理 #}
{% else %}
{# dataというflashがない場合の処理 #}
{% endif %}
{# hogeというflashを表示 #}
{{ app.session.flash('hoge') }}
$this->get('session')->getFlashBag()->set('notice', '更新しました。');
{% if app.session.flashBag.has('data') %}
{# dataというflashがある場合の処理 #}
{% else %}
{# dataというflashがない場合の処理 #}
{% endif %}
{# hogeというflashを表示 #}$this->get('session')->getFlashBag()->set('notice', '更新しました。');のように文字列を一つだけセットしてあっても、app.session.flashBag.get('notice')の中身は必ず配列になります。
{% for flashMessage in app.session.flashBag.get('hoge') %}
{{ flashMessage }}
{% endfor %}
public function getMonthlyAmount($year, $month)支店一覧テンプレートの各行で下記のように呼び出せばいい。
{
return Doctrine::getTable('売上')->getMonthlyAmount($this->getId(), $year, $month);
}
<?php foreach ($pager->getResults() as $branch): ?>
<tr>
<td><?php echo $branch->getName(); ?></td>
//略…ここに支店の所在地とか電話番号とか
<td><?php echo $branch->getMonthlyAmount(); ?></td>
//略…ここに編集ボタンとか詳細ボタンとか削除ボタン辺り。
</tr>
<?php endforeach; ?>
namespace My¥CoolBundle¥Twig¥Extension;#My/CoolBundle/Resources/config/services.yml
class BadKnowHowExtension extends ¥Twig_Extension
{
private $em;
public function __construct($doctrine)
{
$this->em = $doctrine->getEntityManager();
}
public function getName()
{
//他と被らなければ何を入れてもOK
return 'bad_knowhow';
}
public function getFunctions()
{
return array(
'monthly_uriage' => new ¥Twig_Function_Method($this, 'calculateMonthlyAmount'),
);
}
public function calculateMonthlyAmount($branch, $year, $month)
{
return $em->getRepository('MyCoolBundle:売上')->calculateMonthlyAmountForBranch($branch->getId(), $year, $month);
}
}
services:
my.cool_bundle.twig.bad_knowhow_extension:
class: My¥CoolBundle¥Twig¥Extension¥BadKnowHowExtension
arguments:
- @doctrine
tags:
- { name: twig.extension }
{% for branch in list %}
<tr>
<td>{{ branch.name }}</td>
//略
<td>{{ monthly_uriage(branch, year, month) }}</td>
//略
</tr>
{% endfor %}
// Get the security firewall name, login
$providerKey = $this->container->getParameter('fos_user.firewall_name');
$token = new UsernamePasswordToken($user, $password, $providerKey, $user->getRoles());
$this->get("security.context")->setToken($token);
// Fire the login event
$event = new InteractiveLoginEvent($this->getRequest(), $token);
$this->get("event_dispatcher")->dispatch("security.interactive_login", $event);