Updating vendors

1.10.x
Julio Montoya 11 years ago
parent 43efa113d7
commit be582a9647
  1. 5
      vendor/sonata-project/block-bundle/.travis.yml
  2. 23
      vendor/sonata-project/block-bundle/Block/BaseBlockService.php
  3. 37
      vendor/sonata-project/block-bundle/Block/BlockServiceManager.php
  4. 13
      vendor/sonata-project/block-bundle/Block/BlockServiceManagerInterface.php
  5. 5
      vendor/sonata-project/block-bundle/Block/Loader/ServiceLoader.php
  6. 2
      vendor/sonata-project/block-bundle/Block/Service/ContainerBlockService.php
  7. 34
      vendor/sonata-project/block-bundle/Block/Service/MenuBlockService.php
  8. 11
      vendor/sonata-project/block-bundle/CHANGELOG.md
  9. 5
      vendor/sonata-project/block-bundle/Cache/HttpCacheHandler.php
  10. 11
      vendor/sonata-project/block-bundle/Command/DebugBlocksCommand.php
  11. 4
      vendor/sonata-project/block-bundle/DependencyInjection/Compiler/TweakCompilerPass.php
  12. 58
      vendor/sonata-project/block-bundle/DependencyInjection/Configuration.php
  13. 46
      vendor/sonata-project/block-bundle/DependencyInjection/SonataBlockExtension.php
  14. 26
      vendor/sonata-project/block-bundle/Form/Type/ServiceListType.php
  15. 2
      vendor/sonata-project/block-bundle/Model/BaseBlock.php
  16. 3
      vendor/sonata-project/block-bundle/Model/BlockManagerInterface.php
  17. 2
      vendor/sonata-project/block-bundle/Profiler/DataCollector/BlockDataCollector.php
  18. 10
      vendor/sonata-project/block-bundle/Resources/config/block.xml
  19. 2
      vendor/sonata-project/block-bundle/Resources/config/core.xml
  20. 4
      vendor/sonata-project/block-bundle/Resources/config/form.xml
  21. 10
      vendor/sonata-project/block-bundle/Resources/doc/index.rst
  22. 63
      vendor/sonata-project/block-bundle/Resources/doc/reference/advanced_usage.rst
  23. 56
      vendor/sonata-project/block-bundle/Resources/doc/reference/cache.rst
  24. 44
      vendor/sonata-project/block-bundle/Resources/doc/reference/events.rst
  25. 57
      vendor/sonata-project/block-bundle/Resources/doc/reference/exceptions.rst
  26. 25
      vendor/sonata-project/block-bundle/Resources/doc/reference/installation.rst
  27. 13
      vendor/sonata-project/block-bundle/Resources/doc/reference/profiler.rst
  28. 9
      vendor/sonata-project/block-bundle/Resources/doc/reference/provided_blocks.rst
  29. 27
      vendor/sonata-project/block-bundle/Resources/doc/reference/twig_helpers.rst
  30. 54
      vendor/sonata-project/block-bundle/Resources/doc/reference/your_first_block.rst
  31. 23
      vendor/sonata-project/block-bundle/Templating/Helper/BlockHelper.php
  32. 22
      vendor/sonata-project/block-bundle/Tests/Block/BlockLoaderChainTest.php
  33. 24
      vendor/sonata-project/block-bundle/Tests/Block/BlockServiceManagerTest.php
  34. 9
      vendor/sonata-project/block-bundle/Tests/Cache/HttpCacheHandlerTest.php
  35. 46
      vendor/sonata-project/block-bundle/Tests/DependencyInjection/ConfigurationTest.php
  36. 2
      vendor/sonata-project/block-bundle/Tests/Event/TextBlockListenerTest.php
  37. 15
      vendor/sonata-project/block-bundle/Tests/Form/Type/ServiceListTypeTest.php
  38. 25
      vendor/sonata-project/block-bundle/Tests/Templating/Helper/BlockHelperTest.php
  39. 3
      vendor/sonata-project/cache/composer.json
  40. 26
      vendor/sonata-project/cache/lib/Adapter/Cache/MongoCache.php
  41. 4
      vendor/sonata-project/cache/lib/Adapter/Cache/NoopCache.php
  42. 9
      vendor/sonata-project/cache/lib/Adapter/Cache/PRedisCache.php
  43. 5
      vendor/sonata-project/cache/lib/Adapter/Counter/MongoCounter.php
  44. 3
      vendor/sonata-project/cache/test/Adapter/Cache/BaseTest.php
  45. 6
      vendor/sonata-project/cache/test/Adapter/Cache/MongoCacheTest.php
  46. 7
      vendor/sonata-project/cache/test/Adapter/Counter/MongoCounterTest.php
  47. 4
      vendor/sonata-project/core-bundle/.gitignore
  48. 3
      vendor/sonata-project/core-bundle/.travis.yml
  49. 3
      vendor/sonata-project/core-bundle/DependencyInjection/SonataCoreExtension.php
  50. 7
      vendor/sonata-project/core-bundle/Form/Type/DateRangeType.php
  51. 7
      vendor/sonata-project/core-bundle/Form/Type/DateTimeRangeType.php
  52. 5
      vendor/sonata-project/core-bundle/Model/BaseDocumentManager.php
  53. 28
      vendor/sonata-project/core-bundle/Model/BaseManager.php
  54. 3
      vendor/sonata-project/core-bundle/Model/BasePHPCRManager.php
  55. 9
      vendor/sonata-project/core-bundle/Model/ManagerInterface.php
  56. 12
      vendor/sonata-project/core-bundle/Resources/config/form_types.xml
  57. 6
      vendor/sonata-project/core-bundle/Resources/doc/index.rst
  58. 9
      vendor/sonata-project/core-bundle/Resources/doc/reference/doctrine_base_manager.rst
  59. 11
      vendor/sonata-project/core-bundle/Resources/doc/reference/flash_messages.rst
  60. 116
      vendor/sonata-project/core-bundle/Resources/doc/reference/form_types.rst
  61. 15
      vendor/sonata-project/core-bundle/Resources/doc/reference/installation.rst
  62. 5
      vendor/sonata-project/core-bundle/Resources/doc/reference/status_helper.rst
  63. 17
      vendor/sonata-project/core-bundle/Resources/doc/reference/twig_helpers.rst
  64. 8
      vendor/sonata-project/core-bundle/Resources/translations/SonataCoreBundle.en.xliff
  65. 5
      vendor/sonata-project/core-bundle/Resources/views/FlashMessage/render.html.twig
  66. 2
      vendor/sonata-project/core-bundle/Tests/Form/Type/DateRangeTypeTest.php
  67. 19
      vendor/sonata-project/core-bundle/Tests/Model/BaseEntityManagerTest.php
  68. 8
      vendor/sonata-project/core-bundle/composer.json

@ -15,11 +15,12 @@ env:
before_script:
- composer require symfony/symfony:${SYMFONY_VERSION} --prefer-source
- composer install --dev --prefer-source
- sudo pip install -r Resources/doc/requirements.txt
script: phpunit
script: phpunit && cd Resources/doc && sphinx-build -W -b html -d _build/doctrees . _build/html
notifications:
webhooks: http://sonata-project.org/bundles/admin/master/travis
webhooks: http://sonata-project.org/bundles/block/master/travis
matrix:
allow_failures:

@ -11,6 +11,7 @@
namespace Sonata\BlockBundle\Block;
use Sonata\AdminBundle\Validator\ErrorElement;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Sonata\BlockBundle\Model\BlockInterface;
@ -189,4 +190,26 @@ abstract class BaseBlockService implements BlockServiceInterface
'block' => $blockContext->getBlock(),
), $response);
}
/**
* @param FormMapper $form
* @param BlockInterface $block
*
* @return void
*/
public function buildEditForm(FormMapper $form, BlockInterface $block)
{
}
/**
* @param ErrorElement $errorElement
* @param BlockInterface $block
*
* @return void
*/
public function validateBlock(ErrorElement $errorElement, BlockInterface $block)
{
}
}

@ -32,6 +32,8 @@ class BlockServiceManager implements BlockServiceManagerInterface
protected $inValidate;
protected $contexts;
/**
* @param ContainerInterface $container
* @param $debug
@ -40,6 +42,7 @@ class BlockServiceManager implements BlockServiceManagerInterface
public function __construct(ContainerInterface $container, $debug, LoggerInterface $logger = null)
{
$this->services = array();
$this->contexts = array();
$this->container = $container;
}
@ -96,9 +99,17 @@ class BlockServiceManager implements BlockServiceManagerInterface
/**
* {@inheritdoc}
*/
public function add($name, $service)
public function add($name, $service, $contexts = array())
{
$this->services[$name] = $service;
foreach ($contexts as $context) {
if (!array_key_exists($context, $this->contexts)) {
$this->contexts[$context] = array();
}
$this->contexts[$context][] = $name;
}
}
/**
@ -106,7 +117,9 @@ class BlockServiceManager implements BlockServiceManagerInterface
*/
public function setServices(array $blockServices)
{
$this->services = $blockServices;
foreach($blockServices as $name => $service) {
$this->add($name, $service);
}
}
/**
@ -123,6 +136,24 @@ class BlockServiceManager implements BlockServiceManagerInterface
return $this->services;
}
/**
* {@inheritdoc}
*/
public function getServicesByContext($context)
{
if (!array_key_exists($context, $this->contexts)) {
return array();
}
$services = array();
foreach ($this->contexts[$context] as $name) {
$services[$name] = $this->getService($name);
}
return $services;
}
/**
* {@inheritdoc}
*/
@ -142,6 +173,8 @@ class BlockServiceManager implements BlockServiceManagerInterface
}
/**
* @todo: this function should be remove into a proper statefull object
*
* {@inheritdoc}
*/
public function validate(ErrorElement $errorElement, BlockInterface $block)

@ -18,10 +18,11 @@ interface BlockServiceManagerInterface
/**
* @param string $name
* @param string $service
* @param array $contexts
*
* @return void
*/
public function add($name, $service);
public function add($name, $service, $contexts = array());
/**
* Return the block service linked to the link
@ -33,6 +34,8 @@ interface BlockServiceManagerInterface
public function get(BlockInterface $block);
/**
* @deprecated will be remove in 2.4, use the add method instead
*
* @param array $blockServices
*
* @return void
@ -45,7 +48,13 @@ interface BlockServiceManagerInterface
public function getServices();
/**
* @param string $name
*
* @return array
*/
public function getServicesByContext($name);
/**
* @param string $name
*
* @return boolean
@ -60,6 +69,8 @@ interface BlockServiceManagerInterface
public function getService($name);
/**
* @deprecated will be remove in 2.4
*
* @return array
*/
public function getLoadedServices();

@ -32,7 +32,10 @@ class ServiceLoader implements BlockLoaderInterface
public function load($configuration)
{
if (!in_array($configuration['type'], $this->types)) {
throw new \RuntimeException(sprintf('The block type %s does not exist', $configuration['type']));
throw new \RuntimeException(sprintf(
'The block type "%s" does not exist',
$configuration['type']
));
}
$block = new Block;

@ -36,8 +36,10 @@ class ContainerBlockService extends BaseBlockService
$formMapper->add('settings', 'sonata_type_immutable_array', array(
'keys' => array(
array('code', 'text', array('required' => false)),
array('layout', 'textarea', array()),
array('class', 'text', array('required' => false)),
array('template', 'sonata_type_container_template_choice', array())
)
));

@ -94,7 +94,7 @@ class MenuBlockService extends BaseBlockService
if (($name = $block->getSetting('menu_name')) && $name !== "" && !$this->menuProvider->has($name)) {
// If we specified a menu_name, check that it exists
$errorElement->with('menu_name')
->addViolation('soanta.block.menu.not_existing', array('name' => $name))
->addViolation('sonata.block.menu.not_existing', array('name' => $name))
->end();
}
}
@ -104,22 +104,20 @@ class MenuBlockService extends BaseBlockService
*/
public function setDefaultSettings(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'title' => $this->getName(),
'cache_policy' => 'public',
'template' => 'SonataBlockBundle:Block:block_core_menu.html.twig',
'menu_name' => "",
'safe_labels' => false,
'current_class' => 'active',
'first_class' => false,
'last_class' => false,
'current_uri' => null,
'menu_class' => "list-group",
'children_class' => "list-group-item",
'menu_template' => null,
)
);
$resolver->setDefaults(array(
'title' => $this->getName(),
'cache_policy' => 'public',
'template' => 'SonataBlockBundle:Block:block_core_menu.html.twig',
'menu_name' => "",
'safe_labels' => false,
'current_class' => 'active',
'first_class' => false,
'last_class' => false,
'current_uri' => null,
'menu_class' => "list-group",
'children_class' => "list-group-item",
'menu_template' => null,
));
}
/**
@ -167,6 +165,8 @@ class MenuBlockService extends BaseBlockService
* Replaces setting keys with knp menu item options keys
*
* @param array $settings
*
* @return array
*/
protected function getMenuOptions(array $settings)
{

@ -7,6 +7,15 @@ A [BC BREAK] means the update will break the project for many reasons:
* new dependencies
* class refactoring
### 2014-04-23
* [BC BREAK] Complete support for context, add new method in BlockServiceManager (getServicesByContext)
### 2014-03-10
* [BC BREAK] Updated configuration: ``sonata_block.profiler.container_types`` is deprecated in favor of ``sonata_block.container.types``; please update your configuration accordingly.
### 2013-03-28
* [BC BREAK] Intoduction of an BlockContext to hold a BlockInterface instance and the related settings.
* [BC BREAK] Introduction of an BlockContext to hold a BlockInterface instance and the related settings.

@ -29,6 +29,11 @@ class HttpCacheHandler implements HttpCacheHandlerInterface
return;
}
// no block has been rendered
if (null === $this->currentTtl) {
return;
}
// a block has a lower ttl that the current response, so we update the ttl to match
// the one provided in the block
if ($this->currentTtl < $response->getTtl()) {

@ -12,6 +12,7 @@
namespace Sonata\BlockBundle\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\Output;
use Symfony\Component\OptionsResolver\OptionsResolver;
@ -22,11 +23,19 @@ class DebugBlocksCommand extends BaseCommand
{
$this->setName('sonata:block:debug');
$this->setDescription('Debug all blocks available, show default settings of each block');
$this->addOption('context', 'c', InputOption::VALUE_REQUIRED, 'display service for the specified context');
}
public function execute(InputInterface $input, OutputInterface $output)
{
foreach ($this->getBlockServiceManager()->getServices() as $code => $service) {
if ($input->getOption('context')) {
$services = $this->getBlockServiceManager()->getServicesByContext($input->getOption('context'));
} else {
$services = $this->getBlockServiceManager()->getServices();
}
foreach ($services as $code => $service) {
$resolver = new OptionsResolver();
$service->setDefaultSettings($resolver);

@ -29,8 +29,10 @@ class TweakCompilerPass implements CompilerPassInterface
{
$manager = $container->getDefinition('sonata.block.manager');
$parameters = $container->getParameter('sonata_block.blocks');
foreach ($container->findTaggedServiceIds('sonata.block') as $id => $attributes) {
$manager->addMethodCall('add', array($id, $id));
$manager->addMethodCall('add', array($id, $id, isset($parameters[$id]) ? $parameters[$id]['contexts'] : array()));
}
$services = array();

@ -20,6 +20,19 @@ use Symfony\Component\Config\Definition\ConfigurationInterface;
*/
class Configuration implements ConfigurationInterface
{
/**
* @var array
*/
protected $defaultContainerTemplates;
/**
* @param array $defaultContainerTemplates
*/
public function __construct(array $defaultContainerTemplates)
{
$this->defaultContainerTemplates = $defaultContainerTemplates;
}
/**
* {@inheritDoc}
*/
@ -33,6 +46,23 @@ class Configuration implements ConfigurationInterface
->fixXmlConfig('template')
->fixXmlConfig('block')
->fixXmlConfig('block_by_class')
->validate()
->always(function($value) {
foreach ($value['blocks'] as $name => &$block) {
if (count($block['contexts']) == 0) {
$block['contexts'] = $value['default_contexts'];
}
}
if (isset($value['profiler']['container_types']) && !empty($value['profiler']['container_types'])
&& isset($value['container']['types']) && !empty($value['container']['types'])
&& 0 !== count(array_diff($value['profiler']['container_types'], $value['container']['types']))) {
throw new \RuntimeException("You cannot have different config options for sonata_block.profiler.container_types and sonata_block.container.types; the first one is deprecated, in case of doubt use the latter");
}
return $value;
})
->end()
->children()
->arrayNode('profiler')
->addDefaultsIfNotSet()
@ -70,6 +100,29 @@ class Configuration implements ConfigurationInterface
->end()
->end()
->arrayNode('container')
->info('block container configuration')
->addDefaultsIfNotSet()
->fixXmlConfig('type', 'types')
->fixXmlConfig('template', 'templates')
->children()
->arrayNode('types')
->info('container service ids')
->isRequired()
// add default value to well know users of BlockBundle
->defaultValue(array('sonata.block.service.container', 'sonata.page.block.container', 'cmf.block.container', 'cmf.block.slideshow'))
->prototype('scalar')->end()
->end()
->arrayNode('templates')
->info('container templates')
->isRequired()
->defaultValue($this->defaultContainerTemplates)
->prototype('scalar')->end()
->end()
->end()
->end()
->arrayNode('blocks')
->info('configuration per block service')
->useAttributeAsKey('id')
@ -157,4 +210,9 @@ class Configuration implements ConfigurationInterface
return $treeBuilder;
}
public function getConfiguration(array $config, ContainerBuilder $container)
{
return new Configuration(array());
}
}

@ -34,8 +34,21 @@ class SonataBlockExtension extends Extension
*/
public function load(array $configs, ContainerBuilder $container)
{
$bundles = $container->getParameter('kernel.bundles');
$defaultTemplates = array();
if (isset($bundles['SonataPageBundle'])) {
$defaultTemplates['SonataPageBundle:Block:block_container.html.twig'] = "SonataPageBundle default template";
} else {
$defaultTemplates['SonataBlockBundle:Block:block_container.html.twig'] = "SonataBlockBundle default template";
}
if (isset($bundles['SonataSeoBundle'])) {
$defaultTemplates['SonataSeoBundle:Block:block_social_container.html.twig'] = "SonataSeoBundle (to contain social buttons)";
}
$processor = new Processor();
$configuration = new Configuration();
$configuration = new Configuration($defaultTemplates);
$config = $processor->processConfiguration($configuration, $configs);
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
@ -44,6 +57,9 @@ class SonataBlockExtension extends Extension
$loader->load('core.xml');
$loader->load('exception.xml');
$this->fixConfigurationDeprecation($config);
$this->configureBlockContainers($container, $config);
$this->configureContext($container, $config);
$this->configureLoaderChain($container, $config);
$this->configureCache($container, $config);
@ -53,7 +69,6 @@ class SonataBlockExtension extends Extension
$this->configureMenus($container, $config);
$this->configureClassesToCompile();
$bundles = $container->getParameter('kernel.bundles');
if ($config['templates']['block_base'] === null) {
if (isset($bundles['SonataPageBundle'])) {
$config['templates']['block_base'] = 'SonataPageBundle:Block:block_base.html.twig';
@ -67,6 +82,27 @@ class SonataBlockExtension extends Extension
$container->getDefinition('sonata.block.twig.global')->replaceArgument(0, $config['templates']);
}
/**
* @param ContainerBuilder $container
* @param array $config
*/
public function configureBlockContainers(ContainerBuilder $container, array $config)
{
$container->setParameter('sonata.block.container.types', $config['container']['types']);
$container->getDefinition('sonata.block.form.type.container_template')->replaceArgument(0, $config['container']['templates']);
}
/**
* @param array $config
*/
public function fixConfigurationDeprecation(array &$config)
{
if (count(array_diff($config['profiler']['container_types'], $config['container']['types']))) {
$config['container']['types'] = array_merge($config['profiler']['container_types'], $config['container']['types']);
}
}
/**
* @param ContainerBuilder $container
* @param array $config
@ -161,9 +197,6 @@ class SonataBlockExtension extends Extension
$contexts[$context][] = $service;
}
}
$container->getDefinition('sonata.block.form.type.block')
->replaceArgument(1, $contexts);
}
/**
@ -185,7 +218,8 @@ class SonataBlockExtension extends Extension
$definition->setPublic(false);
$definition->addTag('data_collector', array('id' => 'block', 'template' => $config['profiler']['template']));
$definition->addArgument(new Reference('sonata.block.templating.helper'));
$definition->addArgument($config['profiler']['container_types']);
$definition->addArgument($config['container']['types']);
$container->setDefinition('sonata.block.data_collector', $definition);
}

@ -23,16 +23,12 @@ class ServiceListType extends AbstractType
{
protected $manager;
protected $contexts;
/**
* @param BlockServiceManagerInterface $manager
* @param array $contexts
*/
public function __construct(BlockServiceManagerInterface $manager, array $contexts = array())
public function __construct(BlockServiceManagerInterface $manager)
{
$this->manager = $manager;
$this->contexts = $contexts;
}
/**
@ -56,25 +52,19 @@ class ServiceListType extends AbstractType
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$contexts = $this->contexts;
$manager = $this->manager;
$resolver->setRequired(array(
'context',
));
$resolver->setDefaults(array(
'context' => false,
'multiple' => false,
'expanded' => false,
'choices' => function (Options $options, $previousValue) use ($contexts, $manager) {
if (!isset($contexts[$options['context']])) {
if (Kernel::MINOR_VERSION < 3) {
throw new \RuntimeException(sprintf('Invalid context: `%s`', $options['context']));
}
throw new InvalidArgumentException(sprintf('Invalid context: `%s`', $options['context']));
}
'choices' => function (Options $options, $previousValue) use ($manager) {
$types = array();
foreach ($contexts[$options['context']] as $service) {
$types[$service] = sprintf('%s - %s', $manager->getService($service)->getName(), $service);
foreach ($manager->getServicesByContext($options['context']) as $code => $service) {
$types[$code] = sprintf('%s - %s', $service->getName(), $code);
}
return $types;

@ -262,7 +262,7 @@ abstract class BaseBlock implements BlockInterface
return 0;
}
$ttl = $this->getSetting('ttl', 84600);
$ttl = $this->getSetting('ttl', 86400);
foreach ($this->getChildren() as $block) {
$blockTtl = $block->getTtl();

@ -11,7 +11,8 @@
namespace Sonata\BlockBundle\Model;
use Sonata\CoreBundle\Model\ManagerInterface;
use Sonata\CoreBundle\Model\PageableManagerInterface;
interface BlockManagerInterface extends ManagerInterface
interface BlockManagerInterface extends ManagerInterface, PageableManagerInterface
{
}

@ -56,7 +56,7 @@ class BlockDataCollector implements DataCollectorInterface, \Serializable
/**
* Constructor
*
* @param BlockHelper $renderer Block renderer
* @param BlockHelper $blockHelper Block renderer
* @param array $containerTypes array of container types
*/
public function __construct(BlockHelper $blockHelper, array $containerTypes)

@ -9,6 +9,7 @@
<parameter key="sonata.block.service.text.class">Sonata\BlockBundle\Block\Service\TextBlockService</parameter>
<parameter key="sonata.block.service.rss.class">Sonata\BlockBundle\Block\Service\RssBlockService</parameter>
<parameter key="sonata.block.service.menu.class">Sonata\BlockBundle\Block\Service\MenuBlockService</parameter>
<parameter key="sonata.block.service.template.class">Sonata\BlockBundle\Block\Service\TemplateBlockService</parameter>
</parameters>
<services>
@ -41,7 +42,14 @@
<argument>sonata.block.menu</argument>
<argument type="service" id="templating" />
<argument type="service" id="knp_menu.menu_provider" />
<argument type="collection"></argument>
<argument type="collection" />
</service>
<service id="sonata.block.service.template" class="%sonata.block.service.template.class%">
<tag name="sonata.block" />
<argument>sonata.block.template</argument>
<argument type="service" id="templating" />
</service>
</services>
</container>

@ -31,7 +31,7 @@
<argument type="service" id="sonata.block.templating.helper" />
</service>
<service id="sonata.block.templating.helper" class="Sonata\BlockBundle\Templating\Helper\BlockHelper" public="false">
<service id="sonata.block.templating.helper" class="Sonata\BlockBundle\Templating\Helper\BlockHelper">
<tag name="templating.helper" alias="sonata_block" />
<argument type="service" id="sonata.block.manager" />

@ -8,6 +8,10 @@
<service id="sonata.block.form.type.block" class="Sonata\BlockBundle\Form\Type\ServiceListType">
<tag name="form.type" alias="sonata_block_service_choice" />
<argument type="service" id="sonata.block.manager" />
</service>
<service id="sonata.block.form.type.container_template" class="Sonata\BlockBundle\Form\Type\ContainerTemplateType">
<tag name="form.type" alias="sonata_type_container_template_choice" />
<argument />
</service>
</services>

@ -16,3 +16,13 @@ Reference Guide
reference/exceptions
reference/advanced_usage
reference/cache
reference/events
Cookbooks
---------
.. toctree::
:maxdepth: 1
:numbered:
cookbooks/rapid_prototyping

@ -1,28 +1,32 @@
.. index::
single: Advanced
single: Usage
single: Cache
single: Context
Advanced usage
==============
This happens when a block is rendered:
- a block is loaded based on the configuration passed to
``sonata_block_render``
- if caching is configured, the cache is checked and content is returned
if found
- each block model also has a block service, the execute method of it is
called:
- you can put here logic like in a controller
- it calls a template
- the result is a Response object
* A block is loaded based on the configuration passed to ``sonata_block_render``,
* If caching is configured, the cache is checked and content is returned if found,
* Each block model also has a block service, the execute method of it is called:
* You can put here logic like in a controller,
* It calls a template,
* The result is a `Response` object.
Cache
-----
The SonataBlockBundle integrates with the `SonataCacheBundle`_ to provide
several caching solutions. Have a look at the available adapters in the
SonataCacheBundle to see all options.
The ``SonataBlockBundle`` integrated with the `SonataCacheBundle`_ provides several caching solutions.
Have a look at the available adapters in the ``SonataCacheBundle`` to see all options.
Block Context Manager - context cache
-------------------------------------
When using Varnish, Ssi or Js cache the settings passed here are lost:
When using `Varnish`, `Ssi` or `Js` cache, the settings passed here are lost:
.. code-block:: jinja
@ -31,39 +35,28 @@ When using Varnish, Ssi or Js cache the settings passed here are lost:
'url': 'http://sonata-project.org/blog/archive.rss'
}) }}
Taking Js as example, an url is generated and added to some javascript. This
javascript is injected in the page. When the page is loaded, the javascript
calls the url to retrieve the block content.
Taking Js as example, an url is generated and added to some javascript. This javascript is injected in the page. When the page is loaded, the javascript calls the url to retrieve the block content.
The default BlockContextManager automatically adds settings passed from the
template to the ``extra_cache_keys`` with the key ``context``. This allows
cache adapters to rebuild a BlockContext if implemented.
The default ``BlockContextManager`` automatically adds settings passed from the template to the ``extra_cache_keys`` with the key ``context``.
This allows cache adapters to rebuild a ``BlockContext`` if implemented.
In the Js example the cache keys can be added to the url as query parameters.
When the url is called from the javascript the cache adapter handles the
request. It retrieves the settings from the ``context`` parameter and passes
it to the BlockContextManager while creating a new BlockContext. This ensures
the BlockContext is the same as when it was created from a template helper
call without cache enabled.
Still in the Js example, the cache keys can be added to the url as query parameters.
When the url is called from the javascript, the cache adapter handles the request. It retrieves the settings from the ``context`` parameter and passes it to the ``BlockContextManager`` while creating a new ``BlockContext``.
This ensures the ``BlockContext`` is the same as when it was created from a template helper call without cache enabled.
.. note::
The settings are exposed because they are added to the url as parameters,
secure the url if needed.
The settings are exposed because they are added to the url as parameters, secure the url if needed.
Block loading
-------------
Block models are loaded by a chain loader, add your own loader by tagging a
service with ``sonata.block.loader"`` and implement
``Sonata\BlockBundle\Block\BlockLoaderInterface`` in the loader class.
Block models are loaded by a chain loader, add your own loader by tagging a service with ``sonata.block.loader"`` and implement ``Sonata\BlockBundle\Block\BlockLoaderInterface`` in the loader class.
Empty block
-----------
By default the loader interface expects the exception
``Sonata\BlockBundle\Exception\BlockNotFoundException`` if a block is not
found. Return an empty block from your loader class if the default behaviour
for your blocks is to always return content.
By default, the loader interface expects the exception ``Sonata\BlockBundle\Exception\BlockNotFoundException`` if a block is not found.
Return an empty block from your loader class if the default behaviour for your blocks is to always return content.
.. _`SonataCacheBundle`: https://github.com/sonata-project/SonataCacheBundle
.. _`SonataCacheBundle`: https://github.com/sonata-project/SonataCacheBundle

@ -1,22 +1,26 @@
.. index::
single: Cache
single: Behavior
single: Backend
Cache
=====
BlockBundle integrates the CacheBundle to handle block cache. The cache unit stored in the backend is a Response object
from the HttpFoundation Component. Why a Response object ? It is a simple element, which content the data (the body) and some
metadata (the headers). As the block returns a Response object, it is possible to send it to the client, this use case
can be quite usefull for some cache backend (esi, ssi or js)
``BlockBundle`` integrates the CacheBundle to handle block cache. The cache unit stored in the backend is a Response object from the ``HttpFoundation`` Component.
Why a `Response` object ? It is a simple element, which contains the data (the body) and some metadata (the headers).
As the block returns a `Response` object, it is possible to send it to the client, this use case can be quite useful for some cache backends (esi, ssi or js).
Cache Behavior
~~~~~~~~~~~~~~
The BlockBundle assumes that a block can be cached by default, so if a cache backend is configured, the block response
will be stored. The default ttl is 84600 seconds. Now, there are many ways to control this behavior:
The ``BlockBundle`` assumes that a block can be cached by default, so if a cache backend is configured, the block response will be stored.
The default `TTL` is `86400` seconds. Now, there are many ways to control this behavior:
* set a ``ttl`` setting inside the block, so if ``ttl`` = 0, then no cache will be available for the block and its parents
* set a ``use_cache`` setting to ``false`` or ``true``, if the variable is set to ``false`` then no cache will be avaible for the block and its parents
* no cache backend by default! by default there is no cache backend setup, you should focus on raw performance before adding cache layers
* set a ``TTL`` setting inside the block, so if ``ttl`` equals 0, then no cache will be available for the block and its parents,
* set a ``use_cache`` setting to ``false`` or ``true``, if the variable is set to ``false`` then no cache will be available for the block and its parents,
* no cache backend by default! By default, there is no cache backend setup, you should focus on raw performance before adding cache layers.
If you are extending the ``BaseBlockService`` you can use the method ``renderPrivateResponse`` to return a private Response.
If you are extending the ``BaseBlockService``, you can use the method ``renderPrivateResponse`` to return a private `Response`.
.. code-block:: php
@ -40,7 +44,7 @@ If you are extending the ``BaseBlockService`` you can use the method ``renderPri
));
}
or you can use the Response object:
or you can use the `Response` object:
.. code-block:: php
@ -63,26 +67,24 @@ or you can use the Response object:
Block TTL computation
~~~~~~~~~~~~~~~~~~~~~
The BlockBundle uses the TTL value from the Response object to compute the final TTL value. As block can have children, the
smallest ttl need to be used in parent block responses. This job is done by the ``BlockRenderer`` class, this service stores
a state with the last response and compares the ttl with the current response. The state is reset when the block does not have
any parent.
The ``BlockBundle`` uses the `TTL` value from the `Response` object to compute the final `TTL` value. As block can have children, the smallest `TTL` need to be used in parent block responses.
This job is done by the ``BlockRenderer`` class, this service stores a state with the last response and compares the TTL with the current response.
The state is reset when the block does not have any parent.
The cache mechanism will use the TTL to set a valid value when the response is stored into the cache backend.
The cache mechanism will use the `TTL` to set a valid value when the response is stored into the cache backend.
.. note::
If a ttl is set into a block container, the ttl value is not applyed to the final Response object send to the client.
This can be done by using a different mechanism
If a `TTL` is set into a block container, the `TTL` value is not applied to the final Response object sent to the client.
This can be done by using a different mechanism.
Final Response TTL computation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``BlockRendered`` stores a global states for the smallest ttl available, there is another service used to store the smallest
The ``BlockRendered`` stores a global state for the smallest ttl available, there is another service used to store the smallest
``ttl`` for the page: ``HttpCacheHandler``. Why two services ? This has been done to add an extra layer of control.
The ``HttpCacheHandler::updateMetadata`` is called by the templating helper when the response is retrieved, then an event
listener is registered to alter the final Response.
The ``HttpCacheHandler::updateMetadata`` is called by the templating helper when the response is retrieved, then an event listener is registered to alter the final Response.
The service can be configured using the ``http_cache_handler`` key.
@ -99,16 +101,15 @@ Cache Backends
* ``sonata.cache.mongo``: use mongodb to store cache element, this is a nice backend as you can remove some cache element by
only one value. (remove all block where profile.media.id == 3 is used.)
* ``sonata.cache.memcached``: use memcached as a backend, shared accross multiple hosts
* ``sonata.cache.apc``: use apc from PHP runtime, cannot be shared accross multiple hosts, and it is not suitable to store high volume of data
* ``sonata.cache.esi``: use a ESI compatible backend to store the cache, like Varnish
* ``sonata.cache.ssi``: use a SSI compatible backend to store the cache, like Apache or Nginx
* ``sonata.cache.memcached``: use memcached as a backend, shared across multiple hosts
* ``sonata.cache.apc``: use apc from PHP runtime, cannot be shared across multiple hosts, and it is not suitable to store high volume of data
* ``sonata.cache.esi``: use an ESI compatible backend to store the cache, like Varnish
* ``sonata.cache.ssi``: use an SSI compatible backend to store the cache, like Apache or Nginx
Cache configuration
~~~~~~~~~~~~~~~~~~~
The configuration is defined per block service, so if you want to use memcached for a block type ``sonata.page.block.container`` then
use the following configuration:
The configuration is defined per `block service`, so if you want to use `memcached` for a block type ``sonata.page.block.container`` then use the following configuration:
.. code-block:: yaml
@ -126,4 +127,3 @@ Please make sure the memcached backend is configured in the ``sonata_cache`` def
prefix: test # prefix to ensure there is no clash between instances
servers:
- {host: 127.0.0.1, port: 11211, weight: 0}

@ -1,12 +1,15 @@
.. index::
single: Events
single: Block
Events
======
Sometime, you might want to create an area where a block can be added depends on some external settings. A good example is
a Comment mechanism, you might want to create a CommentBundle to render a comment thread on different pages. The
comment area can used disqus or your own solution. As part of a full stack solution, you don't know which solution
is going to be used. However, you know where the comment area will be located.
Sometime, you might want to create an area where a block can be added, depending on some external settings. A good example is
a `Comment mechanism`. You might want to create a ``CommentBundle`` to render a `comment thread` on different pages. The comment area can used `Disqus <http://disqus.com>`_ or your own solution.
As part of a full stack solution, you don't know which solution is going to be used. However, you know where the comment area will be located.
The Event mechanism implemented in the ``SonataBlockBundle`` try to address this situation, and to provide a clean syntax:
The `Event mechanism` implemented in the ``SonataBlockBundle`` tries to address this situation, and to provide a clean syntax:
.. code-block:: jinja
@ -16,14 +19,14 @@ The Event mechanism implemented in the ``SonataBlockBundle`` try to address this
{{ sonata_block_render_event('blog.comment', { 'target': post }) }}
The twig helper will dispatch a ``BlockEvent`` object where services can add ``BlockInterface``. Once the event is processed
the helper will render the available blocks. If there is no block, then the helper will return an empty string.
The `Twig` helper will dispatch a ``BlockEvent`` object where services can add ``BlockInterface``. Once the event is processed, the helper will render the available blocks.
If there is no block, then the helper will return an empty string.
Implementation
~~~~~~~~~~~~~~
You can register a service to listen to the service ``blog.comment``. The actual name for the ``EventDispatcher``
must be prefixed by ``sonata.block.event``. So the current the name will be ``sonata.block.event.blog.comment``.
You can register a service to listen to the service ``blog.comment``. The actual name for the ``EventDispatcher`` must be prefixed by ``sonata.block.event``.
So, the current the name will be ``sonata.block.event.blog.comment``.
.. configuration-block::
@ -41,7 +44,7 @@ must be prefixed by ``sonata.block.event``. So the current the name will be ``so
tags:
- { name: kernel.event_listener, event: sonata.block.blog.comment, method: onBlock}
The event listener must return a ``BlockInterface`` so the rendering workflow will work properly.
The `event listener` must return a ``BlockInterface`` so the rendering workflow will work properly.
.. code-block:: jinja
@ -51,16 +54,6 @@ The event listener must return a ``BlockInterface`` so the rendering workflow wi
class Discus
{
protected $account;
/**
* @param string $account
*/
public function __construct($account)
{
$this->account = $account;
}
/**
* @param BlockEvent
*
@ -77,24 +70,23 @@ The event listener must return a ``BlockInterface`` so the rendering workflow wi
}
}
And that's it! Of course this example suppose that you have a ``BlockServiceInterface`` which can handle the type ``sonata.comment.block.discus``.
And that's it! Of course, this example supposes that you have a ``BlockServiceInterface``, which can handle the type ``sonata.comment.block.discus``.
Profiler Information
~~~~~~~~~~~~~~~~~~~~
If an event is available in the current page, a ``*`` will appear next to the ``blocks`` label in the profiler toolbar. In the
following schema, you have 3 events and 1 generated block.
If an event is available in the current page, a ``*`` will appear next to the ``blocks`` label in the profiler toolbar.
In the following schema, you have 3 events and 1 generated block:
.. figure:: ../images/block_profiler.png
:align: center
:alt: Block profiler with events
:width: 500
You can retrieve event's name in the block panel, the panel include the event's name and the different listeners availables and
You can retrieve event's name in the block panel. The panel includes the event's name and the different listeners available and
the generated blocks (if any).
.. figure:: ../images/block_profiler_event.png
:align: center
:alt: Block profiler with events
:width: 500
:width: 500

@ -1,28 +1,40 @@
.. index::
single: Configuration
single: Exception
single: Filter
single: Renderer
Exception strategy
==================
Any exception thrown by a block service is handled by an exception strategy manager that
determines how the exception should be handled. A default strategy can be defined for all
block types but a specific strategy can also be defined on a per block type basis.
Any exception thrown by a block service is handled by an exception strategy manager that determines how the exception should be handled.
A default strategy can be defined for all block types but a specific strategy can also be defined on a per block type basis.
The exception strategy uses an exception filter and an exception renderer.
The exception strategy uses an `exception filter` and an `exception renderer`.
Filters
-------
The role of an exception filter is to define which exceptions should be handled and which
should be ignored silently. There are currently 4 filters available:
The role of an `exception filter` is to define which exceptions should be handled and which should be ignored silently. There are currently 4 filters available:
* `Debug only`: only handle exceptions when in debug mode (default),
* `Ignore block exception`: only handle exceptions that don't implement ``BlockExceptionInterface``,
* `Keep all`: handle all exceptions,
* `Keep none`: ignore all exceptions.
.. note::
`Debug only` is the `default` filter.
- Debug only: only handle exceptions when in debug mode. (default)
- Ignore block exception: only handle exceptions that don't implement ``BlockExceptionInterface``
- Keep all: handle all exceptions
- Keep non: ignore all exceptions (use with care)
.. warning::
Use the last filter ``Keep none`` with care!
These filters may be modified or completed with others filters in the configuration file:
.. code-block:: yaml
#config.yml
# app/config/config.yml
sonata_block:
exception:
default:
@ -33,13 +45,12 @@ These filters may be modified or completed with others filters in the configurat
keep_all: sonata.block.exception.filter.keep_all
keep_none: sonata.block.exception.filter.keep_none
A default filter may be configured to apply, by default, to all block types. If you wish to
customize a filter on a particular block type, you may also add the following option in the
configuration file:
A default filter may be configured to be applied to all block types. If you wish to customize a filter on a particular block type, you may also add the following option in the configuration file:
.. code-block:: yaml
#config.yml
# app/config/config.yml
sonata_block:
blocks:
sonata.block.service.text:
@ -48,12 +59,11 @@ configuration file:
Renderers
---------
The role of an exception renderer is to define what to do with the exceptions that have passed
the filter. There are currently 3 renderers available:
The role of an `exception renderer` is to define what to do with the exceptions that have passed the filter. There are currently 3 kind of renderer available:
- inline: renders a twig template within the rendering workflow with minimal information regarding the exception.
- inline_debug: renders a twig template with the full debug exception information from symfony.
- throw: throws the exception to let the framework handle the exception.
* `inline`: renders a `Twig` template within the rendering workflow with minimal information regarding the exception,
* `inline_debug`: renders a twig template with the full debug exception information from Symfony,
* `throw`: throws the exception to let the framework handle the exception.
These filters may be modified or completed with other filters in the configuration file:
@ -70,13 +80,12 @@ These filters may be modified or completed with other filters in the configurati
throw: sonata.block.exception.renderer.throw
A default renderer may be configured to apply, by default, to all block types. If you wish to
customize a renderer on a particular block type, you may also add the following option in the
configuration file:
A `default renderer` may be configured to apply, by default, to all block types. If you wish to customize a renderer on a particular block type, you may also add the following option in the configuration file:
.. code-block:: yaml
#config.yml
# app/config/config.yml
sonata_block:
blocks:
sonata.block.service.text:

@ -1,15 +1,24 @@
.. index::
single: Installation
single: Configuration
Installation
============
To begin, add the dependent bundles to the vendor/bundles directory. Add the following lines to the file deps::
To begin, add the dependent bundles to the vendor/bundles directory. Add the following lines to the deps file:
.. code-block:: bash
php composer.phar require sonata-project/block-bundle
Now, add the bundle to the kernel
Now, add the bundle to the kernel:
.. code-block:: php
<?php
// app/AppKernel.php
public function registerbundles()
{
return array(
@ -21,21 +30,21 @@ Now, add the bundle to the kernel
);
}
Some featured provided by this Bundle require the SonataAdminBundle. Please add an explicit
required dependency to your projects composer.json to the SonataAdminBundle with the version
listed in the suggestion of this Bundle.
Some features provided by this Bundle require the ``SonataAdminBundle``.
Please add an explicit required dependency to your project's `composer.json` to
the ``SonataAdminBundle`` with the version listed in the suggestion of this Bundle.
Configuration
-------------
To use the ``BlockBundle``, add the following lines to your application configuration
file.
To use the ``BlockBundle``, add the following lines to your application configuration file:
.. code-block:: yaml
# app/config/config.yml
sonata_block:
default_contexts: [cms]
default_contexts: [sonata_page_bundle]
blocks:
sonata.admin.block.admin_list:
contexts: [admin]

@ -1,16 +1,19 @@
.. index::
single: Profiler
single: Debug
Profiler
========
``BlockBundle`` automatically adds the profiling of blocks in debug mode. It adds a new tab in the symfony web debug
toolbar which contains the number of blocks used on a page. It also provides a panel with the list of all rendered
blocks, their memory consumption and their rendering time.
``BlockBundle`` automatically adds the profiling of blocks in `debug` mode. It adds a new tab in the Symfony web debug toolbar which contains the number of blocks used on a page.
It also provides a panel with the list of all rendered blocks, their memory consumption and their rendering time.
If you want to disable the profiling or configure it, you may add one of the following options in the block
configuration file:
If you want to disable the profiling or configure it, you may add one of the following options in the block configuration file:
.. code-block:: yaml
# app/config/config.yml
sonata_block:
profiler:
enabled: %kernel.debug%

@ -1,7 +1,12 @@
.. index::
single: Twig
single: Helpers
Provided Blocks
===============
Some block services are already provided. You may use them or check out the code to get ideas on how to create your own.
You can also check this documentation: :doc:`your_first_block`.
EmptyBlockService
-----------------
@ -20,7 +25,7 @@ RssBlockService
This block displays an RSS feed.
When you add this block, specify a title and an RSS URL and the last messages from the RSS feed will be displayed in your block.
When you add this block, specify a title and an RSS URL. Then, the last messages from the RSS feed will be displayed in your block.
Base template is ``SonataBlockBundle:Block:block_core_rss.html.twig`` but you may of course override it.
@ -33,4 +38,4 @@ Upon configuration, you may set a KNP Menu name (as specified in `KnpMenuBundle
Set ``cache_policy`` to private if this menu is dedicated to be in a user part.
A specific menu template is provided as well to render Bootstrap3's side menu, you may use it by setting the ``menu_template`` option to ``SonataBlockBundle:Block:block_side_menu_template.html.twig`` (see the implementation in SonataUserBundle or Sonata's ecommerce suite).
A specific menu template is provided as well to render Bootstrap3's side menu, you may use it by setting the ``menu_template`` option to ``SonataBlockBundle:Block:block_side_menu_template.html.twig`` (see the implementation in SonataUserBundle or Sonata's e-commerce suite).

@ -1,13 +1,19 @@
.. index::
single: Twig
single: Helpers
single: Example
single: Usage
Twig Helpers
============
Render a block from its instance
Render a block from its instance:
.. code-block:: jinja
{{ sonata_block_render(block) }}
Render by providing the block's type and options
Render a block by providing the block's type and options:
.. code-block:: jinja
@ -16,7 +22,7 @@ Render by providing the block's type and options
'url': 'http://sonata-project.org/blog/archive.rss'
}) }}
Render by providing the block's cache options
Render a block by providing the block's cache options:
.. code-block:: jinja
@ -25,7 +31,7 @@ Render by providing the block's cache options
'extra_cache_key': extra_cache_key
}) }}
Render a block by calling an event
Render a block by calling an event:
.. code-block:: jinja
@ -33,4 +39,15 @@ Render a block by calling an event
'target': post
}) }}
review the events section for more information: :doc:`events`
.. note::
Review the `Events` section for more information: :doc:`events`
Render a block related to javascripts and stylesheets for the current page implies the helpers to be called at the end of the page:
.. code-block:: jinja
{{ sonata_block_include_stylesheets('screen', app.request.basePath) }}
{{ sonata_block_include_javascripts('screen', app.request.basePath) }}
The ``app.request.basePath`` must be provided if your application is stored in a sub-folder.

@ -1,22 +1,25 @@
.. index::
single: Block
single: Tutorial
single: RSS Block
Your first block
===============
================
This quick tutorial explains how to create a RSS reader block.
This quick tutorial explains how to create a `RSS reader` block.
A block service is just a service which must implement the ``BlockServiceInterface``
interface. There is only one instance of a block service, however there are many block
instances.
A `block service` is just a service which must implement the ``BlockServiceInterface`` interface. There is only one instance of a block service, however there are many block instances.
First namespaces
----------------
The ``BaseBlockService`` implements some basic methods defined by the interface.
The current Rss block will extend this base class. The others `use` statements are required
by the interface and remaining methods.
The current RSS block will extend this base class. The others `use` statements are required by the interface and remaining methods.
.. code-block:: php
<?php
namespace Sonata\BlockBundle\Block;
use Symfony\Component\HttpFoundation\Response;
@ -31,17 +34,17 @@ by the interface and remaining methods.
Default settings
----------------
A block service needs settings to work properly, so to ensure consistency, the service should
define a ``setDefaultSettings`` method. In the current tutorial, the default settings are:
A `block service` needs settings to work properly, so to ensure consistency, the service should define a ``setDefaultSettings`` method.
In the current tutorial, the default settings are:
- url : the feed url
- title : the block title
- template : the template to render the block
* `URL`: the feed url,
* `title`: the block title,
* `template`: the template to render the block.
.. code-block:: php
<?php
function setDefaultSettings(OptionsResolverInterface $resolver)
public function setDefaultSettings(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'url' => false,
@ -53,8 +56,7 @@ define a ``setDefaultSettings`` method. In the current tutorial, the default set
Form Edition
------------
The ``BlockBundle`` relies on the ``AdminBundle`` to manage form edition and keep
a good consistency.
The ``BlockBundle`` relies on the ``AdminBundle`` to manage form edition and keep a good consistency.
.. code-block:: php
@ -69,13 +71,12 @@ a good consistency.
));
}
The validation is done at runtime through a ``validateBlock`` method. You can call any
Symfony2 assertions, like :
The validation is done at runtime through a ``validateBlock`` method. You can call any Symfony2 assertions, like:
.. code-block:: php
<?php
function validateBlock(ErrorElement $errorElement, BlockInterface $block)
public function validateBlock(ErrorElement $errorElement, BlockInterface $block)
{
$errorElement
->with('settings.url')
@ -89,18 +90,17 @@ Symfony2 assertions, like :
->end();
}
The ``sonata_type_immutable_array`` type is a specific form type which allows to edit
an array.
The ``sonata_type_immutable_array`` type is a specific `form type` which allows to edit an array.
Execute
-------
The next step is the execute method, this method must return a ``Response`` object, this
object is used to render the block.
The next step is the `Execute` method. This method must return a ``Response`` object, which is used to render the block.
.. code-block:: php
<?php
public function execute(BlockContextInterface $blockContext, Response $response = null)
{
// merge settings
@ -139,8 +139,7 @@ object is used to render the block.
Template
--------
A block template is very simple, in the current tutorial, we are looping on feeds or if not
defined, a error message is displayed.
A block template is very simple, in the current tutorial. Indeed, we are looping on feeds or if not defined, a error message is displayed.
.. code-block:: jinja
@ -164,7 +163,7 @@ defined, a error message is displayed.
Service
-------
We are almost done! Now just declare the block as a service
We are almost done! Now, just declare the block as a service:
.. code-block:: xml
@ -174,11 +173,12 @@ We are almost done! Now just declare the block as a service
<argument type="service" id="templating" />
</service>
and add it to sonata configuration
and add it to Sonata configuration:
.. code-block:: yaml
#config.yml
# app/config/config.yml
sonata_block:
blocks:
sonata.block.service.rss:

@ -98,31 +98,37 @@ class BlockHelper extends Helper
}
/**
* @param $media screen|all ....
* @param string $media Unused, only kept to not break existing code
* @param string $basePath Base path to prepend to the stylesheet urls.
*
* @return array|string
*/
public function includeJavascripts($media)
public function includeJavascripts($media, $basePath = '')
{
$html = "";
foreach ($this->assets['js'] as $javascript) {
$html .= "\n" . sprintf('<script src="%s" type="text/javascript"></script>', $javascript);
$html .= "\n" . sprintf('<script src="%s%s" type="text/javascript"></script>', $basePath, $javascript);
}
return $html;
}
/**
* @param $media
* @param string $media The css media type to use: all|screen|...
* @param string $basePath Base path to prepend to the stylesheet urls.
*
* @return array|string
*/
public function includeStylesheets($media)
public function includeStylesheets($media, $basePath = '')
{
if(0 === count($this->assets['css'])) {
return "";
}
$html = sprintf("<style type='text/css' media='%s'>", $media);
foreach ($this->assets['css'] as $stylesheet) {
$html .= "\n" . sprintf('@import url(%s);', $stylesheet, $media);
$html .= "\n" . sprintf('@import url(%s%s);', $basePath, $stylesheet);
}
$html .= "\n</style>";
@ -134,6 +140,7 @@ class BlockHelper extends Helper
* Traverse the parent block and its children to retrieve the correct list css and javascript only for main block
*
* @param BlockContextInterface $blockContext
* @param array $stats
*/
protected function computeAssets(BlockContextInterface $blockContext, array &$stats = null)
{
@ -282,7 +289,7 @@ class BlockHelper extends Helper
} else if ($listener instanceof \Closure) {
$results[] = '{closure}()';
} else {
$results[] = 'Unkown type!';
$results[] = 'Unknown type!';
}
}
@ -397,7 +404,7 @@ class BlockHelper extends Helper
* @param BlockInterface $block
* @param array $stats
*
* @return \Sonata\Cache\CacheInterface;
* @return \Sonata\Cache\CacheAdapterInterface;
*/
protected function getCacheService(BlockInterface $block, array &$stats = null)
{

@ -25,11 +25,29 @@ class BlockLoaderChainTest extends \PHPUnit_Framework_TestCase
$loader->load('foo');
}
public function testLoader()
public function testLoaderWithSupportedLoader()
{
$block = $this->getMock('Sonata\BlockBundle\Model\BlockInterface');
$loader = $this->getMock('Sonata\BlockBundle\Block\BlockLoaderInterface');
$loader->expects($this->once())->method('support')->will($this->returnValue(true));
$loader->expects($this->once())->method('load');
$loader->expects($this->once())->method('load')->will($this->returnValue($block));
$loaderChain = new BlockLoaderChain(array($loader));
$this->assertTrue($loaderChain->support('foo'));
$this->assertEquals($block, $loaderChain->load('foo'));
}
/**
* @expectedException \Sonata\BlockBundle\Exception\BlockNotFoundException
*/
public function testLoaderWithUnSupportedLoader()
{
$loader = $this->getMock('Sonata\BlockBundle\Block\BlockLoaderInterface');
$loader->expects($this->once())->method('support')->will($this->returnValue(false));
$loader->expects($this->never())->method('load');
$loaderChain = new BlockLoaderChain(array($loader));

@ -67,4 +67,28 @@ class BlockServiceManagerTest extends \PHPUnit_Framework_TestCase
$manager->get($block);
}
public function testGetEmptyListFromInvalidContext()
{
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
$manager = new BlockServiceManager($container, true);
$service = $this->getMock('Sonata\BlockBundle\Block\BlockServiceInterface');
$manager->add('foo.bar', $service);
$this->assertEmpty($manager->getServicesByContext('fake'));
}
public function testGetListFromValidContext()
{
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
$manager = new BlockServiceManager($container, true);
$service = $this->getMock('Sonata\BlockBundle\Block\BlockServiceInterface');
$manager->add('foo.bar', $service, array('fake'));
$this->assertNotEmpty($manager->getServicesByContext('fake'));
}
}

@ -44,4 +44,13 @@ class HttpCacheHandlerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(42, $response->getTtl());
}
public function testResponseTtlNotAlteredIfNoRenderedBlock()
{
$handler = new HttpCacheHandler();
$handler->alterResponse($response = Response::create()->setTtl(84));
$this->assertEquals(84, $response->getTtl());
}
}

@ -18,10 +18,16 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
{
public function testOptions()
{
$defaultTemplates = array(
"SonataPageBundle:Block:block_container.html.twig" => "SonataPageBundle template",
"SonataSeoBundle:Block:block_social_container.html.twig" => "SonataSeoBundle (to contain social buttons)"
);
$processor = new Processor();
$config = $processor->processConfiguration(new Configuration(), array(array(
'default_contexts' => array('cms')
$config = $processor->processConfiguration(new Configuration($defaultTemplates), array(array(
'default_contexts' => array('cms'),
'blocks' => array('my.block.type' => array())
)));
@ -48,7 +54,22 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
'block_base' => NULL,
'block_container' => NULL,
),
'blocks' => array(),
'container' => array(
'types' => array(
0 => 'sonata.block.service.container',
1 => 'sonata.page.block.container',
2 => 'cmf.block.container',
3 => 'cmf.block.slideshow',
),
'templates' => $defaultTemplates,
),
'blocks' => array(
'my.block.type' => array(
'contexts' => array('cms'),
'cache' => 'sonata.cache.noop',
'settings' => array ()
)
),
'menus' => array(),
'blocks_by_class' => array(),
'exception' => array(
@ -72,4 +93,23 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, $config);
}
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Invalid configuration for path "sonata_block": You cannot have different config options for sonata_block.profiler.container_types and sonata_block.container.types; the first one is deprecated, in case of doubt use the latter
*/
public function testOptionsDuplicated()
{
$defaultTemplates = array(
"SonataPageBundle:Block:block_container.html.twig" => "SonataPageBundle template",
"SonataSeoBundle:Block:block_social_container.html.twig" => "SonataSeoBundle (to contain social buttons)"
);
$processor = new Processor();
$processor->processConfiguration(new Configuration($defaultTemplates), array(array(
'default_contexts' => array('cms'),
'profiler' => array('container_types' => array('test_type')),
'container' => array('types' => array('test_type2'), 'templates' => array()),
)));
}
}

@ -13,8 +13,8 @@ namespace Sonata\BlockBundle\Tests;
use Sonata\BlockBundle\Event\BlockEvent;
use Sonata\BlockBundle\Event\TextBlockListener;
use Sonata\AdminBundle\Admin\AdminInterface;
class TextBlockListenerTest extends \PHPUnit_Framework_TestCase
{

@ -27,13 +27,11 @@ class ServiceListTypeTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('choice', $type->getParent());
}
/**
* @expectedException Symfony\Component\OptionsResolver\Exception\MissingOptionsException
*/
public function testOptionsWithInvalidContext()
{
if (Kernel::MINOR_VERSION < 3) {
$this->setExpectedException('RuntimeException');
} else {
$this->setExpectedException('\Symfony\Component\Form\Exception\InvalidArgumentException');
}
$blockServiceManager = $this->getMock('Sonata\BlockBundle\Block\BlockServiceManagerInterface');
@ -52,7 +50,12 @@ class ServiceListTypeTest extends \PHPUnit_Framework_TestCase
$blockService->expects($this->once())->method('getName')->will($this->returnValue('value'));
$blockServiceManager = $this->getMock('Sonata\BlockBundle\Block\BlockServiceManagerInterface');
$blockServiceManager->expects($this->once())->method('getService')->will($this->returnValue($blockService));
$blockServiceManager
->expects($this->once())
->method('getServicesByContext')
->with($this->equalTo('cms'))
->will($this->returnValue(array('my.service.code' => $blockService)));
$type = new ServiceListType($blockServiceManager, array(
'cms' => array('my.service.code')
));

@ -37,8 +37,12 @@ class BlockHelperTest extends \PHPUnit_Framework_TestCase
public function testRenderEventWithListeners()
{
$blockService = $this->getMock('Sonata\BlockBundle\Block\BlockServiceInterface');
$blockService->expects($this->once())->method('getJavascripts')->will($this->returnValue(array()));
$blockService->expects($this->once())->method('getStylesheets')->will($this->returnValue(array()));
$blockService->expects($this->once())->method('getJavascripts')->will($this->returnValue(array(
'/js/base.js'
)));
$blockService->expects($this->once())->method('getStylesheets')->will($this->returnValue(array(
'/css/base.css'
)));
$blockServiceManager = $this->getMock('Sonata\BlockBundle\Block\BlockServiceManagerInterface');
$blockServiceManager->expects($this->any())->method('get')->will($this->returnValue($blockService));
@ -61,7 +65,6 @@ class BlockHelperTest extends \PHPUnit_Framework_TestCase
'use_cache' => false
));
$block->setType('test');
$event->addBlock($block);
return $event;
@ -70,5 +73,21 @@ class BlockHelperTest extends \PHPUnit_Framework_TestCase
$helper = new BlockHelper($blockServiceManager, array(), $blockRenderer, $blockContextManager, $eventDispatcher);
$this->assertEquals('<span>test</span>', $helper->renderEvent('my.event'));
$this->assertEquals(trim($helper->includeJavascripts('screen', '/application')), '<script src="/application/js/base.js" type="text/javascript"></script>');
$this->assertEquals(trim($helper->includeJavascripts('screen', '')), '<script src="/js/base.js" type="text/javascript"></script>');
$this->assertEquals($helper->includeStylesheets('screen', '/application'), <<<EXPECTED
<style type='text/css' media='screen'>
@import url(/application/css/base.css);
</style>
EXPECTED
);
$this->assertEquals($helper->includeStylesheets('screen', ''), <<<EXPECTED
<style type='text/css' media='screen'>
@import url(/css/base.css);
</style>
EXPECTED
);
}
}

@ -16,7 +16,8 @@
"psr/log": "~1.0",
"doctrine/orm": "~2.2",
"predis/predis": "~0.8,<1.0",
"doctrine/phpcr-odm": "~1.0"
"doctrine/phpcr-odm": "~1.0",
"jackalope/jackalope-doctrine-dbal": "~1.0"
},
"suggest": {
"doctrine/orm": "ORM support",

@ -40,7 +40,7 @@ class MongoCache extends BaseCacheHandler
*/
public function flushAll()
{
return $this->getCollection()->remove(array());
return $this->flush(array());
}
/**
@ -48,7 +48,11 @@ class MongoCache extends BaseCacheHandler
*/
public function flush(array $keys = array())
{
return $this->getCollection()->remove($keys);
$result = $this->getCollection()->remove($keys, array(
'w' => 1
));
return $result['ok'] == 1 && $result['err'] === null;
}
/**
@ -67,7 +71,9 @@ class MongoCache extends BaseCacheHandler
private function getCollection()
{
if (!$this->collection) {
$mongo = new \Mongo(sprintf('mongodb://%s', implode(',', $this->servers)));
$class = self::getMongoClass();
$mongo = new $class(sprintf('mongodb://%s', implode(',', $this->servers)));
$this->collection = $mongo
->selectDB($this->databaseName)
@ -77,6 +83,20 @@ class MongoCache extends BaseCacheHandler
return $this->collection;
}
/**
* Returns the valid Mongo class client for the current php driver
*
* @return string
*/
public static function getMongoClass()
{
if (class_exists('\MongoClient')) {
return '\MongoClient';
}
return '\Mongo';
}
/**
* {@inheritdoc}
*/

@ -10,8 +10,8 @@
namespace Sonata\Cache\Adapter\Cache;
use Sonata\Cache\CacheAdapterInterface;
use Sonata\Cache\CacheElement;
use Sonata\Cache\Exception\UnsupportedException;
class NoopCache extends BaseCacheHandler
{
@ -52,7 +52,7 @@ class NoopCache extends BaseCacheHandler
*/
public function get(array $keys)
{
throw new \RunTimeException('The NoopCache::get() cannot called');
throw new UnsupportedException('The NoopCache::get() cannot called');
}
/**

@ -48,7 +48,14 @@ class PRedisCache extends BaseCacheHandler
*/
public function flush(array $keys = array())
{
return $this->getClient()->del($this->computeCacheKeys($keys));
$this->getClient()->del($this->computeCacheKeys($keys));
// http://redis.io/commands/del
// it is not possible to know is the command succeed as the del command returns
// the number of row deleted.
// we can flush an non existant row
return true;
}
/**

@ -10,6 +10,7 @@
namespace Sonata\Cache\Adapter\Counter;
use Sonata\Cache\Adapter\Cache\MongoCache;
use Sonata\Cache\Counter;
class MongoCounter extends BaseCounter
@ -40,7 +41,9 @@ class MongoCounter extends BaseCounter
private function getCollection()
{
if (!$this->collection) {
$mongo = new \Mongo(sprintf('mongodb://%s', implode(',', $this->servers)));
$class = MongoCache::getMongoClass();
$mongo = new $class(sprintf('mongodb://%s', implode(',', $this->servers)));
$this->collection = $mongo
->selectDB($this->databaseName)

@ -32,7 +32,8 @@ abstract class BaseTest extends \PHPUnit_Framework_TestCase
$cache->set(array('id' => 42), 'data');
$this->assertTrue($cache->has(array('id' => 42)));
$cache->flush(array('id' => 42));
$res = $cache->flush(array('id' => 42));
$this->assertTrue(true === $res); // make sure it's really boolean TRUE
$this->assertFalse($cache->has(array('id' => 42)));
$cacheElement = $cache->get(array('id' => 7));

@ -17,7 +17,9 @@ class MongoCacheTest extends BaseTest
{
public function setUp()
{
if (!class_exists('\Mongo', true)) {
$class = MongoCache::getMongoClass();
if (!class_exists($class, true)) {
$this->markTestSkipped('Mongo is not installed');
}
@ -34,7 +36,7 @@ class MongoCacheTest extends BaseTest
$this->markTestSkipped('MongoDB is not running');
}
$mongo = new \Mongo('mongodb://127.0.0.1:27017');
$mongo = new $class('mongodb://127.0.0.1:27017');
$mongo
->selectDB('sonata_counter_test')

@ -11,6 +11,7 @@
namespace Sonata\Cache\Tests\Adapter\Cache;
use Sonata\Cache\Adapter\Cache\MongoCache;
use Sonata\Cache\Adapter\Counter\MongoCounter;
use Sonata\Cache\Counter;
@ -18,7 +19,9 @@ class MongoCounterTest extends \PHPUnit_Framework_TestCase
{
public function setUp()
{
if (!class_exists('\Mongo', true)) {
$class = MongoCache::getMongoClass();
if (!class_exists($class, true)) {
$this->markTestSkipped('Mongo is not installed');
}
@ -35,7 +38,7 @@ class MongoCounterTest extends \PHPUnit_Framework_TestCase
$this->markTestSkipped('MongoDB is not running');
}
$mongo = new \Mongo('mongodb://127.0.0.1:27017');
$mongo = new $class('mongodb://127.0.0.1:27017');
$mongo
->selectDB('sonata_counter_test')

@ -1,9 +1,9 @@
.idea
.DS_Store
build
/build
phpunit.xml
Resources/doc/_build/*
nbproject
coverage
composer.lock
vendor
/vendor

@ -15,8 +15,9 @@ env:
before_script:
- composer require symfony/symfony:${SYMFONY_VERSION} --no-update
- composer install --dev --prefer-source
- sudo pip install -r Resources/doc/requirements.txt
script: phpunit
script: make test
notifications:
webhooks: http://sonata-project.org/bundles/core/master/travis

@ -37,6 +37,7 @@ class SonataCoreExtension extends Extension
$config = $processor->processConfiguration($configuration, $configs);
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('date.xml');
$loader->load('flash.xml');
$loader->load('form_types.xml');
$loader->load('twig.xml');
@ -81,7 +82,7 @@ class SonataCoreExtension extends Extension
'warning' => array('domain' => 'SonataCoreBundle'),
'sonata_flash_info' => array('domain' => 'SonataAdminBundle'),
)),
'error' => array('types' => array(
'danger' => array('types' => array(
'error' => array('domain' => 'SonataCoreBundle'),
'sonata_flash_error' => array('domain' => 'SonataAdminBundle'),
'sonata_user_error' => array('domain' => 'SonataUserBundle'),

@ -34,8 +34,8 @@ class DateRangeType extends AbstractType
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('start', 'date', array_merge(array('required' => false), $options['field_options']));
$builder->add('end', 'date', array_merge(array('required' => false), $options['field_options']));
$builder->add('start', $options['field_type'], array_merge(array('required' => false), $options['field_options']));
$builder->add('end', $options['field_type'], array_merge(array('required' => false), $options['field_options']));
}
/**
@ -52,7 +52,8 @@ class DateRangeType extends AbstractType
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'field_options' => array()
'field_options' => array(),
'field_type' => 'date'
));
}
}

@ -34,8 +34,8 @@ class DateTimeRangeType extends AbstractType
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('start', 'datetime', array_merge(array('required' => false), $options['field_options']));
$builder->add('end', 'datetime', array_merge(array('required' => false), $options['field_options']));
$builder->add('start', $options['field_type'], array_merge(array('required' => false), $options['field_options']));
$builder->add('end', $options['field_type'], array_merge(array('required' => false), $options['field_options']));
}
/**
@ -52,7 +52,8 @@ class DateTimeRangeType extends AbstractType
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'field_options' => array()
'field_options' => array(),
'field_type' => 'datetime',
));
}
}

@ -11,6 +11,8 @@
namespace Sonata\CoreBundle\Model;
use Doctrine\ODM\MongoDB\DocumentManager;
/**
* Class DocumentBaseManager
*
@ -28,6 +30,9 @@ abstract class BaseDocumentManager extends BaseManager
return $this->getObjectManager()->getConnection();
}
/**
* @return DocumentManager
*/
public function getDocumentManager()
{
return $this->getObjectManager();

@ -49,7 +49,15 @@ abstract class BaseManager implements ManagerInterface
*/
public function getObjectManager()
{
return $this->registry->getManagerForClass($this->class);
$manager = $this->registry->getManagerForClass($this->class);
if (!$manager) {
throw new \RuntimeException(sprintf("Unable to find the mapping information for the class %s."
." Please check the 'auto_mapping' option (http://symfony.com/doc/current/reference/configuration/doctrine.html#configuration-overview)"
." or add the bundle to the 'mappings' section in the doctrine configuration.", $this->class));
}
return $manager;
}
/**
@ -84,6 +92,14 @@ abstract class BaseManager implements ManagerInterface
return $this->getRepository()->findOneBy($criteria, $orderBy);
}
/**
* {@inheritdoc}
*/
public function find($id)
{
return $this->getRepository()->find($id);
}
/**
* {@inheritdoc}
*/
@ -139,16 +155,16 @@ abstract class BaseManager implements ManagerInterface
}
/**
* @param $entity
* @param $object
*
* @throws \InvalidArgumentException
*/
protected function checkObject($entity)
protected function checkObject($object)
{
if (!$entity instanceof $this->class) {
if (!$object instanceof $this->class) {
throw new \InvalidArgumentException(sprintf(
'Entity must be instance of %s, %s given',
$this->class, is_object($entity)? get_class($entity) : gettype($entity)
'Object must be instance of %s, %s given',
$this->class, is_object($object)? get_class($object) : gettype($object)
));
}
}

@ -39,6 +39,9 @@ abstract class BasePHPCRManager extends BaseManager
throw new \LogicException('PHPCR does not use a reference name for a list of data.');
}
/**
* @return \Doctrine\Common\Persistence\ObjectManager
*/
public function getDocumentManager()
{
return $this->getObjectManager();

@ -56,6 +56,15 @@ interface ManagerInterface
*/
public function findOneBy(array $criteria, array $orderBy = null);
/**
* Finds an entity by its primary key / identifier.
*
* @param mixed $id The identifier
*
* @return object
*/
public function find($id);
/**
* Create an empty Entity instance.
*

@ -34,6 +34,18 @@
<argument type="service" id="translator" />
</service>
<service id="sonata.core.form.type.date_picker" class="Sonata\CoreBundle\Form\Type\DatePickerType">
<tag name="form.type" alias="sonata_type_date_picker" />
<argument type="service" id="sonata.core.date.moment_format_converter" />
</service>
<service id="sonata.core.form.type.datetime_picker" class="Sonata\CoreBundle\Form\Type\DateTimePickerType">
<tag name="form.type" alias="sonata_type_datetime_picker" />
<argument type="service" id="sonata.core.date.moment_format_converter" />
</service>
<service id="sonata.core.form.type.equal" class="Sonata\CoreBundle\Form\Type\EqualType">
<tag name="form.type" alias="sonata_type_equal" />
<argument type="service" id="translator" />

@ -1,7 +1,7 @@
Core Bundle
===============
===========
The ``SonataCoreBundle`` provided defaults elements required by the different Sonata's Bundles
The ``SonataCoreBundle`` provided defaults elements required by the different Sonata's Bundles.
Reference Guide
@ -17,3 +17,5 @@ Reference Guide
reference/flash_messages
reference/twig_helpers
reference/doctrine_base_manager
reference/serialization
reference/request_body_param_converter

@ -1,11 +1,15 @@
.. index::
single: Doctrine
single: Managers
Doctrine base entity manager
============================
The bundle comes with an abstract class for your entities managers ``Sonata\CoreBundle\Entity\DoctrineBaseManager``.
The bundle comes with an abstract class for your entities and documents managers ``Sonata\CoreBundle\Model\BaseEntityManager``,``Sonata\CoreBundle\Model\BaseDocumentManager`` and ``Sonata\CoreBundle\Model\BasePHPCRManager``.
Use it in your managers
-----------------------
You just have to extends ``Sonata\CoreBundle\Entity\DoctrineBaseManager` in your managers :
You just have to extend ``Sonata\CoreBundle\Model\BaseEntityManager``, ``Sonata\CoreBundle\Model\BaseDocumentManager`` or ``Sonata\CoreBundle\Model\BasePHPCRManager`` in your managers, for instance:
.. code-block:: php
@ -19,3 +23,4 @@ You just have to extends ``Sonata\CoreBundle\Entity\DoctrineBaseManager` in your
{
}

@ -1,3 +1,6 @@
.. index::
double: Flash Message; Definition
Flash Messages
==============
@ -40,17 +43,17 @@ To use this feature in your PHP classes/controllers, you can use for example:
<?php
$this->get('sonata.core.flashmessage.manager')
$flashManager = $this->get('sonata.core.flashmessage.manager');
$messages = $flashManager->get('success');
To use this feature in your templates, simply include the following template (with an optional domain parameter):
.. code-block:: twig
.. code-block:: jinja
{% include 'SonataCoreBundle:FlashMessage:render.html.twig' %}
Please note that if necessary, you can also specify a translation domain to override configuration here:
.. code-block:: twig
.. code-block:: jinja
{% include 'SonataCoreBundle:FlashMessage:render.html.twig' with {domain: 'MyCustomBundle'} %}
{% include 'SonataCoreBundle:FlashMessage:render.html.twig' with {domain: 'MyCustomBundle'} %}

@ -1,23 +1,52 @@
.. index::
double: Form Type; Definition
Form Types
==========
The bundle comes with some handy form types.
DoctrineORMSerializationType
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This form type reads ``JMSSerializer`` serialization class metadata and uses ``Doctrine`` ORM entity metadata to generate form fields and correct types.
All you have to do is to define a form type service for each entity for which you want to use a form type, like this:
.. code-block:: xml
<service id="my.custom.form.type.post" class="Sonata\CoreBundle\Form\Type\DoctrineORMSerializationType">
<tag name="form.type" alias="my_custom_form_type_comment" />
<argument type="service" id="jms_serializer.metadata_factory" />
<argument type="service" id="doctrine" />
<argument>my_custom_form_type_comment</argument>
<argument>My\CustomBundle\Entity\Comment</argument>
<argument>a_serialization_group</argument>
</service>
The service definition should contain the following arguments:
* The JMSSerializer metadata factory,
* The Doctrine ORM entity manager,
* The form type name,
* The entity class name for which you want to build form,
* The serialization group you want serialization fields have.
sonata_type_immutable_array
^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``Immutable Array`` allows you to edit an array property by defining a type
per key.
The ``Immutable Array`` allows you to edit an array property by defining a type per key.
The type has a ``keys`` parameter which contains the definition for each key.
A definition is an array with 3 options:
* key name
* type: a type name or a ``FormType`` instance
* key name,
* type: a type name or a ``FormType`` instance,
* related type parameters: please refer to the related form documentation.
Let's say a ``Page`` have options property with some fixed key-value pairs, each
value has a different type: integer, url, or string for instance.
Let's say a ``Page`` has options property with some fixed key-value pairs.
Each value has a different type: `integer`, `url`, or `string` for instance.
.. code-block:: php
@ -56,25 +85,24 @@ Now, the property can be edited by setting a type for each type:
sonata_type_boolean
^^^^^^^^^^^^^^^^^^^
The ``boolean`` type is a specialized ``ChoiceType``, where the list of choices is
locked to *no* and *no*.
The ``boolean`` type is a specialized ``ChoiceType``, where the list of choices is locked to *no* and *no*.
Note that for backward compatibility reasons, it will set your value to *1* for *yes* and to *2* for *no*.
If you want to map to a boolean value, just set the option ``transform`` to true. For instance, you need
to do so when mapping to a doctrine boolean.
If you want to map to a boolean value, just set the option ``transform`` to true. For instance, you need to do so when mapping to a doctrine boolean.
sonata_type_translatable_choice
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Deprecated**: use ``ChoiceType`` with the ``translation_domain`` option instead.
.. warning::
``sonata_type_translatable_choice`` is deprecated, use ``ChoiceType`` with the ``translation_domain`` option instead!
The translatable type is a specialized ``ChoiceType`` where the choices values
are translated with the Symfony Translator component.
The translatable type is a specialized ``ChoiceType`` where the choices values are translated with the Symfony Translator component.
The type has one extra parameter:
* ``catalogue``: the catalogue name to translate the value
* ``catalogue``: the catalogue name to translate the value.
.. code-block:: php
@ -103,15 +131,16 @@ The type has one extra parameter:
'catalogue' => 'SonataOrderBundle'
))
.. note::
For more information, you can check the official `ChoiceType documentation <http://symfony.com/doc/current/reference/forms/types/choice.html>`_.
StatusType
^^^^^^^^^^
The ``StatusType`` is not available as a service. However, you can use it to declare your own type to render a choice of
status.
The ``StatusType`` is not available as a service. However, you can use it to declare your own type to render a choice of status.
Let's say, you have a ``Delivery::getStatusList`` method which return a list of status, now you want to create a form type
to expose those values.
Let's say, you have a ``Delivery::getStatusList`` method which returns a list of status. Now, you want to create a form type to expose those values.
.. code-block:: php
@ -153,3 +182,54 @@ And the type can now be used:
<?php
$form->add('deliveryStatus', 'sonata_order_status')
sonata_type_date_picker and sonata_type_datetime_picker
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Those types integrate `Eonasdan's Bootstrap datetimepicker <https://github.com/Eonasdan/bootstrap-datetimepicke>`_ into a Symfony2 form. They both are available as services, and inherit from ``date`` and ``datetime`` default form types.
.. note::
These form types require you to have bootstrap and jquery assets available in your project.
They will allow you to have a JS date picker onto your form fields as follows:
.. image:: ../images/datepicker.png
In order to use them, you'll need to perform a bit of setup:
.. code-block:: yaml
# app/config.yml
twig:
# ...
form:
resources:
# ...
- 'SonataCoreBundle:Form:datepicker.html.twig'
In your layout, you'll need to add the assets dependencies (feel free to adapt this to your needs, for instance to use with assetic):
.. code-block:: html
<head>
<!-- ... -->
<script type="text/javascript" src="path_to_jquery.min.js"></script>
<script type="text/javascript" src="/bundles/sonatacore/public/vendor/moment/min/moment.min.js"></script>
<script type="text/javascript" src="path_to_bootstrap.min.js"></script>
<script type="text/javascript" src="/bundles/sonatacore/public/vendor/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js"></script>
<link rel="stylesheet" href="path_to_bootstrap.min.css" />
<link rel="stylesheet" href="/bundles/sonatacore/public/vendor/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css" />
</head>
Finally, in your form, you may use the form type as follows:
.. code-block:: php
<?php
// ...
$builder
->add('publicationDateStart', 'sonata_type_datetime_picker') // Or sonata_type_date_picker if you don't need the time
// ...
;

@ -1,11 +1,15 @@
.. index::
single: Installation
Installation
============
* Add ``SonataCoreBundle`` to your ``vendor/bundles`` directory with the deps file::
* Add ``SonataCoreBundle`` to your ``vendor/bundles`` directory with the `deps` file:
.. code-block:: json
//composer.json
// composer.json
"require": {
//...
"sonata-project/core-bundle": "~2.2@dev",
@ -13,13 +17,14 @@ Installation
}
* Add ``SonataCoreBundle`` to your application kernel::
* Add ``SonataCoreBundle`` to your application kernel:
.. code-block:: php
<?php
// app/AppKernel.php
public function registerBundles()
{
return array(
@ -29,13 +34,13 @@ Installation
);
}
* Create a configuration file ``sonata_core.yml`` with this content::
* Create a configuration file ``sonata_core.yml`` with this content:
.. code-block:: yaml
sonata_core: ~
* Update the ``config.yml`` with the new resource to import::
* Update the ``config.yml`` with the new resource to import:
.. code-block:: yaml

@ -1,7 +1,10 @@
.. index::
double: Twig Status Helpers; Definition
Twig status helper
==================
The bundle comes with a Twig helper allowing you to generate CSS class names depending on an entity field.
The bundle comes with a `Twig` helper allowing you to generate CSS class names, depending on an entity field.
Define a service
----------------

@ -1,12 +1,25 @@
.. index::
double: Twig Helpers; Definition
Twig Helpers
============
sonata_slugify
--------------
Create a slug from a string
Create a slug from a string:
.. code-block:: twig
.. code-block:: jinja
{{ "my string"|sonata_slugify }} => my-string
sonata_flashmessages_get and sonata_flashmessages_types
-------------------------------------------------------
See :doc:`flash_messages` for more information.
sonata_urlsafeid
----------------
Gets the identifiers of the object as a string that is safe to use in an url.

@ -18,6 +18,14 @@
<source>sonata_core_template_box_file_found_in</source>
<target>This file can be found in</target>
</trans-unit>
<trans-unit id="label_type_equals">
<source>label_type_equals</source>
<target>is equal to</target>
</trans-unit>
<trans-unit id="label_type_not_equals">
<source>label_type_not_equals</source>
<target>is not equal to</target>
</trans-unit>
</body>
</file>
</xliff>

@ -11,8 +11,9 @@ file that was distributed with this source code.
{% for type in sonata_flashmessages_types() %}
{% set domain = domain is defined ? domain : null %}
{% for message in sonata_flashmessages_get(type, domain) %}
<div class="alert alert-{{ type|sonata_status_class }}">
<div class="alert alert-{{ type|sonata_status_class }} alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
{{ message|raw }}
</div>
{% endfor %}
{% endfor %}
{% endfor %}

@ -29,6 +29,6 @@ class DateRangeTypeTest extends TypeTestCase
$options = $resolver->resolve();
$this->assertEquals(array('field_options' => array()), $options);
$this->assertEquals(array('field_options' => array(), 'field_type' => 'date'), $options);
}
}

@ -35,19 +35,34 @@ class BaseEntityManagerTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage The property exception does not exists
*/
public function testException()
{
$this->getManager()->exception;
}
public function testGetEntityManager()
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Unable to find the mapping information for the class classname. Please check the 'auto_mapping' option (http://symfony.com/doc/current/reference/configuration/doctrine.html#configuration-overview) or add the bundle to the 'mappings' section in the doctrine configuration
*/
public function testExceptionOnNonMappedEntity()
{
$registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry');
$registry->expects($this->once())->method('getManagerForClass');
$registry->expects($this->once())->method('getManagerForClass')->will($this->returnValue(null));
$manager = new EntityManager('classname', $registry);
$manager->getObjectManager();
}
public function testGetEntityManager()
{
$objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager');
$registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry');
$registry->expects($this->once())->method('getManagerForClass')->will($this->returnValue($objectManager));
$manager = new EntityManager('classname', $registry);
$manager->em;
}

@ -23,9 +23,13 @@
"twig/twig": "~1.12"
},
"require-dev": {
"sonata-project/exporter": "~1.3",
"doctrine/orm": "~2.4",
"doctrine/phpcr-odm": "~1.0"
"doctrine/phpcr-odm": "~1.0",
"jackalope/jackalope-doctrine-dbal": "~1.0",
"friendsofsymfony/rest-bundle": "~1.1",
"jms/serializer-bundle": "~0.11",
"sensio/framework-extra-bundle": "~2.2",
"sonata-project/exporter": "~1.3"
},
"autoload": {
"psr-4": { "Sonata\\CoreBundle\\": "" }

Loading…
Cancel
Save