Default System Config Values in config.xml

To set default values for system config items use the following:

<config>
    <default>
        <shipnote_options>
            <basic_settings>
                <enabled>1</enabled>
                <frontend_label>Salesman Code</frontend_label>
            </basic_settings>
        </shipnote_options>
    </default>
</config>

For a Store:

<config>
    <stores>
        <store_code>
            <shipnote_options>
                <basic_settings>
                    <enabled>1</enabled>
                    <frontend_label>Salesman Code</frontend_label>
                </basic_settings>
            </shipnote_options>
        </store_code>
    </stores>
</config>

For a Website:

<config>
    <websites>
        <store_code>
            <shipnote_options>
                <basic_settings>
                    <enabled>1</enabled>
                    <frontend_label>Salesman Code</frontend_label>
                </basic_settings>
            </shipnote_options>
        </store_code>
    </websites>
</config>

Installing Magento 2 Using Composer

Install Magento

Standard Install

This will install Magento with no settings present – you will enter these from visiting your site’s URL.

composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition <directory>

Setup Install

This will install Magento pre-populated with settings, so that you won’t have to enter them in the setup process.

php bin/magento setup:install \
    --db-host=127.0.0.1 \
    --db-name=magento2 \
    --db-user=root \
    --db-password=password123 \
    --base-url=http://www.example.com/ \
    --admin-user=admin \
    --admin-firstname=Admin \
    --admin-lastname=User \
    --admin-email=test@example.com \
    --admin-password=password123

bin/magento cron:run
bin/magento cron:run
bin/magento setup:static-content:deploy
bin/magento deploy:mode:set developer
rm -rf var/cache

If you are prompted to enter a username and password, you can retrieve or create these by logging into magentocommerce.com;

  • Click the ‘Connect’ Tab
  • Click ‘Secure Keys’ from the left navigation
  • If you don’t have a secure key listed, enter a name and click ‘Generate new’.
  • The username you need to enter is the Public Key, and the password is the Private Key.

Change the Directory Permissions

find var vendor pub/static pub/media app/etc -type f -exec chmod g+w {} \;
find var vendor pub/static pub/media app/etc -type d -exec chmod g+w {} \;
chmod u+x bin/magento

Complete Installation 

Visiting your website’s URL should walk you through the rest of the installation process. This step will be omitted if the ‘Setup Install’ section above was followed

Installing Sample Data 

The following will install Magento’s sample data with an increased memory limit, as it will tend to fail without it.

php -d memory_limit=2G bin/magento sampledata:deploy

Run Upgrade

bin/magento setup:upgrade

System.xml

The basic structure of system.xml is as follows:

<config>
    <tabs>
      <catalog translate="label" module="catalog">
        <label>Catalog</label>
        <sort_order>100</sort_order>
      </catalog>    
    </tabs>
    <sections>
        <catalog translate="label" module="catalog">		      			 <tab>catalog</tab>
            <sort_order>40</sort_order>
            <class>separator-top</class> <!— any css class —>

            <show_in_website>1</show_in_website>
            <show_in_default>1</show_in_default>
            <show_in_store>1</show_in_store>
            <groups>
		   <general translate="label">
              <label>General</label>
              <show_in_default>1</show_in_default>
              <show_in_website>1</show_in_website>
              <show_in_store>1</show_in_store>

		     <fields>
                   <!— field declarations —>
			   <list_mode translate="label">
                   	<label>List Mode</label>
                   	<frontend_type>select</frontend_type>
			   	<source_model>adminhtml/system_config_source_catalog_listMode</source_model>
                   	<sort_order>1</sort_order>
                   	<show_in_default>1</show_in_default>
                   	<show_in_website>1</show_in_website>
                   	<show_in_store>1</show_in_store>
				<comment>Comment displayed underneath field</comment>
				<backend_model>adminhtml_for_example_only</backend_model>
			   </list_mode>
                </fields>
            </groups>
        </catalog>
    </sections>
</config>
  • Tabs refer to the tabs on the right of the system/configuration page. Tabs are on the same level as sections, but they refer to one another.

  • When a section loads, it gathers all children and orders them by their sort order. So, other modules can add parts to any other module including core, by following the same xpath.

  • Sections, groups and fields can have show_in_default, show_in_store, and show_in_website nodes. If they’re not specified, they default to false. If there are no displayable sections / groups / fields for a tab, then the tab will not show (common gotcha).

  • E.g. Path hints is set to false to show in default (otherwise the admin would look broken).

  • If a tab has no sections defined, then it will not be displayed.

  • Frontend_type is the type of input which will be displayed on the form, not the type which will in any way be displayed on the frontend of the site. Values can be text, textarea, image, select, multiselect (TODO: look at others)

  • source_model is a data provider to give the form element values. E.g. for a select, a class path can be provided of a class which contains a getOptionArray() method, which returns a set of values to be selected. This also works with multiselects.

  • backend_model is a model which is used to do something after the save, such as clear cache or save additional data somewhere else. Should extends Mage_Core_Model_Config_Data, this is because for every form field, if there’s no backend model specified, Magento will instantiate an instance of Mage_Core_Model_Config_Data – this is the model that knows how to save configuration values. This model just extends Mage_Core_Model_Abstract. To add validation, an exception can be thrown from the _beforeSave, or save() method, and a warning or notice can be set on the session object in the _afterSave() method:

public function _afterSave(){
	$helper = Mage::helper('llapgoch_addpcattributes');
	
	if(strpos($this->getValue(), ",") !== false){
		 Mage::getSingleton('core/session')->addNotice($helper->__('Please use spaces to separate your attribute names instead of commas'));
	}
	
	parent::_afterSave();
	
}

Tabs are the left hand titles. Sections refer to the list items which display below them. Groups are the individual panels which display on a section’s page.

Depends

Fields can depend on one another. If a field depends on another field whose value returns false, then the depending field will not show.

<fields>
    <depender translate="label">
        <label>Depender</label>
        <frontend_type>select</frontend_type>
        <show_in_website>1</show_in_website>
        <show_in_default>1</show_in_default>
        <show_in_store>1</show_in_store>
        <sort_order>2</sort_order>
        <source_model>adminhtml/system_config_source_yesno</source_model>
        <depends>
            <twinkie>1</twinkie>
        </depends>
    </depender>
</fields>

In the above example, depender will only show if field twinkie’s value equals 1. Any value can be depended on. The form is updated via javascript in real time to show and hide depended form fields.

Tooltip & Comment

Fields can have tooltip nodes which display the text in a little question mark after the field. A comment node will place a block of text below the field with an upward arrow.

Joining EAV tables on a Non-EAV Collection

Sometimes, we may want to join EAV tables and their associated value tables on other tables via a foreign key. E.g. we have a table which has a download count for customers who are able to download PDFs. We have a column called customer_id which is our foreign key to link us to the customer_entity table.

Our download table has the following columns:

  • download_log_id
  • customer_id
  • file_path
  • type
  • count
  • customer_name
  • created_at
  • updated_at

The table is grouped by file_path, with each download increasing the count column. We can’t use a collection of the type customer/customer, because we’ll end up with duplicate customers with the same ID in the collection, which Magento will throw an error at.

Instead, we’ll use the collection based from the download table’s collection, and will attempt to join the customer’s EAV tables onto it. To add the attributes firstname and lastname, we can use a custom helper which is detailed in full below:

class Stormking_Skin_Helper_Eav extends Mage_Core_Helper_Abstract{
    protected $_aliasIndex = 0;

    public function joinEAV($collection, $mainTableForeignKey, $eavType, $attrCode, $mainTable = 'main_table'){
        $this->_aliasIndex++;

        $entityType = Mage::getModel('eav/entity_type')->loadByCode($eavType);
        $entityTable = $collection->getTable($entityType->getEntityTable());

        //Use an incremented index to make sure all of the aliases for the eav attribute tables are unique.
        $attribute = Mage::getModel("eav/config")->getAttribute($eavType, $attrCode);

        $attr =  Mage::getModel('eav/entity_attribute')->loadByCode($eavType, $attrCode);

        $alias = 'table_' . $this->_aliasIndex;
        $field = $attrCode; // This will either be the original attribute code or 'value'

        if ($attribute->getBackendType() != 'static'){
            $field = 'value';
            $table = $entityTable. '_'.$attribute->getBackendType();

            $collection->getSelect()
                ->joinLeft(array($alias => $table),
                    $mainTable . '.'.$mainTableForeignKey.' = '.$alias.'.entity_id and '.$alias.'.attribute_id = '. $attr->getId(),
                    array($attribute->getAttributeCode() => $alias . "." . $field)
                );
        }else{
            $collection->getSelect()
                ->joinLeft(array($alias => $entityTable),
                $mainTable . '.'.$mainTableForeignKey.' = '. $alias.'.entity_id',
                    $attribute->getAttributeCode()
                );
        }

        // Return the table alias and field name (either $attrCode or value) so we can use the table in future queries
        return array(
            "table" => $alias,
            "field" => $field
        );

    }
}
  • This creates a join for each of the attributes we wish to join on.
  • The table alias is incremented for each use so we don’t get alias clashes.
  • We return the aliased table name and the name of the field used. This will either be the original field name (in the case of static values) or value in the case of values in a corresponding EAV value table.

Applying The Helper

$collection = Mage::getModel('stormkingskin/download_log')->getCollection();

$helper = Mage::helper('llapgoch_core/eav');
$helper->joinEAV($collection, 'customer_id', 'customer', 'email');
$helper->joinEAV($collection, 'customer_id', 'customer', 'firstname');
$helper->joinEAV($collection, 'customer_id', 'customer', 'lastname');
$helper->joinEAV($collection, 'customer_id', 'customer', 'group_id');

Using the Aliased Table in Other Queries

We may want to use the table which has been joined by our joinEAV query in later additions to our collection. In the example above, suppose we then want to join on the customer’s group_id from the customer_entity table on Magento’s customer_group table so that we can get the customer group name.

The joinEAV method returns the aliased table name which we can then use to make this work:

 
$groupTable = $helper->joinEAV($collection, 'customer_id', 'customer', 'group_id');

$collection->getSelect()->joinLeft(
	array('customer_group' => $collection->getTable('customer/customer_group')),
	'customer_group.customer_group_id=' . $groupTable . '.group_id',
	array('customer_group_code')
);

– This allows us to use the table name from our previous query in our new join to pull out the customer_group_code data.

Filtering in Adminhtml Grids

Using the above methods will cause issues with Adminhtml grids. All of the fields not in the main table’s collection will be stripped out and will cause the SQL to fail. Fortunately, there is a workaround albeit a little convoluted:

1. Log each of the aliased table names

In our grid class, create an array where we will store our generated alias names:

protected $_aliasTables = array();

protected function _prepareCollection() {

	$collection = Mage::getModel('stormkingskin/download_log')->getCollection();
	$helper = Mage::helper('stormkingskin/eav');

	$this->_aliasTables['email'] = $helper->joinEAV($collection, 'customer_id', 'customer', 'email');
	$this->_aliasTables['firstname'] = $helper->joinEAV($collection, 'customer_id', 'customer', 'firstname');
	$this->_aliasTables['lastname'] = $helper->joinEAV($collection, 'customer_id', 'customer', 'lastname');

	$groupTable = $helper->joinEAV($collection, 'customer_id', 'customer', 'group_id');

	$collection->getSelect()->joinLeft(
		array('customer_group' => $collection->getTable('customer/customer_group')),
		'customer_group.customer_group_id=' . $groupTable['table'] . '.group_id',
		array('customer_group_code')
	);

	$collection->getSelect()->joinLeft(
		array('customer_address' => $collection->getTable('customer/address_entity')),
		'customer_address.parent_id=main_table.customer_id',
		array("")
	);

	// Use the last parameter to join this to the customer_address table instead of the default main_table
	$this->_aliasTables['company'] = $helper->joinEAV($collection, 'entity_id', 'customer_address', 'company', 'customer_address');

	$this->setCollection($collection);
	return parent::_prepareCollection();
}

– Note that our query to get the company name differs somewhat as we need to rely on the ID from the customer_address_entity table rather than the default main_table. We use a joinLeft just before to set up the relationship between the main_table and the customer_entity_address table. We can then create our relationship between the customer_entity_address model and the entity’s value table.

2. Add a filter condition callback to any affected fields

This will allow us to override Magento’s default query filter builder process and use our own. All of the field aliases will still be stripped off the final query, but we will now be able to use their original names using the aliases from the last step.

protected function _prepareColumns() {
	$this->addColumn('firstname', array(
		'header'	=> $this->__('First Name'),
		'index'		=> 'firstname',
		'filter_condition_callback' => array($this, '_joinUsingHaving'),
	));

	$this->addColumn('lastname', array(
		'header'	=> $this->__('Last Name'),
		'index'		=> 'lastname',
		'filter_condition_callback' => array($this, '_joinUsingHaving'),
	));

	$this->addColumn('company', array(
		'header'	=> $this->__('Company'),
		'index'		=> 'company',
		'filter_condition_callback' => array($this, '_joinUsingHaving'),
	));
}

3. The Filter Condition Callback Method

protected function _joinUsingHaving($collection, $column){

	$index = $column->getIndex();

	if(!isset($this->_aliasTables[$index])){
		return;
	}

	$tableAlias = $this->_aliasTables[$index];
	$value = $collection->getConnection()->quote("%" . $column->getFilter()->getValue() . "%");
	$collection->getSelect()->where($tableAlias['table'] . "." . $tableAlias['field'] . " LIKE " . $value);
}
  • We check the existence of the index we’re looking for in our alias array.
  • We quote our filter value ready for the custom where query.
  • We then use our table alias and field name.

See here for more information on advanced adminhtml collection queries