Frontend (blog module part 4)

Lets define the router first.
Create the file in: etc/frontend/routes.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="standard">
        <route id="blog" frontName="blog">
            <module name="Softanis_Blog" />
        </route>
    </router>
</config>

Now create the controller: Controller/Index/Index.php

namespace Softanis\Blog\Controller\Index;

use \Magento\Framework\App\Action\Action;

class Index extends Action
{
    /** @var  \Magento\Framework\View\Result\Page */
    protected $resultPageFactory;
    /**
     * @param \Magento\Framework\App\Action\Context $context
     */
    public function __construct(\Magento\Framework\App\Action\Context $context,
                                \Magento\Framework\View\Result\PageFactory $resultPageFactory)
    {
        $this->resultPageFactory = $resultPageFactory;
        parent::__construct($context);
    }

    /**
     * Blog Index, shows a list of recent blog posts.
     *
     * @return \Magento\Framework\View\Result\PageFactory
     */
    public function execute()
    {
        return $this->resultPageFactory->create();
    }
}

This will map to: /blog/ /blog/index and /blog/index/index.

To display content in the router, lets create the block class and template file.
Crate Block class file in: Block/PostList.php

namespace Softanis\Blog\Block;

use Softanis\Blog\Api\Data\PostInterface;
use Softanis\Blog\Model\ResourceModel\Post\Collection as PostCollection;

class PostList extends \Magento\Framework\View\Element\Template implements
    \Magento\Framework\DataObject\IdentityInterface
{
    /**
     * @var \Softanis\Blog\Model\ResourceModel\Post\CollectionFactory
     */
    protected $_postCollectionFactory;

    /**
     * Construct
     *
     * @param \Magento\Framework\View\Element\Template\Context $context
     * @param \Softanis\Blog\Model\ResourceModel\Post\CollectionFactory $postCollectionFactory,
     * @param array $data
     */
    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        \Softanis\Blog\Model\ResourceModel\Post\CollectionFactory $postCollectionFactory,
        array $data = []
    ) {
        parent::__construct($context, $data);
        $this->_postCollectionFactory = $postCollectionFactory;
    }

    /**
     * @return \Softanis\Blog\Model\ResourceModel\Post\Collection
     */
    public function getPosts()
    {
        // Check if posts has already been defined
        // makes our block nice and re-usable! We could
        // pass the 'posts' data to this block, with a collection
        // that has been filtered differently!
        if (!$this->hasData('posts')) {
            $posts = $this->_postCollectionFactory
                ->create()
                ->addFilter('is_active', 1)
                ->addOrder(
                    PostInterface::CREATION_TIME,
                    PostCollection::SORT_ORDER_DESC
                );
            $this->setData('posts', $posts);
        }
        return $this->getData('posts');
    }

    /**
     * Return identifiers for produced content
     *
     * @return array
     */
    public function getIdentities()
    {
        return [\Softanis\Blog\Model\Post::CACHE_TAG . '_' . 'list'];
    }

}

Now lets create layout update file to push block in the view.
Create the file: view/frontend/layout/blog_index_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block class="Softanis\Blog\Block\PostList" name="blog.list" template="Softanis_Blog::list.phtml" />
        </referenceContainer>
    </body>
</page>

Now create the template file that we mentioned in layout file.

Template file: view/frontend/templates/list.phtml

/** @var $block \Softanis\Blog\Block\PostList */ ?>
<h1><?php echo __('Latest Blog Posts') ?></h1>
<ul class="blog-post-list">
    <?php /** @var $post \Softanis\Blog\Model\Post */ ?>
    <?php foreach ($block->getPosts() as $post): ?>
        <li class="blog-post-list-item">
            <h3 class="blog-post-item-title">
                <a href="<?php echo $this->getBaseUrl().'blog/'.$post->getUrlKey() ?>"><?php echo $post->getTitle() ?></a>
            </h3>

            <div class="blog-post-item-content">
                <?php echo $post->getContent(); ?>
            </div>

            <div class="blog-post-item-meta">
                <strong><?php echo __('Created at:') ?></strong> <?php echo $post->getCreationTime() ?>
            </div>
        </li>
    <?php endforeach; ?>
</ul>

We’ve waited so long to see some real output, to do this quicker, lets insert some blog post directly in database.

INSERT INTO `softanis_blog_post`
    (`post_id`, `url_key`, `title`, `content`, `is_active`, `creation_time`, `update_time`)
    VALUES
    (NULL, 'test-1', 'test title 1', 'testing content 1', 1, '2015-08-17 22:59:00', '2015-07-17 22:59:00');
INSERT INTO `softanis_blog_post`
    (`post_id`, `url_key`, `title`, `content`, `is_active`, `creation_time`, `update_time`)
    VALUES
    (NULL, 'test-2', 'test title 2', 'testing content 2', 1, '2015-08-17 22:59:00', '2015-07-17 22:59:00');

Now, clear cache and hit /blog from your browser. You will see a list of blog posts!

To handle pretty url like /blog-post-story we have to register a custom router: Controller/Router.php, then we’ll need to edit etc/frontend/di.xml within out module to register our custom router.

Create: Controller/Router.php

namespace Softanis\Blog\Controller;

class Router implements \Magento\Framework\App\RouterInterface
{
    /**
     * @var \Magento\Framework\App\ActionFactory
     */
    protected $actionFactory;

    /**
     * Post factory
     *
     * @var \Softanis\Blog\Model\PostFactory
     */
    protected $_postFactory;

    /**
     * @param \Magento\Framework\App\ActionFactory $actionFactory
     * @param \Softanis\Blog\Model\PostFactory $postFactory
     */
    public function __construct(
        \Magento\Framework\App\ActionFactory $actionFactory,
        \Softanis\Blog\Model\PostFactory $postFactory
    ) {
        $this->actionFactory = $actionFactory;
        $this->_postFactory = $postFactory;
    }

    /**
     * Validate and Match Blog Post and modify request
     *
     * @param \Magento\Framework\App\RequestInterface $request
     * @return bool
     */
    public function match(\Magento\Framework\App\RequestInterface $request)
    {
        $url_key = trim($request->getPathInfo(), '/blog/');
        $url_key = rtrim($url_key, '/');

        /** @var \Softanis\Blog\Model\Post $post */
        $post = $this->_postFactory->create();
        $post_id = $post->checkUrlKey($url_key);
        if (!$post_id) {
            return null;
        }

        $request->setModuleName('blog')
            ->setControllerName('view')
            ->setActionName('index')
            ->setParam('post_id', $post_id);
        $request->setAlias(\Magento\Framework\Url::REWRITE_REQUEST_PATH_ALIAS, $url_key);

        return $this->actionFactory->create('Magento\Framework\App\Action\Forward');
    }
}

To handle pretty url we need to inject our router to Magento router list.
To do so, crate a file in: etc/frontend/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\App\RouterList">
        <arguments>
            <argument name="routerList" xsi:type="array">
                <item name="blog" xsi:type="array">
                    <item name="class" xsi:type="string">Softanis\Blog\Controller\Router</item>
                    <item name="disable" xsi:type="boolean">false</item>
                    <item name="sortOrder" xsi:type="string">70</item>
                </item>
            </argument>
        </arguments>
    </type>
</config>

Let’s create our controller, block, layout and template for viewing a single blog post.
File: Controller/View/Index.php

namespace Softanis\Blog\Controller\View;

use \Magento\Framework\App\Action\Action;

class Index extends Action
{
    /** @var  \Magento\Framework\View\Result\Page */
    protected $resultPageFactory;

    /**
     * @param \Magento\Framework\App\Action\Context $context
     */
    public function __construct(\Magento\Framework\App\Action\Context $context,
                                \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory
    )
    {
        $this->resultForwardFactory = $resultForwardFactory;
        parent::__construct($context);
    }

    /**
     * Blog Index, shows a list of recent blog posts.
     *
     * @return \Magento\Framework\View\Result\PageFactory
     */
    public function execute()
    {
        $post_id = $this->getRequest()->getParam('post_id', $this->getRequest()->getParam('id', false));
        /** @var \Softanis\Blog\Helper\Post $post_helper */
        $post_helper = $this->_objectManager->get('Softanis\Blog\Helper\Post');
        $result_page = $post_helper->prepareResultPost($this, $post_id);
        if (!$result_page) {
            $resultForward = $this->resultForwardFactory->create();
            return $resultForward->forward('noroute');
        }
        return $result_page;
    }
}

We’ve taken a slightly different approach to last time and instead of keeping a lot of the logic within our controller we have moved it out to a helper method.

Create our helper: Helper/Post.php

namespace Softanis\Blog\Controller\View;

use \Magento\Framework\App\Action\Action;

class Index extends Action
{
    /** @var  \Magento\Framework\View\Result\Page */
    protected $resultPageFactory;

    /**
     * @param \Magento\Framework\App\Action\Context $context
     */
    public function __construct(\Magento\Framework\App\Action\Context $context,
                                \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory
    )
    {
        $this->resultForwardFactory = $resultForwardFactory;
        parent::__construct($context);
    }

    /**
     * Blog Index, shows a list of recent blog posts.
     *
     * @return \Magento\Framework\View\Result\PageFactory
     */
    public function execute()
    {
        $post_id = $this->getRequest()->getParam('post_id', $this->getRequest()->getParam('id', false));
        /** @var \Softanis\Blog\Helper\Post $post_helper */
        $post_helper = $this->_objectManager->get('Softanis\Blog\Helper\Post');
        $result_page = $post_helper->prepareResultPost($this, $post_id);
        if (!$result_page) {
            $resultForward = $this->resultForwardFactory->create();
            return $resultForward->forward('noroute');
        }
        return $result_page;
    }
}

Create our Block: Block/PostView.php

namespace Softanis\Blog\Block;

class PostView extends \Magento\Framework\View\Element\Template implements
    \Magento\Framework\DataObject\IdentityInterface
{

    /**
     * Construct
     *
     * @param \Magento\Framework\View\Element\Template\Context $context
     * @param \Softanis\Blog\Model\Post $post
     * @param \Softanis\Blog\Model\PostFactory $postFactory
     * @param array $data
     */
    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        \Softanis\Blog\Model\Post $post,
        \Softanis\Blog\Model\PostFactory $postFactory,
        array $data = []
    )
    {
        parent::__construct($context, $data);
        $this->_post = $post;
        $this->_postFactory = $postFactory;
    }

    /**
     * @return \Softanis\Blog\Model\Post
     */
    public function getPost()
    {
        // Check if posts has already been defined
        // makes our block nice and re-usable! We could
        // pass the 'posts' data to this block, with a collection
        // that has been filtered differently!
        if (!$this->hasData('post')) {
            if ($this->getPostId()) {
                /** @var \Softanis\Blog\Model\Post $page */
                $post = $this->_postFactory->create();
            } else {
                $post = $this->_post;
            }
            $this->setData('post', $post);
        }
        return $this->getData('post');
    }

    /**
     * Return identifiers for produced content
     *
     * @return array
     */
    public function getIdentities()
    {
        return [\Softanis\Blog\Model\Post::CACHE_TAG . '_' . $this->getPost()->getId()];
    }

}

Create our layout file: view/frontend/layout/blog_view_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block class="Softanis\Blog\Block\PostView" name="blog.list" template="Softanis_Blog::view.phtml" />
        </referenceContainer>
    </body>
</page>

And finally our template file: view/frontend/templates/view.phtml

/** @var $block \Softanis\Blog\Block\PostView */
/** @var $post \Softanis\Blog\Model\Post */
$post = $block->getPost();
?>
<h1 class="blog-post-item-title"><?php echo $post->getTitle() ?></h1>

<div class="blog-post-item-content">
    <?php echo $post->getContent(); ?>
</div>

<div class="blog-post-item-meta">
    <strong><?php echo __('Created at:') ?></strong> <?php echo $post->getCreationTime() ?>
</div>

To make sure all the DI configuration has generated, please run php bin/magento setup:di:compile from console.
And that’s it! Now, clear cache and vist blog/ again, and click on a blog post title. You should now see you’re on something like: blog/test-1.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s