Skip to content
This repository was archived by the owner on Sep 16, 2021. It is now read-only.

Commit ff67948

Browse files
committed
Merge pull request #380 from symfony-cmf/cleanup-block-own
cleanup the custom block chapter
2 parents c9b8371 + 55f7e71 commit ff67948

File tree

1 file changed

+116
-53
lines changed

1 file changed

+116
-53
lines changed

bundles/block/create_your_own_blocks.rst

Lines changed: 116 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -17,143 +17,206 @@ a template. So instead you decide to create your own block, the ``RssBlock``.
1717

1818
.. tip::
1919

20-
In this example, we create an ``RssBlock``. This particular block type
21-
already exists in the Symfony2 CMF BlockBundle, so you can also look at
22-
the end result if you want.
20+
In this example, you create an ``RssBlock``. An RSS block already exists in
21+
the CmfBlockBundle, but this one is more powerful as it allows to define a
22+
specific feed URL per block instance.
2323

2424
Create a block document
2525
-----------------------
2626

27-
The first thing you need is an document that holds the data. It is
28-
recommended to extend ``Symfony\Cmf\Bundle\BlockBundle\Doctrine\Phpcr\AbstractBlock``.
29-
You are of course free to do your own document, but need to at least implement
27+
The first thing you need is an document that contains the options and indicates
28+
the location where the RSS feed should be shown. The easiest way is to extend
29+
``Symfony\Cmf\Bundle\BlockBundle\Doctrine\Phpcr\AbstractBlock``, but you are
30+
free to do create your own document. At least, you have to implement
3031
``Sonata\BlockBundle\Model\BlockInterface``. In your document, you
3132
need to define the ``getType`` method which returns the type name of your block,
32-
for instance ``acme_main.block.rss``.
33-
34-
.. code-block:: php
33+
for instance ``acme_main.block.rss``::
3534

3635
// src/Acme/MainBundle/Document/RssBlock.php
3736
namespace Acme\MainBundle\Document;
3837

39-
use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCRODM;
38+
use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCR;
4039
use Symfony\Cmf\Bundle\BlockBundle\Doctrine\Phpcr\AbstractBlock;
4140

4241
/**
43-
* Rss Block
44-
*
45-
* @PHPCRODM\Document(referenceable=true)
42+
* @PHPCR\Document(referenceable=true)
4643
*/
4744
class RssBlock extends AbstractBlock
4845
{
46+
/**
47+
* @PHPCR\String(nullable=true)
48+
*/
49+
private $feedUrl;
50+
51+
/**
52+
* @PHPCR\String()
53+
*/
54+
private $title;
55+
4956
public function getType()
5057
{
5158
return 'acme_main.block.rss';
5259
}
60+
61+
public function getOptions()
62+
{
63+
$options = array(
64+
'title' => $this->title,
65+
);
66+
if ($this->feedUrl) {
67+
$options['url'] = $this->feedUrl;
68+
}
69+
70+
return $options;
71+
}
72+
73+
// Getters and setters for title and feedUrl...
5374
}
5475

5576
Create a Block Service
5677
----------------------
5778

58-
You could choose to use a an already existing block service because the
59-
configuration and logic already satisfy your needs. For our RSS block we
60-
create a service that knows how to handle ``RssBlocks``:
79+
If the configuration and logic already satisfies your needs, you should use an
80+
already existing block service. For your RSS block, you need a custom service
81+
that knows how to fetch the feed data of an ``RssBlock``::
6182

62-
* The method ``setDefaultSettings`` configures a template, title, url and the
63-
maximum amount of items::
64-
65-
6683
// src/Acme/MainBundle/Block/RssBlockService.php
67-
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
84+
namespace Acme\MainBundle\Block;
85+
6886
use Symfony\Component\HttpFoundation\Response;
69-
use Sonata\BlockBundle\Block\BaseBlockService;
87+
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
88+
89+
use Sonata\AdminBundle\Form\FormMapper;
90+
use Sonata\AdminBundle\Validator\ErrorElement;
91+
92+
use Sonata\BlockBundle\Model\BlockInterface;
7093
use Sonata\BlockBundle\Block\BlockContextInterface;
94+
use Sonata\BlockBundle\Block\BaseBlockService;
7195

7296
class RssBlockService extends BaseBlockService
7397
{
74-
// ...
98+
public function getName()
99+
{
100+
return 'Rss Reader';
101+
}
102+
103+
/**
104+
* Define valid options for a block of this type.
105+
*/
75106
public function setDefaultSettings(OptionsResolverInterface $resolver)
76107
{
77108
$resolver->setDefaults(array(
78-
'template' => 'AcmeMainBundle::Block::block_rss.html.twig',
79109
'url' => false,
80110
'title' => 'Feed items',
81-
'maxItems' => 10,
111+
'template' => 'AcmeMainBundle:Block:rss.html.twig',
82112
));
83113
}
84-
// ...
85114

115+
/**
116+
* The block context knows the default settings, but they can be
117+
* overwritten in the call to render the block.
118+
*/
86119
public function execute(BlockContextInterface $blockContext, Response $response = null)
87120
{
88-
/** @var $block RssBlock */
89-
$block = $blockContext->getBlock();
90-
91121
if (!$block->getEnabled()) {
92122
return new Response();
93123
}
94124

95-
$requestParams = $block->resolveRequestParams($this->request, $blockContext);
125+
// merge settings with those of the concrete block being rendered
126+
$settings = $blockContext->getSettings();
127+
$resolver = new OptionsResolver();
128+
$resolver->setDefaults($settings);
129+
$settings = $resolver->resolve($block->getOptions());
130+
131+
$feeds = false;
132+
if ($settings['url']) {
133+
$options = array(
134+
'http' => array(
135+
'user_agent' => 'Sonata/RSS Reader',
136+
'timeout' => 2,
137+
)
138+
);
139+
140+
// retrieve contents with a specific stream context to avoid php errors
141+
$content = @file_get_contents($settings['url'], false, stream_context_create($options));
142+
143+
if ($content) {
144+
// generate a simple xml element
145+
try {
146+
$feeds = new \SimpleXMLElement($content);
147+
$feeds = $feeds->channel->item;
148+
} catch (\Exception $e) {
149+
// silently fail error
150+
}
151+
}
152+
}
96153

97-
return new Response($this->renderer->render(new ControllerReference(
98-
'acme_main.controller.rss:block',
99-
$requestParams
100-
)
101-
));
154+
return $this->renderResponse($blockContext->getTemplate(), array(
155+
'feeds' => $feeds,
156+
'block' => $blockContext->getBlock(),
157+
'settings' => $settings
158+
), $response);
159+
}
160+
161+
// These methods are required by the sonata block service interface.
162+
// They are not used in the CMF. To edit, create a sonata admin or
163+
// something else that builds a form and collects the data.
164+
165+
public function buildEditForm(FormMapper $formMapper, BlockInterface $block)
166+
{
167+
throw new \Exception();
168+
}
169+
170+
public function validateBlock(ErrorElement $errorElement, BlockInterface $block)
171+
{
172+
throw new \Exception();
102173
}
103174
}
104175

105-
The execute method passes the settings to an RSS reader service and forwards
106-
the feed items to a template. (See :ref:`bundle-block-execute` for more on the
107-
block service ``execute`` method).
176+
The execute method simply reads the RSS feed and forwards the items to
177+
a template. See :ref:`bundle-block-execute` for more information on the
178+
``execute`` method of the block service. To not make your page very slow
179+
by fetching the feed on each request, have a look at the
180+
:doc:`cache documentation <cache>`.
108181

109-
Define the service in a configuration file. It is important to tag your BlockService
110-
with ``sonata.block`` to make it known to the SonataBlockBundle.
182+
To make the block work, the last step is to define the service. Do not forget
183+
to tag your service with ``sonata.block`` to make it known to the
184+
SonataBlockBundle. The first argument is the name of the block this service
185+
handles, as per the ``getType`` method of the block. The second argument is the
186+
templating, in order to be able to render this block.
111187

112188
.. configuration-block::
113189

114190
.. code-block:: yaml
115191
116-
acme_main.rss_reader:
117-
class: Acme\MainBundle\Feed\SimpleReader
118-
119192
sandbox_main.block.rss:
120193
class: Acme\MainBundle\Block\RssBlockService
121194
arguments:
122195
- "acme_main.block.rss"
123196
- "@templating"
124-
- "@sonata.block.renderer"
125-
- "@acme_main.rss_reader"
126197
tags:
127198
- {name: "sonata.block"}
128199
129200
.. code-block:: xml
130201
131-
<service id="acme_main.rss_reader" class="Acme\MainBundle\Feed\SimpleReader" />
132-
133202
<service id="sandbox_main.block.rss" class="Acme\MainBundle\Block\RssBlockService">
134203
<tag name="sonata.block" />
135204
136205
<argument>acme_main.block.rss</argument>
137206
<argument type="service" id="templating" />
138-
<argument type="service" id="sonata.block.renderer" />
139-
<argument type="service" id="acme_main.block.rss_reader" />
140207
</service>
141208
142209
.. code-block:: php
143210
144211
use Symfony\Component\DependencyInjection\Definition;
145212
use Symfony\Component\DependencyInjection\Reference;
146213
147-
$container->register('acme_main.rss_reader', 'Acme\MainBundle\Feed\SimpleReader');
148-
149214
$container
150215
->addDefinition('sandbox_main.block.rss', new Definition(
151216
'Acme\MainBundle\Block\RssBlockService',
152217
array(
153218
'acme_main.block.rss',
154219
new Reference('templating'),
155-
new Reference('sonata.block.renderer'),
156-
new Reference('acme_main.rss_reader'),
157220
)
158221
))
159222
->addTag('sonata.block')

0 commit comments

Comments
 (0)