0

I’m trying to create my own module for Magento 1.9.1. In my module, I am trying to call a block in CMS content like this:

{{block type="core/template" template="myNamespace/myModulOutput.phtml"}}

The myModulOutput.phtml template contains a collection from my own controller:

class myNamespace_myModelname_Block extends Mage_Core_Block_Template
{
    public function getCollection()
    {
       // some code
        return $collection;
    }
}

The module seems to be active and shows up in Magento backend with this configuration:

<config>
    <modules>
        <myNamespace_myModulname>
            <version>0.1.0</version>
        </myNamespace_myModulname>
    </modules>
    <global>
        <blocks>
            <myNamespace_myModulname> 
                <class>myNamespace_myModulname_Block</class>
            </myNamespace_myModulname>
        </blocks>
    </global>
</config>

The block class is defined in the file app/code/local/myNamespace/myModulname/Blocks/Index.php.

Is this a valid configuration? I am getting an error on the frontend: Fatal error: Call to a member function getCollection() on a non-object.

EDIT

SOLVED

By explanation from @b.enoit.be I tried the following setting ... it's run ;-)

app/etc/modules/Mynamespace_Mymodulname.xml:

<?xml version="1.0"?>
<config>
    <modules>
        <Mynamespace_Mymodulname>
            <active>true</active>
            <codePool>local</codePool>
            <depends/>
        </Mynamespace_Mymodulname>
    </modules>
</config>

app/code/local/Mynamespace/Mymodulname/Block/Index.php:

    <?php
    class Mynamespace_Mymodulname_Block_Index extends Mage_Core_Block_Template
    {
        public function getTest()
        {
           // some code
            return "mymodul:test";
        }
    }
    ?>

app/code/local/Mynamespace/Mymodulname/etc/config.xml:

<?xml version="1.0"?>
<config>
    <modules>
        <Mynamespace_Mymodulname>
            <version>0.1.0</version>
        </Mynamespace_Mymodulname >
    </modules>
    <global>
        <blocks>
            <mynamespace_mymodulname> 
                <class>Mynamespace_Mymodulname_Block</class>
            </mynamespace_mymodulname >
        </blocks>
    </global>
</config>

CMS-Call

{{block type="mynamespace_mymodulname/index" template="mynamespace/myoutput.phtml"}}

app/design/frontend/myTheme/default/mynamespace/myoutput.phtml:

<?php /** @var $this Mynamespace_Mymodulname_Block_Index */ ?>
<?php echo $this->getTest() ?>

Many thanks for so detailed and meaningful explanation :-)

Community
  • 1
  • 1
SFrey
  • 3
  • 3

2 Answers2

3

If those myNamespace_myModulname are really what you have in your actual code, please first have a look at @fantasticrice answer

Then, if we consider your code with the right naming convention (see note in the end of this post), to start up with, this is not a valid block class :

class Mynamespace_Mymodulename_Block extends Mage_Core_Block_Template{ /* ... */ }

If, like you say it later on, your file is located in

app/code/local/Mynamespace/Mymodulename/Block/Index.php

then the valid block class is

class Mynamespace_Mymodulename_Block_Index extends Mage_Core_Block_Template{ /* ... */ }

Because class name in Magento should always follow the exact same architecture as your path to file (except for controllers, but we are not talking about controllers at all with the code you gave us here) =>

class Mynamespace_Mymodulename_Block_Index === Mynamespace/Mymodulename/Block/Index.php

(see how I just replaced underscore with slashes and postfixed this with a .php extension ?).

Then what you actually want is your your view mynamespace/mymoduleoutput.phtml to use your own module block.
For this, you have to specify the right type for your block.

The type is defined by a combinaison of your handle defined in config.xml and from the path to your block file.

1. The handle

When you define a config.xml for a module, you have to be aware that some part are "fixed" and some parts are "handles".
Meaning that some parts are expected by Magento to be in a limited number of possibilities e.g. frontend or adminhtml or global, blocks, models, helpers, ... there is more here and some are just name to alias your module and to call it or handle as we call that under Magento.

Here you say in your config.xml that for the <global> config (means for both frontend and backend (= admin)) -- fixed -- you want to add to the list of existing <blocks> -- fixed -- of Magento the blocks of your module that would be referenced by the -- handle -- <mynamespace_mymodulename> which will then map to files in which the classes will all begin with Mynamespace_Mymodulename_Block.

That is the first part of the type you want for your own module block.
mynamespace_mymodulename would be equivalent for Magento to Mynamespace_Mymodulename_Block

2. The right block

Then you just have to indicate the right path from the root folder of your Blocks to Magento, which will be the exact same as your folders/files architecture, once again : so in your case, just index.
So as you may now understand, Magento will lookup to a class Mynamespace_Mymodulename_Block_Index (handle + '_' + specified block) in the file Mynamespace/Mymodulename/Block/Index.php as seen earlier.
But if your file was under app/code/local/Mynamespace/Mymodulename/Block/Path/To/File.php you would have path_to_file => class Mynamespace_Mymodulename_Block_Path_To_File, file Mynamespace/Mymodulename/Block/Path/To/File.php

Now that we have the second part of our type we just have to assemble them with a slash : mynamespace_mymodulename/index

So you have to change your call in your cms to :

{{block type="mynamespace_mymodulename/index" template="mynamespace/mymoduleoutput.phtml"}}

And hopefully, with this, it would work.

NOTE 1/3 : As pointed out by @fantasticrice, I would also suggest you to follow the naming convention of class:

Zend Framework standardizes on a class naming convention whereby the names of the classes directly map to the directories in which they are stored. (...)

Class names may only contain alphanumeric characters. Numbers are permitted in class names but are discouraged in most cases. Underscores are only permitted in place of the path separator; the filename "Zend/Db/Table.php" must map to the class name "Zend_Db_Table".

If a class name is comprised of more than one word, the first letter of each new word must be capitalized. Successive capitalized letters are not allowed, e.g. a class "Zend_PDF" is not allowed while "Zend_Pdf" is acceptable.

These conventions define a pseudo-namespace mechanism for Zend Framework. Zend Framework will adopt the PHP namespace feature when it becomes available and is feasible for our developers to use in their applications.

See the class names in the standard and extras libraries for examples of this classname convention.

Source : http://framework.zend.com/manual/1.12/en/coding-standard.naming-conventions.html

NOTE 2/3 : that per convention again, only class are going to have a capitalised file name / folder structure. So, your template folders and files should not have capital in it at all.

NOTE 3/3 : In Magento, again, per convention we do not use capitals in handles.


TL;DR

app/code/local/Mynamespace/Mymodulename/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Mynamespace_Mymodulename>
            <version>0.1.0</version>
        </Mynamespace_Mymodulename>
    </modules>
    <global>
        <blocks>
            <mynamespace_mymodulename> 
                <class>Mynamespace_Mymodulename_Block</class>
            </mynamespace_mymodulename>
        </blocks>
    </global>
</config>

app/code/local/Mynamespace/Mymodulename/Block/Index.php

<?php
class Mynamespace_Mymodulename_Block_Index extends Mage_Core_Block_Template
{
    public function getCollection()
    {
       // some code
        return $collection;
    }
}

CMS content

{{block type="mynamespace_mymodulename/index" template="mynamespace/mymoduleoutput.phtml"}}

app/etc/modules/Mynamespace_Mymodulename.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Mynamespace_Mymodulename>
            <active>true</active>
            <codePool>local</codePool>
            <depends/>
        </Mynamespace_Mymodulename>
    </modules>
</config>

app/design/frontend/base/default/template/mynamespace/mymoduleoutput.phtml

<?php /** @var $this Mynamespace_Mymodulename_Block_Index */ ?>
<?php foreach($this->getCollection() as $item): ?>
    <?php //do something ?>
<?php endforeach; ?>
Community
  • 1
  • 1
β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
  • Good answer, but I think you [must use uppercase names on the path due to `ucwords()` in the autoloader](http://codeserious.com/magento-certification-basics-class-naming-conventions-autoloader/). As a result, the convention of class names usually matches, with Begin_Each_Part as uppercase. – fantasticrice Jun 01 '15 at 22:30
  • @fantasticrice I would surely totally back you up on that one since that is a naming convention inherited from Zend Framework and that, as a convention it should always be followed. But sadly, [class name are insensitive in php](http://stackoverflow.com/a/5260230/2123530) and so, that is not part of the OP issue. – β.εηοιτ.βε Jun 01 '15 at 22:35
  • 1
    Yes, I know the class names are case-insensitive, and that is why the class naming is merely a convention but not mandatory. The **path** is (probably) case-sensitive though, as I mentioned. See the autoloader code in the link I posted, where the subdirectory names are parsed with `ucwords()`. So, while the class may be camelCase_moduleName, the directory will be rendered as CamelCase/ModuleName. – fantasticrice Jun 01 '15 at 22:40
  • Well seen. But those `myNamespace` & `myModulname` really looks like make over ones, and I tend not to relate on them when answering because most people will hide the really **companyName / customerName** they actually have in there. Still your point is valid off course. – β.εηοιτ.βε Jun 01 '15 at 22:55
  • True, thanks. Still, if their fake names don’t follow the conventions then there’s a good chance the real ones don’t either. Glad you found it worth pointing out. :) – fantasticrice Jun 01 '15 at 22:59
  • 1
    @fantasticrice well and when I read that code back, I cannot help to find that ugly when not following convention. So I cleaned up a bit after myself, looks nice now I think. :) – β.εηοιτ.βε Jun 01 '15 at 23:29
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/79371/discussion-between-b-enoit-be-and-fantasticrice). – β.εηοιτ.βε Jun 01 '15 at 23:36
  • @b.enoit.be: many thanks for the detailed answer :-) But: it's does not solved my problem. Again i have the statements reproduced step by step. I edit the type in the block-call to `mynamespace_mymodule/index` .. with your explanation I also understand why .. and no output can see. If I take the `core/template` type and call the class not with `$this->getCollection()`, but with `Mynamespace_Mymodule_Block_Index::getCollection()` it's work and i get the output from the class. you can do it that way ? should I do that then better so ? – SFrey Jun 02 '15 at 07:49
  • @SFrey I wrongly copied `Blocks` from you answer as a folder when it should instead be `Block` in regard to the class names, maybe this will fix, I also edited my answer. And to answer your question, no, not at all, using Magento blocks in a static way should be avoided at all costs. – β.εηοιτ.βε Jun 02 '15 at 11:41
  • There was also a mix between myModelname / myModulname, a lot of typo from my side and some problems in some folders, sorry for that ! I did corrected the whole post and the tl;dr and tested it and can confirm it does work now :) – β.εηοιτ.βε Jun 02 '15 at 19:50
1

There are a couple issues here that I notice, so I will just try to address the ones that can be seen by what you have posted in your question, although what you are trying to accomplish is not totally clear, and you would probably benefit most from reading a guide about Magento’s layout, blocks, and templates:

  1. Your current class names do not follow the Magento autoloader naming conventions, which you’ll notice use ucwords() on each path element. Your class name should be something like: Mynamespace_Mymodulename_Block_Myblockname, which would map to the file app/code/.../Mynamespace/Mymodulename/Block/Myblockname.php. Your config XML should be updated to match.

  2. Your CMS directive currently doesn’t make use of your new block type since it is set to type="core/template" when it should be type="Mynamespace_Mymodulename/Myblockname" if you wish to make use of your block’s custom logic in your template. Your template’s code is not shown, but this is likely why calling getCollection() in your template isn’t working.

If you show more of your work, we might be able to help more.

fantasticrice
  • 1,631
  • 1
  • 21
  • 31