@@ -17,143 +17,206 @@ a template. So instead you decide to create your own block, the ``RssBlock``.
17
17
18
18
.. tip ::
19
19
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 .
23
23
24
24
Create a block document
25
25
-----------------------
26
26
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
30
31
``Sonata\BlockBundle\Model\BlockInterface ``. In your document, you
31
32
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 ``::
35
34
36
35
// src/Acme/MainBundle/Document/RssBlock.php
37
36
namespace Acme\MainBundle\Document;
38
37
39
- use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCRODM ;
38
+ use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCR ;
40
39
use Symfony\Cmf\Bundle\BlockBundle\Doctrine\Phpcr\AbstractBlock;
41
40
42
41
/**
43
- * Rss Block
44
- *
45
- * @PHPCRODM\Document(referenceable=true)
42
+ * @PHPCR\Document(referenceable=true)
46
43
*/
47
44
class RssBlock extends AbstractBlock
48
45
{
46
+ /**
47
+ * @PHPCR\String(nullable=true)
48
+ */
49
+ private $feedUrl;
50
+
51
+ /**
52
+ * @PHPCR\String()
53
+ */
54
+ private $title;
55
+
49
56
public function getType()
50
57
{
51
58
return 'acme_main.block.rss';
52
59
}
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...
53
74
}
54
75
55
76
Create a Block Service
56
77
----------------------
57
78
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 ``: :
61
82
62
- * The method ``setDefaultSettings `` configures a template, title, url and the
63
- maximum amount of items::
64
-
65
-
66
83
// src/Acme/MainBundle/Block/RssBlockService.php
67
- use Symfony\Component\OptionsResolver\OptionsResolverInterface;
84
+ namespace Acme\MainBundle\Block;
85
+
68
86
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;
70
93
use Sonata\BlockBundle\Block\BlockContextInterface;
94
+ use Sonata\BlockBundle\Block\BaseBlockService;
71
95
72
96
class RssBlockService extends BaseBlockService
73
97
{
74
- // ...
98
+ public function getName()
99
+ {
100
+ return 'Rss Reader';
101
+ }
102
+
103
+ /**
104
+ * Define valid options for a block of this type.
105
+ */
75
106
public function setDefaultSettings(OptionsResolverInterface $resolver)
76
107
{
77
108
$resolver->setDefaults(array(
78
- 'template' => 'AcmeMainBundle::Block::block_rss.html.twig',
79
109
'url' => false,
80
110
'title' => 'Feed items',
81
- 'maxItems ' => 10 ,
111
+ 'template ' => 'AcmeMainBundle:Block:rss.html.twig' ,
82
112
));
83
113
}
84
- // ...
85
114
115
+ /**
116
+ * The block context knows the default settings, but they can be
117
+ * overwritten in the call to render the block.
118
+ */
86
119
public function execute(BlockContextInterface $blockContext, Response $response = null)
87
120
{
88
- /** @var $block RssBlock */
89
- $block = $blockContext->getBlock();
90
-
91
121
if (!$block->getEnabled()) {
92
122
return new Response();
93
123
}
94
124
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
+ }
96
153
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();
102
173
}
103
174
}
104
175
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 >`.
108
181
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.
111
187
112
188
.. configuration-block ::
113
189
114
190
.. code-block :: yaml
115
191
116
- acme_main.rss_reader :
117
- class : Acme\MainBundle\Feed\SimpleReader
118
-
119
192
sandbox_main.block.rss :
120
193
class : Acme\MainBundle\Block\RssBlockService
121
194
arguments :
122
195
- " acme_main.block.rss"
123
196
- " @templating"
124
- - " @sonata.block.renderer"
125
- - " @acme_main.rss_reader"
126
197
tags :
127
198
- {name: "sonata.block"}
128
199
129
200
.. code-block :: xml
130
201
131
- <service id =" acme_main.rss_reader" class =" Acme\MainBundle\Feed\SimpleReader" />
132
-
133
202
<service id =" sandbox_main.block.rss" class =" Acme\MainBundle\Block\RssBlockService" >
134
203
<tag name =" sonata.block" />
135
204
136
205
<argument >acme_main.block.rss</argument >
137
206
<argument type =" service" id =" templating" />
138
- <argument type =" service" id =" sonata.block.renderer" />
139
- <argument type =" service" id =" acme_main.block.rss_reader" />
140
207
</service >
141
208
142
209
.. code-block :: php
143
210
144
211
use Symfony\Component\DependencyInjection\Definition;
145
212
use Symfony\Component\DependencyInjection\Reference;
146
213
147
- $container->register('acme_main.rss_reader', 'Acme\MainBundle\Feed\SimpleReader');
148
-
149
214
$container
150
215
->addDefinition('sandbox_main.block.rss', new Definition(
151
216
'Acme\MainBundle\Block\RssBlockService',
152
217
array(
153
218
'acme_main.block.rss',
154
219
new Reference('templating'),
155
- new Reference('sonata.block.renderer'),
156
- new Reference('acme_main.rss_reader'),
157
220
)
158
221
))
159
222
->addTag('sonata.block')
0 commit comments