Milestone 1: How to populate dropdowns?

sbj

Well-known member
Hello,

so finally after reading about php/XF for 1 month, I will try to develop my 1st addon (1st milestone of it).

What I want to do:

So basically what I want to do is a blank page with 6 dropdowns and 6 textboxes.
The dropdowns should list the entries which will be in the database table.
The textboxes should allow me to write into the database, so the dropdowns can show the entries.

My problems so far:


1) I can't get the params passed to the template, altough I defined actions for that.
2)I don't know how to write xenforo-html-form for the template, to submit the entries.
3)I don't know how to populate the dropdowns with the params (which aren't passed currently).
4)The DataWriter forces me to write a update condition method, which I understand for why it is but I don't know how to use it, like what kind of conditions I can give.

Stuff I have to figure out later:

1)Using XFCP system (which is not hard to do) for the extending of core classes.
2)Make php to create a portal page for the template (currently I use a XF page node).
3)Implement a 2nd route, so 1 page lists the dropdowns, the 2nd one lets me enter values.
4)Make dropdown list order by alphabet, instead of dropdown_id.


I called the addon DataDirectory.

upload_2016-6-14_20-10-28.webp

upload_2016-6-14_20-14-23.webp

Code:
</br>

<fieldset>
<form action="{xen:link 'datadirectory/artist'}" method="post"


<dl class="ctrlUnit">
    <dt><label for=".....">Enter Artist Name:</label></dt>
        <dd><input name="....." value="" id="....." class="textCtrl OptOut" type="text"></dd>
</dl>
</br>

<dl class="ctrlUnit">
    <dt><label for=".....">Enter Composer Name:</label></dt>
        <dd><input name="....." value="" id="....." class="textCtrl OptOut" type="text"></dd>
</dl>
</br>

<dl class="ctrlUnit">
    <dt><label for=".....">Enter Lyricist Name:</label></dt>
        <dd><input name="....." value="" id="....." class="textCtrl OptOut" type="text"></dd>
</dl>
</br>

<dl class="ctrlUnit">
    <dt><label for=".....">Enter Makam Name:</label></dt>
        <dd><input name="....." value="" id="....." class="textCtrl OptOut" type="text"></dd>
</dl>
</br>

<dl class="ctrlUnit">
    <dt><label for=".....">Enter Form Name:</label></dt>
        <dd><input name="....." value="" id="....." class="textCtrl OptOut" type="text"></dd>
</dl>
</br>

<dl class="ctrlUnit">
    <dt><label for=".....">Enter Usul Name:</label></dt>
        <dd><input name="....." value="" id="....." class="textCtrl OptOut" type="text"></dd>
</dl>
</br>
</br>
</br>
I need a submut button here!
</br>
</form>

</fieldset>
</br>
</br>
</br>
</br>
<fieldset>
<dl class="ctrlUnit">
    <dt><label for="...">Artist:</label></dt>
        <dd>
            <select name="..." class="textCtrl autoSize" id="."></select>
        </dd>

</br>
<dl class="ctrlUnit">
    <dt><label for="...">Composer:</label></dt>
        <dd>
            <select name="..." class="textCtrl autoSize" id="."></select>
        </dd>
</dl>
</br>
<dl class="ctrlUnit">
    <dt><label for="...">Lyricist:</label></dt>
        <dd>
            <select name="..." class="textCtrl autoSize" id="."></select>
        </dd>
</dl>
</br>
<dl class="ctrlUnit">
    <dt><label for="...">Makam:</label></dt>
        <dd>
            <select name="..." class="textCtrl autoSize" id="."></select>
        </dd>
</dl>
</br>
<dl class="ctrlUnit">
    <dt><label for="...">Form:</label></dt>
        <dd>
            <select name="..." class="textCtrl autoSize" id="."></select>
        </dd>
</dl>
</br>
<dl class="ctrlUnit">
    <dt><label for="...">Usul:</label></dt>
        <dd>
            <select name="..." class="textCtrl autoSize" id="."></select>
        </dd>
</dl>

</dl>
</fieldset>

(See 2nd post for the rest of the codes)


I am new to php/coding and to creating addons. Any kind of help is highly appreciated.
Thank you.
 
Code:
<?php


class sbj_DataDirectory_Install_Installer
{
    protected static $table = array(
        'createQuery' => 'CREATE TABLE IF NOT EXISTS `xf_sbj_DataDirectory_dropdowns` (          
                `dropdown_id` INT( 10 ) NOT NULL AUTO_INCREMENT,
                `Artist` VARCHAR ( 255 ),
                `Composer` VARCHAR ( 255 ),
                `Lyricist` VARCHAR ( 255 ),
                `Makam` VARCHAR ( 255 ),
                `Form` VARCHAR ( 255 ),
                `Usul` VARCHAR ( 255 ),
                PRIMARY KEY (`dropdown_id`)
                )
            ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;',
        'dropQuery' => 'DROP TABLE IF EXISTS `xf_sbj_DataDirectory_dropdowns`'
    );


    public static function install()
    {
        $db = XenForo_Application::get('db');
        $db->query(self::$table['createQuery']);
    }

    public static function uninstall()
    {
        $db = XenForo_Application::get('db');
        $db->query(self::$table['dropQuery']);
    }
}

Code:
<?php
/**
* Created by PhpStorm.
* User: sbj
* Date: 13.06.2016
* Time: 19:20
*/

class sbj_DataDirectory_Model_DataDirectoryModel extends XenForo_Model
{
    public function getDataDirectoryByArtist()
    {
        return $this->_getDb()->fetchCol('
        SELECT Artist FROM xf_sbj_Datadirectory_dropdowns ORDER BY dropdown_id');
    }

    public function getDataDirectoryByComposer()
    {
        return $this->_getDb()->fetchCol('
        SELECT Composer FROM xf_sbj_Datadirectory_dropdowns ORDER BY dropdown_id');
    }

    public function getDataDirectoryByLyricist()
    {
        return $this->_getDb()->fetchCol('
        SELECT Lyricist FROM xf_sbj_Datadirectory_dropdowns ORDER BY dropdown_id');
    }

    public function getDataDirectoryByMakam()
    {
        return $this->_getDb()->fetchCol('
        SELECT Makam FROM xf_sbj_Datadirectory_dropdowns ORDER BY dropdown_id');
    }

    public function getDataDirectoryByForm()
    {
        return $this->_getDb()->fetchCol('
        SELECT Form FROM xf_sbj_Datadirectory_dropdowns ORDER BY dropdown_id');
    }

    public function getDataDirectoryByUsul()
    {
        return $this->_getDb()->fetchCol('
        SELECT Usul FROM xf_sbj_Datadirectory_dropdowns ORDER BY dropdown_id');
    }
}

Code:
<?php

class sbj_DataDirectory_Datawriter_DataDirectoryDataWriter extends XenForo_DataWriter
{
    protected function _getFields()
    {
        return array(
            'xf_sbj_DataDirectory_dropdowns' => array(

                'dropdown_id' => array(
                    'type' => self::TYPE_UINT,
                    'autoincrement' => true
                ),
                'Artist' => array(
                    'type' => self::TYPE_STRING,
                    'required' => false
                ),
                'Composer' => array(
                    'type' => self::TYPE_STRING,
                    'required' => false
                ),
                'Lyricist' => array(
                    'type' => self::TYPE_STRING,
                    'required' => false
                ),
                'Makam' => array(
                    'type' => self::TYPE_STRING,
                    'required' => false
                ),
                'Form' => array(
                    'type' => self::TYPE_STRING,
                    'required' => false
                ),
                'Usul' => array(
                    'type' => self::TYPE_STRING,
                    'required' => false
                ),
            )
        );
    }

    protected function _getExistingData($data)
    {
        return array (
            array(
                'Artist' => $this->getModelFromCache('sbj_DataDirectory_Model_DataDirectoryModel')->getDataDirectoryByArtist(),
                'Composer' => $this->getModelFromCache('sbj_DataDirectory_Model_DataDirectoryModel')->getDataDirectoryByComposer(),
                'Lyricist' => $this->getModelFromCache('sbj_DataDirectory_Model_DataDirectoryModel')->getDataDirectoryByLyricist(),
                'Makam' => $this->getModelFromCache('sbj_DataDirectory_Model_DataDirectoryModel')->getDataDirectoryByMakam(),
                'Form' => $this->getModelFromCache('sbj_DataDirectory_Model_DataDirectoryModel')->getDataDirectoryByForm(),
                'Usul' => $this->getModelFromCache('sbj_DataDirectory_Model_DataDirectoryModel')->getDataDirectoryByUsul()
            )
        );
    }

    protected function _getUpdateCondition($tableName)
    {
        return 'dropdown_id = ' . $this->_db->quote($this->getExisting('dropdown_id'));
    }
}

Code:
<?php


class sbj_DataDirectory_ControllerPublic_DataDirectoryControllerPublic extends XenForo_ControllerPublic_Abstract
{
    public function actionWriteArtist()
    {
        $text = $this->_input->filterSingle('Artist', XenForo_Input::STRING);
        $dw = XenForo_DataWriter::create('sbj_DataDirectory_DataWriter_DataDirectoryDatawriter');
        $dw->set('Artist', $text);
        $dw->save();

        return $this->responseRedirect(XenForo_ControllerResponse_Redirect::SUCCESS, $this->getDynamicRedirect());
    }

    public function actionWriteComposer()
    {
        $text = $this->_input->filterSingle('Composer', XenForo_Input::STRING);
        $dw = XenForo_DataWriter::create('sbj_DataDirectory_DataWriter_DataDirectoryDatawriter');
        $dw->set('Composer', $text);
        $dw->save();

        return $this->responseRedirect(XenForo_ControllerResponse_Redirect::SUCCESS, $this->getDynamicRedirect());
    }

    public function actionWriteLyricist()
    {
        $text = $this->_input->filterSingle('Lyricist', XenForo_Input::STRING);
        $dw = XenForo_DataWriter::create('sbj_DataDirectory_DataWriter_DataDirectoryDatawriter');
        $dw->set('Lyricist', $text);
        $dw->save();

        return $this->responseRedirect(XenForo_ControllerResponse_Redirect::SUCCESS, $this->getDynamicRedirect());
    }

    public function actionWriteMakam()
    {
        $text = $this->_input->filterSingle('Makam', XenForo_Input::STRING);
        $dw = XenForo_DataWriter::create('sbj_DataDirectory_DataWriter_DataDirectoryDatawriter');
        $dw->set('Makam', $text);
        $dw->save();

        return $this->responseRedirect(XenForo_ControllerResponse_Redirect::SUCCESS, $this->getDynamicRedirect());
    }

    public function actionWriteForm()
    {
        $text = $this->_input->filterSingle('Form', XenForo_Input::STRING);
        $dw = XenForo_DataWriter::create('sbj_DataDirectory_DataWriter_DataDirectoryDatawriter');
        $dw->set('Form', $text);
        $dw->save();

        return $this->responseRedirect(XenForo_ControllerResponse_Redirect::SUCCESS, $this->getDynamicRedirect());
    }

    public function actionWriteUsul()
    {
        $text = $this->_input->filterSingle('Usul', XenForo_Input::STRING);
        $dw = XenForo_DataWriter::create('sbj_DataDirectory_DataWriter_DataDirectoryDatawriter');
        $dw->set('Usul', $text);
        $dw->save();

        return $this->responseRedirect(XenForo_ControllerResponse_Redirect::SUCCESS, $this->getDynamicRedirect());
    }
  
    //
    //
    //

    public function actionReadArtist()
    {
        $viewParams = array('DataDirectoryArtist' => $this->getModelFromCache('sbj_DataDirectory_Model_DataDirectoryModel')->getDataDirectoryByArtist());
      
        return $this->responseView('XenForo_ViewPublic_Base', 'datadirectory_template', $viewParams);
    }

    public function actionReadComposer()
    {
        $viewParams = array('DataDirectoryComposer' => $this->getModelFromCache('sbj_DataDirectory_Model_DataDirectoryModel')->getDataDirectoryByComposer());

        return $this->responseView('XenForo_ViewPublic_Base', 'datadirectory_template', $viewParams);
    }

    public function actionReadLyricist()
    {
        $viewParams = array('DataDirectoryLyricist' => $this->getModelFromCache('sbj_DataDirectory_Model_DataDirectoryModel')->getDataDirectoryByLyricist());

        return $this->responseView('XenForo_ViewPublic_Base', 'datadirectory_template', $viewParams);
    }

    public function actionReadMakam()
    {
        $viewParams = array('DataDirectoryMakam' => $this->getModelFromCache('sbj_DataDirectory_Model_DataDirectoryModel')->getDataDirectoryByMakam());

        return $this->responseView('XenForo_ViewPublic_Base', 'datadirectory_template', $viewParams);
    }

    public function actionReadForm()
    {
        $viewParams = array('DataDirectoryForm' => $this->getModelFromCache('sbj_DataDirectory_Model_DataDirectoryModel')->getDataDirectoryByForm());

        return $this->responseView('XenForo_ViewPublic_Base', 'datadirectory_template', $viewParams);
    }

    public function actionReadUsul()
    {
        $viewParams = array('DataDirectoryUsul' => $this->getModelFromCache('sbj_DataDirectory_Model_DataDirectoryModel')->getDataDirectoryByUsul());

        return $this->responseView('XenForo_ViewPublic_Base', 'datadirectory_template', $viewParams);
    }
}

Code:
<?php


class sbj_DataDirectory_Route_Prefix_DataDirectoryRoutePrefix implements XenForo_Route_Interface
{
    public function match($routePath, Zend_Controller_Request_Http $request, XenForo_Router $router)
    {
        $action = $router->resolveActionWithIntegerParam($routePath, $request, 'dropdown_id');
        return $router->getRouteMatch('sbj_DataDirectory_ControllerPublic_DataDirectoryControllerPublic', $action, 'datadirectory_template');
    }

    public function buildLink($originalPrefix, $outputPrefix, $action, $extension, $data, array &$extraParams)
    {
        return XenForo_Link::buildBasicLinkWithIntegerParam($outputPrefix, $action, $extension, $data, 'dropdown_id');
    }
}
 
Your getExistingData method is wrong. You should return an array of the data for the record being updated.

For example:

PHP:
protected function _getExistingData($data)
{
   if (!$primaryKey = $this->_getExistingPrimaryKey($data)) // This gets the primary key from the data passed in. This defaults to the auto increment field. If you don't have one, you need to set the key of the key field in the second parameter.
   {
       return false; // No primary key, can't get data
   }

   return array('xf_sbj_DataDirectory_dropdowns' => $this->_getDataDirectoryModel()->getDataDirectoryById($primaryKey)); // Return an array of data for each table for the record being modified.
}

Generally, you would have the getXById method in the model set to use the fetchRow method of the database instance. Use bound parameters when passing in the ID.

If this is the Index action (which I'm assuming it is), you need to define an index action to get all the relevant column values for the select fields, and then pass them as a template parameter (an array of the values) to the template, where you use a foreach loop to output an <option> tag for each select.

As an aside, your template is completely not using the correct syntax (you're missing closing tags, and you really shouldn't use line breaks).

Peruse some of the public templates to get a better idea of how it should look :)

Liam
 
  • Like
Reactions: sbj
Thank you Liam.

When I created the DataWriter, it told me to define 3 methods, cause the parent class has had them. But as I said above, I don't know how to use the _getUpdateCondition.
And I thought I figured out _getExistingData, but it seems I was wrong.

My problem is now, according to your corrections, that I don't have these methods in my model:
Code:
getDataDirectoryModel()
getDataDirectoryById

Generally, you would have the getXById method in the model set to use the fetchRow method of the database instance.

But I don't want to fetch rows, I want to fetch columns. Cause each column will have the entries for each dropdown. It makes no sense to fetch the row.
I don't know how I should rewrite my model in this case. And I also don't have a getDataDirectoryById as you can see. I don't understand for what I need it. I thought with _getExistingData I must get the data which exists, so in my case all 6 columns, which I put in the array (as you can see in my method).

Use bound parameters when passing in the ID.
What does this mean?

If this is the Index action (which I'm assuming it is), you need to define an index action to get all the relevant column values for the select fields, and then pass them as a template parameter (an array of the values) to the template,
But I thought I am doing this already. I wrote 6 actionWriteX methods to push the value to the template. Do you mean I should write only one method instead of 6 by passing an array of an array (array(array1, array2,...))?


As an aside, your template is completely not using the correct syntax
The thing is, with phpstorm I can easily look up any used function/method by going to the parent class (ctrl+mouse1), even though I don't understand much of it, there are commented.
For the template syntax there is no such thing, where I can see what syntax is used for what, it is confusing. I need to work on this, you are right.
 
As far as template syntax goes, you're manually creating each input but this falls down as you add more as you'll only have maybe 6 inputs defined but 10 db entries.

Look at sidebar_online_users to see how xenforo uses contentcheck and a foreach loop to generate username entries for everyone online.

Also you're using the <br> tag to generate spaces between your inputs. This should be done via css using margin/padding attributes.
 
  • Like
Reactions: sbj
Thanks. I know my template syntax sucks, but this is my 1st milestone anyway. I'm gonna add more stuff to the page.

So for now I use <br> tags and such, just the minimal thing for now. When everything is done, I'll figure out the design and use css. So that is the last thing I will do. For now I would be just happy to see params passed to the template :).
 
So I've added this to my DataWriter:
Code:
    protected function _getExistingData($data)
    {
        if (!$primaryKey = $this->_getExistingPrimaryKey($data)) // This gets the primary key from the data passed in. This defaults to the auto increment field. If you don't have one, you need to set the key of the key field in the second parameter.
        {
            return false; // No primary key, can't get data
        }

        return array('xf_sbj_DataDirectory_dropdowns' => $this->getModelFromCache('sbj_DataDirectory_Model_DataDirectoryModel')->getDataDirectoryById($primaryKey)); // Return an array of data for each table for the record being modified.
    }

And added this method to my Model:

Code:
    public function getDataDirectoryById($dropdownid)
    {
        return $this->_getDb()->fetchRow('
            SELECT * FROM xf_sbj_Datadirectory_dropdowns WHERE dropdown_id = ?', $dropdownid);
    }

But still my Controller isn't able to send the params to the template.
What do I do wrong here?
 
Top Bottom