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

Commit cd1d523

Browse files
committed
Merge pull request #341 from symfony-cmf/create_cms_book
Refactoring basic CMS tutorial into book
2 parents 76c673d + 3a1ce2d commit cd1d523

12 files changed

+1932
-1918
lines changed

cookbook/create_basic_cms_auto_routing.rst

Lines changed: 0 additions & 1916 deletions
This file was deleted.
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
Routing and Automatic Routing
2+
-----------------------------
3+
4+
The routes (URLs) to your content will be automatically created and updated
5+
using the RoutingAutoBundle. This bundle uses a configuration language to
6+
specify automatic creation of routes, which can be a bit hard to grasp the
7+
first time you see it.
8+
9+
For a full a full explanation refer to the
10+
:doc:`../../bundles/routing_auto/index`.
11+
12+
In summary, you will configure the auto routing system to create a new auto
13+
routing document in the routing tree for every post or content created. The
14+
new route will be linked back to the target content:
15+
16+
.. image:: ../../_images/cookbook/basic-cms-objects.png
17+
18+
The paths above represent the path in the PHPCR-ODM document tree. In the next
19+
section you will define ``/cms/routes`` as the base path for routes, and subsequently
20+
the contents will be avilable at the following URLs:
21+
22+
* **Home**: ``http://localhost:8000/page/home``
23+
* **About**: ``http://localhost:8000/page/about``
24+
* etc.
25+
26+
Installation
27+
~~~~~~~~~~~~
28+
29+
Ensure that you have the following package installed:
30+
31+
.. code-block:: javascript
32+
33+
{
34+
...
35+
require: {
36+
...
37+
"symfony-cmf/routing-auto-bundle": "1.0.*@alpha"
38+
},
39+
...
40+
}
41+
42+
.. note::
43+
44+
You are installing the bleeding edge version of the routing-auto bundle.
45+
46+
Enable the routing bundles to your kernel::
47+
48+
class AppKernel extends Kernel
49+
{
50+
public function registerBundles()
51+
{
52+
$bundles = array(
53+
// ...
54+
new Symfony\Cmf\Bundle\RoutingBundle\CmfRoutingBundle(),
55+
new Symfony\Cmf\Bundle\RoutingAutoBundle\CmfRoutingAutoBundle(),
56+
);
57+
58+
// ...
59+
}
60+
}
61+
62+
.. note::
63+
64+
The `symfony-cmf/routing-bundle` package is installed automatically as
65+
`symfony-cmf/routing-auto-bundle` depends on it.
66+
67+
Enable the Dynamic Router
68+
~~~~~~~~~~~~~~~~~~~~~~~~~
69+
70+
The RoutingAutoBundle uses the CMF `RoutingBundle`_ which enables routes to
71+
be provided from a database (in addition to being provided from
72+
the routing configuration files as in core Symfony 2).
73+
74+
Add the following to your application configuration:
75+
76+
.. configuration-block::
77+
78+
.. code-block:: yaml
79+
80+
# /app/config/config.yml
81+
cmf_routing:
82+
chain:
83+
routers_by_id:
84+
cmf_routing.dynamic_router: 20
85+
router.default: 100
86+
dynamic:
87+
enabled: true
88+
persistence:
89+
phpcr:
90+
route_basepath: /cms/routes
91+
92+
.. code-block:: xml
93+
94+
<!-- app/config/config.xml -->
95+
<container xmlns="http://symfony.com/schema/dic/services">
96+
<config xmlns="http://cmf.symfony.com/schema/dic/routing">
97+
<chain>
98+
<router-by-id id="cmf_routing.dynamic_router">20</router-by-id>
99+
<router-by-id id="router.default">100</router-by-id>
100+
</chain>
101+
<dynamic>
102+
<persistence>
103+
<phpcr route-basepath="/cms/routes" />
104+
</persistence>
105+
</dynamic>
106+
</config>
107+
</container>
108+
109+
.. code-block:: php
110+
111+
// app/config/config.php
112+
$container->loadFromExtension('cmf_routing', array(
113+
'dynamic' => array(
114+
'persistence' => array(
115+
'phpcr' => array(
116+
'enabled' => true,
117+
'route_basepath' => '/cms/routes',
118+
),
119+
),
120+
),
121+
));
122+
123+
This will:
124+
125+
#. Cause the default Symfony router to be replaced by the chain router. The
126+
chain router enables you to have multiple routers in your application. You
127+
add the dynamic router (which can retrieve routes from the database) and
128+
the default Symfony router (which retrieves routes from configuration
129+
files). The number indicates the order of precedence - the router with the
130+
lowest number will be called first;
131+
#. Configure the **dynamic** router which you have added to the router chain.
132+
You specify that it should use the PHPCR backend and that the *root* route
133+
can be found at ``/cms/routes``.
134+
135+
Auto Routing Configuration
136+
~~~~~~~~~~~~~~~~~~~~~~~~~~
137+
138+
Create the following file in your applications configuration directory:
139+
140+
.. code-block:: yaml
141+
142+
# app/config/routing_auto.yml
143+
cmf_routing_auto:
144+
mappings:
145+
Acme\BasicCmsBundle\Document\Page:
146+
content_path:
147+
pages:
148+
provider: [specified, { path: /cms/routes/page }]
149+
exists_action: use
150+
not_exists_action: create
151+
content_name:
152+
provider: [content_method, { method: getTitle }]
153+
exists_action: auto_increment
154+
not_exists_action: create
155+
156+
Acme\BasicCmsBundle\Document\Post:
157+
content_path:
158+
blog_path:
159+
provider: [specified, { path: /cms/routes/post }]
160+
exists_action: use
161+
not_exists_action: create
162+
date:
163+
provider: [content_datetime, { method: getDate}]
164+
exists_action: use
165+
not_exists_action: create
166+
content_name:
167+
provider: [content_method, { method: getTitle }]
168+
exists_action: auto_increment
169+
not_exists_action: create
170+
171+
This will configure the routing auto system to automatically create and update
172+
route documents for both the ``Page`` and ``Post`` documents.
173+
174+
In summary:
175+
176+
* The ``content_path`` key represents the parent path of the content, e.g.
177+
``/if/this/is/a/path`` then the ``content_path``
178+
represents ``/if/this/is/a``;
179+
* Each element under ``content_path`` represents a section of the URL;
180+
* The first element ``blog_path`` uses a *provider* which *specifies* a
181+
path. If that path exists then it will do nothing;
182+
* The second element uses the ``content_datetime`` provider, which will
183+
use a ``DateTime`` object returned from the specified method on the
184+
content object (the ``Post``) and create a path from it, e.g.
185+
``2013/10/13``;
186+
* The ``content_name`` key represents the last part of the path, e.g. ``path``
187+
from ``/if/this/is/a/path``.
188+
189+
Now you will need to include this configuration:
190+
191+
.. configuration-block::
192+
193+
.. code-block:: yaml
194+
195+
# src/Acme/BasicCmsBundle/Resources/config/config.yml
196+
imports:
197+
- { resource: routing_auto.yml }
198+
199+
.. code-block:: xml
200+
201+
<!-- src/Acme/BasicCmsBUndle/Resources/config/config.yml -->
202+
<?xml version="1.0" encoding="UTF-8" ?>
203+
<container
204+
xmlns="http://symfony.com/schema/dic/services"
205+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
206+
xsi:schemaLocation="http://symfony.com/schema/dic/services
207+
http://symfony.com/schema/dic/services/services-1.0.xsd">
208+
209+
<import resource="routing_auto.yml"/>
210+
</container>
211+
212+
.. code-block:: php
213+
214+
// src/Acme/BasicCmsBundle/Resources/config/config.php
215+
216+
// ...
217+
$this->import('routing_auto.yml');
218+
219+
and reload the fixtures:
220+
221+
.. code-block:: bash
222+
223+
$ php app/console doctrine:phpcr:fixtures:load
224+
225+
Have a look at what you have:
226+
227+
.. code-block:: bash
228+
229+
$ php app/console doctrine:phpcr:node:dump
230+
ROOT:
231+
cms:
232+
pages:
233+
Home:
234+
routes:
235+
page:
236+
home:
237+
post:
238+
2013:
239+
10:
240+
12:
241+
my-first-post:
242+
my-second-post:
243+
my-third-post:
244+
my-forth-post:
245+
posts:
246+
My First Post:
247+
My Second Post:
248+
My Third Post:
249+
My Forth Post:
250+
251+
The routes have been automatically created!
252+
253+
.. _`routingautobundle documentation`: http://symfony.com/doc/current/cmf/bundles/routing_auto.html
254+
.. _`SonataDoctrinePhpcrAdminBundle`: https://github.com/sonata-project/SonataDoctrinePhpcrAdminBundle
255+
.. _`routingbundle`: http://symfony.com/doc/master/cmf/bundles/routing/index.html
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Conclusion
2+
----------
3+
4+
And thats it! Well done. You have created a very minimum but functional
5+
CMS which can act as a good foundation for larger projects!
6+
7+
You can checkout the completed CMS on Github:
8+
9+
* https://github.com/dantleech/tutorial-basic-cms
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
Controllers and Templates
2+
-------------------------
3+
4+
Go to the URL http://localhost:8000/page/home in your browser - this should be
5+
your page, but it says that it cannot find a controller. In other words it has
6+
found the *page referencing route* for your page but Symfony does not know what
7+
to do with it.
8+
9+
You can map a default controller for all instances of ``Page``:
10+
11+
.. configuration-block::
12+
13+
.. code-block:: yaml
14+
15+
# app/config/config.yml
16+
cmf_routing:
17+
dynamic:
18+
# ...
19+
controllers_by_class:
20+
Acme\BasicCmsBundle\Document\Page: Acme\BasicCmsBundle\Controller\DefaultController::pageAction
21+
22+
.. code-block:: xml
23+
24+
<!-- app/config/config.xml -->
25+
<?xml version="1.0" encoding="UTF-8" ?>
26+
27+
<container xmlns="http://cmf.symfony.com/schema/dic/services"
28+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
29+
30+
<config xmlns="http://cmf.symfony.com/schema/dic/routing">
31+
<dynamic generic-controller="cmf_content.controller:indexAction">
32+
<!-- ... -->
33+
<controllers-by-class
34+
class="Acme\BasicCmsBundle\Document\Page"
35+
>
36+
Acme\BasicCmsBundle\Controller\DefaultController::pageAction
37+
</controllers-by-class>
38+
</dynamic>
39+
</config>
40+
</container>
41+
42+
.. code-block:: php
43+
44+
// app/config/config.php
45+
$container->loadFromExtension('cmf_routing', array(
46+
'dynamic' => array(
47+
// ...
48+
'controllers_by_class' => array(
49+
'Acme\BasicCmsBundle\Document\Page' => 'Acme\BasicCmsBundle\Controller\DefaultController::pageAction',
50+
),
51+
),
52+
));
53+
54+
This will cause requests to be forwarded to this controller when the route
55+
which matches the incoming request is provided by the dynamic router **and**
56+
the content document that that route references is of class
57+
``Acme\BasicCmsBundle\Document\Page``.
58+
59+
Now create the action in the default controller - you can pass the ``Page``
60+
object and all the ``Posts`` to the view::
61+
62+
// src/Acme/BasicCmsBundle/Controller/DefaultController.php
63+
64+
// ...
65+
class DefaultController extends Controller
66+
{
67+
// ...
68+
69+
/**
70+
* @Template()
71+
*/
72+
public function pageAction($contentDocument)
73+
{
74+
$dm = $this->get('doctrine_phpcr')->getManager();
75+
$posts = $dm->getRepository('Acme\BasicCmsBundle\Document\Post')->findAll();
76+
77+
return array(
78+
'page' => $contentDocument,
79+
'posts' => $posts,
80+
);
81+
}
82+
}
83+
84+
The ``Page`` object is passed automatically as ``$contentDocument``.
85+
86+
Add a corresponding twig template (note that this works because you use the
87+
``@Template`` annotation):
88+
89+
.. configuration-block::
90+
91+
.. code-block:: html+jinja
92+
93+
{# src/Acme/BasicCmsBundle/Resources/views/Default/page.html.twig #}
94+
<h1>{{ page.title }}</h1>
95+
<p>{{ page.content|raw }}</p>
96+
<h2>Our Blog Posts</h2>
97+
<ul>
98+
{% for post in posts %}
99+
<li><a href="{{ path(post) }}">{{ post.title }}</a></li>
100+
{% endfor %}
101+
</ul>
102+
103+
.. code-block:: html+php
104+
105+
<!-- src/Acme/BasicCmsBundle/Resources/views/Default/page.html.twig -->
106+
<h1><?php echo $page->getTitle() ?></h1>
107+
<p><?php echo $page->getContent() ?></p>
108+
<h2>Our Blog Posts</h2>
109+
<ul>
110+
<?php foreach($posts as $post) : ?>
111+
<li>
112+
<a href="<?php echo $view['router']->generate($post) ?>">
113+
<?php echo $post->getTitle() ?>
114+
</a>
115+
</li>
116+
<?php endforeach ?>
117+
</ul>
118+
119+
Now have another look at: http://localhost:8000/page/home
120+
121+
Notice what is happening with the post object and the ``path`` function - you
122+
pass the ``Post`` object and the ``path`` function will pass the object to the
123+
router and because it implements the ``RouteReferrersReadInterface`` the
124+
``DynamicRouter`` will be able to generate the URL for the post.
125+
126+
Click on a ``Post`` and you will have the same error that you had before when
127+
viewing the page at ``/home`` and you can resolve it in the same way.

0 commit comments

Comments
 (0)