【问题标题】:Joomla 2.5 creating component and saving dataJoomla 2.5 创建组件并保存数据
【发布时间】:2013-04-13 03:32:04
【问题描述】:

我一直在使用这个文档(我可以在网上找到的唯一一个)来构建一个组件: http://docs.joomla.org/Developing_a_Model-View-Controller_Component/2.5/Introduction

我可以在一定程度上理解它,但它确实缺乏任何定义。我创建的组件在一定程度上可以工作,尽管我遇到了一些更奇怪的问题。

基本上,我需要该组件做的只是加载一个设置区域来设置一些值,并通过它能够更改这些值。以下是我所拥有的细分:

表单视图,从数据库加载表单数据。 用于保存/应用和取消的工具栏设置。

这加载没有错误,根据我发现的 joomla 上的所有文档,通过在模型中连接一个 JTable 初始化一个 JControllerForm 实例,简单的表单保存应该会自动工作。然而,即使代码中的任何地方都绝对没有引用以 s 结尾的视图(主视图是tireapi,表单总是重定向到tireapis)。

这会引发 500 错误,因为没有具有该视图的场所集。该文档确实包含一个视图列表,但是我只有一行需要编辑,因此列表毫无意义。我知道可以将参数设置为组件,而不是创建一个数据库字段,但是我找不到任何与之相关的文档。

我正在寻找的是如何阻止组件重定向到不存在的视图并正确保存数据的方向。链接到不仅显示示例代码,而且描述功能及其工作方式的文档将是有益的。

这是一些代码,请随时指出我可能完全忽略的任何内容(我是创建组件的新手):

tireapi.php:

<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted access');

// import joomla controller library
jimport('joomla.application.component.controller');

// Get an instance of the controller prefixed by TireAPI
$controller = JController::getInstance('TireAPI');

// Get the task
$jinput = JFactory::getApplication()->input;
$task = $jinput->get('task', "", 'STR' );

// Perform the Request task
$controller->execute($task);

// Redirect if set by the controller
$controller->redirect();
?>

controller.php:

<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted access');

// import Joomla controller library
jimport('joomla.application.component.controller');

class TireAPIController extends JController{
    function display($cachable = false){
        // set default view if not set
        $input = JFactory::getApplication()->input;
        $input->set('view', $input->getCmd('view', 'TireAPI'));

        // call parent behavior
        parent::display($cachable);
    }
}
?>

控制器/tireapi.php:

<?php
// No direct access to this file
 defined('_JEXEC') or die('Restricted access');
// import Joomla controllerform library
jimport('joomla.application.component.controllerform');
class TireAPIControllerTireAPI extends JControllerForm{}
?>

模型/tireapi.php:

<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
// import the Joomla modellist library
jimport('joomla.application.component.modeladmin');
class TireAPIModelTireAPI extends JModelAdmin{
    protected $settings; //define settings

    public function getTable($type = 'TireAPI', $prefix = 'TireAPITable', $config = array()){
        return JTable::getInstance($type, $prefix, $config);
    }

    public function getSettings(){ //grab settings from database
        if(!isset($this->settings)){
            $table = $this->getTable();
            $table->load(1);
            $this->settings = $table;
        }
        return $this->settings;
    }
    public function getForm($data = array(), $loadData = true){
    // Get the form.
        $form = $this->loadForm('com_tireapi.tireapi', 'tireapi',
            array('control' => 'jform', 'load_data' => $loadData));
        if (empty($form)){
            return false;
        }
        return $form;
    }
    protected function loadFormData(){
        // Check the session for previously entered form data.
        $data = JFactory::getApplication()->getUserState('com_tireapi.edit.tireapi.data', array());
        if (empty($data)){
            $data = $this->getSettings();
        }
        return $data;
    }
}
?>

tables/tireapi.php:

<?php
// No direct access
defined('_JEXEC') or die('Restricted access');

// import Joomla table library
jimport('joomla.database.table');
class TireAPITableTireAPI extends JTable
{
    function __construct( &$db ) {
        parent::__construct('#__tireapi', 'id', $db);
    }
}
?>

views/tireapi/view.html.php:

<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted access');

// import Joomla view library
jimport('joomla.application.component.view');

class TireAPIViewTireAPI extends JView{
        function display($tpl = null){

            $form = $this->get('Form');
            $item = $this->get('Settings');

            // Check for errors.
            if(count($errors = $this->get('Errors'))){
                JError::raiseError(500, implode('<br />', $errors));
                return false;
            }
            // Assign data to the view
            $this->item = $item;
            $this->form = $form;

            $this->addToolBar();

            // Display the template
            parent::display($tpl);
        }
        protected function addToolBar() {
            $input = JFactory::getApplication()->input;
            JToolBarHelper::title(JText::_('COM_TIREAPI_MANAGER_TIREAPIS'));
            JToolBarHelper::apply('tireapi.apply');
            JToolBarHelper::save('tireapi.save');
            JToolBarHelper::cancel('tireapi.cancel');
        }
}
?>

views/tireapi/tmpl/default.php:

<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted Access');

// load tooltip behavior
JHtml::_('behavior.tooltip');
?>
<form action="<?php echo JRoute::_('index.php?option=com_tireapi&layout=edit&id='.(int) $this->item->id); ?>"
      method="post" name="adminForm" id="tireapi-form">
    <fieldset class="adminform">
            <legend><?php echo JText::_( 'COM_TIREAPI_DETAILS' ); ?></legend>
            <ul class="adminformlist">
    <?php foreach($this->form->getFieldset() as $field): ?>
                    <li><?php echo $field->label;echo $field->input;?></li>
    <?php endforeach; ?>
            </ul>
    </fieldset>
    <div>
        <input type="hidden" name="task" value="tireapi.edit" />
        <?php echo JHtml::_('form.token'); ?>
    </div>
</form>

这些是我能想到的所有可能重要的文件,让我知道是否应该再包含。

更新: 现在我可以停止重定向问题,但它不会保存数据。 我收到此错误: 您不得使用该链接直接访问该页面 (#1)。

这是让这个极其基本的管理功能发挥作用的最后一道障碍。有任何想法吗? 为了澄清,我通过一个 xml 文件设置表单并且正确加载,甚至用数据库中的正确数据填充它们。但是,当我单击“应用”时,它只会将我引导回出现上面列出的错误的表单,而不会保存。

【问题讨论】:

    标签: joomla joomla2.5 admin


    【解决方案1】:

    您未解决的主要问题是您希望它重定向到哪里? Joomla 默认重定向到列表视图(除非您直接指定列表视图,否则通过在视图名称中添加“s”)。

    您可以通过多种方式覆盖它:

    在您的控制器 (controllers/tireapi.php) 中,设置您自己的列表视图。我认为您甚至可以将其设为相同的视图:

    function __construct() {
        $this->view_list = 'tireapi';
        parent::__construct();
    }
    

    重写保存函数以更改保存后发生的重定向(再次在控制器中)。这可以通过将自然发生的重定向更改为其他内容来实现:

    public function save($key = null, $urlVar = null) {
        $return = parent::save($key, $urlVar);
        $this->setRedirect(JRoute::_('index.php?option=com_tireapi&view=tireapi'));
        return $return;
    }
    

    其中一个应该可以为您解决问题。

    ** 更新

    要让项目最初结帐,您需要更改组件处理未设置视图的方式。现在您只需设置视图,而是让我们重定向!更新的 controller.php 如下。

    <?php
    // No direct access to this file
    defined('_JEXEC') or die('Restricted access');
    
    // import Joomla controller library
    jimport('joomla.application.component.controller');
    
    class TireAPIController extends JController{
        function display($cachable = false){
            // set default view if not set
            $input = JFactory::getApplication()->input;
            $view = $input->get('view');
            if (!$view) {
                JFactory::getApplication()->redirect('index.php?option=com_tireapi&task=tireapi.edit&id=1');
                exit();
            }
    
            // call parent behavior
            parent::display($cachable);
        }
    }
    ?>
    

    注意:如果不止一个人需要编辑它,这将非常糟糕,因为系统会在您打开组件时检查它,如果您在保存后还让它重定向回此页面。因为那样它总是会被最后一个编辑它的人签出,所以下一个人将无法打开它。如果只有一个人编辑就可以了。

    第二个注意事项:如果您不想破解结帐系统,您也可以在保存过程中忽略它,这与黑客攻击的级别基本相同。

    下面是libraries/joomla/application/component/ 中controllerform.php 中保存功能的副本。这是正常保存时运行的内容(因为您从哪里继承。我已经删除了检查项目是否已签出。所以如果你把它放在你的tireapi.php控制器中parent::save...位所在的位置,它将运行相反,您不必费心检查项目(即忽略所有 cmets ...)。(老实说,在您的情况下,您可能可以删除更多内容,但这就是保存时发生的情况,顺便说一句!)

    public function save($key = null, $urlVar = null)
    {
        // Check for request forgeries.
        JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
    
        // Initialise variables.
        $app   = JFactory::getApplication();
        $lang  = JFactory::getLanguage();
        $model = $this->getModel();
        $table = $model->getTable();
        $data  = JRequest::getVar('jform', array(), 'post', 'array');
        $checkin = property_exists($table, 'checked_out');
        $context = "$this->option.edit.$this->context";
        $task = $this->getTask();
    
        // Determine the name of the primary key for the data.
        if (empty($key))
        {
            $key = $table->getKeyName();
        }
    
        // To avoid data collisions the urlVar may be different from the primary key.
        if (empty($urlVar))
        {
            $urlVar = $key;
        }
    
        $recordId = JRequest::getInt($urlVar);
    
        // Populate the row id from the session.
        $data[$key] = $recordId;
    
        // The save2copy task needs to be handled slightly differently.
        if ($task == 'save2copy')
        {
            // Check-in the original row.
            if ($checkin && $model->checkin($data[$key]) === false)
            {
                // Check-in failed. Go back to the item and display a notice.
                $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError()));
                $this->setMessage($this->getError(), 'error');
    
                $this->setRedirect(
                    JRoute::_(
                        'index.php?option=' . $this->option . '&view=' . $this->view_item
                        . $this->getRedirectToItemAppend($recordId, $urlVar), false
                    )
                );
    
                return false;
            }
    
            // Reset the ID and then treat the request as for Apply.
            $data[$key] = 0;
            $task = 'apply';
        }
    
        // Access check.
        if (!$this->allowSave($data, $key))
        {
            $this->setError(JText::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'));
            $this->setMessage($this->getError(), 'error');
    
            $this->setRedirect(
                JRoute::_(
                    'index.php?option=' . $this->option . '&view=' . $this->view_list
                    . $this->getRedirectToListAppend(), false
                )
            );
    
            return false;
        }
    
        // Validate the posted data.
        // Sometimes the form needs some posted data, such as for plugins and modules.
        $form = $model->getForm($data, false);
    
        if (!$form)
        {
            $app->enqueueMessage($model->getError(), 'error');
    
            return false;
        }
    
        // Test whether the data is valid.
        $validData = $model->validate($form, $data);
    
        // Check for validation errors.
        if ($validData === false)
        {
            // Get the validation messages.
            $errors = $model->getErrors();
    
            // Push up to three validation messages out to the user.
            for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++)
            {
                if ($errors[$i] instanceof Exception)
                {
                    $app->enqueueMessage($errors[$i]->getMessage(), 'warning');
                }
                else
                {
                    $app->enqueueMessage($errors[$i], 'warning');
                }
            }
    
            // Save the data in the session.
            $app->setUserState($context . '.data', $data);
    
            // Redirect back to the edit screen.
            $this->setRedirect(
                JRoute::_(
                    'index.php?option=' . $this->option . '&view=' . $this->view_item
                    . $this->getRedirectToItemAppend($recordId, $urlVar), false
                )
            );
    
            return false;
        }
    
        // Attempt to save the data.
        if (!$model->save($validData))
        {
            // Save the data in the session.
            $app->setUserState($context . '.data', $validData);
    
            // Redirect back to the edit screen.
            $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_SAVE_FAILED', $model->getError()));
            $this->setMessage($this->getError(), 'error');
    
            $this->setRedirect(
                JRoute::_(
                    'index.php?option=' . $this->option . '&view=' . $this->view_item
                    . $this->getRedirectToItemAppend($recordId, $urlVar), false
                )
            );
    
            return false;
        }
    
        // Save succeeded, so check-in the record.
        if ($checkin && $model->checkin($validData[$key]) === false)
        {
            // Save the data in the session.
            $app->setUserState($context . '.data', $validData);
    
            // Check-in failed, so go back to the record and display a notice.
            $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError()));
            $this->setMessage($this->getError(), 'error');
    
            $this->setRedirect(
                JRoute::_(
                    'index.php?option=' . $this->option . '&view=' . $this->view_item
                    . $this->getRedirectToItemAppend($recordId, $urlVar), false
                )
            );
    
            return false;
        }
    
        $this->setMessage(
            JText::_(
                ($lang->hasKey($this->text_prefix . ($recordId == 0 && $app->isSite() ? '_SUBMIT' : '') . '_SAVE_SUCCESS')
                    ? $this->text_prefix
                    : 'JLIB_APPLICATION') . ($recordId == 0 && $app->isSite() ? '_SUBMIT' : '') . '_SAVE_SUCCESS'
            )
        );
    
        // Redirect the user and adjust session state based on the chosen task.
        switch ($task)
        {
            case 'apply':
                // Set the record data in the session.
                $recordId = $model->getState($this->context . '.id');
                $this->holdEditId($context, $recordId);
                $app->setUserState($context . '.data', null);
                $model->checkout($recordId);
    
                // Redirect back to the edit screen.
                $this->setRedirect(
                    JRoute::_(
                        'index.php?option=' . $this->option . '&view=' . $this->view_item
                        . $this->getRedirectToItemAppend($recordId, $urlVar), false
                    )
                );
                break;
    
            case 'save2new':
                // Clear the record id and data from the session.
                $this->releaseEditId($context, $recordId);
                $app->setUserState($context . '.data', null);
    
                // Redirect back to the edit screen.
                $this->setRedirect(
                    JRoute::_(
                        'index.php?option=' . $this->option . '&view=' . $this->view_item
                        . $this->getRedirectToItemAppend(null, $urlVar), false
                    )
                );
                break;
    
            default:
                // Clear the record id and data from the session.
                $this->releaseEditId($context, $recordId);
                $app->setUserState($context . '.data', null);
    
                // Redirect to the list screen.
                $this->setRedirect(
                    JRoute::_(
                        'index.php?option=' . $this->option . '&view=' . $this->view_list
                        . $this->getRedirectToListAppend(), false
                    )
                );
                break;
        }
    
        // Invoke the postSave method to allow for the child class to access the model.
        $this->postSaveHook($model, $validData);
    
        return true;
    }
    

    【讨论】:

    • 它确实有助于重定向,但是我明白了:您不允许使用该链接直接访问该页面(#1)。当我尝试保存时,因为它是一页,所以我也删除了“保存”以支持“应用”。
    • 链接到页面时,请尝试使用以下链接:index.php?option=com_tireapi&task=tireapi.edit。 (从菜单项或任何地方都是如此,通常在您没有的列表视图中完成。)这会调用控制器中的编辑功能,该功能应在定向到表单之前为您检查项目。该结帐应该使您能够编辑它
    • 我会对此进行调查,但是一个可预见的问题是如何更改管理面板中的链接。这是在组件菜单中加载的,我从未见过任何更改此链接的方法。不过我会继续调整。
    • 就我所见,更深入地添加 &task 并不会检查我是否签入/签出。基本上我的理解是,在第一次失败后我让它重定向到 &task=tireapi.edit 然后从那里检查它,如果我理解正确的话。但是再次编辑并单击保存再次执行相同的操作。
    • 对不起。您必须将 '&id=1' 添加到 url 以便它知道检查项目 1!完整的 url 应该是 index.php?option=com_tireapi&task=tireapi.edit&id=1。对于菜单项,如果没有指定视图,我们必须更改发生的情况。我会用一个选项更新答案。
    【解决方案2】:

    这不是一个真正的答案,但你的问题也不是一个真正的问题(我必须阅读 3 次)。

    带有复数“s”(表示轮胎皮)的东西就是Joomla!自动执行。你可以覆盖它。

    我建议你看看核心组件(如 com_banners)。

    您需要做的是避免重定向。您应该只有“保存”按钮,这应该让您保持在同一页面上。而“保存并关闭”会将您重定向到复数页面。

    【讨论】:

    • 我主要是用 apply 进行测试,尽管我现在确实删除了保存并关闭。但是“应用”的问题是它没有像我“认为”它应该读取 jTables 数据(我没有看到任何文档解释它)。也很抱歉让这个问题看起来很复杂,大约有 60 种不同的代码变体并且没有成功,所以我在写它时有点恼火。
    • 如前所述,查看核心组件并弄清楚它们是如何工作的。使用 Save 或 Save & Close 应该无关紧要,仍然会使用 JTable。
    猜你喜欢
    • 2012-06-21
    • 2013-03-10
    • 1970-01-01
    • 1970-01-01
    • 2013-03-13
    • 2012-11-24
    • 2016-03-08
    • 2012-10-12
    • 2013-05-01
    相关资源
    最近更新 更多