syno/money-bundle
Composer 安装命令:
composer require syno/money-bundle
包简介
This is a Symfony bundle that integrates moneyphp/money library (Fowler pattern): https://github.com/moneyphp/money.
README 文档
README
This bundle is used to integrate the Money library from mathiasverraes into a Symfony project.
This library is based on Fowler's Money pattern
- This bundle is tested and is stable with Symfony 3.4, 4.3, 4.4, 5.0, 6.0
Quick Start
use Money\Money; use Tbbc\MoneyBundle\Form\Type\MoneyType; // the money library $fiveEur = Money::EUR(500); $tenEur = $fiveEur->add($fiveEur); list($part1, $part2, $part3) = $tenEur->allocate(array(1, 1, 1)); assert($part1->equals(Money::EUR(334))); assert($part2->equals(Money::EUR(333))); assert($part3->equals(Money::EUR(333))); // a service that stores conversion ratios $pairManager = $this->get('tbbc_money.pair_manager'); $usd = $pairManager->convert($tenEur, 'USD'); // a form integration $formBuilder->add('price', MoneyType::class);
Features
- Integrates money library from mathiasverraes
- Twig filters and PHP helpers for helping with money and currencies in templates
- A storage system for currency ratios
- A ratioProvider system for fetching ratio from externals api
- Symfony form integration
- Console commands for different operations
- A configuration parser for specifying website used currencies
- Access to the history of currency ratio fetched
- Money formatter i18n
Table of contents
Installation
Use Composer and install with
$ composer require tbbc/money-bundle
If you use Symfony 3 then add the bundle to AppKernel:
public function registerBundles() { $bundles = array( // ... new Tbbc\MoneyBundle\TbbcMoneyBundle(), ); }
For Symfony 4 and higher add the bundle to config/bundles.php (if it was not automatically added during the installation of the package):
return [ // ... Tbbc\MoneyBundle\TbbcMoneyBundle::class => ['all' => true], ];
For Symfony 3, in your config.yml, add the currencies you want to use and the reference currency. For Symfony 4 and higher create a file like config/packages/tbbc_money.yml and add it there.
tbbc_money: currencies: ["USD", "EUR"] reference_currency: "EUR" decimals: 2
In your config.yml or config/packages/tbbc_money.yml, add the form fields presentations
twig: form_themes: - '@TbbcMoney/Form/fields.html.twig'
You should also register custom Doctrine Money type:
doctrine: dbal: types: money: Tbbc\MoneyBundle\Type\MoneyType
Usage
Money Library integration
use Money\Money; $fiveEur = Money::EUR(500); $tenEur = $fiveEur->add($fiveEur); list($part1, $part2, $part3) = $tenEur->allocate(array(1, 1, 1)); assert($part1->equals(Money::EUR(334))); assert($part2->equals(Money::EUR(333))); assert($part3->equals(Money::EUR(333))); $pair = new CurrencyPair(new Currency('EUR'), new Currency('USD'), 1.2500); $usd = $pair->convert($tenEur); $this->assertEquals(Money::USD(1250), $usd);
Form integration
You have 3 new form types (under Tbbc\MoneyBundle\Form\Type namespace):
- CurrencyType : asks for a currency among currencies defined in config.yml
- MoneyType : asks for an amount and a currency
- SimpleMoneyType : asks for an amount and sets the currency to the reference currency set in config.yml
Example :
use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; // I create my form $form = $this->createFormBuilder() ->add('name', TextType::class) ->add('price', MoneyType::class, [ 'data' => Money::EUR(1000), //EUR 10 ]) ->add('save', SubmitType::class) ->getForm();
Manipulating the form
With MoneyType you can manipulate the form elements with
amount_options for the amount field, and currency_options for the currency field, fx if you want to change the label.
$form = $this->createFormBuilder() ->add('price', MoneyType::class, [ 'data' => Money::EUR(1000), //EUR 10 'amount_options' => array( 'label' => 'Amount', ), 'currency_options' => array( 'label' => 'Currency', ), ]) ->getForm();
With CurrencyType only currency_options can be used, and with SimpleMoneyType only amount_options can be used.
Saving Money with Doctrine
Solution 1 : two fields in the database
Note that there are 2 columns in the DB table : $priceAmount and $priceCurrency and only one getter/setter : getPrice and setPrice.
The get/setPrice methods are dealing with these two columns transparently.
- Advantage : your DB is clean and you can do sql sum, group by, sort,... with the amount and the currency in two different columns in your db
- Disadvantage : it is ugly in the entity.
<?php namespace App\AdministratorBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Money\Currency; use Money\Money; /** * TestMoney * * @ORM\Table("test_money") * @ORM\Entity */ class TestMoney { /** * @var integer * * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @var integer * * @ORM\Column(name="price_amount", type="integer") */ private $priceAmount; /** * @var string * * @ORM\Column(name="price_currency", type="string", length=64) */ private $priceCurrency; /** * Get id * * @return integer */ public function getId() { return $this->id; } /** * get Money * * @return Money */ public function getPrice() { if (!$this->priceCurrency) { return null; } if (!$this->priceAmount) { return new Money(0, new Currency($this->priceCurrency)); } return new Money($this->priceAmount, new Currency($this->priceCurrency)); } /** * Set price * * @param Money $price * @return TestMoney */ public function setPrice(Money $price) { $this->priceAmount = $price->getAmount(); $this->priceCurrency = $price->getCurrency()->getCode(); return $this; } }
Solution 2 : use Doctrine type
There is only one string column in your DB table. The money object is manually serialized by the new Doctrine type.
1.25€ is serialized in your DB by 'EUR 125'. This format is stable. It won't change in future releases..
The new Doctrine type name is "money".
- Advantage : The entity is easy to create and use
- Disadvantage : it is more difficult to directly request the db in SQL.
<?php namespace App\AdministratorBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Money\Money; /** * TestMoney * * @ORM\Table("test_money") * @ORM\Entity */ class TestMoney { /** * @var integer * * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @var Money * * @ORM\Column(name="price", type="money") */ private $price; /** * Get id * * @return integer */ public function getId() { return $this->id; } /** * get Money * * @return Money */ public function getPrice() { return $this->price; } /** * Set price * * @param Money $price * @return TestMoney */ public function setPrice(Money $price) { $this->price = $price; return $this; } }
Conversion manager
Convert an amount into another currency
$pairManager = $this->get("tbbc_money.pair_manager"); $usd = $pairManager->convert($amount, 'USD');
Save a conversion value in a DB
use Money\Money; $pairManager = $this->get("tbbc_money.pair_manager"); $pairManager->saveRatio('USD', 1.25); // save in ratio file in CSV $eur = Money::EUR(100); $usd = $pairManager->convert($amount, 'USD'); $this->assertEquals(Money::USD(125), $usd);
Money formatter
<?php namespace My\Controller\IndexController; use Money\Money; use Money\Currency; class IndexController extends Controller { public function myAction() { $moneyFormatter = $this->get('tbbc_money.formatter.money_formatter'); $price = new Money(123456789, new Currency('EUR')); // best method (added in 2.2+ version) \Locale::setDefault('fr_FR'); $formatedPrice = $moneyFormatter->localizedFormatMoney($price); // 1 234 567,89 € $formatedPrice = $moneyFormatter->localizedFormatMoney($price, 'en'); // €1,234,567.89 // old method (before v2.2) $formattedPrice = $moneyFormatter->formatMoney($price); // 1 234 567,89 $formattedCurrency = $moneyFormatter->formatCurrency($price); // € } }
Twig integration
{{ $amount | money_localized_format('fr') }} => 1 234 567,89 €
{{ $amount | money_localized_format('en_US') }} => €1,234,567.89
{{ $amount | money_localized_format }} => depends on your default locale
{{ $amount | money_format }}
{{ $amount | money_as_float }}
{{ $amount | money_get_currency | currency_symbol }}
{{ $amount | money_get_currency | currency_name }}
{{ $amount | money_convert("USD") | money_format }}
{{ $amount | money_format_currency }}
PHP templating integration
<span class="price"><?php echo $view['tbbc_money']->format($price) ?></span> <span class="money"><?php echo $view['tbbc_money_currency']->formatCurrencyAsSymbol($price->getCurrency()) ?></span>
Fetching ratio values from remote provider
# save a ratio in the storage ./bin/console tbbc:money:ratio-save USD 1.25 # display ratio list ./bin/console tbbc:money:ratio-list # fetch all the ratio for all defined currencies from an external API ./bin/console tbbc:money:ratio-fetch
Change the ratio provider
The ratio provider by default is base on the service 'tbbc_money.ratio_provider.ecb'
This bundles contains three ratio providers :
- tbbc_money.ratio_provider.ecb based on the data provided here https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml
- tbbc_money.ratio_provider.yahoo_finance based on the Yahoo finance APIs https://developer.yahoo.com/ (yahoo does not provide this api anymore)
- tbbc_money.ratio_provider.google based on the https://www.google.com/finance/converter service (google does not provide this api anymore)
You can change the service to use in the config.yml file :
tbbc_money:
[...]
ratio_provider: tbbc_money.ratio_provider.google
Additional rate providers from Exchanger
This project integrates https://github.com/florianv/exchanger library to work with currency exchange rates from various services.
Installation:
composer require "florianv/exchanger" "php-http/message" "php-http/guzzle6-adapter"
Configuration:
First, you need to add services you would like to use into your services.yml file, e.g:
ratio_provider.service.ecb:
class: Exchanger\Service\EuropeanCentralBank
Second, you need to update ratio provider used by MoneyBundle on your config.yml file:
tbbc_money:
[...]
ratio_provider: ratio_provider.service.ecb
Recommended:
Some providers focus on a limited set of currencies, but give better data. You can use several rate providers seamlessly on your project by bundling them into the chain. If some provider does not support certain currency, next provider in the chain would be attempted.
Example of chained providers:
ratio_provider.service.ecb:
class: Exchanger\Service\EuropeanCentralBank
ratio_provider.service.rcb:
class: Exchanger\Service\RussianCentralBank
ratio_provider.service.cryptonator:
class: Exchanger\Service\Cryptonator
ratio_provider.service.array:
class: Exchanger\Service\PhpArray
arguments:
-
'EUR/USD': 1.157
'EUR/AUD': 1.628
ratio_provider.service.default:
class: Exchanger\Service\Chain
arguments:
-
- "@ratio_provider.service.ecb"
- "@ratio_provider.service.rcb"
- "@ratio_provider.service.cryptonator"
- "@ratio_provider.service.array"
As you can see here 4 providers would be attempted one after another until conversion rate is found. Check this page for a fill list of supported services and their configurations: https://github.com/florianv/exchanger/blob/master/doc/readme.md#supported-services
And then you need to assign rate provider on your config.yml file:
tbbc_money:
[...]
ratio_provider: ratio_provider.service.default
Create your own ratio provider
A ratio provider is a service that implements the Tbbc\MoneyBundle\Pair\RatioProviderInterface.
I recommend that you read the PHP doc of the interface to understand how to implement a new ratio provider.
The new ratio provider has to be registered as a service.
To use the new ratio provider, you should set the service to use in the config.yml by giving the service name.
tbbc_money:
[...]
ratio_provider: tbbc_money.ratio_provider.google
automatic currency ratio fetch
Add to your crontab :
1 0 * * * /my_app_dir/bin/console tbbc:money:ratio-fetch > /dev/null
MoneyManager : create a money object from a float
Create a money object from a float can be a bit tricky because of rounding issues.
<?php $moneyManager = $this->get("tbbc_money.money_manager"); $money = $moneyManager->createMoneyFromFloat('2.5', 'USD'); $this->assertEquals("USD", $money->getCurrency()->getCode()); $this->assertEquals(250, $money->getAmount());
history of currency ratio with the pairHistoryManager
Doctrine is required to use this feature.
In order to get the ratio history, you have to enable it in the configuration and to use Doctrine.
tbbc_money: currencies: ["USD", "EUR"] reference_currency: "EUR" enable_pair_history: true
Then you can use the service :
$pairHistoryManager = $this->get("tbbc_money.pair_history_manager"); $dt = new \DateTime("2012-07-08 11:14:15.638276"); // returns ratio for at a given date $ratio = $pairHistoryManager->getRatioAtDate('USD',$dt); // returns the list of USD ratio (relative to the reference value) $ratioList = $pairHistoryManager->getRatioHistory('USD',$startDate, $endDate);
RatioStorage
Two storages for storing ratios are available : CSV File, or Doctrine By default, TbbcMoneyBundle is configured with CSV File.
If you want to switch to a Doctrine storage, edit your config.yml
tbbc_money: storage: doctrine
Update your database schema :
./bin/console doctrine:schema:update --force
With the Doctrine storage, currency ratio will use the default entity manager and will store data inside the tbbc_money_doctrine_storage_ratios
Custom NumberFormatter in MoneyFormatter
The MoneyFormatter::localizedFormatMoney ( service 'tbbc_money.formatter.money_formatter' ) use the php NumberFormatter class ( http://www.php.net/manual/en/numberformatter.formatcurrency.php ) to format money.
You can :
- give your own \NumberFormatter instance as a parameter of MoneyFormatter::localizedFormatMoney
- subclass the MoneyFormatter and rewrite the getDefaultNumberFormater method to set a application wide NumberFormatter
Using the TbbcMoneyBundle without Doctrine
You have to disable the pair history service in order to use the TbbcMoneyBundle without Doctrine.
tbbc_money:
enable_pair_history: true
Note : you can imagine to code your own PairHistoryManager for MongoDB or Propel, it is very easy to do. Don't hesitate to submit a PR with your code and your tests.
Optimizations
In your config.yml, you can :
- select the templating engine to use. By default, only Twig is loaded.
- define the decimals count after a unit (ex : 12.25€ : 2 decimals ; 11.5678€ : 4 decimals)
tbbc_money: currencies: ["USD", "EUR"] reference_currency: "EUR" decimals: 2 enable_pair_history: true ratio_provider: tbbc_money.ratio_provider.yahoo_finance
Contributing
- Take a look at the list of issues.
- Fork
- Write a test (for either new feature or bug)
- Make a PR
Requirements
- PHP 5.3.9+
- Symfony 2.8+
Authors
Philippe Le Van - kitpages.fr - twitter : @plv
Thomas Tourlourat - Wozbe - twitter: @armetiz
Status
Stable
what is functional:
- integration of the money library
- configuration parser
- pairManager
- Travis CI integration
- form integration
- Twig presentation for forms
- Twig filters
- commands for ratio creation and ratio display
- automatic ratio fetch (with 2 ratio providers)
- history of currency ratio
syno/money-bundle 适用场景与选型建议
syno/money-bundle 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 22.89k 次下载、GitHub Stars 达 0, 最近一次更新时间为 2021 年 02 月 03 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「money」 「currency」 「conversion」 「fowler」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 syno/money-bundle 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 syno/money-bundle 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 syno/money-bundle 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Beyonic PHP Library
A library that provides a simple infrastructure to create your own converters and to perform any conversion you want
Fixer.io data provider for Peso
TCMB Currenct Converter
Color conversion library
Convert and serve jpeg/png to webp with PHP (if at all possible)
统计信息
- 总下载量: 22.89k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 23
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2021-02-03