diff --git a/Admin/PostAdmin.php b/Admin/PostAdmin.php index 53eb350..1ebf52d 100644 --- a/Admin/PostAdmin.php +++ b/Admin/PostAdmin.php @@ -13,8 +13,7 @@ protected function configureFormFields(FormMapper $formMapper) ->add('title') ->add('slug') ->add('text') - ->add('tags', 'tags') - ; + ->add('tags', 'tags'); } protected function configureListFields(ListMapper $listMapper) @@ -22,7 +21,6 @@ protected function configureListFields(ListMapper $listMapper) $listMapper ->addIdentifier('slug') ->add('title') - ->add('created') - ; + ->add('created'); } } \ No newline at end of file diff --git a/Bridge/Doctrine/Form/DataTransformer/EntitiesToStringTransformer.php b/Bridge/Doctrine/Form/DataTransformer/EntitiesToStringTransformer.php index a3108f3..fdc59e3 100644 --- a/Bridge/Doctrine/Form/DataTransformer/EntitiesToStringTransformer.php +++ b/Bridge/Doctrine/Form/DataTransformer/EntitiesToStringTransformer.php @@ -2,6 +2,8 @@ namespace Stfalcon\Bundle\BlogBundle\Bridge\Doctrine\Form\DataTransformer; +use Doctrine\ORM\EntityManager; +use Stfalcon\Bundle\BlogBundle\Entity\TagManager; use Symfony\Component\Form\DataTransformerInterface; use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\ArrayCollection; @@ -16,20 +18,22 @@ class EntitiesToStringTransformer implements DataTransformerInterface { /** - * @var Doctrine\ORM\EntityManager + * @var EntityManager $em */ protected $em; + /** @var string $class */ + protected $class; /** - * Constructor injection. Set entity manager to object + * Constructor injection * - * @param Doctrine\ORM\EntityManager $em Entity manager object - * - * @return void + * @param EntityManager $em + * @param string $class */ - public function __construct(\Doctrine\ORM\EntityManager $em) + public function __construct(EntityManager $em, $class) { $this->em = $em; + $this->class = $class; } /** @@ -38,6 +42,8 @@ public function __construct(\Doctrine\ORM\EntityManager $em) * @param Collection|null $collection A collection of entities or NULL * * @return string|null An string of tags or NULL + * + * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException */ public function transform($collection) { @@ -51,7 +57,7 @@ public function transform($collection) $array = array(); foreach ($collection as $entity) { - array_push($array, $entity->getText()); + $array[] = $entity->getText(); } return implode(', ', $array); @@ -63,6 +69,8 @@ public function transform($collection) * @param string|null $data Input string data * * @return Collection|null + * + * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException */ public function reverseTransform($data) { @@ -75,12 +83,11 @@ public function reverseTransform($data) if (!is_string($data)) { throw new UnexpectedTypeException($data, 'string'); } - + $repository = $this->em->getRepository($this->class); foreach ($this->_stringToArray($data) as $text) { - $tag = $this->em->getRepository("StfalconBlogBundle:Tag") - ->findOneBy(array('text' => $text)); + $tag = $repository->findOneBy(array('text' => $text)); if (!$tag) { - $tag = new \Stfalcon\Bundle\BlogBundle\Entity\Tag($text); + $tag = new $this->class($text); $this->em->persist($tag); } $collection->add($tag); @@ -93,6 +100,8 @@ public function reverseTransform($data) * Convert string of tags to array * * @param string $string + * + * @return array */ private function _stringToArray($string) { diff --git a/Bridge/Doctrine/Form/Type/TagsType.php b/Bridge/Doctrine/Form/Type/TagsType.php index 580c2af..b1a523b 100644 --- a/Bridge/Doctrine/Form/Type/TagsType.php +++ b/Bridge/Doctrine/Form/Type/TagsType.php @@ -2,6 +2,7 @@ namespace Stfalcon\Bundle\BlogBundle\Bridge\Doctrine\Form\Type; +use Doctrine\ORM\EntityManager; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Bridge\Doctrine\RegistryInterface; @@ -14,19 +15,21 @@ */ class TagsType extends AbstractType { - - protected $registry; + /** @var EntityManager $em */ + protected $em; + /** @var string $class */ + protected $class; /** * Constructor injection * - * @param RegistryInterface $registry Doctrine registry object - * - * @return void + * @param EntityManager $em + * @param string $class */ - public function __construct(RegistryInterface $registry) + public function __construct(EntityManager $em, $class) { - $this->registry = $registry; + $this->em = $em; + $this->class = $class; } /** @@ -39,8 +42,8 @@ public function __construct(RegistryInterface $registry) */ public function buildForm(FormBuilderInterface $builder, array $options) { - $builder->prependClientTransformer( - new EntitiesToStringTransformer($this->registry->getEntityManager()) + $builder->addModelTransformer( + new EntitiesToStringTransformer($this->em, $this->class) ); } diff --git a/Controller/AbstractController.php b/Controller/AbstractController.php deleted file mode 100644 index 6d2a2c6..0000000 --- a/Controller/AbstractController.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ -class AbstractController extends Controller -{ - - /** - * Added disqus_shortname to request data array - * - * @param array $requestData - * @return array - */ - protected function _getRequestDataWithDisqusShortname($requestData) - { - $config = $this->container->getParameter('stfalcon_blog.config'); - return array_merge( - $requestData, - array('disqus_shortname' => $config['disqus_shortname']) - ); - } - -} \ No newline at end of file diff --git a/Controller/CommentController.php b/Controller/CommentController.php index 23bb1f4..42d112b 100644 --- a/Controller/CommentController.php +++ b/Controller/CommentController.php @@ -5,7 +5,8 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Component\HttpFoundation\Response; -use Stfalcon\Bundle\BlogBundle\Entity\Post; + +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Controller for actions with DISQUS comments @@ -18,16 +19,22 @@ class CommentController extends Controller /** * Synchronization comments count with disqus * - * @param Post $post Post object + * @param string $slug Slug of post * * @return Response * @Route("/blog/post/{slug}/disqus-sync", name="blog_post_disqus_sync") + * + * @throws NotFoundHttpException */ - public function disqusSyncAction(Post $post) + public function disqusSyncAction($slug) { // @todo. нужно доставать полный список ЗААПРУВЛЕННЫХ комментариев или // колличество комментариев к записи (если такой метод появится в API disqus) // после чего обновлять их колличество в БД + $post = $this->get('stfalcon_blog.post.repository')->findOneBy(array('slug' => $slug)); + if (!$post) { + throw new NotFoundHttpException(); + } $post->setCommentsCount($post->getCommentsCount() + 1); $em = $this->get('doctrine.orm.entity_manager'); diff --git a/Controller/PostController.php b/Controller/PostController.php index fba49b8..88a1afd 100644 --- a/Controller/PostController.php +++ b/Controller/PostController.php @@ -3,54 +3,44 @@ namespace Stfalcon\Bundle\BlogBundle\Controller; use Symfony\Component\HttpFoundation\Response; +use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; - -use Stfalcon\Bundle\BlogBundle\Entity\Post; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * PostController * * @author Stepan Tanasiychuk */ -class PostController extends AbstractController +class PostController extends Controller { - - private function _getRequestArrayWithDisqusShortname($array) - { - $config = $this->container->getParameter('stfalcon_blog.config'); - return array_merge( - $array, - array('disqus_shortname' => $config['disqus_shortname']) - ); - } - /** * List of posts for admin * + * @param int $page Page number + * + * @return array + * * @Route("/blog/{title}/{page}", name="blog", * requirements={"page"="\d+", "title"="page"}, * defaults={"page"="1", "title"="page"}) * @Template() - * - * @param int $page Page number - * - * @return array */ public function indexAction($page) { - $allPosts = $this->get('doctrine')->getEntityManager() - ->getRepository("StfalconBlogBundle:Post")->getAllPosts(); - $posts= $this->get('knp_paginator')->paginate($allPosts, $page, 10); + $allPostsQuery = $this->get('stfalcon_blog.post.repository')->findAllPostsAsQuery(); + $posts= $this->get('knp_paginator')->paginate($allPostsQuery, $page, 10); if ($this->has('application_default.menu.breadcrumbs')) { $breadcrumbs = $this->get('application_default.menu.breadcrumbs'); $breadcrumbs->addChild('Блог')->setCurrent(true); } - return $this->_getRequestArrayWithDisqusShortname(array( - 'posts' => $posts - )); + return array( + 'posts' => $posts, + 'disqus_shortname' => $this->container->getParameter('stfalcon_blog.disqus_shortname') + ); } /** @@ -59,21 +49,28 @@ public function indexAction($page) * @Route("/blog/post/{slug}", name="blog_post_view") * @Template() * - * @param Post $post + * @param string $slug * * @return array + * + * @throws NotFoundHttpException */ - public function viewAction(Post $post) + public function viewAction($slug) { + $post = $this->get('stfalcon_blog.post.repository')->findOneBy(array('slug' => $slug)); + if (!$post) { + throw new NotFoundHttpException(); + } if ($this->has('application_default.menu.breadcrumbs')) { $breadcrumbs = $this->get('application_default.menu.breadcrumbs'); $breadcrumbs->addChild('Блог', array('route' => 'blog')); $breadcrumbs->addChild($post->getTitle())->setCurrent(true); } - return $this->_getRequestArrayWithDisqusShortname(array( - 'post' => $post - )); + return array( + 'post' => $post, + 'disqus_shortname' => $this->container->getParameter('stfalcon_blog.disqus_shortname') + ); } /** @@ -87,14 +84,11 @@ public function rssAction() { $feed = new \Zend\Feed\Writer\Feed(); - $config = $this->container->getParameter('stfalcon_blog.config'); - - $feed->setTitle($config['rss']['title']); - $feed->setDescription($config['rss']['description']); + $feed->setTitle($this->container->getParameter('stfalcon_blog.rss.title')); + $feed->setDescription($this->container->getParameter('stfalcon_blog.rss.description')); $feed->setLink($this->generateUrl('blog_rss', array(), true)); - $posts = $this->get('doctrine')->getEntityManager() - ->getRepository("StfalconBlogBundle:Post")->getAllPosts(); + $posts = $this->get('stfalcon_blog.post.repository')->findAllPosts(); foreach ($posts as $post) { $entry = new \Zend\Feed\Writer\Entry(); $entry->setTitle($post->getTitle()); @@ -117,8 +111,7 @@ public function rssAction() */ public function lastAction($count = 1) { - $posts = $this->get('doctrine')->getEntityManager() - ->getRepository("StfalconBlogBundle:Post")->getLastPosts($count); + $posts = $this->get('stfalcon_blog.post.repository')->findLastPosts($count); return array('posts' => $posts); } diff --git a/Controller/TagController.php b/Controller/TagController.php index a4969d8..6c6ed13 100644 --- a/Controller/TagController.php +++ b/Controller/TagController.php @@ -4,15 +4,16 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; +use Symfony\Bundle\FrameworkBundle\Controller\Controller; -use Stfalcon\Bundle\BlogBundle\Entity\Tag; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * TagController * * @author Stepan Tanasiychuk */ -class TagController extends AbstractController +class TagController extends Controller { /** @@ -23,15 +24,23 @@ class TagController extends AbstractController * defaults={"page"="1", "title"="page"}) * @Template() * - * @param Tag $tag - * @param int $page page number + * @param string $text text of tag + * @param int $page page number * * @return array + * + * @throws NotFoundHttpException */ - public function viewAction(Tag $tag, $page) + public function viewAction($text, $page) { + $tag = $this->get('stfalcon_blog.tag.repository')->findOneBy(array('text' => $text)); + if (!$tag) { + throw new NotFoundHttpException(); + } + + $postsQuery = $this->get('stfalcon_blog.post.repository')->findPostsByTagAsQuery($tag); $posts = $this->get('knp_paginator') - ->paginate($tag->getPosts(), $page, 10); + ->paginate($postsQuery, $page, 10); if ($this->has('menu.breadcrumbs')) { $breadcrumbs = $this->get('menu.breadcrumbs'); @@ -39,10 +48,11 @@ public function viewAction(Tag $tag, $page) $breadcrumbs->addChild($tag->getText())->setIsCurrent(true); } - return $this->_getRequestDataWithDisqusShortname(array( + return array( 'tag' => $tag, 'posts' => $posts, - )); + 'disqus_shortname' => $this->container->getParameter('stfalcon_blog.disqus_shortname') + ); } } \ No newline at end of file diff --git a/DependencyInjection/StfalconBlogExtension.php b/DependencyInjection/StfalconBlogExtension.php index 43c814a..d7e425c 100644 --- a/DependencyInjection/StfalconBlogExtension.php +++ b/DependencyInjection/StfalconBlogExtension.php @@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Definition\Processor; /** * This is the class that loads and manages StfalconBlogBundle configuration @@ -27,15 +28,42 @@ class StfalconBlogExtension extends Extension */ public function load(array $configs, ContainerBuilder $container) { - $config = array(); - foreach ($configs as $c) { - $config = array_merge($config, $c); + $config = $configs[0]; + + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + + $container->setParameter('stfalcon_blog.disqus_shortname', $config['disqus_shortname']); + $container->setParameter('stfalcon_blog.rss.title', $config['rss']['title']); + $container->setParameter('stfalcon_blog.rss.description', $config['rss']['description']); + $loader->load('orm.xml'); + if (isset($config['post']['entity'])) { + $container->setParameter('stfalcon_blog.post.entity', $config['post']['entity']); + } + if (isset($config['tag']['entity'])) { + $container->setParameter('stfalcon_blog.tag.entity', $config['tag']['entity']); } - $container->setParameter('stfalcon_blog.config', $config); + if (isset($config['post']['repository'])) { + $container->setParameter('stfalcon_blog.post.repository', $config['post']['repository']); + } + if (isset($config['tag']['repository'])) { + $container->setParameter('stfalcon_blog.tag.repository', $config['tag']['repository']); + } + $loader->load('admin.xml'); - $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); - $loader->load('service.xml'); + if (isset($config['post']['admin']['class'])) { + $container->setParameter('stfalcon_blog.post.admin.class', $config['post']['admin']['class']); + } + if (isset($config['post']['admin']['controller'])) { + $container->setParameter('stfalcon_blog.post.admin.controller', $config['post']['admin']['controller']); + } + if (isset($config['tag']['admin']['class'])) { + $container->setParameter('stfalcon_blog.tag.admin.class', $config['tag']['admin']['class']); + } + if (isset($config['tag']['admin']['controller'])) { + $container->setParameter('stfalcon_blog.tag.admin.controller', $config['tag']['admin']['controller']); + } + $loader->load('services.xml'); } } \ No newline at end of file diff --git a/Entity/BasePost.php b/Entity/BasePost.php new file mode 100644 index 0000000..faf599c --- /dev/null +++ b/Entity/BasePost.php @@ -0,0 +1,275 @@ + + * @ORM\MappedSuperclass + */ +class BasePost +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + protected $id; + + /** + * Post title + * + * @var string $title + * @Assert\NotBlank() + * @ORM\Column(name="title", type="string", length=255) + */ + protected $title = ''; + + /** + * @var string $slug + * + * @Assert\NotBlank() + * @Assert\MinLength(3) + * @ORM\Column(name="slug", type="string", length=128, unique=true) + */ + protected $slug; + + /** + * Post text + * + * @var string $text + * @Assert\NotBlank() + * @ORM\Column(name="text", type="text") + */ + protected $text; + + /** + * Post text as HTML code + * + * @var string $textAsHtml + * @ORM\Column(name="text_as_html", type="text") + */ + protected $textAsHtml; + + /** + * Tags for post + * + * @var ArrayCollection + */ + protected $tags; + + /** + * @var \DateTime $created + * + * @ORM\Column(type="datetime") + * @Gedmo\Timestampable(on="create") + */ + protected $created; + + /** + * @var \DateTime $updated + * + * @ORM\Column(type="datetime") + * @Gedmo\Timestampable(on="update") + */ + protected $updated; + + /** + * @var int $commentsCount + * + * @ORM\Column(type="integer") + */ + protected $commentsCount = 0; + + /** + * @return mixed + */ + public function getId() + { + return $this->id; + } + + /** + * Set post title + * + * @param string $title Text of the title + * + * @return void + */ + public function setTitle($title) + { + $this->title = $title; + } + + /** + * Get post title + * + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Set post slug + * + * @param string $slug Unique text identifier + * + * @return void + */ + public function setSlug($slug) + { + $this->slug = $slug; + } + + /** + * Get post slug + * + * @return string + */ + public function getSlug() + { + return $this->slug; + } + + /** + * Set post text + * + * @param string $text Text for post + * + * @return void + */ + public function setText($text) + { + $this->text = $text; + $this->textAsHtml = $this->_transformTextAsHtml($text); + } + + /** + * Get post text + * + * @return string + */ + public function getText() + { + return $this->text; + } + + /** + * Get post text as HTML code + * + * @return string + */ + public function getTextAsHtml() + { + return $this->textAsHtml; + } + + + /** + * Transform post text to html + * + * @param string $text Source post text + * + * @return string Post text as html + */ + private function _transformTextAsHtml($text) + { + // update text html code + require_once __DIR__ . '/../Resources/vendor/geshi/geshi.php'; + + $text = preg_replace_callback( + '/
\r?\n?(.*?)\r?\n?\<\/pre>/is',
+            function($data) {
+                $geshi = new \GeSHi($data[2], $data[1]);
+
+                return $geshi->parse_code();
+            }, $text
+        );
+
+        return $text;
+    }
+
+    /**
+     * Set time when post created
+     *
+     * @param \DateTime $created A time when post created
+     *
+     * @return void
+     */
+    public function setCreated(\DateTime $created)
+    {
+        $this->created = $created;
+    }
+
+    /**
+     * Get time when post created
+     *
+     * @return \DateTime
+     */
+    public function getCreated()
+    {
+        return $this->created;
+    }
+
+    /**
+     * Set time when post updated
+     *
+     * @param \DateTime $updated A time when post updated
+     *
+     * @return void
+     */
+    public function setUpdated(\DateTime $updated)
+    {
+        $this->updated = $updated;
+    }
+
+    /**
+     * Get time when post updated
+     *
+     * @return \DateTime
+     */
+    public function getUpdated()
+    {
+        return $this->updated;
+    }
+
+    /**
+     * Set comments count for post
+     *
+     * @param int $commentsCount A count of comments for post
+     *
+     * @return void
+     */
+    public function setCommentsCount($commentsCount)
+    {
+        $this->commentsCount = $commentsCount;
+    }
+
+    /**
+     * Get comments count for post
+     *
+     * @return int
+     */
+    public function getCommentsCount()
+    {
+        return $this->commentsCount;
+    }
+
+    /**
+     * This method allows a class to decide how it will react when it is treated like a string
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->getTitle()?$this->getTitle():'';
+    }
+}
\ No newline at end of file
diff --git a/Entity/BaseTag.php b/Entity/BaseTag.php
new file mode 100644
index 0000000..b7c1240
--- /dev/null
+++ b/Entity/BaseTag.php
@@ -0,0 +1,80 @@
+
+ *
+ * @ORM\MappedSuperclass
+ */
+class BaseTag
+{
+    /**
+     * @ORM\Id
+     * @ORM\Column(type="integer")
+     * @ORM\GeneratedValue(strategy="AUTO")
+     */
+    protected $id;
+
+    /**
+     * Tag text
+     *
+     * @var string $text
+     * @Assert\NotBlank()
+     * @ORM\Column(name="text", type="string", length=255)
+     */
+    protected $text = '';
+
+    /**
+     * @var ArrayCollection
+     */
+    protected $posts;
+
+    /**
+     * Get Tag id
+     *
+     * @return int
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * Set Tag text
+     *
+     * @param string $text A tag text
+     *
+     * @return void
+     */
+    public function setText($text)
+    {
+        $this->text = $text;
+    }
+
+    /**
+     * Get Tag text
+     *
+     * @return string
+     */
+    public function getText()
+    {
+        return $this->text;
+    }
+
+    /**
+     * This method allows a class to decide how it will react when it is treated like a string
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->getText()?$this->getText():'';
+    }
+}
\ No newline at end of file
diff --git a/Entity/Post.php b/Entity/Post.php
index 6ac668b..1fcffe8 100644
--- a/Entity/Post.php
+++ b/Entity/Post.php
@@ -3,104 +3,30 @@
 namespace Stfalcon\Bundle\BlogBundle\Entity;
 
 use Doctrine\Common\Collections\ArrayCollection;
-use Doctrine\ORM\Mapping as ORM;
 use Symfony\Component\Validator\Constraints as Assert;
-use Gedmo\Mapping\Annotation as Gedmo;
+use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Post entity
- *
- * @author Stepan Tanasiychuk 
- * @ORM\Table(name="blog_posts")
+ * @ORM\Table(name="blog_posts_base")
  * @ORM\Entity(repositoryClass="Stfalcon\Bundle\BlogBundle\Repository\PostRepository")
  */
-class Post
+class Post extends BasePost
 {
-    /**
-     * Post id
-     *
-     * @var integer $id
-     * @ORM\Column(name="id", type="integer")
-     * @ORM\Id
-     * @ORM\GeneratedValue(strategy="AUTO")
-     */
-    private $id;
-
-    /**
-     * Post title
-     *
-     * @var string $title
-     * @Assert\NotBlank()
-     * @ORM\Column(name="title", type="string", length=255)
-     */
-    private $title = '';
-
-    /**
-     * @var string $slug
-     *
-     * @Assert\NotBlank()
-     * @Assert\MinLength(3)
-     * @ORM\Column(name="slug", type="string", length=128, unique=true)
-     */
-    private $slug;
-
-    /**
-     * Post text
-     *
-     * @var text $text
-     * @Assert\NotBlank()
-     * @ORM\Column(name="text", type="text")
-     */
-    private $text;
-
-    /**
-     * Post text as HTML code
-     *
-     * @var text $textAsHtml
-     * @ORM\Column(name="text_as_html", type="text")
-     */
-    private $textAsHtml;
-
     /**
      * Tags for post
      *
      * @var ArrayCollection
      * @Assert\NotBlank()
-     * @ORM\ManyToMany(targetEntity="Stfalcon\Bundle\BlogBundle\Entity\Tag")
-     * @ORM\JoinTable(name="blog_posts_tags",
+     * @ORM\ManyToMany(targetEntity="Tag")
+     * @ORM\JoinTable(name="blog_posts_tags_base",
      *      joinColumns={@ORM\JoinColumn(name="post_id", referencedColumnName="id")},
      *      inverseJoinColumns={@ORM\JoinColumn(name="tag_id", referencedColumnName="id")}
      *      )
      */
-    private $tags;
-
-    /**
-     * @var \DateTime $created
-     *
-     * @ORM\Column(type="datetime")
-     * @Gedmo\Timestampable(on="create")
-     */
-    private $created;
-
-    /**
-     * @var \DateTime $updated
-     *
-     * @ORM\Column(type="datetime")
-     * @Gedmo\Timestampable(on="update")
-     */
-    private $updated;
-
-    /**
-     * @var int $commentsCount
-     *
-     * @ORM\Column(type="integer")
-     */
-    private $commentsCount = 0;
+    protected $tags;
 
     /**
      * Initialization properties for new post entity
-     *
-     * @return void
      */
     public function __construct()
     {
@@ -108,29 +34,7 @@ public function __construct()
     }
 
     /**
-     * Get post id
-     *
-     * @return int
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set tags to post
-     *
-     * @param $tags Tags collection
-     *
-     * @return void
-     */
-    public function setTags($tags)
-    {
-        $this->tags = $tags;
-    }
-
-    /**
-     * Get all tags
+     * Get tags for this post
      *
      * @return ArrayCollection
      */
@@ -140,179 +44,10 @@ public function getTags()
     }
 
     /**
-     * Set post title
-     *
-     * @param string $title Text of the title
-     *
-     * @return void
-     */
-    public function setTitle($title)
-    {
-        $this->title = $title;
-    }
-
-    /**
-     * Get post title
-     *
-     * @return string
-     */
-    public function getTitle()
-    {
-        return $this->title;
-    }
-
-    /**
-     * Set post slug
-     *
-     * @param string $slug Unique text identifier
-     *
-     * @return void
-     */
-    public function setSlug($slug)
-    {
-        $this->slug = $slug;
-    }
-
-    /**
-     * Get post slug
-     *
-     * @return string
-     */
-    public function getSlug()
-    {
-        return $this->slug;
-    }
-
-    /**
-     * Set post text
-     *
-     * @param string $text Text for post
-     *
-     * @return void
-     */
-    public function setText($text)
-    {
-        $this->text = $text;
-        $this->textAsHtml = $this->_transformTextAsHtml($text);
-    }
-
-    /**
-     * Get post text
-     *
-     * @return string
-     */
-    public function getText()
-    {
-        return $this->text;
-    }
-
-    /**
-     * Get post text as HTML code
-     *
-     * @return string
-     */
-    public function getTextAsHtml()
-    {
-        return $this->textAsHtml;
-    }
-
-
-    /**
-     * Transform post text to html
-     *
-     * @param string $text Source post text
-     *
-     * @return string Post text as html
+     * @param ArrayCollection $tags
      */
-    private function _transformTextAsHtml($text)
-    {
-        // update text html code
-        require_once __DIR__ . '/../Resources/vendor/geshi/geshi.php';
-
-        $text = preg_replace_callback(
-            '/
\r?\n?(.*?)\r?\n?\<\/pre>/is',
-            function($data) {
-                $geshi = new \GeSHi($data[2], $data[1]);
-                return $geshi->parse_code();
-            }, $text
-        );
-
-        return $text;
-    }
-
-    /**
-     * Set time when post created
-     *
-     * @param \DateTime $created A time when post created
-     *
-     * @return void
-     */
-    public function setCreated(\DateTime $created)
-    {
-        $this->created = $created;
-    }
-
-    /**
-     * Get time when post created
-     *
-     * @return \DateTime
-     */
-    public function getCreated()
-    {
-        return $this->created;
-    }
-
-    /**
-     * Set time when post updated
-     *
-     * @param \DateTime $updated A time when post updated
-     *
-     * @return void
-     */
-    public function setUpdated(\DateTime $updated)
-    {
-        $this->updated = $updated;
-    }
-
-    /**
-     * Get time when post updated
-     *
-     * @return \DateTime
-     */
-    public function getUpdated()
-    {
-        return $this->updated;
-    }
-
-    /**
-     * Set comments count for post
-     *
-     * @param int $commentsCount A count of comments for post
-     *
-     * @return void
-     */
-    public function setCommentsCount($commentsCount)
-    {
-        $this->commentsCount = $commentsCount;
-    }
-
-    /**
-     * Get comments count for post
-     *
-     * @return int
-     */
-    public function getCommentsCount()
-    {
-        return $this->commentsCount;
-    }
-
-    /**
-     * This method allows a class to decide how it will react when it is treated like a string
-     *
-     * @return string
-     */
-    public function __toString()
+    public function setTags($tags)
     {
-        return $this->getTitle();
+        $this->tags = $tags;
     }
-}
\ No newline at end of file
+}
diff --git a/Entity/Tag.php b/Entity/Tag.php
index 6a3a5a4..5b2b8e9 100644
--- a/Entity/Tag.php
+++ b/Entity/Tag.php
@@ -3,50 +3,26 @@
 namespace Stfalcon\Bundle\BlogBundle\Entity;
 
 use Doctrine\Common\Collections\ArrayCollection;
-use Doctrine\ORM\Mapping as ORM;
 use Symfony\Component\Validator\Constraints as Assert;
+use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Stfalcon\Bundle\BlogBundle\Entity\Tag
- *
- * @author Stepan Tanasiychuk 
  * @ORM\Table(name="blog_tags")
- * @ORM\Entity
+ * @ORM\Entity(repositoryClass="Stfalcon\Bundle\BlogBundle\Repository\TagRepository")
  */
-class Tag
+class Tag extends BaseTag
 {
     /**
-     * Tag id
+     * @var ArrayCollection
      *
-     * @var integer $id
-     * @ORM\Column(name="id", type="integer")
-     * @ORM\Id
-     * @ORM\GeneratedValue(strategy="AUTO")
+     * @ORM\ManyToMany(targetEntity="Post", mappedBy="tags")
      */
-    private $id;
-
-    /**
-     * Tag text
-     *
-     * @var text $text
-     * @Assert\NotBlank()
-     * @ORM\Column(name="text", type="string", length=255)
-     */
-    private $text = '';
-
-    /**
-     * @var Doctrine\Common\Collections\ArrayCollection
-     *
-     * @ORM\ManyToMany(targetEntity="Stfalcon\Bundle\BlogBundle\Entity\Post", mappedBy="tags")
-     */
-    private $posts;
+    protected $posts;
 
     /**
      * Entity constructor
      *
      * @param string $text A tag text
-     *
-     * @return void
      */
     public function  __construct($text = null)
     {
@@ -54,38 +30,6 @@ public function  __construct($text = null)
         $this->posts = new ArrayCollection();
     }
 
-    /**
-     * Get Tag id
-     *
-     * @return int
-     */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Set Tag text
-     *
-     * @param string $text A tag text
-     *
-     * @return void
-     */
-    public function setText($text)
-    {
-        $this->text = $text;
-    }
-
-    /**
-     * Get Tag text
-     *
-     * @return string
-     */
-    public function getText()
-    {
-        return $this->text;
-    }
-
     /**
      * Get posts for this tag
      *
@@ -97,12 +41,10 @@ public function getPosts()
     }
 
     /**
-     * This method allows a class to decide how it will react when it is treated like a string
-     *
-     * @return string
+     * @param ArrayCollection $posts
      */
-    public function __toString()
+    public function setPosts($posts)
     {
-        return $this->getText();
+        $this->posts = $posts;
     }
-}
\ No newline at end of file
+}
diff --git a/Repository/PostRepository.php b/Repository/PostRepository.php
index d0e21e2..cd6b328 100644
--- a/Repository/PostRepository.php
+++ b/Repository/PostRepository.php
@@ -13,42 +13,37 @@ class PostRepository extends EntityRepository
 {
 
     /**
-     * Get all posts
+     * Find all posts
      *
      * @return array
      */
-    public function getAllPosts()
+    public function findAllPosts()
     {
-        $query = $this->getEntityManager()->createQuery('
-            SELECT
-                p
-            FROM
-                StfalconBlogBundle:Post p
-            ORDER BY
-                p.created DESC
-            ');
+        $query = $this->findAllPostsAsQuery();
 
         return $query->getResult();
     }
 
     /**
-     * Get last posts
+     * @return mixed
+     */
+    public function findAllPostsAsQuery()
+    {
+        return $this->createQueryBuilder('p')
+            ->orderBy('p.created', 'DESC')
+            ->getQuery();
+    }
+
+    /**
+     * Find last posts
      *
      * @param integer $count Max count of returned posts
      *
      * @return array
      */
-    public function getLastPosts($count = null)
+    public function findLastPosts($count = null)
     {
-        $query = $this->getEntityManager()->createQuery('
-            SELECT
-                p
-            FROM
-                StfalconBlogBundle:Post p
-            ORDER BY
-                p.created DESC
-            ');
-
+        $query = $this->findAllPostsAsQuery();
         if ((int) $count) {
             $query->setMaxResults($count);
         }
@@ -56,4 +51,21 @@ public function getLastPosts($count = null)
         return $query->getResult();
     }
 
+    /**
+     * Find posts by tag as query
+     *
+     * @param mixed $tag
+     *
+     * @return mixed
+     */
+    public function findPostsByTagAsQuery($tag)
+    {
+        return $this->createQueryBuilder('p')
+            ->join('p.tags', 't')
+            ->where('t = :tag')
+            ->orderBy('p.created', 'DESC')
+            ->setParameter('tag', $tag)
+            ->getQuery();
+    }
+
 }
\ No newline at end of file
diff --git a/Resources/config/admin.xml b/Resources/config/admin.xml
new file mode 100644
index 0000000..14862fa
--- /dev/null
+++ b/Resources/config/admin.xml
@@ -0,0 +1,31 @@
+
+
+
+
+    
+        Stfalcon\Bundle\BlogBundle\Admin\PostAdmin
+        Stfalcon\Bundle\BlogBundle\Admin\TagAdmin
+
+        SonataAdminBundle:CRUD
+        SonataAdminBundle:CRUD
+    
+
+    
+        
+            
+            
+            %stfalcon_blog.post.entity%
+            %stfalcon_blog.post.admin.controller%
+        
+
+        
+            
+            
+            %stfalcon_blog.tag.entity%
+            %stfalcon_blog.tag.admin.controller%
+        
+    
+
+
diff --git a/Resources/config/orm.xml b/Resources/config/orm.xml
new file mode 100644
index 0000000..ab941fa
--- /dev/null
+++ b/Resources/config/orm.xml
@@ -0,0 +1,29 @@
+
+
+
+
+    
+        Stfalcon\Bundle\BlogBundle\Entity\Post
+        Stfalcon\Bundle\BlogBundle\Entity\Tag
+
+        Stfalcon\Bundle\BlogBundle\Repository\PostRepository
+        Stfalcon\Bundle\BlogBundle\Repository\TagRepository
+    
+
+    
+        
+            %stfalcon_blog.post.entity%
+        
+
+        
+            %stfalcon_blog.tag.entity%
+        
+    
+
+
diff --git a/Resources/config/service.xml b/Resources/config/service.xml
deleted file mode 100644
index c9c2597..0000000
--- a/Resources/config/service.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-    
-        Stfalcon\Bundle\BlogBundle\Admin\PostAdmin
-        Stfalcon\Bundle\BlogBundle\Entity\Post
-
-        Stfalcon\Bundle\BlogBundle\Admin\TagAdmin
-        Stfalcon\Bundle\BlogBundle\Entity\Tag
-    
-
-    
-        
-            
-            
-            %stfalcon_blog.admin.post.entity%
-            
-        
-        
-            
-            
-            %stfalcon_blog.admin.tag.entity%
-            
-        
-
-        
-            
-            
-        
-
-        
-            
-            
-        
-    
-
\ No newline at end of file
diff --git a/Resources/config/services.xml b/Resources/config/services.xml
new file mode 100644
index 0000000..ef92819
--- /dev/null
+++ b/Resources/config/services.xml
@@ -0,0 +1,19 @@
+
+
+
+
+    
+        
+            
+            
+        
+
+        
+            
+            
+            %stfalcon_blog.tag.entity%
+        
+    
+
\ No newline at end of file
diff --git a/Resources/doc/index.md b/Resources/doc/index.md
index 551fc42..ebaa8b9 100644
--- a/Resources/doc/index.md
+++ b/Resources/doc/index.md
@@ -106,10 +106,23 @@ In YAML:
 # app/config/config.yml
 # StfalconBlogBundle Configuration
 stfalcon_blog:
-    disqus_shortname: "your-disqus-shortname-goes-here"
+    disqus_shortname:     ~ # Required
     rss:
-        title: "your-blog-title-goes-here"
-        description: "your-blog-description-goes-here"
+        title:                ~
+        description:          ~
+    post:
+        entity:               ~ # Required
+        repository:           ~ # Required
+        admin:
+            class:                Stfalcon\Bundle\BlogBundle\Admin\PostAdmin
+            controller:           SonataAdminBundle:CRUD
+    tag:
+        entity:               ~ # Required
+        repository:           ~ # Required
+        admin:
+            class:                Stfalcon\Bundle\BlogBundle\Admin\TagAdmin
+            controller:           SonataAdminBundle:CRUD
+
 
 # Sonata Configuration
 sonata_block: