All posts by zsolti

Magento: Rows are not updated in custom EAV model tables

Another day, another annoying Magento issue. I’ve created a new EAV model, and when I thought that all things are finished, I’ve found out that in the model’s entity tables (_int, _varchar, etc.) the rows are not updated, but always inserted.
Viktor Hesselbom’s article pointed out that the problem’s root is in _updateAttribute method.
What remained to me is to fix it. You will have to override in your resource model the _updateAttribute method. Here is the solution:

 

protected function _updateAttribute($object, $attribute, $valueId, $value)
{
    $table = $attribute->getBackend()->getTable();
    if (!isset($this->_attributeValuesToSave[$table])) {
        $this->_attributeValuesToSave[$table] = array();
    }

    $entityIdField = $attribute->getBackend()->getEntityIdField();

    $data   = array(
        'entity_type_id'    => $object->getEntityTypeId(),
        $entityIdField      => $object->getId(),
        'attribute_id'      => $attribute->getId(),
        'value'             => $this->_prepareValueForSave($value, $attribute)
    );
    if ($valueId)
    {
        $data['value_id'] = $valueId;
    }

    $this->_attributeValuesToSave[$table][] = $data;

    return $this;
}

Only the value_id has been added to the $data array, if $valueId is set. In rest it is the same as the original method.
Good luck!

How to show the default checkbox near a Magento attribute

It is annoying that Magento doesn’t have a much easier way to insert this thing. What am I talking about? See this screenshot:

Default checkbox in product pages

Well, this is quite an ugly solution, but maybe it will work for you. First of all, on product pages there is a custom renderer for each element, that is why it is shown there. So, if you have the following element:

$name = $fieldset->addField('name', 'text', array(
    'name' => 'name',
    'required' => true,
    'class' => 'required-entry',
    'label' => Mage::helper('some_helper')->__('Name'),
));

you will have to render it with a custom renderer:

if ($name)
{
    $name->setRenderer(
        $this->getLayout()->createBlock('adminhtml/catalog_form_renderer_fieldset_element')
    );
}

At this point you should have the third column with the scope-label class. But the checkbox near it still won’t show up. For that we have to set the following for the form:

$storeObj = new Varien_Object();
$storeId = $this->getRequest()->getParam("store");
$storeObj->setId($storeId);
$storeObj->setStoreId($storeId);
$form->setDataObject($storeObj);

Now you should see also the checkbox. That’s all folks!

How to remove a tab from the Customer or Product page in the backend

I’ve been looking for this in google or stackoverflow for a couple of hours, before I found out how things work. We are talking about the backend. Lets begin with the Product tabs, because it is easier.

1. Removing Product tabs
a) Open your layout xml file (default: catalog.xml)
b) Search for <adminhtml_catalog_product_edit>
c) Add removeTab action Inside the “product_tabs” block

<reference name="left">
   <block type="adminhtml/catalog_product_edit_tabs" name="product_tabs">
      <action method="removeTab">
          <name>NAME_OF_TAB</name>
      </action>
   </block>
</reference>

You can find out the NAME_OF_TAB, by inspecting the tab’s anchor (<a>) and looking for the “name” attribute.

2. Removing Customer tabs
a) You have to override Mage_Adminhtml_Block_Customer_Edit_Tabs because the Magento guys did a small typo there: they are adding tabs in _beforeToHtml() method instead of _prepareLayout(). So first you have to modify your config.xml and add:

<global>
    <blocks>
        <adminhtml>
            <rewrite>
                <customer_edit_tabs>Yourmodule_Customer_Block_Edit_Tabs</customer_edit_tabs>
            </rewrite>
        </adminhtml>
    </blocks>
</global>

In Yourmodule_Customer_Block_Edit_Tabs just copy and paste the Mage_Adminhtml_Block_Customer_Edit_Tabs contents (don’t forget to change the class name!), and rename _beforeToHtml() method to _prepareLayout()

b) Add the removeTab action into your layout xml (default: customer.xml):

<adminhtml_customer_edit>
    <reference name="left">
        <block type="adminhtml/customer_edit_tabs" name="customer_edit_tabs">
            <action method="removeTab">
                <name>NAME_OF_TAB</name>
            </action>
        </block>
    </reference>
</adminhtml_customer_edit>

Finding out the NAME_OF_TAB is similar as for the products (see step 1).

That’s all folks!

Magento: eav attribute frontend input types

Sometimes you are looking for a specific frontend input types of the eav attributes, but you don’t have a full list. You can find the types in lib/Varien/Data/Form/Element. Here they are:

  • Button
  • Checkbox
  • Checkboxes
  • Collection
  • Column
  • Date
  • Editor
  • Fieldset
  • File
  • Gallery
  • Hidden
  • Image
  • Imagefile
  • Label
  • Link
  • Multiline
  • Multiselect
  • Note
  • Obscure
  • Password
  • Radio
  • Radios
  • Reset
  • Select
  • Submit
  • Text
  • Textarea
  • Time

Magento: Changing street line count of customer address programatically

Nowadays I’m facing interesting programming challenges. The last one was the changing of street line count of a customer address. As you may know, Magento doesn’t have a good documentation for similar situations. So here it is how I did it.

Create an upgrade script in your module. Inside you should have something like:

$installer = $this;
$installer->startSetup();
$db = $this->getConnection();
$path = "customer/address/street_lines";
$value = 3; // change this with your new street line value
// here you will read the config id of the street line
// if exists update it, else insert it, naturally you could have used a replace into syntax also
// this is just for simplicity. Be careful, this solution does not treat cases when
// you are using multiple stores with different settings
$sql = $db->prepare("
    SELECT config_id
    FROM core_config_data 
    WHERE 
        path = :path
    ");
$sql->execute(array(
    ":path" => $path
));
$result = $sql->fetch();
if (!empty($result))
{
    $sql = $db->prepare("
        UPDATE core_config_data
        SET
            value = :value
        WHERE
            config_id = :config_id
    ");
    $sql->execute(array(
        ":config_id" => $result['config_id'],
        ":value" => $value
    ));
}
else
{
    $sql = $db->prepare("
        INSERT INTO core_config_data (path, value)
        VALUES(:path, :value)
    ");
    $sql->execute(array(
        ":path" => $path,
        ":value" => $value
    ));
}
// next step is to read the attribute id of "street" from the eav_attribute table
$sql = $db->prepare("
    SELECT attribute_id
    FROM eav_attribute
    WHERE 
        entity_type_id = 2 AND
        attribute_code = 'street'
    ");
$sql->execute();
$result = $sql->fetch();
$streetAttributeId = $result['attribute_id'];
// update "multiline_count" column in "customer_eav_attribute" table
$sql = $db->prepare("
UPDATE customer_eav_attribute
SET multiline_count = :value
WHERE
attribute_id = :street_attribute_id
");
$sql->execute(array(
":value" => $value,
":street_attribute_id" => $streetAttributeId
));

$installer->endSetup();

Briefly: you have to save in core_config_data and also in customer_eav_attribute table your new street line value.

Running Phpunit PHAR under Windows

Installing the Phpunit sometimes is not that easy. Especially when we want to do it quickly and under Windows.
There are a couple of ways doing it, but it is not well documented how to use it with a PHAR under Windows. For those who don’t know what is a PHAR, it is a PHp ARchive, which in practice is a zip file containing the whole structure of a site, and you can run it easily like a simple php file.
Running the Phpunit test can be done in several ways: using just command line, or using an external tool like Ant.
First of all you have to place your phpunit.phar in a path ex. c:mypath. Then configure Phpunit xml file. Example:

<phpunit>
  <testsuites>
    <testsuite name="my test">
      <file>c:/work/tests/SomeTest.php</file>
      <file>c:/work/tests/AnotherTest.php</file>
    </testsuite>
  </testsuites>
</phpunit>

I named it tests.xml and put it in folder c:worktests. You can write your own test files and place it wherever you want (in my build file they are placed in c:worktests). Example:

class SomeTest extends PHPUnit_Framework_TestCase
{
    public function testPushAndPop()
    {
        $stack = array();
        $this->assertEquals(0, count($stack));

        array_push($stack, 'foo');
        $this->assertEquals('foo', $stack[count($stack)-1]);
        $this->assertEquals(1, count($stack));

        $this->assertEquals('foo', array_pop($stack));
        $this->assertEquals(0, count($stack));
    }
}

Now you can run it with:
PATH_TO_PHPphp.exe c:mypathphpunit.phar -c c:workteststests.xml.

That’s it. Additionally, if you want this to be part of an Ant build, there is a simple way doing it:

1. You must have java to be able to run Ant
2. Create a build.xml for Ant. Example:

<project name="MyProject" basedir="PATH_TO_PHPUNIT_PHAR">
	<description>
		simple example build file
	</description>
	<target name="phpunit" description="Run unit tests with PHPUnit">
		<exec executable="PATH_TO_PHP/php.exe" failonerror="true">
			<arg value="phpunit.phar"/>
			<arg value="-c"/>
			<arg value="c:/work/tests/tests.xml"/>
		</exec>
	</target>
</project>

3. Run: ant.bat phpunit

How to create a UNIQUE column in magento install script

You can add unique columns in your install script easier than you thought. After searching for a while in search engines without any results, the good old method helped: search in all files of magento… The good news is that you get the results of my searches right here:

<?php
$installer = $this;
$installer->startSetup();
$table = $installer->getConnection()
->newTable($installer->getTable(SOME_TABLE_NAME_DEFINITION))

->addColumn(“my_unique_column_name”,…)

->addIndex($installer->getIdxName(SOME_TABLE_NAME_DEFINITION, array(‘my_unique_column_name’), Varien_Db_Adapter_Interface::INDEX_TYPE_UNIQUE), “my_unique_column_name”,array(“type” => Varien_Db_Adapter_Interface::INDEX_TYPE_UNIQUE));
$installer->getConnection()->createTable($table);

$installer->endSetup();

How to find out the real cause of the boring RuntimeException in ZF2

Nowadays I’m just getting annoying error stack traces from zend framework 2 with a message like:

PHP Fatal error:  Uncaught exception 'ZendViewExceptionRuntimeException' with message 'ZendViewRendererPhpRenderer::render: Unable to render template "error"; resolver could not resolve to a file' in C:worksrcvendorzendframeworkzendframeworklibraryZendViewRendererPhpRenderer.php:451

and naturally the stack trace doesn’t even shows where the real problem happens.

I tried to put the problematic code lines in a try-catch block, but the same happened: uncaught exception. That was really weird, because I tried to catch Exception, which should be every exception’s parent class. Later I found out that since the current file is in a specific namespace, then I have to specify the exact class type of the Exception class. For example:

<?php
namespace SomeName;
class Whatever
{
    try
    {
        //some erroneous code
    }
    catch (Exception $e)
    {
        echo $e;
    }

}

If you would try to catch Exception, then the exception class should be SomeNameException, so that’s why you have to use it like Exception, so php will try to find the class in the root namespace.

 

Setting the doctype in Zend Framework 2

I recently had to change the doctype in a Zend Framework 2 environment. It was kind of difficult to find the answer so I share with you. Simply set the doctype before “running it” in index.php like this:

<?php
/**
 * This makes our life easier when dealing with paths. Everything is relative
 * to the application root now.
 */
chdir(dirname(__DIR__));

// Setup autoloading
include 'init_autoloader.php';

$doctypeHelper = new ZendViewHelperDoctype();
$doctypeHelper->setDoctype($doctypeHelper::HTML5);

// Run the application!
ZendMvcApplication::init(include 'config/application.config.php')->run();

The possible doctypes are:

XHTML11            
XHTML1_STRICT      
XHTML1_TRANSITIONAL
XHTML1_FRAMESET    
XHTML1_RDFA        
XHTML1_RDFA11      
XHTML_BASIC1       
XHTML5             
HTML4_STRICT       
HTML4_LOOSE        
HTML4_FRAMESET     
HTML5              
CUSTOM_XHTML       
CUSTOM

That’s all folks.

Grid column types in Magento

I recently had to implement a new grid in Magento, and I wasn’t sure what should I write to the type parameter:

protected function _prepareColumns()
{
      $this->addColumn('some_column_id', array(
              'header' => Mage::helper('core')->__('Some column name'),
              'index' => 'some_column_index',
              'type' => '???',
      ));
...

So I made a little bit of research to find out all the types available. I don’t want to go in details, so here is the list:

  • action
  • checkbox
  • concat
  • country
  • currency
  • date
  • datetime
  • input
  • interface
  • ip
  • longtext
  • massaction
  • number
  • options
  • price
  • radio
  • select
  • store
  • text
  • theme
  • wrapline

You will find all these types in /app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column/Renderer folder. You can also make your own grid types similarly.