How to make a Mage2Gen snippet

How to make a Mage2Gen snippet

In this blog post we are going to create a snippet for Mage2Gen. Mage2Gen is a Magento 2 module creator library, go check out the web implementation.

We are going to make a small snippet to add preference's to the Mage2Gen module that is being created. The snippet will have two input's, the class or interface you want to preference and if it's a class or interface. Based on this input it will create the implementation class and the di.xml file.

Requirements:

  • Python 3
  • Copy of the source code (git clone https://github.com/krukas/Mage2Gen.git)

Basic snippet

First we are going to create a basic snippet in the Mage2Gen library. For creating a snippet you only have to implement one class mage2gen.snippet. The basic snippet that works, sort of work because it does nothing is:

# snippets/preference.py 
from mage2gen import Module, Phpclass, Phpmethod, Xmlnode, StaticFile, Snippet, SnippetParam

class PreferenceSnippet(Snippet):

    def add(self, extra_params=None):
        pass

Import class in snippets/__init__.py, this will load the snippet automatically for the cli and web application:

# snippets/__init__.py 
from .preference import PreferenceSnippet

Creating the basic preference class

For creating the class we need input from the user. We will ask for the class namespace and type (class or interface). Determining the type is for example possible if everybody always suffix the class namespace with Interface. But not everybody plays by the same rules, so we are going to ask what kind of type it's.

class PreferenceSnippet(Snippet):

    TYPE_CLASS = 'class'
    TYPE_INTERFACE = 'interface'

    TYPE_CHOISES = [
        [TYPE_CLASS, 'Class'],
        [TYPE_INTERFACE, 'Interface']
    ]

    def add(self, full_namespace, namespace_type=TYPE_INTERFACE, extra_params=None):

        # create class
        preference_class = Phpclass(
            class_namespace='Preference\\{}'.format(full_namespace.strip('\\')).replace('Interface', ''),
            extends=full_namespace if namespace_type == self.TYPE_CLASS else None,
            implements=[full_namespace] if namespace_type == self.TYPE_INTERFACE else None
        )

        # Add class to module
        self.add_class(preference_class)

As you can see we are creating a PHP class by using `Phpclass'. Phpclass can be merged together into one class, when for example the same class is added by the same snippet or other snippets.

Adding preference to the di.xml

So know we have added the preference class to the module. We must let Magento know that we are preferring this class. In Magento you do that by making a preference in the etc/di.xml file. Mage2Gen have it's own xml node class for creating xml's.

def add(self, full_namespace, namespace_type=TYPE_INTERFACE, extra_params=None):
    # ...

    # Create XML node tree
    config = Xmlnode('config', attributes={'xsi:noNamespaceSchemaLocation':"urn:magento:framework:ObjectManager/etc/config.xsd"}, nodes=[
        Xmlnode('preference', attributes={'for': full_namespace, 'type': preference_class.class_namespace})
    ])

    # Adding node tree to module for etc/di.xml
    self.add_xml('etc/di.xml', config)

One of the difference the Mage2Gen Xmlnode have over other implementations, is what makes a node unique. This is the combination of node name and some attributes. The attributes that's make a node unique is configurable by node, the default is id and name.

This combination for what makes a node unique, is used when merging two xml node tree's.

Customize the params

The snippet classmethod function params will automatically detect the params from the add function. The params function will be used by the cli and web application for generating there interface. We are going to override this function for adding more information and control over the params.

@classmethod
def params(cls):
    return [
        SnippetParam(name='full_namespace', 
            required=True,
            description='Example: Magento\Catalog\Model\Product',
            regex_validator=r'^[\w\\]+$',
            error_message='Only alphanumeric, underscore and backslash characters are allowed'),
        SnippetParam(name='namespace_type', 
            required=True, 
            choises=cls.TYPE_CHOISES, 
            default=cls.TYPE_INTERFACE),
    ]

Whit the override we changed two things. For the full_namespace we added a description and regex validation. The reasoning for using regex validation is that it's easy to add and implement by the cli and web application. We changed the namespace_type to a choices list, this will make the input a select box for the web application.

Ready to go

That's it for creating a Mage2Gen snippet. If you start up the cli application a new snippet option is added. And if you create a pull request for the snippet it will be available on mage2gen.com.

The complete snippet will look like this:

from mage2gen import Module, Phpclass, Phpmethod, Xmlnode, StaticFile, Snippet, SnippetParam


class PreferenceSnippet(Snippet):

    TYPE_CLASS = 'class'
    TYPE_INTERFACE = 'interface'

    TYPE_CHOISES = [
        [TYPE_CLASS, 'Class'],
        [TYPE_INTERFACE, 'Interface']
    ]

    def add(self, full_namespace, namespace_type=TYPE_INTERFACE, extra_params=None):

        # create class
        preference_class = Phpclass(
            class_namespace='Preference\\{}'.format(full_namespace.strip('\\')).replace('Interface', ''),
            extends=full_namespace if namespace_type == self.TYPE_CLASS else None,
            implements=[full_namespace] if namespace_type == self.TYPE_INTERFACE else None
        )

        # Add class to module
        self.add_class(preference_class)

        # Create XML node tree
        config = Xmlnode('config', attributes={'xsi:noNamespaceSchemaLocation':"urn:magento:framework:ObjectManager/etc/config.xsd"}, nodes=[
            Xmlnode('preference', attributes={'for': full_namespace, 'type': preference_class.class_namespace})
        ])

        # Adding node tree to module for etc/di.xml
        self.add_xml('etc/di.xml', config)

    @classmethod
    def params(cls):
        return [
            SnippetParam(name='full_namespace', 
                required=True,
                description='Example: Magento\Catalog\Model\Product',
                regex_validator=r'^[\w\\]+$',
                error_message='Only alphanumeric, underscore and backslash characters are allowed'),
            SnippetParam(name='namespace_type', 
                required=True, 
                choises=cls.TYPE_CHOISES, 
                default=cls.TYPE_INTERFACE),
        ]
Previous Post Mage2Gen Next Post Meet Magento 2017

There are no comments yet.

Leave a Comment

You may format you comment with Markdown.