diff --git a/Core/CurrencyExchangeGetterInterface.php b/Core/CurrencyExchangeGetterInterface.php new file mode 100755 index 0000000..1d8f435 --- /dev/null +++ b/Core/CurrencyExchangeGetterInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ContentBundle\Core; + +/** + * This interface defines structure for currency rates download driver. + */ +interface CurrencyExchangeGetterInterface +{ + /** + * Returns array of currency rates. For example: ['USD' => 1, 'EUR' => '1.678']. + * + * @return array + */ + public function getRates(); + + /** + * Returns default currency name. + * + * @return string + */ + public function getDefaultCurrencyName(); +} diff --git a/Exception/Currency/RatesNotLoadedException.php b/Exception/Currency/RatesNotLoadedException.php new file mode 100644 index 0000000..e2b6323 --- /dev/null +++ b/Exception/Currency/RatesNotLoadedException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ContentBundle\Exception\Currency; + +/** + * This exception is thrown when we try to retrieve currency rates while it's not loaded. + */ +class RatesNotLoadedException extends \RuntimeException +{ +} diff --git a/Exception/Currency/UndefinedCurrencyException.php b/Exception/Currency/UndefinedCurrencyException.php new file mode 100644 index 0000000..b444c33 --- /dev/null +++ b/Exception/Currency/UndefinedCurrencyException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ContentBundle\Exception\Currency; + +/** + * This exceptions is thrown when we request for currency that does not exists. + */ +class UndefinedCurrencyException extends \UnexpectedValueException +{ +} diff --git a/Service/Currency/CurrencyExchangeService.php b/Service/Currency/CurrencyExchangeService.php new file mode 100644 index 0000000..d00f699 --- /dev/null +++ b/Service/Currency/CurrencyExchangeService.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ContentBundle\Service\Currency; + +use ONGR\ContentBundle\Exception\Currency\UndefinedCurrencyException; + +/** + * This class handles currency rates download and exchange. + */ +class CurrencyExchangeService +{ + /** + * @var CurrencyRatesService + */ + protected $rates = null; + + /** + * @var string + */ + protected $defaultCurrency; + + /** + * @param CurrencyRatesService $rates + * @param string $defaultCurrency + */ + public function __construct(CurrencyRatesService $rates, $defaultCurrency) + { + $this->rates = $rates; + $this->defaultCurrency = $defaultCurrency; + } + + /** + * @param string $currency + * + * @return float + * @throws UndefinedCurrencyException + */ + public function getCurrencyRate($currency) + { + $rates = $this->rates->getRates(); + + if (isset($rates[$currency])) { + return $rates[$currency]; + } + + throw new UndefinedCurrencyException('Currency ' . $currency . ' not found.'); + } + + /** + * @return array|null + */ + public function getCurrencies() + { + return $this->rates->getRates(); + } + + /** + * Rate calculation. + * + * @param float|int $amount + * @param string $toCurrency + * @param null $fromCurrency + * + * @return float + */ + public function calculateRate($amount, $toCurrency, $fromCurrency = null) + { + if (!isset($fromCurrency)) { + $fromCurrency = $this->defaultCurrency; + } + + if ($this->rates->getBaseCurrency() != $fromCurrency) { + $amount = $amount / $this->getCurrencyRate($fromCurrency); + } + + if ($toCurrency == $this->rates->getBaseCurrency()) { + return $amount; + } + + return $amount * $this->getCurrencyRate($toCurrency); + } +} diff --git a/Service/Currency/CurrencyRatesService.php b/Service/Currency/CurrencyRatesService.php new file mode 100644 index 0000000..07ee3e0 --- /dev/null +++ b/Service/Currency/CurrencyRatesService.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ContentBundle\Service\Currency; + +use ONGR\ContentBundle\Core\CurrencyExchangeGetterInterface; +use ONGR\ContentBundle\Exception\Currency\RatesNotLoadedException; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerInterface; +use Stash\Interfaces\ItemInterface; +use Stash\Interfaces\PoolInterface; + +/** + * This class provides currency rates. + */ +class CurrencyRatesService implements LoggerAwareInterface +{ + /** + * @var CurrencyExchangeGetterInterface + */ + protected $driver; + + /** + * @var PoolInterface + */ + protected $pool; + + /** + * @var bool + */ + protected $poorManLoad = true; + + /** + * @var null|array + */ + protected $rates = null; + + /** + * @var LoggerInterface + */ + protected $logger; + + /** + * @param CurrencyExchangeGetterInterface $driver Driver. + * @param PoolInterface $pool Cache pool. + * @param bool $poorManLoad Set to true if we want to load currencies on request. + */ + public function __construct(CurrencyExchangeGetterInterface $driver, PoolInterface $pool, $poorManLoad = true) + { + $this->driver = $driver; + $this->pool = $pool; + $this->poorManLoad = $poorManLoad; + } + + /** + * @return ItemInterface + */ + protected function getCachePoolItem() + { + return $this->pool->getItem('fox_currency'); + } + + /** + * This method returns exchange rates. + * + * @throws RatesNotLoadedException + * @return array + */ + public function getRates() + { + if (isset($this->rates)) { + return $this->rates; + } + + /** @var ItemInterface $item */ + $item = $this->getCachePoolItem(); + + $this->rates = $item->get(); + if (isset($this->rates)) { + return $this->rates; + } + + if ($this->poorManLoad) { + $this->logger && $this->logger->notice('Auto reloaded currency rates on CurrencyRatesService'); + $this->reloadRates(); + } else { + throw new RatesNotLoadedException('Currency rates are not loaded and could not be loaded on demand'); + } + + return $this->rates; + } + + /** + * Returns actual base currency name. + * + * @return string + */ + public function getBaseCurrency() + { + return $this->driver->getDefaultCurrencyName(); + } + + /** + * Reloads rates using given driver. + */ + public function reloadRates() + { + $this->rates = $this->driver->getRates(); + $this->getCachePoolItem()->set($this->rates); + } + + /** + * {@inheritdoc} + */ + public function setLogger(LoggerInterface $logger) + { + $this->logger = $logger; + } +} diff --git a/Tests/Functional/Service/ContentServiceTest.php b/Tests/Functional/Service/ContentServiceTest.php index 2cf0e26..eb14bf3 100644 --- a/Tests/Functional/Service/ContentServiceTest.php +++ b/Tests/Functional/Service/ContentServiceTest.php @@ -36,8 +36,8 @@ protected function getDataArray() '_id' => 3, 'slug' => 'awsome', ], - ] - ] + ], + ], ]; } diff --git a/Tests/Unit/Twig/PriceExtensionTest.php b/Tests/Unit/Twig/PriceExtensionTest.php new file mode 100755 index 0000000..07721dc --- /dev/null +++ b/Tests/Unit/Twig/PriceExtensionTest.php @@ -0,0 +1,636 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fox\UtilsBundle\Tests\Functional\Twig; + +use ONGR\ContentBundle\Service\Currency\CurrencyExchangeService; +use ONGR\ContentBundle\Service\Currency\CurrencyRatesService; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use ONGR\ContentBundle\Twig\PriceExtension; + +class PriceExtensionTest extends WebTestCase +{ + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|\Psr\Log\LoggerInterface + */ + protected function getLogger() + { + return $this->getMock('Psr\Log\LoggerInterface'); + } + + /** + * @param array $rates + * @param string $base + * + * @return \PHPUnit_Framework_MockObject_MockObject|CurrencyRatesService + */ + protected function getRatesService($rates, $base) + { + $mock = $this->getMockBuilder('Fox\UtilsBundle\Service\Currency\CurrencyRatesService') + ->disableOriginalConstructor()->getMock(); + + $mock->expects($this->any())->method('getRates')->will($this->returnValue($rates)); + $mock->expects($this->any())->method('getDefaultCurrency')->will($this->returnValue($base)); + + return $mock; + } + + /** + * Test data getter. + * + * @return array[] + */ + public function testGetFormattedPriceData() + { + $out = []; + + // Case 0. + $out[] = [ + '1.990 €', + 1990.0, + '€', + ',', + '.', + 0, + null, + null, + null, + ]; + // Case 1. + $out[] = [ + '199 €', + 199.0, + '€', + ',', + '.', + 0, + null, + null, + null, + ]; + // Case 2. + $out[] = [ + '19,90 €', + 19.9, + '€', + ',', + '.', + 0, + null, + null, + null, + ]; + // Case 3. + $out[] = [ + '1,99 €', + 1.99, + '€', + ',', + '.', + 0, + null, + null, + null, + ]; + // Case 4. + $out[] = [ + '1.990,00 €', + 1990.0, + '€', + ',', + '.', + 2, + null, + null, + null, + ]; + // Case 5: base currency different than the one that we are converting from EUR to USD. + $out[] = [ + '$ 1.334,50', + 1000.0, + '', + ',', + '.', + 2, + 'USD', + 'EUR', + null, + ]; + // Case 6: custom format to price from EUR to USD. + $out[] = [ + '$ 1.334,50 $', + 1000.0, + '', + ',', + '.', + 2, + 'USD', + 'EUR', + '$ %s $', + ]; + // Case 7: converting to USD. + $out[] = [ + '$ 1.334,50', + 1000, + '', + ',', + '.', + 2, + 'USD', + null, + null, + ]; + + return $out; + } + + /** + * Test get formatted price. + * + * @param string $expected + * @param float $price + * @param string $currencySign + * @param string $decPointSeparator + * @param string $thousandsSeparator + * @param int $decimalPlaces + * @param string $toCurrency + * @param string $fromCurrency + * @param string $customFormat + * + * @dataProvider testGetFormattedPriceData() + */ + public function testGetFormattedPrice( + $expected, + $price, + $currencySign, + $decPointSeparator, + $thousandsSeparator, + $decimalPlaces, + $toCurrency, + $fromCurrency, + $customFormat + ) { + $rates = [ + 'EUR' => '1', + 'USD' => '1.3345', + 'LTL' => '3.4546', + ]; + + $formatsMap = [ + 'EUR' => '%s €', + 'USD' => '$ %s', + ]; + + $exchangeService = new CurrencyExchangeService($this->getRatesService($rates, 'EUR'), 'EUR'); + $extension = new PriceExtension($currencySign, $decPointSeparator, $thousandsSeparator, null, $formatsMap); + + // EUR set by default. + $extension->setCurrency('EUR'); + $extension->setCurrencyExchangeService($exchangeService); + + $this->assertEquals( + $expected, + $extension->getFormattedPrice($price, $decimalPlaces, $toCurrency, $fromCurrency, $customFormat) + ); + } + + /** + * Tests if extension contains functions. + */ + public function testGetFunctions() + { + $extension = new PriceExtension('', '', ''); + $this->assertNotEmpty($extension->getFunctions(), 'Extension should contain functions.'); + } + + /** + * Expected filters getter. + * + * @return array + */ + public function getExpectedFilters() + { + return [ + ['fox_price'], + ]; + } + + /** + * Tests get filters. + * + * @param string $filter + * + * @dataProvider getExpectedFilters() + */ + public function testGetFilters($filter) + { + $extension = new PriceExtension('', '', ''); + + $filters = $extension->getFilters(); + + $exists = false; + foreach ($filters as $filterObject) { + if ($filterObject->getName() == $filter) { + $exists = true; + $this->assertTrue(is_callable($filterObject->getCallable())); + $node = new \Twig_Node(); + $this->assertEquals(['html'], $filterObject->getSafe($node)); + break; + } + } + + $this->assertTrue($exists); + } + + /** + * Require correct extension name. + */ + public function testGetName() + { + $extension = new PriceExtension('', '', ''); + $this->assertEquals('price_extension', $extension->getName()); + } + + /** + * Test currency setter. + */ + public function testCurrencySetter() + { + $extension = new PriceExtension('', '', ''); + $extension->setCurrency("USD"); + + $this->assertEquals("USD", $extension->getCurrency()); + } + + public function testDefaultCurrency() + { + $extension = new PriceExtension('', '.', '', 'EUR'); + + $this->assertEquals("EUR", $extension->getCurrency()); + } + + /** + * Data provider for testPriceWithCurrency + * + * @return array + */ + public function testPriceWithCurrencyData() + { + $out = []; + + $rates = [ + "EUR" => "1", + "USD" => "1.3345", + "LTL" => "3.4546", + ]; + + $out[] = [$rates, 'EUR', 100, '100 ']; + $out[] = [$rates, 'USD', 100, '133.45 ']; + $out[] = [$rates, 'LTL', 100, '345.46 ']; + + return $out; + } + + + /** + * @dataProvider testPriceWithCurrencyData + */ + public function testPriceWithCurrency($rates, $currency, $price, $expected) + { + $exchangeService = new CurrencyExchangeService($this->getRatesService($rates, 'EUR'), 'EUR'); + + $extension = new PriceExtension('', '.', '', $currency); + $extension->setCurrencyExchangeService($exchangeService); + + $result = $extension->getFormattedPrice($price, 0); + + $this->assertEquals($expected, $result); + } + + public function testFormatWithDefaultCurrency() + { + $rates = [ + "EUR" => "1", + "USD" => "1.3345", + ]; + + $formatMap = [ + "EUR" => "Price in euros: %s", + "USD" => "Price in dollars: %s", + ]; + + $exchangeService = new CurrencyExchangeService($this->getRatesService($rates, 'EUR'), 'EUR'); + + $extension = new PriceExtension('', '.', '', "EUR", $formatMap); + $extension->setCurrencyExchangeService($exchangeService); + + $result = $extension->getFormattedPrice(1000, 0, null, null); + $expected = "Price in euros: 1000"; + + $this->assertEquals($expected, $result); + } + + /** + * Data provider for testPriceListFormatting + * + * @return array + */ + public function testPriceListFormattingData() + { + $out = []; + $rates = [ + 'SEK' => '8.79', + 'EUR' => '1', + 'USD' => '1.3345', + 'LTL' => '3.4546', + ]; + $toPrintList = [ + 'SEK', + 'EUR' + ]; + //#0 printlist currencies with all formats specified + $formatsMap = [ + 'SEK' => '%s Svensk krona', + 'EUR' => '%s Euros' + ]; + $expectedParams = ['currencies' => [ + [ + 'stringValue' => '8790 Svensk krona', + 'tla' => 'sek' + ], + [ + 'stringValue' => '1000 Euros', + 'tla' => 'eur' + ] + ]]; + $out[] = [$rates, $toPrintList, $formatsMap, $expectedParams]; + //#1 printlist currencies with one format specified + $formatsMap = [ + 'SEK' => '%s Svensk krona', + ]; + $expectedParams = ['currencies' => [ + [ + 'stringValue' => '8790 Svensk krona', + 'tla' => 'sek' + ], + [ + 'stringValue' => '1000 EUR', + 'tla' => 'eur' + ] + ]]; + $out[] = [$rates, $toPrintList, $formatsMap, $expectedParams]; + return $out; + } + + /** + * Test getPriceList output formatting + * + * @dataProvider testPriceListFormattingData + * + * @param $rates + * @param $toPrintList + * @param $formatMap + * @param $expectedParams + */ + public function testPriceListFormatting($rates, $toPrintList, $formatMap, $expectedParams) + { + $exchangeService = new CurrencyExchangeService($this->getRatesService($rates, 'EUR'), 'EUR'); + $extension = new PriceExtension('EUR', '.', '', 'EUR', $formatMap, $toPrintList); + $extension->setCurrencyExchangeService($exchangeService); + $env = $this->getMock('stdClass', ['render']); + $env->expects($this->once())->method('render')->with( + 'testTemplate', + $expectedParams + )->will($this->returnValue($extension->getFormattedPrice(1000))); + + $extension->getPriceList($env, 1000, 'testTemplate', null); + } + + + /** + * Data provider for testPriceListWithCurrency + * + * @return array + */ + public function testPriceListWithCurrencyData() + { + $out = []; + $rates = [ + 'SEK' => '0.1', + 'EUR' => '1', + 'USD' => '1.3345', + 'LTL' => '3.4546', + ]; + $toPrintList = [ + 'EUR', + 'LTL' + ]; + //#0 the default currency is used + $expectedParams = ['currencies' => [ + [ + 'stringValue' => '100 ', + 'tla' => 'eur' + ], + [ + 'stringValue' => '345.46 ', + 'tla' => 'ltl' + ] + ]]; + $out[] = [$rates, $toPrintList, 'EUR', 100, $expectedParams]; + //#1 currency not in the toPrintlist is used + $expectedParams = ['currencies' => [ + [ + 'stringValue' => '74.93 ', + 'tla' => 'eur' + ], + [ + 'stringValue' => '258.87 ', + 'tla' => 'ltl' + ] + ]]; + $out[] = [$rates, $toPrintList, 'USD', 100, $expectedParams]; + //#2 currency in the toPrintlist is used + $expectedParams = ['currencies' => [ + [ + 'stringValue' => '28.95 ', + 'tla' => 'eur' + ], + [ + 'stringValue' => '100 ', + 'tla' => 'ltl' + ] + ]]; + $out[] = [$rates, $toPrintList, 'LTL', 100, $expectedParams]; + return $out; + } + + /** + * Test getPriceList with currency + * @dataProvider testPriceListWithCurrencyData + * + * @param $rates + * @param $toPrintList + * @param $currency + * @param $price + * @param $expectedParams + */ + public function testPriceListWithCurrency($rates, $toPrintList, $currency, $price, $expectedParams) + { + $exchangeService = new CurrencyExchangeService($this->getRatesService($rates, 'EUR'), 'EUR'); + $extension = new PriceExtension('', '.', '', $currency, null, $toPrintList); + $extension->setCurrencyExchangeService($exchangeService); + $env = $this->getMock('stdClass', ['render']); + $env->expects($this->once())->method('render')->with( + 'testTemplate', + $expectedParams + ); + $extension->getPriceList($env, $price, 'testTemplate', $currency); + } + + /** + * Test getPriceList with default currency + */ + public function testPriceListWithDefaultCurrency() + { + $rates = [ + 'EUR' => '1', + 'USD' => '1.3345', + 'LTL' => '3.4546' + ]; + $toPrintList = [ + 'EUR', + 'LTL' + ]; + $exchangeService = new CurrencyExchangeService($this->getRatesService($rates, 'EUR'), 'EUR'); + $extension = new PriceExtension('', '.', '', 'EUR', null, $toPrintList); + $extension->setCurrencyExchangeService($exchangeService); + $expectedParams = ['currencies' => [ + [ + 'stringValue' => '1000 ', + 'tla' => 'eur' + ], + [ + 'stringValue' => '3454.60 ', + 'tla' => 'ltl' + ] + ]]; + $env = $this->getMock('stdClass', ['render']); + $env->expects($this->once())->method('render')->with( + 'testTemplate', + $expectedParams + ); + $extension->getPriceList($env, 1000, 'testTemplate', null); + } + + /** + * Test getCurrencyList + */ + public function testCurrencyList() + { + $rates = [ + 'EUR' => '1', + 'USD' => '1.3345', + 'LTL' => '3.4546' + ]; + $toPrintList = [ + 'EUR', + 'LTL', + 'USD' + ]; + $exchangeService = new CurrencyExchangeService($this->getRatesService($rates, 'EUR'), 'EUR'); + $extension = new PriceExtension('', '.', '', 'EUR', null, $toPrintList); + $extension->setCurrencyExchangeService($exchangeService); + $expectedParams = ['currencies' => [ + [ + 'stringValue' => 'EUR', + 'tla' => 'eur', + 'default' => true + ], + [ + 'stringValue' => 'LTL', + 'tla' => 'ltl', + 'default' => false + ], + [ + 'stringValue' => 'USD', + 'tla' => 'usd', + 'default' => false + ] + ]]; + $env = $this->getMock('stdClass', ['render']); + $env->expects($this->once())->method('render')->with( + 'testTemplate', + $expectedParams + ); + $extension->getCurrencyList($env, 'testTemplate'); + } + + /** + * Test getCurrencyListCss + */ + public function testCurrencyListCss() + { + $rates = [ + 'EUR' => '1', + 'USD' => '1.3345', + 'LTL' => '3.4546' + ]; + $toPrintList = [ + 'EUR', + 'LTL', + 'USD' + ]; + $exchangeService = new CurrencyExchangeService($this->getRatesService($rates, 'EUR'), 'EUR'); + $extension = new PriceExtension('', '.', '', 'EUR', null, $toPrintList); + $extension->setCurrencyExchangeService($exchangeService); + $expectedParams = ['currencies' => [ + [ + 'stringValue' => 'eur' + ], + [ + 'stringValue' => 'ltl' + ], + [ + 'stringValue' => 'usd' + ] + ]]; + $env = $this->getMock('stdClass', ['render']); + $env->expects($this->once())->method('render')->with( + 'testTemplate', + $expectedParams + ); + $extension->getCurrencyListCss($env, 'testTemplate'); + } + + /** + * Test behavior when there is no currency exchange service defined + */ + public function testNoCurrencyExchange() + { + $extension = new PriceExtension('', '.', '', 'EUR', null, []); + + $this->assertEquals('', $extension->getFormattedPrice(2, 0, 'a', 'b')); + } + + /** + * Test case when we pass undefined currency + */ + public function testUndefinedCurrency() + { + $exchangeService = new CurrencyExchangeService($this->getRatesService([], 'EUR'), 'EUR'); + $extension = new PriceExtension('', '.', '', 'EUR', null, []); + $extension->setCurrencyExchangeService($exchangeService); + $extension->setLogger($this->getLogger()); + + $this->assertEquals('', $extension->getFormattedPrice(2, 0, 'a', 'b')); + } +} diff --git a/Twig/PriceExtension.php b/Twig/PriceExtension.php new file mode 100755 index 0000000..52a232e --- /dev/null +++ b/Twig/PriceExtension.php @@ -0,0 +1,338 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ContentBundle\Twig; + +use ONGR\ContentBundle\Exception\Currency\UndefinedCurrencyException; +use ONGR\ContentBundle\Service\Currency\CurrencyExchangeService; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerInterface; + +/** + * PriceExtension class. + */ +class PriceExtension extends \Twig_Extension implements LoggerAwareInterface +{ + /** + * Extension name + */ + const NAME = 'price_extension'; + + /** + * @var string Currency sign. + */ + protected $currencySign; + + /** + * @var string Decimal point separator. + */ + protected $decPointSeparator; + + /** + * @var string Thousands separator. + */ + protected $thousandsSeparator; + + /** + * @var null Currency. + */ + protected $currency = null; + + /** + * @var CurrencyExchangeService Service which provide currency exchange rates. + */ + protected $currencyService = null; + + /** + * @var array Contains formats for each currency. + */ + protected $formatsMap; + + /** + * @var array Array of currencies to be listed in twig while using the "list" functions. + */ + protected $toListMap; + + /** + * @var LoggerInterface + */ + protected $logger = null; + + /** + * Constructor. + * + * @param string $currencySign + * @param string $decPointSeparator + * @param string $thousandsSeparator + * @param array $currency + * @param array $formatsMap + * @param array $toListMap + */ + public function __construct( + $currencySign, + $decPointSeparator, + $thousandsSeparator, + $currency = null, + $formatsMap = [], + $toListMap = [] + ) { + $this->currencySign = $currencySign; + $this->decPointSeparator = $decPointSeparator; + $this->thousandsSeparator = $thousandsSeparator; + $this->currency = $currency; + $this->formatsMap = $formatsMap; + $this->toListMap = $toListMap; + } + + /** + * @return \Twig_SimpleFilter[] + */ + public function getFilters() + { + $functions = []; + $functions[] = new \Twig_SimpleFilter( + 'fox_price', + [$this, 'getFormattedPrice'], + ['is_safe' => ['html']] + ); + $functions[] = new \Twig_SimpleFilter( + 'fox_price_list', + [$this, 'getPriceList'], + [ + 'needs_environment' => true, + 'is_safe' => ['html'], + ] + ); + + return $functions; + } + + /** + * @return \Twig_SimpleFunction[] + */ + public function getFunctions() + { + $functions[] = new \Twig_SimpleFunction( + 'fox_price_currency_list', + [$this, 'getCurrencyList'], + [ + 'needs_environment' => true, + 'is_safe' => ['html'], + ] + ); + + $functions[] = new \Twig_SimpleFunction( + 'fox_price_currency_list_css', + [$this, 'getCurrencyListCss'], + [ + 'needs_environment' => true, + 'is_safe' => ['html'], + ] + ); + + return $functions; + } + + /** + * Returns formatted price. + * + * @param float $price + * @param int $decimals + * @param string $toCurrency + * @param string $fromCurrency + * @param string $customFormat + * + * @return string + */ + public function getFormattedPrice( + $price, + $decimals = 0, + $toCurrency = null, + $fromCurrency = null, + $customFormat = null + ) { + $targetCurrency = $toCurrency ? $toCurrency : $this->currency; + + if ($targetCurrency) { + if (isset($this->currencyService)) { + try { + $price = $this->currencyService->calculateRate($price, $targetCurrency, $fromCurrency); + } catch (UndefinedCurrencyException $ex) { + $this->logger && $this->logger->error( + 'Got undefined currency on PriceExtension', + ['message' => $ex->getMessage()] + ); + + return ''; + } + } else { + $this->logger && $this->logger->error('Currency service is undefined on PriceExtension'); + + return ''; + } + } + + if (abs($price) > floor(abs($price))) { + $decimals = 2; + } + + $formattedPrice = number_format($price, $decimals, $this->decPointSeparator, $this->thousandsSeparator); + + $printFormat = null; + if ($customFormat) { + $printFormat = $customFormat; + } elseif (isset($this->formatsMap[$targetCurrency])) { + $printFormat = $this->formatsMap[$targetCurrency]; + } + + if ($printFormat) { + return sprintf($printFormat, $formattedPrice); + } else { + return "{$formattedPrice} {$this->currencySign}"; + } + } + + /** + * Returns specified prices formatted by a specified template. + * + * @param \Twig_Environment $environment + * @param int $price + * @param string $template + * @param null $fromCurrency + * + * @return string + */ + public function getPriceList( + $environment, + $price, + $template = 'FoxUtilsBundle:Price:priceList.html.twig', + $fromCurrency = null + ) { + $values = []; + foreach ($this->toListMap as $targetCurrency) { + $values[] = [ + 'stringValue' => $this->getFormattedPrice($price, 0, $targetCurrency, $fromCurrency), + 'tla' => strtolower($targetCurrency), + ]; + } + + return $environment->render( + $template, + ['currencies' => $values] + ); + } + + /** + * Returns all available currencies. + * + * @param \Twig_Environment $environment + * @param string $template + * + * @return string + */ + public function getCurrencyList($environment, $template = 'FoxUtilsBundle:Price:currencyList.html.twig') + { + $values = []; + foreach ($this->toListMap as $targetCurrency) { + $values[] = [ + 'stringValue' => $targetCurrency, + 'tla' => strtolower($targetCurrency), + 'default' => (strcasecmp($targetCurrency, $this->currency) == 0) ? true : false, + ]; + } + + return $environment->render( + $template, + ['currencies' => $values] + ); + } + + /** + * Returns css for hiding and displaying currencies. + * + * @param \Twig_Environment $environment + * @param string $template + * + * @return string + */ + public function getCurrencyListCss($environment, $template = 'FoxUtilsBundle:Price:currencyListCss.html.twig') + { + $values = []; + foreach ($this->toListMap as $targetCurrency) { + $values[] = [ + 'stringValue' => strtolower($targetCurrency), + ]; + } + + return $environment->render( + $template, + ['currencies' => $values] + ); + } + + /** + * Returns name of the extension. + * + * @return string + */ + public function getName() + { + return self::NAME; + } + + /** + * @param null $currency + */ + public function setCurrency($currency) + { + $this->currency = $currency; + } + + /** + * @return string + */ + public function getCurrency() + { + return $this->currency; + } + + /** + * @param CurrencyExchangeService $currencyService + */ + public function setCurrencyExchangeService($currencyService) + { + $this->currencyService = $currencyService; + } + + /** + * {@inheritdoc} + */ + public function setLogger(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * @param array $toListMap + */ + public function setToListMap($toListMap) + { + $this->toListMap = $toListMap; + } + + /** + * @param array $formatsMap + */ + public function setFormatsMap($formatsMap) + { + $this->formatsMap = $formatsMap; + } +} diff --git a/composer.json b/composer.json index e9ab709..f85fdad 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ "require-dev": { "phpunit/phpunit": "~4.1", "squizlabs/php_codesniffer": "~1.5", - "ongr/ongr-strict-standard": "~1.0.0-beta" + "ongr/ongr-strict-standard": "~1.0.0-beta", + "tedivm/stash-bundle": "0.3.*" }, "suggest": { "ongr/router-bundle": "Adds SEO friendly URLs"