This bundle is used to integrate the Money library from mathiasverraes into a Symfony project.
This library is based on Fowler's Money pattern
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);
Use Composer and install with
$ composer require tbbc/money-bundle
Then add the bundle in AppKernel :
public function registerBundles()
{
$bundles = array(
// ...
new Tbbc\MoneyBundle\TbbcMoneyBundle(),
);
}
In your config.yml, add the currencies you want to use and the reference currency.
tbbc_money:
currencies: ["USD", "EUR"]
reference_currency: "EUR"
decimals: 2
In your config.yml, add the form fields presentations
twig:
form:
resources:
- 'TbbcMoneyBundle:Form:fields.html.twig'
You should also register custom Doctrine Money type:
doctrine:
dbal:
types:
money: Tbbc\MoneyBundle\Type\MoneyType
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);
You have 3 new form types (under Tbbc\MoneyBundle\Form\Type namespace):
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();
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.
<?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;
}
}
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".
<?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;
}
}
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);
<?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);
// €
}
}
{{ $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.currency | currency_symbol }}
{{ $amount.currency.code }}
{{ $amount | money_convert("USD") | money_format }}
{{ $amount | money_format_currency }}
<span class="price"><?php echo $view['tbbc_money']->format($price) ?></span>
<span class="money"><?php echo $view['tbbc_money_currency']->formatCurrencyAsSymbol($price->getCurrency()) ?></span>
# 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
This bundle uses florianv/swap library for requesting currency exchange rates from various providers.
The ratio provider is using yahoo finance provider. You can change the provider to use in the config.yml file:
tbbc_money:
[...]
ratio_provider: "google_provider"
Here google_provider is the name of the service which you need to define with Dependency Injection before using, e.g. like so:
services:
curl:
class: Ivory\HttpAdapter\CurlHttpAdapter
google_provider:
class: Swap\Provider\GoogleFinanceProvider
arguments:
- "@curl"
You can use any providers as described at florianv/swap project, or even combination of those. The following example will try to use Yahoo provider and if it fails will switch to Google Provider instead:
services:
curl:
class: Ivory\HttpAdapter\CurlHttpAdapter
yahoo_provider:
class: Swap\Provider\YahooFinanceProvider
arguments:
- "@curl"
google_provider:
class: Swap\Provider\GoogleFinanceProvider
arguments:
- "@curl"
chain_provider:
class: Swap\Provider\ChainProvider
arguments:
- ["@yahoo_provider", "@google_provider"]
tbbc_money:
[...]
ratio_provider: "chain_provider"
A ratio provider is a service that implements the Swap\ProviderInterface.
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: your_provider_service
Add to your crontab :
1 0 * * * /my_app_dir/bin/console tbbc:money:ratio-fetch > /dev/null
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());
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);
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
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 :
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.
In your config.yml, you can :
tbbc_money:
currencies: ["USD", "EUR"]
reference_currency: "EUR"
decimals: 2
enable_pair_history: true
ratio_provider: tbbc_money.ratio_provider.yahoo_finance
./bin/console), for version 2.8 you should use ./app/console instead.MoneyType::class) is only supported since PHP 5.5, if you have an older version, you should use the full
class name instead (e.g. Tbbc\MoneyBundle\Type\MoneyType)Philippe Le Van - kitpages.fr - twitter : @plv Thomas Tourlourat - Wozbe - twitter: @armetiz
Stable
what is functional:
How can I help you explore Laravel packages today?