Przeglądaj źródła

Merge pull request #27 from jaimesuez/master

[Docs-Tutorial]
Thomas 13 lat temu
rodzic
commit
3f6de23a85

+ 87 - 70
Resources/doc/tutorial/creating_your_first_admin_class/defining_admin_class.rst

@@ -14,7 +14,8 @@ First, you need to create an Admin/PostAdmin.php file
 .. code-block:: php
 
     <?php
-    namespace Sonata\NewsBundle\Admin;
+    // src/Tutorial/BlogBundle/Admin/PostAdmin.php
+    namespace Tutorial\BlogBundle\Admin;
 
     use Sonata\AdminBundle\Admin\Admin;
     use Sonata\AdminBundle\Form\FormMapper;
@@ -24,10 +25,15 @@ First, you need to create an Admin/PostAdmin.php file
 
     use Knp\Menu\ItemInterface as MenuItemInterface;
 
-    use Application\Sonata\NewsBundle\Entity\Comment;
+    use Tutorial\BlogBundle\Entity\Comment;
 
     class PostAdmin extends Admin
     {
+        /**
+         * @param \Sonata\AdminBundle\Show\ShowMapper $showMapper
+         *
+         * @return void
+         */
         protected function configureShowField(ShowMapper $showMapper)
         {
             $showMapper
@@ -39,6 +45,11 @@ First, you need to create an Admin/PostAdmin.php file
             ;
         }
 
+        /**
+         * @param \Sonata\AdminBundle\Form\FormMapper $formMapper
+         *
+         * @return void
+         */
         protected function configureFormFields(FormMapper $formMapper)
         {
             $formMapper
@@ -51,98 +62,77 @@ First, you need to create an Admin/PostAdmin.php file
                 ->with('Tags')
                     ->add('tags', 'sonata_type_model', array('expanded' => true))
                 ->end()
-                ->with('Options', array('collapsed' => true))
-                    ->add('commentsCloseAt')
-                    ->add('commentsEnabled', null, array('required' => false))
-                    ->add('commentsDefaultStatus', 'choice', array('choices' => Comment::getStatusList()))
+                ->with('Comments')
+                    ->add('comments', 'sonata_type_model')
+                ->end()
+                ->with('System Information', array('collapsed' => true))
+                    ->add('created_at')
                 ->end()
             ;
         }
 
+        /**
+         * @param \Sonata\AdminBundle\Datagrid\ListMapper $listMapper
+         *
+         * @return void
+         */
         protected function configureListFields(ListMapper $listMapper)
         {
             $listMapper
                 ->addIdentifier('title')
                 ->add('enabled')
+                ->add('abstract')
+                ->add('content')
                 ->add('tags')
-                ->add('commentsEnabled')
+                ->add('_action', 'actions', array(
+                    'actions' => array(
+                        'view' => array(),
+                        'edit' => array(),
+                        'delete' => array(),
+                    )
+                ))
             ;
         }
 
+        /**
+         * @param \Sonata\AdminBundle\Datagrid\DatagridMapper $datagridMapper
+         *
+         * @return void
+         */
         protected function configureDatagridFilters(DatagridMapper $datagridMapper)
         {
             $datagridMapper
                 ->add('title')
                 ->add('enabled')
-                ->add('tags', 'orm_many_to_many', array('filter_field_options' => array('expanded' => true, 'multiple' => true)))
-                ->add('with_open_comments', 'callback', array(
-                    'template' => 'SonataAdminBundle:CRUD:filter_callback.html.twig',
-                    'filter_options' => array(
-                        'filter' => array($this, 'getWithOpenCommentFilter'),
-                        'type'   => 'checkbox'
-                    ),
-                    'filter_field_options' => array(
-                        'required' => false
-                    )
-                ))
+                ->add('tags', null, array('field_options' => array('expanded' => true, 'multiple' => true)))
             ;
         }
+    }
 
-        public function getWithOpenCommentFilter($queryBuilder, $alias, $field, $value)
-        {
-            if (!$value) {
-                return;
-            }
-
-            $queryBuilder->leftJoin(sprintf('%s.comments', $alias), 'c');
-            $queryBuilder->andWhere('c.status = :status');
-            $queryBuilder->setParameter('status', Comment::STATUS_MODERATE);
-        }
-
-        protected function configureSideMenu(MenuItemInterface $menu, $action, Admin $childAdmin = null)
-        {
-            if (!$childAdmin && !in_array($action, array('edit'))) {
-                return;
-            }
-
-            $admin = $this->isChild() ? $this->getParent() : $this;
-
-            $id = $admin->getRequest()->get('id');
+Second, register the PostAdmin class inside the DIC in your config file:
 
-            $menu->addChild(
-                $this->trans('view_post'),
-                array('uri' => $admin->generateUrl('edit', array('id' => $id)))
-            );
+.. code-block:: yaml
 
-            $menu->addChild(
-                $this->trans('link_view_comment'),
-                array('uri' => $admin->generateUrl('sonata.news.admin.comment.list', array('id' => $id)))
-            );
-        }
-    }
+    # app/config/config.yml
+    services:
+       tutorial.blog.admin.post:
+          class: Tutorial\BlogBundle\Admin\PostAdmin
+          tags:
+            - { name: sonata.admin, manager_type: orm, group: tutorial_blog, label: post }
+          arguments: [null, Tutorial\BlogBundle\Entity\Post, TutorialBlogBundle:PostAdmin]
 
-Second, register the PostAdmin class inside the DIC in your config file:
+Or if you're using a XML configuration file:
 
 .. code-block:: xml
 
-    <service id="sonata.news.admin.post" class="Sonata\NewsBundle\Admin\PostAdmin">
-        <tag name="sonata.admin" manager_type="orm" group="sonata_blog" label="post"/>
+    <service id="tutorial.blog.admin.post" class="Tutorial\BlogBundle\Admin\PostAdmin">
+        <tag name="sonata.admin" manager_type="orm" group="tutorial_blog" label="post"/>
 
         <argument/>
-        <argument>Sonata\NewsBundle\Entity\Post</argument>
-        <argument>SonataNewsBundle:PostAdmin</argument>
+        <argument>Tutorial\BlogBundle\Entity\Post</argument>
+        <argument>TutorialBlogBundle:PostAdmin</argument>
     </service>
 
-Or if you're using a YML configuration file:
-
-.. code-block:: yaml
-
-    services:
-       sonata.news.admin.post:
-          class: Sonata\NewsBundle\Admin\PostAdmin
-          tags:
-            - { name: sonata.admin, manager_type: orm, group: sonata_blog, label: post }
-          arguments: [null, Sonata\NewsBundle\Entity\Post, SonataNewsBundle:PostAdmin]
 
 These is the minimal configuration required to display the entity inside the
 dashboard and interact with the CRUD interface. Following this however, you will
@@ -160,7 +150,8 @@ Tweak the TagAdmin class
 .. code-block:: php
 
     <?php
-    namespace Sonata\NewsBundle\Admin;
+    // src/Tutorial/BlogBundle/Admin/TagAdmin.php
+    namespace Tutorial\BlogBundle\Admin;
 
     use Sonata\AdminBundle\Admin\Admin;
     use Sonata\AdminBundle\Datagrid\ListMapper;
@@ -168,6 +159,8 @@ Tweak the TagAdmin class
     use Sonata\AdminBundle\Validator\ErrorElement;
     use Sonata\AdminBundle\Form\FormMapper;
 
+    use Tutorial\BlogBundle\Entity\Tag;
+
     class TagAdmin extends Admin
     {
         /**
@@ -202,7 +195,6 @@ Tweak the TagAdmin class
         {
             $listMapper
                 ->addIdentifier('name')
-                ->add('slug')
                 ->add('enabled')
             ;
         }
@@ -222,13 +214,28 @@ Tweak the TagAdmin class
         }
     }
 
+And register the TagAdmin class inside the DIC in your config file:
+
+.. code-block:: yaml
+
+    # app/config/config.yml
+    services:
+       #...
+       tutorial.blog.admin.tag:
+          class: Tutorial\BlogBundle\Admin\TagAdmin
+          tags:
+            - { name: sonata.admin, manager_type: orm, group: tutorial_blog, label: tag }
+          arguments: [null, Tutorial\BlogBundle\Entity\Tag, TutorialBlogBundle:TagAdmin]
+
+
 Tweak the CommentAdmin class
 ----------------------------
 
 .. code-block:: php
 
     <?php
-    namespace Sonata\NewsBundle\Admin;
+    // src/Tutorial/BlogBundle/Admin/TagAdmin.php
+    namespace Tutorial\BlogBundle\Admin;
 
     use Sonata\AdminBundle\Admin\Admin;
     use Sonata\AdminBundle\Form\FormMapper;
@@ -249,7 +256,6 @@ Tweak the CommentAdmin class
         {
             if(!$this->isChild()) {
                 $formMapper->add('post', 'sonata_type_model', array(), array('edit' => 'list'));
-    //            $formMapper->add('post', 'sonata_type_admin', array(), array('edit' => 'inline'));
             }
 
             $formMapper
@@ -257,7 +263,6 @@ Tweak the CommentAdmin class
                 ->add('email')
                 ->add('url', null, array('required' => false))
                 ->add('message')
-                ->add('status', 'choice', array('choices' => Comment::getStatusList(), 'expanded' => true, 'multiple' => false))
             ;
         }
 
@@ -282,7 +287,6 @@ Tweak the CommentAdmin class
         {
             $listMapper
                 ->addIdentifier('name')
-                ->add('getStatusCode', 'text', array('label' => 'status_code', 'sortable' => 'status'))
                 ->add('post')
                 ->add('email')
                 ->add('url')
@@ -298,7 +302,7 @@ Tweak the CommentAdmin class
 
             $actions['enabled'] = array(
                 'label' => $this->trans('batch_enable_comments'),
-                'ask_confirmation' => false,
+                'ask_confirmation' => true,
             );
 
             $actions['disabled'] = array(
@@ -309,3 +313,16 @@ Tweak the CommentAdmin class
             return $actions;
         }
     }
+
+And register the TagAdmin class inside the DIC in your config file:
+
+.. code-block:: yaml
+
+    # app/config/config.yml
+    services:
+       #...
+       tutorial.blog.admin.comment:
+          class: Tutorial\BlogBundle\Admin\CommentAdmin
+          tags:
+            - { name: sonata.admin, manager_type: orm, group: tutorial_blog, label: comment }
+          arguments: [null, Tutorial\BlogBundle\Entity\Comment, TutorialBlogBundle:CommentAdmin]

+ 7 - 3
Resources/doc/tutorial/creating_your_first_admin_class/defining_crud_controller.rst

@@ -17,7 +17,9 @@ CommentAdminController
 .. code-block:: php
 
     <?php
-    namespace Sonata\NewsBundle\Controller;
+    // src/Tutorial/BlogBundle/Controller/CommentAdminController.php
+
+    namespace Tutorial\BlogBundle\Controller;
 
     use Sonata\AdminBundle\Controller\CRUDController as Controller;
     
@@ -32,7 +34,8 @@ PostAdminController
 .. code-block:: php
 
     <?php
-    namespace Sonata\NewsBundle\Controller;
+    // src/Tutorial/BlogBundle/Controller/PostAdminController.php
+    namespace Tutorial\BlogBundle\Controller;
 
     use Sonata\AdminBundle\Controller\CRUDController as Controller;
 
@@ -47,7 +50,8 @@ TagAdminController
 .. code-block:: php
 
     <?php
-    namespace Sonata\NewsBundle\Controller;
+    // src/Tutorial/BlogBundle/Controller/TagAdminController.php
+    namespace Tutorial\BlogBundle\Controller;
 
     use Sonata\AdminBundle\Controller\CRUDController as Controller;
 

+ 154 - 194
Resources/doc/tutorial/creating_your_first_admin_class/defining_entities.rst

@@ -8,260 +8,220 @@ entities as provided by Doctrine.
 Model definition
 ----------------
 
-Comment
-~~~~~~~
+No we need to create the entities that will be used in the blog:
+
+Post
+~~~~
 
 .. code-block:: php
 
     <?php
+    // src/Tutorial/BlogBundle/Entity/Post.php
+    namespace Tutorial\BlogBundle\Entity;
 
-    class Comment
+    use Doctrine\ORM\Mapping as ORM;
+
+    /**
+     * @ORM\Entity
+     */
+    class Post
     {
-        const STATUS_MODERATE   = 2;
-        const STATUS_VALID   = 1;
-        const STATUS_INVALID = 0;
 
-        protected $name;
-        protected $email;
-        protected $url;
-        protected $message;
-        protected $createdAt;
-        protected $updatedAt;
-        protected $status = self::STATUS_VALID;
-        protected $post;
 
-        public static function getStatusList()
-        {
-            return array(
-                self::STATUS_MODERATE => 'moderate',
-                self::STATUS_INVALID => 'invalid',
-                self::STATUS_VALID   => 'valid',
-            );
-        }
+        /**
+         * @ORM\Id
+         * @ORM\Column(type="integer")
+         * @ORM\GeneratedValue(strategy="AUTO")
+         */
+        protected $id;
 
-        public function preInsert($object)
-        {
-            $object->setCreatedAt(new \DateTime);
-            $object->setUpdatedAt(new \DateTime);
-        }
 
-        public function preUpdate($object)
-        {
-            $object->setUpdatedAt(new \DateTime);
-        }
-    }
+        /**
+         * @ORM\Column(type="string", length="255")
+         * @validation:Validation({
+         *      @validation:MinLength(limit=10),
+         *      @validation:NotBlank(),
+         *      @validation:MaxLength(limit=255)
+         * })
+         */
+        protected $title;
 
-Post
-~~~~
+        /**
+         * @ORM\Column(type="text")
+         */
+        protected $abstract;
 
-.. code-block:: php
 
-    <?php
-    class Post
-    {
-        protected $title;
-        protected $slug;
-        protected $abstract;
+        /**
+         * @ORM\Column(type="text")
+         * @validation:Validation({
+         *      @validation:NotBlank()
+         * })
+         */
         protected $content;
-        protected $tags;
-        protected $comments;
+
+        /**
+         * @ORM\Column(type="boolean")
+         */
         protected $enabled;
-        protected $publicationDateStart;
-        protected $createdAt;
-        protected $updatedAt;
-        protected $commentsEnabled = true;
-        protected $commentsCloseAt;
-        protected $commentsDefaultStatus;
 
-        public function __construct()
-        {
-            $this->tags     = new \Doctrine\Common\Collections\ArrayCollection;
-            $this->comments = new \Doctrine\Common\Collections\ArrayCollection;
-        }
 
-        public function preInsert($object)
+        /**
+         * @ORM\Column(type="datetime")
+         */
+        protected $created_at;
+
+        /**
+         * @ORM\OneToMany(targetEntity="Comment", mappedBy="post")
+         */
+        protected $comments;
+
+        /**
+         * @ORM\ManyToMany(targetEntity="Tag")
+         */
+        protected $tags;
+
+        public function __toString() 
         {
-            $object->setCreatedAt(new \DateTime);
-            $object->setUpdatedAt(new \DateTime);
+          return $this->getTitle();
         }
 
-        public function preUpdate($object)
+        public function __construct()
         {
-            $object->setUpdatedAt(new \DateTime);
+            $this->tags     = new \Doctrine\Common\Collections\ArrayCollection;
+            $this->comments = new \Doctrine\Common\Collections\ArrayCollection;
+            $this->created_at = new \DateTime("now");;
         }
-    }
 
 Tag
 ~~~
 
 .. code-block:: php
 
+    <?php
+    // src/Tutorial/BlogBundle/Entity/Tag.php
+    namespace Tutorial\BlogBundle\Entity;
+
+    use Doctrine\ORM\Mapping as ORM;
+
+    /**
+     * @ORM\Entity
+     */
     class Tag
     {
+        /**
+         * @ORM\Id
+         * @ORM\Column(type="integer")
+         * @ORM\GeneratedValue(strategy="AUTO")
+         */
+        protected $id;     
+
+        /**
+         * @ORM\Column(type="string")
+         * @validation:Validation({
+         *      @validation:NotBlank()
+         * })
+         */
         protected $name;
-        protected $slug;
-        protected $createdAt;
-        protected $updatedAt;
+
+        /**
+         * @ORM\Column(type="boolean")
+         */
         protected $enabled;
+
+        /**
+         * @ORM\ManyToMany(targetEntity="Post")
+         */
         protected $posts;
 
-        public function preInsert($object)
+        public function __toString() 
         {
-            $object->setCreatedAt(new \DateTime);
-            $object->setUpdatedAt(new \DateTime);
+          return $this->getName();
         }
 
-        public function preUpdate($object)
+        public function __construct()
         {
-            $object->setUpdatedAt(new \DateTime);
+            $this->posts = new \Doctrine\Common\Collections\ArrayCollection();
         }
-    }
-
-
-Mapping definition
-------------------
 
 Comment
 ~~~~~~~
 
-.. code-block:: xml
-
-    <?xml version="1.0" encoding="utf-8"?>
-    <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xsi="http://www.w3.org/2001/XMLSchema-instance" schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
-        <entity name="Sonata\NewsBundle\Entity\Comment" table="news__comment">
-            <id name="id" type="integer" column="id">
-                <generator strategy="AUTO"/>
-            </id>
+.. code-block:: php
 
-            <field name="name"              type="string"       column="name"          />
-            <field name="url"               type="string"       column="url"           />
-            <field name="email"             type="string"       column="email"           />
-            <field name="message"           type="text"         column="message"       />
-            <field name="status"            type="integer"      column="status"        default="false" />
-            <field name="createdAt"         type="datetime"     column="created_at" />
-            <field name="updatedAt"         type="datetime"     column="updated_at" />
+    <?php
+    // src/Tutorial/BlogBundle/Entity/Comment.php
+    namespace Tutorial\BlogBundle\Entity;
 
-            <lifecycle-callbacks>
-              <lifecycle-callback type="prePersist" method="prePersist"/>
-              <lifecycle-callback type="preUpdate" method="preUpdate"/>
-            </lifecycle-callbacks>
+    use Doctrine\ORM\Mapping as ORM;
 
-            <many-to-one field="post" target-entity="Sonata\NewsBundle\Entity\Post">
-               <join-column name="post_id" referenced-column-name="id" />
-            </many-to-one>
-        </entity>
-    </doctrine-mapping>
+    /**
+     * @ORM\Entity
+     */
+    class Comment
+    {
+        /**
+         * @ORM\Id
+         * @ORM\Column(type="integer")
+         * @ORM\GeneratedValue(strategy="AUTO")
+         */
+        protected $id;  
+
+        /**
+         * @ORM\Column(type="string")
+         * @validation:Validation({
+         *      @validation:NotBlank()
+         * })
+         */
+        protected $name;
 
 
-Post
-~~~~
+        /**
+         * @ORM\Column(type="string")
+         * @validation:Validation({
+         *      @validation:NotBlank()
+         * })
+         */
+        protected $email;
 
-.. code-block:: xml
-
-    <?xml version="1.0" encoding="utf-8"?>
-    <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xsi="http://www.w3.org/2001/XMLSchema-instance" schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
-        <entity name="Sonata\NewsBundle\Entity\Post" table="news__post">
-
-            <id name="id" type="integer" column="id">
-                <generator strategy="AUTO"/>
-            </id>
-
-            <field name="title"             type="string"       column="title"           />
-            <field name="abstract"          type="text"         column="abstract"           />
-            <field name="content"           type="text"         column="content"           />
-            <field name="enabled"           type="boolean"      column="enabled"        default="false" />
-            <field name="slug"              type="string"      column="slug" />
-            <field name="publicationDateStart"   type="datetime"   column="publication_date_start"    nullable="true"/>
-            <field name="commentsEnabled"    type="boolean"   column="comments_enabled" default="true"/>
-            <field name="commentsClose_at"   type="datetime"  column="comments_close_at" nullable="true"/>
-            <field name="commentsDefaultStatus"   type="integer"  column="comments_default_status" nullable="false"/>
-            <field name="createdAt"          type="datetime"   column="created_at" />
-            <field name="updatedAt"          type="datetime"   column="updated_at" />
-
-            <lifecycle-callbacks>
-                <lifecycle-callback type="prePersist" method="prePersist"/>
-                <lifecycle-callback type="preUpdate" method="preUpdate"/>
-            </lifecycle-callbacks>
-
-            <many-to-many
-                field="tags"
-                target-entity="Sonata\NewsBundle\Entity\Tag"
-                inversed-by="posts"
-                fetch="EAGER"
-                >
-
-                <cascade>
-                   <cascade-persist />
-                </cascade>
-
-                <join-table name="news__post_tag">
-                    <join-columns>
-                        <join-column name="post_id" referenced-column-name="id"/>
-                    </join-columns>
-
-                    <inverse-join-columns>
-                        <join-column name="tag_id" referenced-column-name="id"/>
-                    </inverse-join-columns>
-                </join-table>
-            </many-to-many>
-
-            <one-to-many
-                field="comments"
-                target-entity="Sonata\NewsBundle\Entity\Comment"
-                mapped-by="post">
-
-                <cascade>
-                    <cascade-persist/>
-                </cascade>
-                <join-columns>
-                    <join-column name="id" referenced-column-name="post_id" />
-                </join-columns>
-
-                <order-by>
-                    <order-by-field name="created_at" direction="DESC" />
-                </order-by>
-
-            </one-to-many>
-        </entity>
-    </doctrine-mapping>
 
+        /**
+         * @ORM\Column(type="string")
+         */
+        protected $url;
 
-Comment
-~~~~~~~
 
-.. code-block:: xml
+        /**
+         * @ORM\Column(type="text")
+         * @validation:Validation({
+         *      @validation:NotBlank()
+         * })
+         */
+        protected $message;
 
-    <?xml version="1.0" encoding="utf-8"?>
-    <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xsi="http://www.w3.org/2001/XMLSchema-instance" schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
+        /**
+         * @ORM\ManyToOne(targetEntity="Post")
+         */
+        protected $post;
 
-        <entity name="Sonata\NewsBundle\Entity\Tag" table="news__tag">
 
-            <id name="id" type="integer" column="id">
-                <generator strategy="AUTO"/>
-            </id>
+        public function __toString() 
+        {
+          return $this->getName();
+        }
 
-            <field name="name"          type="string"       column="title"           />
-            <field name="enabled"       type="boolean"      column="enabled"        default="false" />
-            <field name="slug"          type="string"      column="slug"    />
-            <field name="createdAt"     type="datetime"   column="created_at" />
-            <field name="updatedAt"     type="datetime"   column="updated_at" />
 
-            <lifecycle-callbacks>
-                <lifecycle-callback type="prePersist" method="prePersist"/>
-                <lifecycle-callback type="preUpdate" method="preUpdate"/>
-            </lifecycle-callbacks>
 
-            <many-to-many field="posts" target-entity="Sonata\NewsBundle\Entity\Post" mapped-by="tags" >
-            </many-to-many>
+Generate getter and setter
+--------------------------
 
-        </entity>
+Fill the entities with getters and setters running the command:
 
-    </doctrine-mapping>
+  php app/console doctrine:generate:entities Tutorial
 
+Creating Database
+-----------------
 
-Generate getter and setter
---------------------------
+Create the database related to the entities and the mapping running:
 
-Run the doctrine command "doctrine:generate:entities" to fill in the relevant
-getter/setter methods for your new entities. This is usually accomplished by
-using the "console" application in your application directory.
+  php app/console doctrine:schema:update --force

+ 104 - 0
Resources/doc/tutorial/creating_your_first_admin_class/installation.rst

@@ -0,0 +1,104 @@
+Installation
+============
+
+Download bundles
+----------------
+
+To begin, add the dependent bundles to the ``vendor/bundles`` directory. Add
+the following lines to the file ``deps``::
+
+  [SonatajQueryBundle]
+      git=http://github.com/sonata-project/SonatajQueryBundle.git
+      target=/bundles/Sonata/jQueryBundle
+
+  [SonataUserBundle]
+      git=http://github.com/sonata-project/SonataUserBundle.git
+      target=/bundles/Sonata/UserBundle
+
+  [SonataAdminBundle]
+      git=http://github.com/sonata-project/SonataAdminBundle.git
+      target=/bundles/Sonata/AdminBundle
+
+  [KnpMenuBundle]
+      git=https://github.com/KnpLabs/KnpMenuBundle.git
+      target=/bundles/Knp/Bundle/MenuBundle
+
+  [KnpMenu]
+      git=https://github.com/KnpLabs/KnpMenu.git
+      target=/knp/menu
+
+  [SonataDoctrineORMAdminBundle]
+      git=http://github.com/sonata-project/SonataDoctrineORMAdminBundle.git
+      target=/bundles/Sonata/DoctrineORMAdminBundle
+
+
+and run::
+
+  bin/vendors install
+
+Configuration
+-------------
+
+Next, be sure to enable the bundles in your autoload.php and AppKernel.php
+files:
+
+.. code-block:: php
+
+  <?php
+  // app/autoload.php
+  $loader->registerNamespaces(array(
+      // ...
+      'Sonata'                         => __DIR__.'/../vendor/bundles',
+      'Knp\Bundle'                     => __DIR__.'/../vendor/bundles',
+      'Knp\Menu'                       => __DIR__.'/../vendor/knp/menu/src',
+      // ...
+  ));
+
+  // app/AppKernel.php
+  public function registerBundles()
+  {
+      return array(
+          // ...
+          new Sonata\jQueryBundle\SonatajQueryBundle(),
+          new Sonata\AdminBundle\SonataAdminBundle(),
+          new Knp\Bundle\MenuBundle\KnpMenuBundle(),
+          new Sonata\DoctrineORMAdminBundle\SonataDoctrineORMAdminBundle(),
+          // ...
+      );
+  }
+
+The bundle also contains several routes. Import them by adding the following
+code to your application's routing file:
+
+.. code-block:: yaml
+
+    # app/config/routing.yml
+    admin:
+        resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml'
+        prefix: /admin
+
+    _sonata_admin:
+        resource: .
+        type: sonata_admin
+        prefix: /admin
+
+The last step is to generate the blog bundle in which we will work.
+
+  php app/console generate:bundle --namespace=Tutorial/BlogBundle
+
+And we enable it:
+
+.. code-block:: php
+
+  <?php
+  // app/autoload.php
+  $loader->registerNamespaces(array(
+      // ...
+      'Tutorial'      => __DIR__.'/../src',
+      // ...
+  ));
+
+At this point you can access to the dashboard with the url:
+
+  http://yoursite.local/admin/dashboard
+