Magento 2 module development – A comprehensive guide – Part 1

This article will discuss the following topics:

 

1) Requirements, documents, installation

The most significant document sources for Magento 2.0 can be reached at the following two links: http://devdocs.magento.com/ https://github.com/magento/magento2 Before installing Magento 2.0, you need to provide the minimal settings of the development environment to be able to install the system. You can find detailed information here: http://devdocs.magento.com/guides/v2.0/install-gde/bk-install-guide.html The basic requirements can be seen right at the start.

  • PHP 5.5.x and greater versions
  • MySQL 5.6.x and greater versions

If you do not currently have these, you need to update your local development environment before installation.

You can use three methods for installation.

1. In the first, you simply download the two available versions from the official Magento website. One of them includes only the basic system, the other provides some Sample Data to the system during the installation process.   The two packages are available here: https://www.magentocommerce.com/download

2. According to the second method, you clone the Magento system from Github. It has the advantage that you can always update the system with Git in your development environment. It is important to mention that you get the develop branch while doing the Git cloning, so after the cloning has been finished, it is worth switching over (git checkout) to 2.0 or merchant beta branches.   Magento 2.0 Github is available at: https://github.com/magento/magento2

3. The third option is Metapackage installation. Here you install Magento 2.0 with the help of the composer after downloading the package. This means that you will get the core Magento extensions from the official Magento Repository and other “components” from another repository. Metapackage installation guide: devdocs magento integrator install

 

2) Appropriate IDE settings before starting developments

After you have successfully installed Magento 2.0 in your development environment and it functions properly, it is recommended to set the IDE to be used for developments. In this article it refers to Jetbrains PhpStorm. Magento 2.0 applies the following: devdocs magento tech stack

First, after opening the project, it is recommended to run the Detect PSR-0 Namspace Roots command in PhpStorm, which can be found in the menu, but IDE will show it automatically anyway. The next step is setting the appropriate PHP CodeSniffer. By default, IDE supports PSR-1 and PSR-2 coding standards, so choosing PSR-2 is a good idea, but I recommend using PSR-4 (note, it is not in PhpStorm by default).

I’m not writing about making these settings in IDE. If you open an etc/module.xml file belonging to any extension in the vendor/magento directory, you can see that URNs are not resolved. Magento 2.0 has a good number of development commands that can be helpful throughout the development process. You can see the list of these commands the following way:

  • enter the bin directory in your terminal
  • give the “php magento” command
  • as a result, all the commands are listed that can be used in the development process

You can solve the URN resolve problem with the following command: php bin/magento dev:urn-catalog:generate .idea/misc.xml (you can see that it is not run from the bin directory, but from the root directory) Before the development starts, switch off the whole cache (in the admin area or with the help of the php Magento commands mentioned above).

If you make your developments using Apache servers, you should set the developer mode as SetEnv MAGE_MODE developer in the .htaccess file located in the root directory. It is also recommended to switch off xdebug (php.ini setting) in your development environment so that processes can run much faster.

[bctt tweet=”It is recommended to set the IDE properties to be used for Magento 2 developments.” username=”aionhill”]

 

3) Structuring and creating our basic Magento 2.0 extension

After installing Magento 2.0 (not cloned from Github), we immediately realize that the familiar app/code directory is missing and all core Magento modules are found under the vendor/magento directory. Not to worry about it, the first step is to create the app/code directory. After finishing with that, we can create the base directory of our own module (Vendor/Module). In our case, it is Aion/Test directory.   Next, two directories are created:

  • app/code/Aion/Test/etc
  • app/code/Aion/Test/Helper

The module’s base definition file is placed in the app/code/Aion/Test/etc, under the name module.xml

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Aion_Test" setup_version="2.0.0">
        <sequence>
            <module name="Magento_Store"/>
        </sequence>
    </module>
</config>

We define the name of the module in the file (Vendor_Module -> Aion_Test), the version number (2.0.0 in our case) and the dependencies, which the core Magento_Store module. In the next step, we create the Helper file. Our helper file is this: app/code/Aion/Test/Helper/Data.php. Let’s see what it contains:

<?php
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Helper;

/**
 * Aion Test helper
 */
class Data extends \Magento\Framework\App\Helper\AbstractHelper
{
    /**
     * Path to store config if extension is enabled
     *
     * @var string
     */
    const XML_PATH_ENABLED = 'aion/basic/enabled';

    /**
     * Check if extension enabled
     *
     * @return string|null
     */
    public function isEnabled()
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_ENABLED,
            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
        );
    }
}

 

It is enough to create an “empty” helper file, however, we have already implemented our first function (public function is Enabled()) and a constant value. The string defined in the constant value will get its meaning later in the extension’s admin configuration panel (system.xml). Next, we insert the registration.php file in the module directory (app/code/Aion/Test), which is used for “recognizing” the module within the Magento 2.0 system. Let’s see what it includes:

 

<?php
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Aion_Test',
    __DIR__
); 

 

We can copy and insert the file’s content from any of the core Magento 2.0 modules, the point is to have our own module at the second parameter (Vendor_Modul -> Aion_Test). Since Composer is used by the core Magento 2.0 modules for updates, we too create our own composer.json file. It includes the following:

 

{
    "name": "aion/module-test",
    "description": "N/A",
    "require": {
        "php": "~5.5.0|~5.6.0|~7.0.0",
        "magento/module-config": "100.0.*",
        "magento/module-store": "100.0.*",
        "magento/module-backend": "100.0.*",
        "magento/framework": "100.0.*"
    },
    "type": "magento2-module",
    "version": "100.0.2",
    "license": [
        "OSL-3.0",
        "AFL-3.0"
    ],
    "autoload": {
        "files": [
            "registration.php"
        ],
        "psr-4": {
            "Aion\\Test\\": ""
        }
    }
}

 

The composer.json includes the basic information of our module, e.g. dependencies, licence, version data.

Almost done, but there are still two files missing for a nicely built-up module. These two files are located in the module’s directory (app/code/Aion/Test): README.md and COPYING.txt.

The first one is a Git description, the second one contains the licence information, which is OSL 3.0 in our case. So our module structure looks like this: app/code

Aion/

Test/

etc/module.xml

Helper/Data.php

composer.json

COPYING.txt

README.md

registration.php

 

Next, we need to activate the module and check if Magento 2.0 has recognized it appropriately. In the Magento 1.x system this could be carried out by updating the frontend or admin page, however, with version 2.0, we need to apply the commands already described earlier.

Now enter the /bin directory in the terminal and execute the php magento module:status command. If everything has been done correctly, then the Enabled modules list and the Disabled modules list (inactive or not yet enabled) will be listed. In the latter we will find our own module: Aion_Test. If our own module is not displayed, some mistake has been made.

Next, we execute the php magento module:enable Aion_Test command in the /bin directory in the terminal. Now we can see that the module is recognized and approved (enabled) by the system. This means that ’Aion_Test’ => 1 entry has been placed in the array within the app/etc/config.php file. Then you get a notification to “register” the module on a database level as well.

So you give the php magento setup:upgrade command in the terminal. During the command execution, Magento 2.0 runs through all the core modules and custom modules and updates the system, i.e. runs the database schematics and data scripts if it finds a higher version number than the current one. In the case of our module it means that the 2.0.0 version number, as described earlier, will be entered in the setup_module table.

Now, if we log in to the admin area and select the Store / Configuration menu and then Advanced / Advanced page, then our module is displayed in the list in an “enabled” stage. So our basic module is finished.

 

4) Managing module admin configuration and user roles

After our basic module is finished, we should create its admin configuration and user roles settings. First, we create the file needed for configuration, which will be located in the app/code/Aion/Test/etc/adminhtml directory under the name system.xml.

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <tab id="aion_tab" translate="label" sortOrder="500">
            <label>Aion Extensions</label>
        </tab>
        <section id="aion" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1">
            <label>Test Extension</label>
            <tab>aion_tab</tab>
            <resource>Aion_Test::test</resource>
            <group id="basic" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Basic</label>
                <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Enable Extension</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
            </group>
            <group id="more" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>More</label>
                <field id="variable" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Some Variable</label>
                </field>
            </group>
        </section>
    </system>
</config>

 

We define a <tab> tag in the file, thus our module appears under a separate menu (Aion Extensions -> Test Extension) on the Stores / Configuration page in the admin area. We implement two tabs here (with basic and more IDs). In the first we place the basic No / Yes select type configuration variable, which we use for enabling or disabling the module. Its query has been implemented earlier in the Data.php file (Helper/Data.php). For the sake of an example, we create a text type configuration variable named ‘Variable’ for later use.

Additionally, an important element in the file is the <resource> tag, which will be used with user roles management implementation. In our case it is Aion_Test::test.

Next we create the file necessary for user roles management, which will be located in the app/code/Aion/Test/etc directory under the name acl.xml. This contains the following:

 

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
    <acl>
        <resources>
            <resource id="Magento_Backend::admin">
                <resource id="Magento_Backend::stores">
                    <resource id="Magento_Backend::stores_settings">
                        <resource id="Magento_Config::config">
                            <resource id="Aion_Test::test" title="Aion Test Extension Section" />
                        </resource>
                    </resource>
                </resource>
            </resource>
        </resources>
    </acl>
</config>

 

Having finished with this, we check if the user roles settings of the module have been created properly in the Role Resource (Admin user roles) section. Open the System / User Roles menu and then select any of the administrative groups (there is one by default) in which select Role Resources. Then under Custom, select Resource Access and check if you can find your own module in the role resources tree (hierarchy).

Next, we make sure if the configuration menu and its content, created earlier, appears. In the admin area we select Stores / Configuration, on the left, and check if our own menu (Aion Extensions) and its content (Test Extension) are displayed.

We define the basic configuration values in the config.xml file in the app/code/Aion/Test/etc directory:

 

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <aion>
            <basic>
                <enabled>1</enabled> 
            </basic>
        </aion>
    </default>
</config>

It is clearly visible that the tags seen in the file correspond to the IDs in the system.xml.

 

5) Basic frontend controller, block, layout and template

Now we will create our own frontend controller together with the corresponding layout and router configuration. And then we implement a template file and a block belonging to it.

First, we create the controller file, which is an Action in reality. We implement this with the Index.php located in the app/code/Aion/Test/Controller/Index/ directory. This file includes the following:

 

<?php
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Controller\Index;

use Magento\Framework\App\Action\Context;
use Magento\Framework\View\Result\PageFactory;
use Magento\Framework\Controller\Result\ForwardFactory;
use Magento\Framework\Exception\NotFoundException;
use Magento\Framework\App\RequestInterface;

/**
 * Contact index controller
 */
class Index extends \Magento\Framework\App\Action\Action
{
    /**
     * @var PageFactory
     */
    protected $resultPageFactory;

    /**
     * @var ForwardFactory
     */
    protected $resultForwardFactory;

    /**
     * @var \Aion\Test\Helper\Data
     */
    protected $helper;

    /**
     * Index constructor.
     *
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     * @param \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory
     * @param \Aion\Test\Helper\Data $helper
     */
    public function __construct(
        Context $context,
        PageFactory $resultPageFactory,
        ForwardFactory $resultForwardFactory,
        \Aion\Test\Helper\Data $helper
    ) {
        $this->resultPageFactory = $resultPageFactory;
        $this->resultForwardFactory = $resultForwardFactory;
        $this->helper = $helper;
        parent::__construct($context);
    }

    /**
     * Dispatch request
     *
     * @param RequestInterface $request
     * @return \Magento\Framework\App\ResponseInterface
     * @throws \Magento\Framework\Exception\NotFoundException
     */
    public function dispatch(RequestInterface $request)
    {
        if (!$this->helper->isEnabled()) {
            throw new NotFoundException(__('Page not found.'));
        }
        return parent::dispatch($request);
    }

    /**
     * Aion Test Page
     *
     * @return \Magento\Framework\View\Result\Page
     */
    public function execute()
    {
        /** @var \Magento\Framework\View\Result\Page $resultPage */
        $resultPage = $this->resultPageFactory->create();
        $resultPage->getConfig()->getTitle()->set(__('Aion Test Page'));
        if (!$resultPage) {
            $resultForward = $this->resultForwardFactory->create();
            return $resultForward->forward('noroute');
        }
        return $resultPage;
    }
} 

 

There are three crucial functions in the file, all of them are defined in the parent class. We use the __construct function for injecting the other classes we want to use (e.g. helper class, dependency injection). The dispatch function will run automatically after __construct. Here we check if our module is enabled or not and then we manage it accordingly.

Finally, the execute function means the action itself, which is Index action in this case. While in operation, we create the $resultPageFactory object (which will build up the page based on the layout configuration, to be executed later). We define a page title with the object’s setConfig function. Next we check if the page has been created or not. If no error has occurred, we return with the $resultPageFactory object. Otherwise, we redirect the action to a noroute (i.e. 404) page.

 

We create the routers.xml file in the app/code/Aion/Test/etc/frontend directory in which we define which URL we want to use to call the controllers for our module. The file contains the following:

 

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="standard">
        <route id="test" frontName="test">
            <module name="Aion_Test" />
        </route>
    </router>
</config>

It can be seen in the router configuration that we assigned “test” URL to our module. So in this case the {{base_url}}test/index/index will call the controller (more precisely, the execute function  – Index action – within it), detailed above. Naturally, it is not necessary to define the index controller and action in the URL. {{base_url}}test/ will redirect to the same location.

Next we create the layout file app/code/Aion/Test/view/frontend/layout under the name test_index_index.xml. It is obvious that the file name follows the router -> controller -> action names. The file contains the following:

 

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <head>
        <title>Aion Test Page</title>
    </head>
    <body>
        <referenceContainer name="content">
            <block class="Aion\Test\Block\Test" name="testPage" template="Aion_Test::test.phtml" />
        </referenceContainer>
    </body>
</page>

 

We define the basic title of the page in the <head> section of the layout file and we also define a block and a template file assigned to it in the content section. We create the block in the app/code/Aion/Test/Block directory in the Test.php. The file includes the following:

 

<?php
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Block;

use Magento\Framework\View\Element\Template;

/**
 * Aion Test Page block
 */
class Test extends Template
{
    /**
     * @param Template\Context $context
     * @param array $data
     */
    public function __construct(Template\Context $context, array $data = [])
    {
        parent::__construct($context, $data);
    }

    /**
     * Test function
     *
     * @return string
     */
    public function getTest()
    {
        return 'This is a test function for some logic...';
    }
}  

 

We can notice that we have not defined anything in the __construct function presented in the example, so this can be omitted. However, it is recommended to create it at the beginning if we want to implement a storeManager, Helper or other elements later on.

The template file is to be located in the app/code/Aion/Test/view/frontend/templates directory under the name test.phtml. The file contains the following:

 

<?php
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
?>

<?php
/**
 * @var $block \Aion\Test\Block\Test
 */
?>

<p><?php echo $block->getTest(); ?></p>
<p><?php echo __('This is a text.') ?></p>

 

In this example the template file calls the test function defined in the Block class and also displays a string, which is embedded (inserted) in a translate function.

When all is finished, our module looks like this:

app/code

Aion/

Test/

Block/Test.php

Controller/Index/Index.php

etc/adminhtml/system.xml

/frontend/routes.xml

acl.xml

config.xml

module.xml

Helper/Data.php

view/frontend

/layout/test_index_index.xml

/templates/test.phtml

composer.json

COPYING.txt

README.md

registration.php

 

So far we have seen how to create and build up a basic Magento 2.0 module. In the following, we will see how to create a database table and how to add basic data and models to it that are needed to manage table data and also how to add a collection to it. Furthermore, we will check if these data are “comprehensible” in terms of frontend development and give a method for displaying them.

 

6) Creating the database table belonging to the module – 1

 

We create the database table, belonging to the module, with the help of an installer script already known form Magento 1.x. However, its file name and structure is a little different.

We create the database table in the InstallSchema.php in the app/code/Aion/Test/Setup directory. The file contains the following:

 

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Aion\Test\Setup;

use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\DB\Adapter\AdapterInterface;

/**
 * @codeCoverageIgnore
 */
class InstallSchema implements InstallSchemaInterface
{
    /**
     * Install table
     *
     * @param \Magento\Framework\Setup\SchemaSetupInterface $setup
     * @param \Magento\Framework\Setup\ModuleContextInterface $context
     * @throws \Zend_Db_Exception
     */
    public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
    {
        $installer = $setup;

        $installer->startSetup();
        /**
         * Create table 'aion_test'
         */
        $table = $installer->getConnection()->newTable(
            $installer->getTable('aion_test')
        )->addColumn(
            'test_id',
            \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
            null,
            ['identity' => true, 'nullable' => false, 'primary' => true],
            'Test ID'
        )->addColumn(
            'name',
            \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
            255,
            ['nullable' => false],
            'Test Name'
        )->addColumn(
            'email',
            \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
            255,
            ['nullable' => false],
            'Test Email'
        )->addColumn(
            'creation_time',
            \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
            null,
            ['nullable' => false, 'default' => \Magento\Framework\DB\Ddl\Table::TIMESTAMP_INIT],
            'Test Creation Time'
        )->addColumn(
            'update_time',
            \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
            null,
            ['nullable' => false, 'default' => \Magento\Framework\DB\Ddl\Table::TIMESTAMP_INIT_UPDATE],
            'Test Modification Time'
        )->addColumn(
            'is_active',
            \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
            null,
            ['nullable' => false, 'default' => '1'],
            'Is Test Active'
        )->addIndex(
            $setup->getIdxName(
                $installer->getTable('aion_test'),
                ['name', 'email'],
                AdapterInterface::INDEX_TYPE_FULLTEXT
            ),
            ['name', 'email'],
            ['type' => AdapterInterface::INDEX_TYPE_FULLTEXT]
        )->setComment(
            'Aion Test Table'
        );
        $installer->getConnection()->createTable($table);

        $installer->endSetup();
    }
}

 

The script creates the sample table belonging to the module under the name “aion_test”. Then it creates the following fields:

  • test_id – primary key, smallint (6)
  • name – varchar (255)
  • email – varchar (255)
  • creation_time – timestamp
  • update_time – timestamp
  • is_active – smallint (6)

After that, it is highly recommended to add indexes (addIndex) to text, varchar or other types of columns in which we will make searches or will filter the existing collection. In our example, these two fields are name and email.

 

7) Creating the database table belonging to the module – 2

Previously, we gave a version number to our module with the help of the module.xml located in the app/code/Aion/Test/etc directory, in which we defined the basic version and then with the help of terminal commands for enabling the module, the version number could be included in the “setup module” Magento 2.0 table.

We have two options for running the aforementioned script: either increasing the version number in the module or deleting the module’s earlier version number. Since we are only at the beginning of the development of our module, it is recommended to delete the entry related to our module from the “setup module” Magento 2.0 table. After finishing with this, run again the setup:upgrade magento command from the terminal. If everything has been set appropriately, the table assigned to our module is created.

 

8) Creating the models and collection belonging to the module

In order to load data to the table or make queries for data, we need to create the module’s model, resource and collection files.

We create the basic model file in Test.php in the app/code/Aion/Test/Model directory. This file includes the following:

 

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Model;

/**
 * Aion Test model
 *
 * @method \Aion\Test\Model\ResourceModel\Test _getResource()
 * @method \Aion\Test\Model\ResourceModel\Test getResource()
 * @method string getId()
 * @method string getName()
 * @method string getEmail()
 * @method setSortOrder()
 * @method int getSortOrder()
 */
class Test extends \Magento\Framework\Model\AbstractModel
{
    /**
     * Statuses
     */
    const STATUS_ENABLED = 1;
    const STATUS_DISABLED = 0;

    /**
     * Aion Test cache tag
     */
    const CACHE_TAG = 'aion_test';

    /**
     * @var string
     */
    protected $_cacheTag = 'aion_test';

    /**
     * Prefix of model events names
     *
     * @var string
     */
    protected $_eventPrefix = 'aion_test';

    /**
     * @return void
     */
    protected function _construct()
    {
        $this->_init('Aion\Test\Model\ResourceModel\Test');
    }

    /**
     * Get identities
     *
     * @return array
     */
    public function getIdentities()
    {
        return [self::CACHE_TAG . '_' . $this->getId(), self::CACHE_TAG . '_' . $this->getId()];
    }

    /**
     * Prepare item's statuses
     *
     * @return array
     */
    public function getAvailableStatuses()
    {
        return [self::STATUS_ENABLED => __('Enabled'), self::STATUS_DISABLED => __('Disabled')];
    }

}

 

We define the two cache tags needed for operating Magento 2.0 cache. It is also important to define an event prefix to use it as part of the event name if using observers later on. The most important thing to do now is defining the resource model belonging to the model in the _construct() function. It is recommended to create the getAvailableStatuses() function and the two STATUS_* constant values for later use.

It is not a must to implement the getIdentities() function, but it still would be useful for the proper operation of cache management.

After we have created our basic model file, we should create the resource model for it. We create the basic resource model file in Test.php in the app/code/Aion/Test/Model/ResourceModel directory. The file includes the following:

 

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Model\ResourceModel;

/**
 * Aion Test resource model
 */
class Test extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
    /**
     * Define main table
     *
     * @return void
     */
    protected function _construct()
    {
        $this->_init('aion_test', 'test_id');
    }
          
}

 

Now the most important thing is to implement the protected _construct() function, where we define which field represents the primary key in the table created earlier.

Having finished with this, we create the collection with the help of the resource model. We create the basic collection class file in Collection.php in the app/code/Aion/Test/Model/ResourceModel/Test directory. The file includes the following:

 

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Model\ResourceModel\Test;

/**
 * Aion Test collection
 */
class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
    /**
     * @var string
     */
    protected $_idFieldName = 'test_id';

    /**
     * Store manager
     *
     * @var \Magento\Store\Model\StoreManagerInterface
     */
    protected $storeManager;

    /**
     * @param \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory
     * @param \Psr\Log\LoggerInterface $logger
     * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy
     * @param \Magento\Framework\Event\ManagerInterface $eventManager
     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
     * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection
     * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb|null $resource
     */
    public function __construct(
        \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory,
        \Psr\Log\LoggerInterface $logger,
        \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
        \Magento\Framework\Event\ManagerInterface $eventManager,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Framework\DB\Adapter\AdapterInterface $connection = null,
        \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null
    ) {
        parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $connection, $resource);
        $this->storeManager = $storeManager;
    }

    /**
     * Define resource model
     *
     * @return void
     */
    protected function _construct()
    {
        $this->_init('Aion\Test\Model\Test', 'Aion\Test\Model\ResourceModel\Test');
    }
}

 

We implement the _construct() function in the collection class, in which we basically define which resource model class belongs to the model class that was created earlier. Essentially, it would be enough and suitable for having a collection, but thinking about the future, it is better to inject the store manager class in the public __construct() function at this moment for later use, because multistore support is mandatory with every module.

After finishing with all three files, we have managed to create those classes with which we can write and read data using our database table created earlier.

 

9) Loading up data to the table using script

 

In order to be able to add data to the module’s basic table using script, we needed to produce the model structure. We create the data script in InstallData.php in the app/code/Aion/Test/Setup directory. The file includes the following:

 

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Setup;

use Aion\Test\Model\Test;
use Aion\Test\Model\TestFactory;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;

/**
 * @codeCoverageIgnore
 */
class InstallData implements InstallDataInterface
{
    /**
     * Test factory
     *
     * @var TestFactory
     */
    private $testFactory;

    /**
     * Init
     *
     * @param TestFactory $testFactory
     */
    public function __construct(TestFactory $testFactory)
    {
        $this->testFactory = $testFactory;
    }

    /**
     * {@inheritdoc}
     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
     */
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $testItems = [
            [
                'name' => 'John Doe',
                'email' => 'john.doe@example.com',
                'is_active' => 1,
            ],
            [
                'name' => 'Jane Doe',
                'email' => 'jane.doe@example.com',
                'is_active' => 0,
            ],

            [
                'name' => 'Steve Test',
                'email' => 'steve.test@example.com',
                'is_active' => 1,
            ],
        ];

        /**
         * Insert default items
         */
        foreach ($testItems as $data) {
            $this->createTest()->setData($data)->save();
        }

        $setup->endSetup();
    }

    /**
     * Create Test item
     *
     * @return Test
     */
    public function createTest()
    {
        return $this->testFactory->create();
    }
}

 

We implement the install(…) function in the InstallData class, in which we create test data in a multi-dimensional array. We iterate through it and then call the createTest() function which has the “task” to create the new Test Model. Then, after adding the bundles as data, we save the model. Here, it is important to mention the TestFactory class which was created in the class and injected in the __construct() function. This class is automatically created by Magento 2.0 in the /var/generation/Aion/Test/Model directory when it is running for the first time ($this->testFactory-> create()).

Since we are still at the beginning of our module development, let’s discard manually the previously created “aion_test” table from the database and delete the entry belonging to our module in the “setup_module” Magento 2.0 table.

Of course, alternatively, we can run data upload in the command line (terminal) using version number upgrade, thus we do not need to discard manually our previously created table and there is no need to deal separately with the “setup_module” table either.

 

10) Table update script and version upgrade

While developing our module, we need to modify frequently the basic database table, sometimes creating a new database table. In the case of Magento 1.x, we could do this with upgrade scripts either with respect to database or to data. It is the same with Magento 2.0, but luckily, now we can manage all the database scripts within one file instead of separate files.

We create the database update script in UpgradeSchema.php in the app/code/Aion/Test/Setup directory. The file includes the following:

 

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Aion\Test\Setup;

use Magento\Framework\Setup\UpgradeSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;

/**
 * @codeCoverageIgnore
 */
class UpgradeSchema implements UpgradeSchemaInterface
{
    /**
     * Upgrades DB schema, add sort_order
     *
     * @param SchemaSetupInterface $setup
     * @param ModuleContextInterface $context
     * @return void
     */
    public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context)
    {
        if (version_compare($context->getVersion(), '2.0.1') < 0) {
            $setup->startSetup();
            $setup->getConnection()->addColumn(
                $setup->getTable('aion_test'),
                'sort_order',
                [
                    'type' => \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
                    'length' => null,
                    'nullable' => false,
                    'default' => 0,
                    'comment' => 'Test Sort Order'
                ]
            );
            $setup->endSetup();
        }
    }
}

 

In this example, with the help of upgrade script, we add a new field (sort_order) to the basic database table („aion_test”). The implemented modification in the upgrade function will run only if the module’s version number reaches the 2.0.1 value, seen in the example.

 

11) Frontend data display

In order to display the created data on frontend, we need to modify the frontend template and the block class belonging to it.

The block class has already been prepared previously, now we complement it.

We create the block class in Test.php in the app/code/Aion/Block/ directory. The file contains the following:

 

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Block;

use Magento\Framework\View\Element\Template;

/**
 * Aion Test Page block
 */
class Test extends Template
{
    /**
     * @var \Aion\Test\Model\Test
     */
    protected $test;

    /**
     * Test factory
     *
     * @var \Aion\Test\Model\TestFactory
     */
    protected $testFactory;

    /**
     * @var \Aion\Test\Model\ResourceModel\Test\CollectionFactory
     */
    protected $itemCollectionFactory;

    /**
     * @var \Aion\Test\Model\ResourceModel\Test\Collection
     */
    protected $items;

    /**
     * Test constructor.
     *
     * @param \Magento\Framework\View\Element\Template\Context $context
     * @param \Aion\Test\Model\Test $test
     * @param \Aion\Test\Model\TestFactory $testFactory
     * @param array $data
     */
    public function __construct(
        Template\Context $context,
        \Aion\Test\Model\Test $test,
        \Aion\Test\Model\TestFactory $testFactory,
        \Aion\Test\Model\ResourceModel\Test\CollectionFactory $itemCollectionFactory,
        array $data = []
    ) {
        $this->test = $test;
        $this->testFactory = $testFactory;
        $this->itemCollectionFactory = $itemCollectionFactory;
        parent::__construct($context, $data);
    }

    /**
     * Retrieve Test instance
     *
     * @return \Aion\Test\Model\Test
     */
    public function getTestModel()
    {
        if (!$this->hasData('test')) {
            if ($this->getTestId()) {
                /** @var \Aion\Test\Model\Test $test */
                $test = $this->testFactory->create();
                $test->load($this->getTestId());
            } else {
                $test = $this->test;
            }
            $this->setData('test', $test);
        }
        return $this->getData('test');
    }

    /**
     * Get items
     *
     * @return bool|\Aion\Test\Model\ResourceModel\Test\Collection
     */
    public function getItems()
    {
        if (!$this->items) {
            $this->items = $this->itemCollectionFactory->create()->addFieldToSelect(
                '*'
            )->addFieldToFilter(
                'is_active',
                ['eq' => \Aion\Test\Model\Test::STATUS_ENABLED]
            )->setOrder(
                'creation_time',
                'desc'
            );
        }
        return $this->items;
    }

    /**
     * Get Test Id
     *
     * @return int
     */
    public function getTestId()
    {
        return 1;
    }

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

 

We implement the previously created Test Model and the testFactory belonging to it as well as the collectionFactory (itemCollectionFactory), into the constructor of the block class. The collectionFactory class is very similar to the testFactory, which is generated by Magento 2.0 as well in the var/generation/Aion/Test/Model/ResourceModel/Test/ after its first call. The getTestModel() function creates a model, and is responsible for loading with the appropriate ID, while the getItems() function creates a complete collection.

We can write the data, for testing, in the template file belonging to the block. The file includes the following:

 

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
?>

<?php
/**
 * @var $block \Aion\Test\Block\Test
 */
?>
<div class="aion-test">
    <h2><?php echo __('This is a test extension!') ?></h2>
    <!-- See a sample model -->
    <?php \Zend_Debug::dump($block->getTestModel()->getData()); ?>
    <!-- See a sample collection -->
    <?php \Zend_Debug::dump($block->getItems()->getData()); ?>

    <!-- See a sample collection iteration -->
    <?php $items = $block->getItems(); ?>
    <?php if ($items->getSize()) : ?>
        <?php foreach ($items as $item) : ?>
            <h3><?php echo $block->stripTags($item->getName()) ?></h3>
            <p>
                <span><?php echo __('Email:'); ?></span>&nbsp;
                <span><?php echo $item->getEmail() ?></span>
            </p>
        <?php endforeach; ?>
    <?php endif; ?>
</div>

 

So we now have added our own database table to our basic Magento 2.0 module and loaded it with test data. We have also created the model, resource and collection needed for it and also displayed the test data on frontend.

 

END OF PART 1

In Part 2, we describe how to create the admin grid and the necessary controllers of the module.

See also Part 3 (observers) and Part 4 (Knockout JS).

 

 

433 replies
  1. BennyLip says:

    Секрет евреев: Еврейские мужчины лечат простатит за 2-3 недели!
    Один раз в жизни! Раз и навсегда! Узнаем как… http://bit.ly/3bqeXp9

  2. Jeanice Goodwater says:

    Wow, fantastic weblog layout! How long have you been blogging for? you make blogging glance easy. The full glance of your web site is fantastic, let alone the content material!

  3. Thomas Suk says:

    This is the right blog for anyone who wants to find out about this topic. You realize so much its almost hard to argue with you (not that I actually would want…HaHa). You definitely put a new spin on a topic thats been written about for years. Great stuff, just great!

  4. Palmer Dold says:

    Thanks for some other great article. Where else may just anyone get that kind of information in such a perfect approach of writing? I have a presentation next week, and I’m on the search for such info.

  5. contrarian definition says:

    I loved as much as you will receive carried out right here. The sketch is tasteful, your authored material stylish. nonetheless, you command get got an nervousness over that you wish be delivering the following. unwell unquestionably come further formerly again since exactly the same nearly very often inside case you shield this hike.

  6. Elliot wave says:

    Thank you so much for giving everyone an exceptionally superb opportunity to discover important secrets from this blog. It can be so awesome and as well , jam-packed with fun for me personally and my office colleagues to visit your site at the very least 3 times weekly to read through the new secrets you have got. And definitely, I am always astounded with all the awesome pointers served by you. Selected 4 facts on this page are absolutely the finest I’ve ever had.

  7. stock market graph 2018 says:

    I’m impressed, I have to say. Actually hardly ever do I encounter a weblog that’s both educative and entertaining, and let me tell you, you have hit the nail on the head. Your concept is outstanding; the problem is something that not sufficient individuals are speaking intelligently about. I am very completely satisfied that I stumbled across this in my search for something relating to this.

  8. most volatile stocks says:

    I’ll immediately grab your rss as I can not find your email subscription link or e-newsletter service. Do you’ve any? Kindly let me know so that I could subscribe. Thanks.

  9. brainwash says:

    I really like your writing style, wonderful information, thanks for posting :D. “The superfluous is very necessary.” by Francois Marie Arouet Voltaire.

  10. market timing strategies says:

    It is appropriate time to make a few plans for the long run and it’s time to be happy. I have read this post and if I could I wish to counsel you some interesting things or tips. Perhaps you can write subsequent articles regarding this article. I wish to learn even more issues approximately it!

  11. what is hysteria says:

    It is appropriate time to make some plans for the future and it is time to be happy. I’ve read this post and if I could I want to suggest you few interesting things or advice. Maybe you could write next articles referring to this article. I wish to read more things about it!

  12. media lies quotes says:

    You could certainly see your expertise in the work you write. The world hopes for even more passionate writers like you who are not afraid to say how they believe. Always go after your heart.

  13. multiculturalism definition says:

    Hi this is kinda of off topic but I was wondering if blogs use WYSIWYG editors or if you have to manually code with HTML. I’m starting a blog soon but have no coding knowledge so I wanted to get advice from someone with experience. Any help would be greatly appreciated!

  14. Gabriellenor says:

    Hello to all
    In this enigmatical time, I disposition you all
    Rise your relations and friends

  15. Josephtef says:

    https://bit.ly/3chvo6B Попперсы европейского качества производства Франции и Англии! Самый большой ассортимент и низкие цены!

  16. kex says:

    http://reyna.userbet.xyz ПРИСОЕДИНЯЙТЕСЬ СЕЙЧАС И ПОЛУЧИТЕ 100$ К ВАШЕМУ ПЕРВОМУ ДЕПОЗИТУ!
    Управляйте роботом самостоятельно! Контроль уровня риска!

  17. ArleneSully says:

    Привет.
    Мое имя Киска.
    Познакомлюсь с мужчиной для встречи. Приеду к тебе на район или встримся у меня. Живу в соседнем подъезде.

    Мои видео

  18. wolverdon Filmes says:

    Nice read, I just passed this onto a colleague who was doing a little research on that. And he actually bought me lunch as I found it for him smile So let me rephrase that: Thanks for lunch! “We steal if we touch tomorrow. It is God’s.” by Henry Ward Beecher.

  19. erjilopterin says:

    I would like to thank you for the efforts you have put in writing this site. I am hoping the same high-grade site post from you in the upcoming also. Actually your creative writing abilities has inspired me to get my own web site now. Really the blogging is spreading its wings quickly. Your write up is a good example of it.

  20. erjilo pterin says:

    My brother recommended I would possibly like this website. He was entirely right. This submit truly made my day. You cann’t believe just how much time I had spent for this info! Thank you!

  21. erjilo pterin says:

    Great blog! Is your theme custom made or did you download it from somewhere? A design like yours with a few simple tweeks would really make my blog jump out. Please let me know where you got your theme. With thanks

  22. Izrada spomenika says:

    Magnificent items from you, man. I have keep in mind your stuff prior to and you are just extremely excellent. I really like what you’ve received right here, really like what you’re stating and the best way during which you assert it. You make it enjoyable and you still take care of to keep it smart. I cant wait to learn far more from you. That is really a terrific web site.

  23. T-shirts says:

    Heya! I’m at work surfing around your blog from my new apple iphone! Just wanted to say I love reading through your blog and look forward to all your posts! Carry on the excellent work!

  24. Daftar Joker123 says:

    Spot on with this write-up, I actually think this web site needs much more consideration. I’ll probably be once more to read way more, thanks for that info.

  25. Slot VivoSlot says:

    Thank you for every other wonderful post. The place else may anybody get that kind of information in such an ideal way of writing? I have a presentation next week, and I am on the search for such info.

  26. armas comprar says:

    I¦ve learn a few excellent stuff here. Certainly worth bookmarking for revisiting. I surprise how so much attempt you put to make this sort of excellent informative web site.

  27. hospedagem de sites says:

    What i do not realize is in reality how you’re not actually a lot more well-favored than you may be now. You are very intelligent. You know thus considerably with regards to this subject, made me personally imagine it from so many varied angles. Its like men and women are not fascinated except it’s one thing to do with Woman gaga! Your own stuffs great. Always take care of it up!

  28. daftar situs judi online says:

    Wow that was unusual. I just wrote an really long comment but after I clicked submit my comment didn’t appear. Grrrr… well I’m not writing all that over again. Regardless, just wanted to say superb blog!

  29. สล็อตXo says:

    What i do not understood is actually how you’re now not really much more well-preferred than you might be now. You’re so intelligent. You already know therefore significantly in the case of this topic, made me for my part consider it from so many numerous angles. Its like women and men are not fascinated until it is one thing to accomplish with Girl gaga! Your own stuffs great. At all times care for it up!

  30. สล็อต says:

    Hiya, I’m really glad I’ve found this info. Nowadays bloggers publish only about gossips and internet and this is actually frustrating. A good blog with exciting content, that’s what I need. Thanks for keeping this web site, I’ll be visiting it. Do you do newsletters? Can not find it.

  31. Purchase Instagram Likes says:

    Hello, Neat post. There’s a problem along with your site in internet explorer, might check this… IE nonetheless is the marketplace leader and a good part of folks will omit your great writing because of this problem.

  32. TikTok Hearts says:

    Someone essentially help to make seriously articles I would state. This is the very first time I frequented your web page and thus far? I amazed with the research you made to make this particular publish amazing. Great job!

  33. more hints says:

    You completed a number of fine points there. I did a search on the theme and found a good number of folks will consent with your blog.

  34. navegantes says:

    Normally I don’t read article on blogs, but I wish to say that this write-up very forced me to try and do so! Your writing style has been surprised me. Thanks, very nice article.

  35. College says:

    F*ckin’ awesome things here. I am very happy to see your post. Thanks a lot and i am looking forward to touch you. Will you please drop me a mail?

  36. donanim says:

    I’m still learning from you, as I’m trying to reach my goals. I absolutely love reading all that is written on your blog.Keep the posts coming. I liked it!

  37. Jeff L Cox Los Angeles says:

    Most of what you state is supprisingly accurate and that makes me ponder why I had not looked at this in this light before. Your article truly did turn the light on for me personally as far as this particular issue goes. But there is one point I am not really too cozy with and whilst I make an effort to reconcile that with the actual central theme of the position, permit me see exactly what the rest of your visitors have to point out.Well done.

  38. Jefferson L Cox says:

    Wow, amazing blog layout! How long have you been blogging for? you made blogging look easy. The overall look of your web site is excellent, let alone the content!

  39. website here says:

    Thanks for any other informative site. Where else could I get that type of info written in such an ideal means? I’ve a project that I am simply now operating on, and I’ve been at the glance out for such info.

  40. look at here says:

    I loved as much as you will receive carried out right here. The sketch is tasteful, your authored subject matter stylish. nonetheless, you command get got an nervousness over that you wish be delivering the following. unwell unquestionably come more formerly again as exactly the same nearly very often inside case you shield this hike.

  41. see it here says:

    naturally like your web site but you have to check the spelling on quite a few of your posts. Several of them are rife with spelling problems and I find it very bothersome to tell the truth nevertheless I will definitely come back again.

  42. basics says:

    I am curious to find out what blog system you happen to be working with? I’m having some minor security issues with my latest blog and I would like to find something more safe. Do you have any solutions?

  43. post says:

    Great wordpress blog here.. It’s hard to find quality writing like yours these days. I really appreciate people like you! take care

  44. car gloss coating says:

    hello there and thank you for your information – I’ve certainly picked up anything new from right here. I did however expertise some technical issues using this web site, as I experienced to reload the site a lot of times previous to I could get it to load properly. I had been wondering if your hosting is OK? Not that I am complaining, but sluggish loading instances times will very frequently affect your placement in google and could damage your high-quality score if advertising and marketing with Adwords. Anyway I’m adding this RSS to my email and could look out for much more of your respective fascinating content. Ensure that you update this again soon..

  45. Royal CBD says:

    It’s hard to find educated people for this topic, but you sound like
    you know what you’re talking about! Thanks

    P.S. If you have a minute, would love your feedback on my new website
    re-design. You can find it by searching for «royal cbd» —
    no sweat if you can’t.

    Keep up the good work!

  46. bulldog puppies for sale says:

    Great – I should certainly pronounce, impressed with your site. I had no trouble navigating through all the tabs as well as related information ended up being truly simple to do to access. I recently found what I hoped for before you know it at all. Reasonably unusual. Is likely to appreciate it for those who add forums or something, web site theme . a tones way for your customer to communicate. Nice task..

  47. Daftar Joker388 says:

    F*ckin¦ remarkable things here. I am very satisfied to peer your post. Thank you a lot and i’m having a look ahead to contact you. Will you please drop me a e-mail?

  48. Daftar FafaSlot says:

    Hello, i think that i noticed you visited my weblog thus i got here to “go back the choose”.I’m trying to find issues to enhance my site!I suppose its adequate to use a few of your ideas!!

  49. Download Joker123 says:

    Having read this I thought it was very informative. I appreciate you taking the time and effort to put this article together. I once again find myself spending way to much time both reading and commenting. But so what, it was still worth it!

  50. Login Joker123 says:

    A lot of thanks for every one of your efforts on this web site. My daughter loves participating in internet research and it is simple to grasp why. All of us learn all regarding the lively medium you render insightful guidelines via the web site and even foster response from other ones about this point plus our girl is in fact becoming educated a whole lot. Take pleasure in the remaining portion of the new year. You have been doing a great job.

  51. Joker388 says:

    I used to be recommended this web site by my cousin. I am not certain whether or not this put up is written by him as no one else understand such targeted about my trouble. You are amazing! Thanks!

  52. Daftar VivoSlot says:

    Very interesting information!Perfect just what I was looking for! “Oh, I don’t blame Congress. If I had 600 billion at my disposal, I’d be irresponsible, too.” by Lichty and Wagner.

  53. WalterSn says:

    What is it – V7BOMDEFEX
    Please tell me-where is it? Or what is it V7BOMDEFEX ?

  54. hospedagem de site barata says:

    I’ve been browsing online greater than 3 hours these days, but I never discovered any attention-grabbing article like yours. It is beautiful price sufficient for me. In my opinion, if all site owners and bloggers made good content material as you did, the internet might be a lot more helpful than ever before.

  55. hospedagem de site barata says:

    I like what you guys are up also. Such clever work and reporting! Carry on the superb works guys I¦ve incorporated you guys to my blogroll. I think it will improve the value of my web site :)

  56. nunca diga que sua esposa esta gorda says:

    Do you mind if I quote a few of your articles as long as I provide credit and sources back to your webpage? My website is in the exact same area of interest as yours and my visitors would really benefit from some of the information you present here. Please let me know if this alright with you. Thank you!

  57. Deuses says:

    Hi would you mind stating which blog platform you’re using? I’m planning to start my own blog soon but I’m having a difficult time selecting between BlogEngine/Wordpress/B2evolution and Drupal. The reason I ask is because your design seems different then most blogs and I’m looking for something completely unique. P.S Apologies for getting off-topic but I had to ask!

  58. kevin david contact says:

    I’ve been surfing on-line greater than 3 hours nowadays, yet I never found any attention-grabbing article like yours. It is lovely worth sufficient for me. In my view, if all website owners and bloggers made good content as you probably did, the net might be much more useful than ever before.

  59. kevin david bbb says:

    You really make it seem so easy along with your presentation however I in finding this topic to be actually something which I believe I’d never understand. It seems too complicated and extremely wide for me. I’m having a look forward to your subsequent put up, I will try to get the grasp of it!

  60. Login FafaSlot says:

    I’ve been absent for a while, but now I remember why I used to love this website. Thanks , I¦ll try and check back more often. How frequently you update your site?

  61. Daftar Slot Online says:

    I like the valuable information you provide in your articles. I’ll bookmark your weblog and check again here regularly. I’m quite certain I’ll learn many new stuff right here! Best of luck for the next!

  62. Download Joker123 says:

    Thank you for some other great article. Where else could anybody get that type of information in such a perfect way of writing? I have a presentation next week, and I am at the search for such info.

  63. MatthewWew says:

    Хотите добывать криптовалюту, но не знаете, с чего начать? Попробуйте CryptoTab — первый в мире браузер со встроенной функцией майнинга. Он быстрый и простой в использовании — а еще он сделает веб-серфинг выгодным! https://bit.ly/2M8fHnp|

    Переходите по ссылке, устанавливайте браузер, участвуйте в конкурсе и получайте $ !!!

  64. Slot Online says:

    Thanks a bunch for sharing this with all of us you actually recognize what you’re speaking about! Bookmarked. Kindly additionally consult with my web site =). We could have a hyperlink trade agreement among us!

  65. Daftar Joker123 says:

    I am not positive the place you are getting your info, but good topic. I must spend some time finding out much more or figuring out more. Thank you for excellent info I was in search of this information for my mission.

  66. job advertising site says:

    I haven’t checked in here for a while because I thought it was getting boring, but the last several posts are good quality so I guess I’ll add you back to my everyday bloglist. You deserve it my friend :)

  67. papel de parede para sala de jantar 3d says:

    We absolutely love your blog and find nearly all of your post’s to be exactly I’m looking for. Would you offer guest writers to write content to suit your needs? I wouldn’t mind writing a post or elaborating on most of the subjects you write about here. Again, awesome blog!

  68. สล็อต says:

    I was very pleased to find this web-site.I wanted to thanks for your time for this wonderful read!! I definitely enjoying every little bit of it and I have you bookmarked to check out new stuff you blog post.

  69. abrindo uma loja says:

    What i do not understood is actually how you are not really much more well-liked than you might be now. You are so intelligent. You realize thus significantly relating to this subject, made me personally consider it from so many varied angles. Its like women and men aren’t fascinated unless it’s one thing to do with Lady gaga! Your own stuffs nice. Always maintain it up!

  70. Game podcasts says:

    Undeniably consider that which you stated. Your favorite reason appeared to be at the net the simplest factor to be aware of. I say to you, I definitely get irked at the same time as other folks consider concerns that they just do not recognize about. You managed to hit the nail upon the highest and outlined out the whole thing with no need side effect , other people can take a signal. Will likely be again to get more. Thanks

  71. Game podcasts says:

    Wonderful blog! I found it while searching on Yahoo News. Do you have any tips on how to get listed in Yahoo News? I’ve been trying for a while but I never seem to get there! Cheers

  72. Game podcasts says:

    Great work! That is the type of info that should be shared across the internet. Shame on the seek engines for no longer positioning this put up upper! Come on over and visit my site . Thanks =)

  73. criminal lawyer average salary says:

    Simply wish to say your article is as astounding. The clearness in your post is just great and i could assume you’re an expert on this subject. Fine with your permission allow me to grab your RSS feed to keep updated with forthcoming post. Thanks a million and please keep up the gratifying work.

  74. criminal lawyer average salary says:

    hello there and thank you to your information – I have definitely picked up something new from right here. I did on the other hand experience several technical points the usage of this website, as I skilled to reload the site many times previous to I could get it to load properly. I were thinking about if your web host is OK? No longer that I’m complaining, but sluggish loading cases times will very frequently affect your placement in google and could injury your quality score if advertising and ***********|advertising|advertising|advertising and *********** with Adwords. Anyway I’m including this RSS to my email and could look out for much extra of your respective fascinating content. Make sure you replace this again very soon..

  75. criminal lawyer cost says:

    Its like you read my mind! You appear to know a lot about this, like you wrote the book in it or something. I think that you could do with a few pics to drive the message home a bit, but other than that, this is fantastic blog. A fantastic read. I will certainly be back.

  76. Industry News says:

    Hello my family member! I want to say that this article is awesome, nice written and come with approximately all vital infos. I’d like to peer extra posts like this.

  77. SMS says:

    I love looking through a post that can make people think. Also, many thanks for permitting me to comment!

  78. Jerryjerty says:

    Знаете ли вы?
    Планета — глазное яблоко может быть пригодна для жизни в одних районах и непригодна в других.
    Советский разведчик-нелегал создал в Европе разведгруппу, успешно проработавшую всю войну.
    Не удержавшись от писательства, Амалия Кахана-Кармон создала одну из важнейших книг в истории Израиля.
    Карьера не помешала фарерскому футболисту играть в гандбол, записать три музыкальных альбома, издать пять книг и сняться в восьми фильмах.
    Согласно мифу, Марута Сар пыталась примирить Арарат и Арагац, но не смогла.

    arbeca

  79. Jerryjerty says:

    Знаете ли вы?
    После 50 черепно-мозговых травм регбист завершил карьеру, опасаясь получить синдром деменции.
    Иногда для поддержки экономики деньги «разбрасывают с вертолёта».
    Крейсер «Берик» на рейде в Девонпорте
    Персонажу французской комедии о Фантомасе советские подростки подражали всерьёз.
    Член Зала хоккейной славы готов был играть где угодно, лишь бы не переходить в тренеры.

    arbeca

  80. Jerryjerty says:

    Знаете ли вы?
    Каждая шестая яркая галактика во Вселенной очень сильно испускает газы.
    Новый вид пауков-скакунов был назван по имени писателя в честь юбилея его самой известной книги о гусенице.
    Плата за проезд в последний путь у древних была скорее символической.
    Китайскую пустыню засадили лесами и открыли там фешенебельный курорт.
    Согласно мифу, Марута Сар пыталась примирить Арарат и Арагац, но не смогла.

    arbeca

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published.