@ -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.
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
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.
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
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
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
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:
``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:
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).
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
@ -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
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
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:
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):
@ -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