【问题标题】:Yii CJuiAutoComplete for Multiple valuesYii CJuiAutoComplete 用于多个值
【发布时间】:2012-11-22 14:00:40
【问题描述】:

我是 Yii 初学者,目前正在开发一个标签系统,其中有 3 个表:

  • 问题(id、内容、create_d、...等)

  • 标签(id,tag)

  • Issue_tag_map (id,tag_id_fk,issue_id_fk)

在我的 /Views/Issue/_form 中,我添加了一个 MultiComplete 扩展来检索多个标签 ID 和标签,

我使用了afterSave 函数,以便将Issue_id 和自动完成的Tag_ids 直接存储在Issue_tag_map 表中,其中它是HAS_MANY 关系。

很遗憾,没有返回任何内容。

我想知道是否有办法将自动完成的 Tag_ids 存储在临时属性中,然后将其传递给模型的 afterSave() 函数。

我已经找了一段时间了,这让我发疯了,因为我觉得我错过了一个非常简单的步骤!

任何帮助或任何形式的建议都非常感谢!

Views/Issue/_form中的MultiComplete

    <?php

     echo $form->labelEx($model, 'Tag');
     $this->widget('application.extension.MultiComplete', array(
    'model' => $model,
    'attribute' => '', //Was thinking of creating a temporary here
    'name' => 'tag_autocomplete',
    'splitter' => ',',
    'sourceUrl' => $this->createUrl('Issue/tagAutoComplete'),
    // Controller/Action path for action we created in step 4.
    // additional javascript options for the autocomplete plugin
    'options' => array(
        'minLength' => '2',
    ),
    'htmlOptions' => array(
        'style' => 'height:20px;',
    ),
));
echo $form->error($model, 'issue_comment_id_fk');
?>

AfterSave 在/model/Issue

    protected function afterSave() {
    parent::afterSave();

    $issue_id = Yii::app()->db->getLastInsertID();

    $tag; //here I would explode the attribute retrieved by the view form
    // an SQL with two placeholders ":issue_id" and ":tag_id"
    if (is_array($tag))
        foreach ($tag as $tag_id) {
            $sql = "INSERT INTO issue_tag_map (issue_id_fk, tag_id_fk)VALUES(:issue_id,:tag_id)";     

            $command = Yii::app()->db->createCommand($sql);
           // replace the placeholder ":issue_id" with the actual issue value

            $command->bindValue(":issue_id", $issue_id, PDO::PARAM_STR);
           // replace the placeholder ":tag_id" with the actual tag_id value

            $command->bindValue(":tag_id", $tag_id, PDO::PARAM_STR);
            $command->execute();
        }
}

这是问题模型中用于填充标签的自动完成 sourceUrl:

    public static function tagAutoComplete($name = '') {

    $sql = 'SELECT id ,tag AS label FROM tag WHERE tag LIKE :tag';
    $name = $name . '%';
    return Yii::app()->db->createCommand($sql)->queryAll(true, array(':tag' => $name));

/controllers/IssueController 中的 actionTagAutoComplete:

// This function will echo a JSON object 
// of this format:
// [{id:id, name: 'name'}]
function actionTagAutocomplete() {

    $term = trim($_GET['term']);
    if ($term != '') {
        $tags = issue::tagAutoComplete($term);
        echo CJSON::encode($tags);
        Yii::app()->end();
    }
}

编辑

窗体中的小部件:

   <div class="row" id="checks" >
    <?php
    echo $form->labelEx($model, 'company',array('title'=>'File Company Distrubution; Companies can be edited by Admins'));

    ?>
   <?php
    $this->widget('application.extension.MultiComplete', array(
        'model' => $model,
        'attribute' => 'company',
        'splitter' => ',',
        'name' => 'company_autocomplete',
        'sourceUrl' => $this->createUrl('becomEn/CompanyAutocomplete'),
        'options' => array(
            'minLength' => '1',
        ),
        'htmlOptions' => array(
            'style' => 'height:20px;',
            'size' => '45',
        ),
    ));
    echo $form->error($model, 'company');
    ?>
</div>

更新功能:

    $model = $this->loadModel($id);
    .....
      if (isset($_POST['News'])) {
         $model->attributes = $_POST['News'];
        $model->companies = $this->getRecordsFromAutocompleteString($_POST['News']  
       ['company']);
    ......
     ......
      getRecordsFromAutocompleteString():
  public static cordsFromAutocompleteString($string) {
    $string = trim($string);
    $stringArray = explode(", ", $string);
    $stringArray[count($stringArray) - 1] = str_replace(",", "", $stringArray[count($stringArray) - 1]);
    $criteria = new CDbCriteria();
    $criteria->select = 'id';
    $criteria->condition = 'company =:company';
    $companies = array();
    foreach ($stringArray as $company) {
        $criteria->params = array(':company' => $company);
        $companies[] = Company::model()->find($criteria);
    }
    return $companies;
}

更新 由于“值”属性在此扩展中未正确实现,因此我指的是将此功能扩展到模型:

    public function afterFind() {
       //tag is the attribute used in form
      $this->tag = $this->getAllTagNames();
      parent::afterFind();
   }

【问题讨论】:

  • 回答前只问两个小问题:自动完成功能正常,对吧?您是否在模型中定义了问题和标签之间的关系? (我需要这些信息来使我的答案更准确)

标签: autocomplete yii yii-extensions


【解决方案1】:

您应该在 IssueTag 模型中定义的问题和标签之间建立关系(应该是 many_many 关系)。

所以在IssueController 中,当您将数据发送到createupdate 模型问题时,您会得到相关的标签(在我的例子中,我得到一个字符串,如“错误、问题、...” )。 然后你需要在你的控制器中解析这个字符串,得到对应的模型并分配给相关的标签。

这是一个通用示例:

//In the controller's method where you add/update the record
$issue->tags = getRecordsFromAutocompleteString($_POST['autocompleteAttribute'], 'Tag', 'tag');

这里是我调用的方法:

//parse your string ang fetch the related models
public static function getRecordsFromAutocompleteString($string, $model, $field)
        {
            $string = trim($string);
            $stringArray = explode(", ", $string);
            $stringArray[count($stringArray) - 1] = str_replace(",", "", $stringArray[count($stringArray) - 1]);
            return CActiveRecord::model($model)->findAllByAttributes(array($field => $stringArray));
        }

所以现在你的 $issue->tags 是一个包含所有相关标签对象的数组。

在您的 afterSave 方法中,您将能够做到:

protected function afterSave() {
    parent::afterSave();

    //$issue_id = Yii::app()->db->getLastInsertID(); Don't need it, yii is already doing it
    foreach ($this->tags as $tag) {
        $sql = "INSERT INTO issue_tag_map (issue_id_fk, tag_id_fk)VALUES(:issue_id,:tag_id)";     
        $command = Yii::app()->db->createCommand($sql);
        $command->bindValue(":issue_id", $this->id, PDO::PARAM_INT);
        $command->bindValue(":tag_id", $tag->id, PDO::PARAM_INT);
        $command->execute();
        }
}

现在上面的代码是一个基本的解决方案。我鼓励您使用activerecord-relation-behavior's extension 来保存相关模型。 使用此扩展,您无需在 afterSave 方法中定义任何内容,您只需要做:

$issue->tags = getRecordsFromAutocompleteString($_POST['autocompleteAttribute'], 'Tag', 'tag');
$issue->save(); // all the related models are saved by the extension, no afterSave defined!

然后您可以通过获取 id 以及自动完成中的标签来优化脚本,并将选定的 id 存储在 Json 数组中。这样您就不必执行 sql 查询 getRecordsFromAutocompleteString 来获取 ID。使用上面提到的扩展程序,您将能够:

$issue->tags = CJSON::Decode($_POST['idTags']);//will obtain array(1, 13, ...)
$issue->save(); // all the related models are saved by the extension, the extension is handling both models and array for the relation!

编辑:

如果您想填写自动完成字段,您可以定义以下函数:

public static function appendModelstoString($models, $fieldName)
    {
        $list = array();
        foreach($models as $model)
        {
            $list[] = $model->$fieldName;
        }
        return implode(', ', $list);
    }

您提供字段名称(在您的情况下为 tag)和相关模型列表,它将生成适当的字符串。然后将字符串传递给视图并将其作为自动完成字段的默认值。

回答您的编辑:

在您的控制器中,您说该模型的公司是您从自动完成表单中添加的公司:

$model->companies = $this->getRecordsFromAutocompleteString($_POST['News']  
   ['company']);

因此,如果相关公司不在表格中,则不会将其保存为相关模型。 您有 2 个解决方案:

  1. 每次在显示之前将已经存在的相关模型放入表单的自动完成字段中,这样它们就会再次保存为相关模型,并且不会从相关模型中消失

    $this->widget('application.extensions.multicomplete.MultiComplete', array(
                    'name' => 'people',
                    'value' => (isset($people))?$people:'',
                    'sourceUrl' => array('searchAutocompletePeople'),
    ));
    
  2. 在你的控制器中调用getRecordsFromAutocompleteString之前你添加已经存在的模型模型。

    $model->companies = array_merge(
        $model->companies, 
        $this->getRecordsFromAutocompleteString($_POST['News']['company'])
    );
    

【讨论】:

  • 非常感谢您的回复!我目前已经创建了这个关系:'tags' => array(self::MANY_MANY, 'Tag', 'issue_tag_map(tag_id_fk,issue_id_fk)',我现在面临的唯一问题是将属性从模型传递给自动完成功能
  • 目前自动完成功能不起作用或者您想在提交表单后获得结果?
  • 我在为自动完成字段分配临时属性时遇到问题。此外,当我分配一个进行测试时,例如: getRecordsFromAutocompleteString($_POST['isseuetempattrib'], 'Tag', 'tag') ,它要么未在控制器中识别,要么属性在模型中返回 null!我正在考虑使用 CheckBoxList 而不是自动完成功能并将两种模型结合起来:Issue 和 Issue_tag_map 在一种形式中
  • 我已经安装了 activerecord-relation-behavior 的扩展,现在我可以明确地理解这是多么简单!我添加了一个 Many_Many 关系,并在 Update-Create 函数中添加了: $mode->tags = (必须存储的标签数组) 不幸的是,我仍在试图弄清楚如何从 _form 传递标签数组,因为我不知道要初始化或使用哪个属性!
  • 一般来说,我正在做的是自动完成源 url 返回一个数组,其中包含标签的 id 和 tag。然后在 javascript 中,当用户选择一个标签(自动完成小部件中的“选择”事件)时,我将 id 添加到一个隐藏字段,其中包含所有选定 id 的 json 数组({1,3,5,6,.. .}) 当我收到 POST 请求时,我将隐藏字段解码为您分配的 php 数组,以供记录(例如:$mode->tags = CJson::Decode($POST['the_hidden_​​field']);)
猜你喜欢
  • 2013-03-12
  • 1970-01-01
  • 2014-04-27
  • 2015-09-20
  • 1970-01-01
  • 2012-09-09
  • 1970-01-01
  • 2011-06-02
  • 1970-01-01
相关资源
最近更新 更多