diff --git a/_images/quick_tour/3rd-party-bundles-homepage.png b/_images/quick_tour/3rd-party-bundles-homepage.png index 1032fee5..ea6844e9 100644 Binary files a/_images/quick_tour/3rd-party-bundles-homepage.png and b/_images/quick_tour/3rd-party-bundles-homepage.png differ diff --git a/_images/quick_tour/3rd-party-bundles-sonata-admin.png b/_images/quick_tour/3rd-party-bundles-sonata-admin.png index 672c3b93..f38ab265 100644 Binary files a/_images/quick_tour/3rd-party-bundles-sonata-admin.png and b/_images/quick_tour/3rd-party-bundles-sonata-admin.png differ diff --git a/_images/quick_tour/big-picture-createjs-bar.png b/_images/quick_tour/big-picture-createjs-bar.png deleted file mode 100644 index 874f3e23..00000000 Binary files a/_images/quick_tour/big-picture-createjs-bar.png and /dev/null differ diff --git a/_images/quick_tour/big-picture-edit-page.png b/_images/quick_tour/big-picture-edit-page.png deleted file mode 100644 index 1756d59b..00000000 Binary files a/_images/quick_tour/big-picture-edit-page.png and /dev/null differ diff --git a/_images/quick_tour/big-picture-new-page.png b/_images/quick_tour/big-picture-new-page.png deleted file mode 100644 index 9e22e2c5..00000000 Binary files a/_images/quick_tour/big-picture-new-page.png and /dev/null differ diff --git a/_images/quick_tour/the-model-new-page.png b/_images/quick_tour/the-model-new-page.png deleted file mode 100644 index 459bc089..00000000 Binary files a/_images/quick_tour/the-model-new-page.png and /dev/null differ diff --git a/_images/quick_tour/the-router-new-page.png b/_images/quick_tour/the-router-new-page.png new file mode 100644 index 00000000..040bc6b8 Binary files /dev/null and b/_images/quick_tour/the-router-new-page.png differ diff --git a/big-picture-home.png b/big-picture-home.png new file mode 100644 index 00000000..a7180990 Binary files /dev/null and b/big-picture-home.png differ diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index 7a90256b..240332c7 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -4,7 +4,7 @@ The Big Picture =============== -Start using the Symfony CMF in 10 minutes! This chapter will walk you through +Start using the Symfony CMF in 10 minutes! This quick tour will walk you through the base concepts of the Symfony CMF and get you started with it. It's important to know that the Symfony CMF is a collection of bundles which @@ -13,7 +13,7 @@ Framework. Before you read further, you should at least have a basic knowledge of the Symfony Framework. If you don't know Symfony, start by reading the `Symfony Framework Quick Tour`_. -Solving the framework versus CMS dilemma +Solving the Framework versus CMS Dilemma ---------------------------------------- Before starting a new project, there is a difficult decision on whether it @@ -23,38 +23,33 @@ other hand, when choosing to use a CMS, it's more difficult to build custom application functionality. It is impossible or at least very hard to customize the core parts of the CMS. -The CMF is created to solve this framework versus CMS dilemma. It provides -bundles, so you can easily add CMS features to your project. But it also -provides flexibility and in all cases you are using the framework, so you can -build custom functionality the way you want. This is called a `decoupled CMS`_. +The Symfony CMF is created to solve this framework versus CMS dilemma. It +provides Symfony bundles to easily add CMS features to your project. Yet, as +you're still using the Symfony framework, you can build any custom functionality +you can think of. This flexibility is called a `decoupled CMS`_. The bundles provided by the Symfony CMF can work together, but they are also able to work standalone. This means that you don't need to add all bundles, you -can decide to only use one of them (e.g. only the RoutingBundle or the MediaBundle). +can decide to only use one of them (e.g. only the RoutingBundle). -Downloading the Symfony CMF Standard Edition --------------------------------------------- +Downloading the Symfony CMF Sandbox +----------------------------------- -When you want to start using the CMF for a new project, you can download the +To explore the CMF, it is best to download the Symfony CMF Sandbox. The sandbox +contains demonstrations for many of the CMF features and is a good playground +to familiarize yourself with the CMF. + +When you want to start an actual project with the CMF, best download the Symfony CMF Standard Edition. The Symfony CMF Standard Edition is similar to the `Symfony Standard Edition`_, but contains and configures essential Symfony -CMF bundles. It also adds a very simple bundle to show some of the basic -Symfony CMF features. +CMF bundles. -The best way to download the Symfony CMF Standard Edition is using Composer_: +The best way to download the Symfony CMF Sandbox is using Composer_: .. code-block:: bash - $ composer create-project symfony-cmf/standard-edition cmf '1.2.1' - -.. note:: + $ composer create-project symfony-cmf/sandbox cmf-sandbox - The `AcmeDemoBundle` that is used in this tour was removed in - version 1.3 of the Symfony CMF Standard Edition. Since then it has - become the skeleton for a simple CMS application. This is why we - install version 1.2.1. If you insist on checking out the most - recent versions of the CMF, check out `symfony-cmf/cmf-sandbox`. - Setting up the Database ~~~~~~~~~~~~~~~~~~~~~~~ @@ -63,15 +58,20 @@ something you are used to doing when creating Symfony applications, but the Symfony CMF needs a database in order to make a lot of things configurable using an admin interface. -To quickly get started, it is expected that you have enabled the sqlite +To quickly get started, it is expected that you have enabled the sqlite PHP extension. After that, run these commands: .. code-block:: bash + $ cd cmf-sandbox + $ cp app/config/phpcr_doctrine_dbal.yml.dist app/config/phpcr.yml + # Or when you're on a Windows PC: + # $ copy app\config\phpcr_doctrine_dbal.yml.dist app\config\phpcr.yml + $ php bin/console doctrine:database:create - $ php bin/console doctrine:phpcr:init:dbal + $ php bin/console doctrine:phpcr:init:dbal --force $ php bin/console doctrine:phpcr:repository:init - $ php bin/console doctrine:phpcr:fixtures:load + $ php bin/console doctrine:phpcr:fixtures:load -n .. tip:: @@ -88,11 +88,13 @@ The Request Flow .. tip:: - When you have at least PHP 5.4, use the ``server:run`` command to run a - local server for the demo. Otherwise, use a ``localhost`` and prefix the URLs - in this document with ``/path-to-project/web/app_dev.php/``. + Use the ``server:run`` command to run a local server for the demo. If you + use a web server like Nginx or Apache, you need to prefix the URLs + in this document with ``app_dev.php/`` (and the path to the ``web`` folder + inside the Symfony project, if that is not already the root folder of your + server). -Now, the Standard Edition is ready to use. Navigate to the homepage +Now, the Sandbox is ready to use. Navigate to the homepage (``http://localhost:8000/``) to see the demo: .. image:: ../_images/quick_tour/big-picture-home.png @@ -170,53 +172,24 @@ this template. A view also uses a Menu, provided by the KnpMenuBundle_, and it can have integration with Create.js, for live editing. -Adding a New Page ------------------ - -Now you know the request flow, you can start adding a new page. In the Symfony -CMF Standard Edition, the data is stored in data files, which are loaded when -executing the ``doctrine:phpcr:fixtures:load`` command. To add a new page, you -just need to edit such a data file, which is located in the -``src/Acme/DemoBundle/Resources/data`` directory: - -.. code-block:: yaml - - # src/Acme/DemoBundle/Resources/data/pages.yml - Symfony\Cmf\Bundle\SimpleCmsBundle\Doctrine\Phpcr\Page: - # ... - - quick_tour: - id: /cms/simple/quick_tour - label: "Quick Tour" - title: "Reading the Quick Tour" - body: "I've added this page while reading the quick tour" - -After this, you need to run the ``doctrine:phpcr:fixtures:load`` to reflect -the changes on the database and after refreshing, you can see your new page! - -.. image:: ../_images/quick_tour/big-picture-new-page.png - -Live Editing +The Fixtures ------------ -Now is the time you become an admin of this site and editing the content using -the Web Interface. To do this click on "Admin Login" and use the provided -credentials. - -You'll see that you have a new bar at the top of the page: - -.. image:: ../_images/quick_tour/big-picture-createjs-bar.png - -This bar is generated by the `Create.js`_ library. The Symfony CMF integrates -the CreatePHP_ and `Create.js`_ libraries using a CreateBundle. This enables -you to edit a page using a full WYSIWYG editor when you are reading the page. - -Now you can change the content of our new page using Create.js: - -.. image:: ../_images/quick_tour/big-picture-edit-page.png - -After clicking "save", the changes are saved using the CreateBundle and the -content is updated. +Now you know the request flow, you can start editing content. While the normal +usage will be to edit content through a web interface, the CMF sandbox also +supports loading content from static files. This is mainly useful for testing +purposes. + +The fixtures are loaded with the ``doctrine:phpcr:fixtures:load`` command. To +edit the home page, edit the first entry in +``src/AppBundle/Resources/data/page.yml`` to say something different. Then, run +the ``doctrine:phpcr:fixtures:load`` command to get the changes into the +content repository. After refreshing the browser, you can see your +modifications! + +Don't worry, editing fixture files is only done for developing and testing. The +CMF comes with a Sonata admin integration for convenient online editing, or you +can build your own editing systems. Final Thoughts -------------- diff --git a/quick_tour/the_model.rst b/quick_tour/the_model.rst index 3c95fad6..f887c376 100644 --- a/quick_tour/the_model.rst +++ b/quick_tour/the_model.rst @@ -11,8 +11,8 @@ of the CMF. .. note:: Again, this chapter is talking about the PHPCR storage layer. But the CMF - is storage agnostically created, meaning it is not tied to specific storage - system. + is written in a storage agnostic way, meaning it is not tied to specific + storage system. Getting Familiar with PHPCR --------------------------- @@ -23,7 +23,7 @@ data stored with PHPCR has a relationship with at least one other data: its parent. The inverse relation also exists, you can also get the children of a data element. -Let's take a look at the dump of the tree of the Standard Edition you +Let's take a look at the dump of the tree of the CMF Sandbox you downloaded in the previous chapter. Go to your directory and execute the following command: @@ -36,36 +36,49 @@ The result will be the PHPCR tree: .. code-block:: text ROOT: - cms: - simple: - about: - contact: - map: - team: - quick_tour: - dynamic: - docs: - demo: - demo_redirect: - hardcoded_dynamic: - hardcoded_static: - -Each data is called a *node* in PHPCR. In this tree, there are 13 nodes and -one ROOT node (created by PHPCR). You may have already seen the document you -created in the previous section, it's called ``quick_tour`` (and it's path is -``/cms/simple/quick_tour``). When using the SimpleCmsBundle, all nodes are -stored in the ``/cms/simple`` path. + cms: + menu: + main: + admin-item: + projects-item: + cmf-item: + company-item: + team-item: + ... + content: + home: + phpcr_locale:en: + phpcr_locale:fr: + phpcr_locale:de: + seoMetadata: + additionalInfoBlock: + child1: + ... + routes: + en: + company: + team: + more: + about: + ... + +Each data is called a *node* in PHPCR. Everything is attached under the ROOT +node (created by PHPCR itself). Each node has properties, which contain the data. The content, title and label -you set for your page are saved in such properties for the ``quick_tour`` +you set for your page are saved in such properties for the ``home`` node. You can view these properties by adding the ``--props`` switch to the -dump command. +dump command: + +.. code-block:: bash + + $ php bin/console doctrine:phpcr:node:dump --props /cms/content/home .. note:: Previously, the PHPCR tree was compared with a Filesystem. While this - gives you a good image of what happens, it's not the truth. You can - better compare it to an XML file, where each node is an element and its + gives you a good image of what happens, it's not the only truth. You can + compare it to an XML file, where each node is an element and its properties are attributes. Doctrine PHPCR-ODM @@ -74,37 +87,39 @@ Doctrine PHPCR-ODM The Symfony CMF uses the `Doctrine PHPCR-ODM`_ to interact with PHPCR. Doctrine allows a user to create objects (called *documents*) which are directly persisted into and retrieved from the PHPCR tree. This is similar to -the Doctrine ORM used by the Symfony2 Framework, but then for PHPCR. +the Doctrine ORM provided by default in the Symfony Standard Edition, but for +PHPCR instead of SQL databases. -Creating a Page with code -------------------------- +Creating Content from Code +-------------------------- -Now you know a little bit more about PHPCR and you know the tool to interact -with it, you can start using it yourself. In the previous chapter, you created -a page by using a yaml file which was parsed by the SimpleCmsBundle. This -time, you'll create a page by doing it yourself. +Now that you know a little bit more about PHPCR and you know the tool to interact +with it, you can start using it yourself. In the previous chapter, you edited +a page by using a Yaml file which was parsed by the fixture loader of the +sandbox. This time, you'll create a page with PHP code. First, you have to create a new DataFixture to add your new page. You do this -by creating a new class in the AcmeDemoBundle:: +by creating a new class in the AppBundle:: - // src/Acme/DemoBundle/DataFixtures/PHPCR/LoadPageData.php - namespace Acme\DemoBundle\DataFixtures\PHPCR; + // src/AppBundle/DataFixtures/PHPCR/LoadQuickTourData.php + namespace AppBundle\DataFixtures\PHPCR; use Doctrine\Common\Persistence\ObjectManager; use Doctrine\Common\DataFixtures\FixtureInterface; use Doctrine\Common\DataFixtures\OrderedFixtureInterface; - class LoadPageData implements FixtureInterface, OrderedFixtureInterface + class LoadQuickTourData implements FixtureInterface, OrderedFixtureInterface { public function getOrder() { // refers to the order in which the class' load function is called // (lower return values are called first) - return 10; + return 100; } public function load(ObjectManager $documentManager) { + // you will add code to this method in the next steps } } @@ -112,63 +127,48 @@ The ``$documentManager`` is the object which will persist the document to PHPCR. But first, you have to create a new Page document:: use Doctrine\ODM\PHPCR\DocumentManager; - use Symfony\Cmf\Bundle\SimpleCmsBundle\Doctrine\Phpcr\Page; + use Symfony\Cmf\Bundle\ContentBundle\Doctrine\Phpcr\StaticContent; // ... public function load(ObjectManager $documentManager) { if (!$documentManager instanceof DocumentManager) { - $class = get_class($documentManager); - throw new \RuntimeException("Fixture requires a PHPCR ODM DocumentManager instance, instance of '$class' given."); + throw new \RuntimeException(sprintf( + 'Fixture requires a PHPCR ODM DocumentManager instance, instance of "%s" given.', + get_class($documentManager) + )); } - $page = new Page(); // create a new Page object (document) - $page->setName('new_page'); // the name of the node - $page->setLabel('Another new Page'); - $page->setTitle('Another new Page'); - $page->setBody('I have added this page myself!'); + $content = new StaticContent(); + $content->setName('quick-tour'); // the name of the node + $content->setTitle('Quick tour new Page'); + $content->setBody('I have added this page myself!'); } -Each document needs a parent. In this case, the parent should just be the root +Each document needs a parent. In this case, the parent should be the content root node. To do this, we first retrieve the root document from PHPCR and then set it as its parent:: - // ... public function load(ObjectManager $documentManager) { - if (!$documentManager instanceof DocumentManager) { - $class = get_class($documentManager); - throw new \RuntimeException("Fixture requires a PHPCR ODM DocumentManager instance, instance of '$class' given."); - } - // ... - - // get root document (/cms/simple) - $simpleCmsRoot = $documentManager->find(null, '/cms/simple'); - - $page->setParentDocument($simpleCmsRoot); // set the parent to the root + // get the root document + $contentRoot = $documentManager->find(null, '/cms/content'); + $content->setParentDocument($contentRoot); // set the parent to the root } -And at last, we have to tell the Document Manager to persist our Page +And finally, we have to tell the Document Manager to persist our content document using the Doctrine API:: - // ... public function load(ObjectManager $documentManager) { - if (!$documentManager instanceof DocumentManager) { - $class = get_class($documentManager); - throw new \RuntimeException("Fixture requires a PHPCR ODM DocumentManager instance, instance of '$class' given."); - } - // ... - $documentManager->persist($page); // add the Page in the queue - $documentManager->flush(); // add the Page to PHPCR + $documentManager->persist($content); // tell the document manager to track the content + $documentManager->flush(); // doctrine is like a toilet: never forget to flush } -Now you need to execute the ``doctrine:phpcr:fixtures:load`` command again and -then you can visit your website again. You'll see your new page you added! - -.. image:: ../_images/quick_tour/the-model-new-page.png +Now you need to execute the ``doctrine:phpcr:fixtures:load`` command again. +When dumping the nodes again, your new page should show up under ``/cms/content/quick-tour``! .. seealso:: diff --git a/quick_tour/the_router.rst b/quick_tour/the_router.rst index e7eddcaf..32bdc52e 100644 --- a/quick_tour/the_router.rst +++ b/quick_tour/the_router.rst @@ -4,27 +4,27 @@ The Router ========== -Welcome at the third part of the Quick Tour. You seem to have fallen in love -with the CMF, getting this far! And that's a good thing, as you will learn -about the backbone of the CMF in this chapter: The Router. +Welcome at the third part of the Quick Tour. Well done, making it this far! +And that's a good thing, as you will learn about the backbone of the CMF in +this chapter: The Router. The Backbone of the CMF ----------------------- -As already said, the router is the backbone. To understand this, you have a -good view of what a CMS tries to do. In a normal Symfony application, a route +The router is central to the CMF. To understand this, let us +look at what a CMS tries to do. In a normal Symfony application, a route refers to a controller which can handle a specific entity. Another route refers to another controller which can handle another entity. This way, a route is tied to a controller. In fact, using the Symfony core you are also -limited at this. +limited by this pattern. -But if you look at the base of a CMS, it only needs to handle 1 type of +But if you look at the base of a CMS, it only needs to handle one type of entity: The Content. So most of the routes don't have to be tied to a controller anymore, as only one controller is needed. The Route has to be tied -to a specific Content object, which - on its side - can reference a specific +to a specific Content object, which - on its side - may need a specific template and controller. -Other parts of the CMF are also related to the Router. To give 2 examples: The +Other parts of the CMF are also related to the Router. Two examples: The menu is created by generating specific routes using the Router and the blocks are displayed to specific routes (as they are related to a template). @@ -35,162 +35,51 @@ In the first chapter, you have already learned that routes are loaded from the database using a special ``DynamicRouter``. This way, not all routes need to be loaded each request. -Matching routes from a PHPCR is really simple. If you remember the previous -chapter, you know that you can get the ``quick_tour`` page from PHPCR using -``/cms/simple/quick_tour``. The URL to get this page is ``quick_tour``. Some -other examples: +Matching routes from a PHPCR is straightforward: The router takes the request +path and looks for a document with that path. Some examples: .. code-block:: text /cms - /simple - /about # /about Route - /contact # /contact Route - /team # /contact/team Route - /docs # /contact/docs Route + /routes + /en # /en Route + /company # /en/company Route + /team # /en/company/team Route + /about # /en/about Route + /de # /de Route + /ueber # /de/ueber Route OK, you got it? The only thing the Router has to do is prefix the route with a -specific path prefix and load that document. In the case of the SimpleCmsBundle, -all routes are prefixed with ``/cms/simple``. +specific path prefix and load that document. In the case of the RoutingBundle, +all routes are prefixed with ``/cms/routes``. -You see that a route like ``/contact/team``, which consist of 2 "path units", -has 2 documents in the PHPCR tree: ``contact`` and ``team``. - -Chaining multiple Routers -------------------------- - -You may need to have several prefixes or several routes. For instance, you may -want to use both the ``DynamicRouter`` for the page routes, but also the -static routing files from Symfony for your custom logic. To be able to do that, -the CMF provides a ``ChainRouter``. This router chains over multiple router -and stops whenever a router matches. - -By default, the ``ChainRouter`` overrides the Symfony router and only has the -core router in its chain. You can add more routers to the chain in the -configuration or by tagging the router services. For instance, the router used -by the SimpleCmsBundle is a service registered by that bundle and tagged with -``cmf_routing.router``. +You see that a route like ``/company/team``, which consist of two "path units", +has two documents in the PHPCR tree: ``company`` and ``team``. Creating a new Route -------------------- -Now you know the basics of routing, you can add a new route to the tree. In -the configuration file, configure a new chain router so that you can put your -new routes in ``/cms/routes``: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - - # ... - cmf_routing: - chain: - routers_by_id: - # the standard DynamicRouter - cmf_routing.dynamic_router: 200 - - # the core symfony router - router.default: 100 - dynamic: - persistence: - phpcr: - route_basepaths: - - /cms/routes - # /cms/routes is the default base path, the above code is - # equivalent to: - # phpcr: true - - .. code-block:: xml - - - - - - - - - - 200 - - - 100 - - - - - - /cms/routes - - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('cmf_routing', array( - 'chain' => array( - 'routers_by_id' => array( - // the standard DynamicRouter - 'cmf_routing.dynamic_router' => 200, - - // the core symfony router - 'router.default' => 100, - ), - ), - 'dynamic' => array( - 'persistence' => array( - 'phpcr' => array( - 'route_basepaths' => '/cms/routes', - ), - /* /cms/routes is the default base path, the above code is - equivalent to: - 'phpcr' => true, - */ - ), - ), - )); - -Now you can add a new ``Route`` to the tree using Doctrine:: - - // src/Acme/DemoBundle/DataFixtures/PHPCR/LoadRoutingData.php - namespace Acme\DemoBundle\DataFixtures\PHPCR; +Now you know the basics of routing, you can add a new route to the tree using +Doctrine:: + + // src/AppBundle/DataFixtures/PHPCR/LoadQuickTourData.php + namespace AppBundle\DataFixtures\PHPCR; use Doctrine\Common\Persistence\ObjectManager; use Doctrine\Common\DataFixtures\FixtureInterface; use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\ODM\PHPCR\DocumentManager; - use PHPCR\Util\NodeHelper; - use Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\Route; - class LoadRoutingData implements FixtureInterface, OrderedFixtureInterface + class LoadQuickTourData implements FixtureInterface, OrderedFixtureInterface { - public function getOrder() - { - return 20; - } - public function load(ObjectManager $documentManager) { - if (!$documentManager instanceof DocumentManager) { - $class = get_class($documentManager); - throw new \RuntimeException("Fixture requires a PHPCR ODM DocumentManager instance, instance of '$class' given."); - } - - $session = $documentManager->getPhpcrSession(); - NodeHelper::createPath($session, '/cms/routes'); + // static content from model chapter, resulting in $content being defined + // ... $routesRoot = $documentManager->find(null, '/cms/routes'); - $route = new Route(); // set $routesRoot as the parent and 'new-route' as the node name, // this is equal to: @@ -198,27 +87,31 @@ Now you can add a new ``Route`` to the tree using Doctrine:: // $route->setParentDocument($routesRoot); $route->setPosition($routesRoot, 'new-route'); - $page = $documentManager->find(null, '/cms/simple/quick_tour'); - $route->setContent($page); + $route->setContent($content); $documentManager->persist($route); // put $route in the queue $documentManager->flush(); // save it } } - -Above we implemented the ``OrderedFixtureInterface`` so that our routes were loaded in the correct sequence relative to other fixtures. Now execute the ``doctrine:phpcr:fixtures:load`` command again. This creates a new node called ``/cms/routes/new-route``, which will display our ``quick_tour`` page when you go to ``/new-route``. -.. tip:: +.. image:: ../_images/quick_tour/the-router-new-page.png - When doing this in a real app, you may want to use a ``RedirectRoute`` - instead. +Chaining multiple Routers +------------------------- + +Usually, you want to use both the ``DynamicRouter`` for the editable routes, +but also the static routing files from Symfony for your custom logic. To be +able to do that, the CMF provides a ``ChainRouter``. This router tries each +registered router and stops on the first router that returns a match. -.. TODO write something about templates_by_class, etc. +By default, the ``ChainRouter`` overrides the Symfony router and only has the +core and dynamic router in its chain. You can add more routers to the chain in the +configuration or by tagging the router services with ``cmf_routing.router``. Final Thoughts -------------- @@ -228,7 +121,7 @@ basics of the Symfony CMF. First, you have learned about the Request flow and quickly learned each new step in this process. After that, you have learned more about the default storage layer and the routing system. -The Routing system is created together with some developers from Drupal8. In +The Routing system is created together with some developers from Drupal 8. In fact, Drupal 8 uses the Routing component of the Symfony CMF. The Symfony CMF also uses some 3rd party bundles from others and integrated them into PHPCR. In :doc:`the next chapter ` you'll learn more about diff --git a/quick_tour/the_third_party_bundles.rst b/quick_tour/the_third_party_bundles.rst index 6d2cef25..3dc5fa45 100644 --- a/quick_tour/the_third_party_bundles.rst +++ b/quick_tour/the_third_party_bundles.rst @@ -5,77 +5,81 @@ The Third Party Bundles ======================= You're still here? You already learned the basics of the Symfony CMF and you -just wanted to learn more and more? Then you can read this chapter! This +want to learn more and more? Then you can read this chapter! This chapter will walk you quickly through some other CMF bundles. Most of the -other bundles are based on the shoulders of some giants, like the KnpMenuBundle_ +other bundles are integrations of great existing bundles like the KnpMenuBundle_ or SonataAdminBundle_. The MenuBundle -------------- Let's start with the MenuBundle. If you visit the page, you can see a nice -menu. You can find the rendering of this menu in the layout view in the -AcmeDemoBundle: +menu. You can find the rendering of this menu in the base view: .. code-block:: html+jinja - + - + {{ knp_menu_render('main', { template: 'includes/main_menu.html.twig' }) }} + -As you can see, the menu is rendered by the ``knp_menu_render`` tag. This +As you can see, the menu is rendered by the ``knp_menu_render`` function. This seems a bit a strange, we are talking about the CmfMenuBundle and not the -KnpMenuBundle, aren't we? That's correct, but as a matter of facts the -CmfMenuBundle is just a tiny layer on top of the KnpMenuBundle. +KnpMenuBundle, aren't we? That's correct, but the CmfMenuBundle is just a tiny +layer on top of the KnpMenuBundle. -Normally, the argument of ``knp_menu_render()`` is the menu name to render, -but when using the CmfMenuBundle, it's a node id. In this case, the menu -contains all items implementing the ``NodeInterface`` inside the -``/cms/simple`` (since the basepath in the Standard Edition is ``/cms``). +Normally, the argument of ``knp_menu_render()`` is the menu name to render, but +when using the CmfMenuBundle, it's a node name. In this case, the menu contains +all items implementing the ``NodeInterface`` inside the ``/cms/menu/main`` path +(since the basepath in the CMF Sandbox is ``/cms/menu``). -.. note:: +Creating a new Menu Entry +~~~~~~~~~~~~~~~~~~~~~~~~~ - Apart from including a PHPCR menu provider, the CmfMenuBundle also - provides Admin classes. See the section about `Sonata Admin`_ to learn - more about this. +To add our quick tour page to the menu, we need to add a menu entry. +The menu object references the content, which in turn is referenced +by the route so that the URL can be created:: -The CreateBundle ----------------- + // src/AppBundle/DataFixtures/PHPCR/LoadQuickTourData.php + namespace AppBundle\DataFixtures\PHPCR; -You've already seen this bundle in the first chapter. This bundle integrates -the CreatePHP_ library (which uses the `Create.js`_ library) into Symfony2 -using the FOSRestBundle_. + use Doctrine\Common\Persistence\ObjectManager; + use Doctrine\Common\DataFixtures\FixtureInterface; + use Doctrine\Common\DataFixtures\OrderedFixtureInterface; + use Doctrine\ODM\PHPCR\DocumentManager; + use PHPCR\Util\NodeHelper; + use Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\Route; -The Create.js library works using a REST layer. All elements on a page get -`RDFa Mappings`_, which tells Create.js how to map the element to the document. -When you save the page, the new content is passed to the REST api and saved in -the database. - -Rendering content with RDFa mappings can be very easy, as you can see in the -Standard Edition: + class LoadQuickTourData implements FixtureInterface, OrderedFixtureInterface + { + public function load(ObjectManager $documentManager) + { + // static content from model chapter, resulting in $content being defined + // ... -.. code-block:: html+jinja + $menuMain = $documentManager->find(null, '/cms/menu/main'); + $menu = new MenuNode(); + $menu->setParentDocument($menuMain); + $menu->setName('quick-tour'); + $menu->setLabel('Quick Tour'); + $menu->setContent($content); - {% block main %} - {% createphp cmfMainContent as="rdf" %} - {{ rdf|raw }} - {% endcreatephp %} - {% endblock %} + $documentManager->persist($menu); + $documentManager->flush(); + } + } -This will output the content object using `
` elements. You can also -customize this completely by using the ``createphp_*`` functions. +Re-run the fixtures loading command and then refresh the web site. The +menu entry is added at the bottom of the menu! The BlockBundle --------------- -If you visit the homepage of the Standard Edition, you'll see three blocks: +If you visit the homepage of the Sandbox, you'll see five blocks: .. image:: ../_images/quick_tour/3rd-party-bundles-homepage.png @@ -84,12 +88,6 @@ the BlockBundle, which is a tiny layer on top of the SonataBlockBundle_. It provides the ability to store the blocks using PHPCR and it adds some commonly used blocks. -The three blocks in the Standard Edition are custom blocks. A block is handled -by a block service. You can find this service in the -``Acme\DemoBundle\Block\UnitBlockService`` class. Since the blocks are -persisted using PHPCR, it also needs a block document, which is located in -``Acme\DemoBundle\Document\UnitBlock``. - The SeoBundle ------------- @@ -97,7 +95,7 @@ There is also a SeoBundle. This bundle is build on top of the SonataSeoBundle_. It provides a way to extract SEO information from a document and to make SEO information editable using an admin. -To integrate the SeoBundle into the Standard Edition, you need to include it in +To integrate the SeoBundle into the Sandbox, you need to include it in your project with ``composer require symfony-cmf/seo-bundle`` and then register both the CMF and the Sonata bundle in the ``AppKernel``:: @@ -106,11 +104,11 @@ both the CMF and the Sonata bundle in the ``AppKernel``:: // ... public function registerBundles() { - $bundles = array( + $bundles = [ // ... new Sonata\SeoBundle\SonataSeoBundle(), new Symfony\Cmf\Bundle\SeoBundle\CmfSeoBundle(), - ); + ]; // ... } @@ -121,12 +119,12 @@ the CmfSeoBundle can extract the title from a content object: # app/config/config.yml cmf_seo: - title: "%%content_title%% | Standard Edition" + title: "%%content_title%% | CMF Sandbox" The ``%%content_title%%`` will be replaced by the title extracted from the content object. The last thing you need to do is using this title as the title element. To do this, replace the ```` tag line in the -``src/Acme/DemoBundle/Resources/views/layout.html.twig`` template with this: +``src/AppBundle/Resources/views/layout.html.twig`` template with this: .. code-block:: html+jinja @@ -142,7 +140,7 @@ you can configure a default title: # app/config/config.yml sonata_seo: page: - title: Standard Edition + title: CMF Sandbox .. caution:: @@ -158,35 +156,22 @@ Sonata Admin ------------ We have explained you that the CMF is based on a database, in order to make it -editable by an admin without changing the code. But we haven't told you how -that admin will be able to maintain the website. Now it's time to reveal how -to do that: Using the SonataAdminBundle_. All the CMF bundles that define -editable elements also provide integration to make those elements editable in -Sonata Admin. +editable by editor users without changing the code. But we haven't told you yet +how an editor is able to maintain the website. Now it's time to reveal how +to do that: Using the SonataAdminBundle_. The CmfSonataPhpcrAdminIntegrationBundle +provides admin classes for all documents provided by the core CMF bundles. -By default, all Admin classes in the CMF bundles will be activated when the -SonataDoctrinePHPCRAdminBundle_ is installed. You can switch off the Admin -class in the configuration. For instance, to disable the MenuBundle Admin -classes, you would do: +By default, the Admin classes are all deactivated. Activate them for the bundles +that you need admins for. For instance, to enable the MenuBundle Admin classes, +you would do: .. code-block:: yaml # app/config/config.yml - cmf_menu: - persistence: - phpcr: - use_sonata_admin: false - -You can also disable/enable all CMF Admin classes by configuring this on the -``cmf_core`` bundle: - -.. code-block:: yaml - - # app/config/config.yml - cmf_core: - persistence: - phpcr: - use_sonata_admin: false + cmf_sonata_phpcr_admin_integration: + bundles: + menu: + enabled: true When the Admin classes are activated, the admin can go to ``/admin`` (if you installed the SonataAdminBundle correctly) and find the well-known admin @@ -199,6 +184,9 @@ As you can see on the left, the admin uses the live admin tree, where the admin can click on the nodes to edit, remove or move them. +See the :doc:`Sonata Admin Integration Documentation <../bundles/sonata_phpcr_admin_integration/introduction>` +to learn about the configuration options for each admin. + Final Thoughts -------------- diff --git a/spelling_word_list.txt b/spelling_word_list.txt index 78964ae5..d0802fb0 100644 --- a/spelling_word_list.txt +++ b/spelling_word_list.txt @@ -23,6 +23,7 @@ checkbox ckfinder CKeditor cmf +CMS coffeescript config configurator @@ -57,6 +58,7 @@ licensor login lookup lunetics +Nginx metadata migrator mixin