Search code examples
phpzend-frameworkbootloaderzend-form-element

Autoloading custom Zend_Form_Element


I can't get Zend to autoload a custom form element class. I did things exactly as Marcin describes here (except that my classes start with 'Zend' and not 'my' but I'm getting this error:

Warning: include_once(Zend\Form\Element\Div.php) [function.include-once]: failed to open stream: No such file or directory

I have Zend_Form_Element_Div inside forms\elements\ and Zend_View_Helper_FormDiv inside views\helpers\

Basically, every folder in the error message is missng an 's', the right path is Zend\Forms\Elements\Div.php

I also have this in my bootstrap, though I'm not sure if it's necessary, but I'm also using this for my forms and models folder (and some others, but I don't think there's need to post them all):

<?php
    $resourceLoader->addResourceTypes(array(
        'model' => array(
        'namespace' => 'Model',
        'path' => 'models'
        ),
        'element' => array(
        'namespace' => 'Element',
        'path' => 'elements'
        ),
        'form' => array(
        'namespace' => 'Form',
        'path' => 'forms'
        )
));
?>

(Is there actually any other way of doing this autoloading? Instead of declaring every single folder?)

Update:

  • Element_Div in application/forms/elements/Div.php
  • In my forms init() method: $this->addElementPrefixPath('Element_', APPLICATION_PATH . '/forms/elements');
  • Error I'm getting: Fatal error: Class 'Element_Div' not found in C:\xampplite\htdocs\code\application\forms\PostForm.php on line 63

Solution

  • You essentially have to tell the form where to find custom elements by using:

    $form->addElementPrefixPath()

    In your case, you would use - either within the form's init() or __construct() method - something like:

    $this->addElementPrefixPath('Zend_Form_Element_', APPLICATION_PATH . '/elements);;

    However, I have agree with @Marcin. Naming your own classes with the Zend_ pseudo-namespace is ill-advised. Either:

    1. Decide on an application namespace and declare it in your Bootstrap when you create your $resourceLoader

    2. Create an custom library that resides on your include path - probably at the same level as the Zend library - and put your custom stuff out there.

    Let me know if you need more details on either of these suggestions and I'll fatten up the explanations a bit.

    Update based on comments

    Using an empty appnamespace, your call to addElementPrefixPath() now changes to:

    $this->addElementPrefixPath('Element_', APPLICATION_PATH . '/elements);

    And I guess you could remove the elements entry from the $resourceLoader definition in your Bootstrap since it's really not doing anything.

    Update 2

    I assumed that you were adding the element to the form using the shortname, something like:

    $form->addElement('div', 'my_div');
    

    In this circumstance, we need to tell the $form and its plugin registry where to find an element of type 'div'. That's why we dealt with $form->addElementPrefixPath().

    However, from the error message you are reporting, it appears that you are adding your custom element to the form using something like:

    $div = new Element_Div();
    $form->addElement($div, 'my_div');
    

    In this case, it is not the $form and its plugin registry that has to worry about finding/loading/instantiating the custom element; it is the $autoloader via its $resourceLoader. In that case, there is no need for the $form->addElementPrefixPath(), which is essentially a hint to the form on how to find custom elements invoked by shortname.

    What we need is to configure the $resourceLoader back in Bootstrap so it knows where to find the class. Assuming you stick with empty appnamespace (so your class is named Element_Div) and you place the file in application/forms/elements/Div.php, then the $resourceLoader call is as follows:

    $resourceLoader->addResourceTypes(array(
        'model' => array(
            'namespace' => 'Model_',
            'path' => 'models'
        ),
        'element' => array(
            'namespace' => 'Element_',
            'path' => 'forms/elements'
        ),
        'form' => array(
            'namespace' => 'Form_',
             'path' => 'forms'
        )
    ));
    

    That should do it. [Famous last words, eh?]