Yii Framework Forum

How to create tags for posts in yii2


(Arunwebber) #1

I am trying to create a simple cms with yii2 In that i have created a post with wysiwig editor Now i want to add taggable functionality to the same post how can i do that i gone through some of the articles in web related to that i want add a simple taggable functionality And when user clciks on that tab they have to redirected to a page contains posts with the tags.

Here is the code for simple form

  <?php $form = ActiveForm::begin(); ?>

    <?= $form->field($model, 'title')->textInput(['maxlength' => true]) ?>

    <?= $form->field($model, 'content')->widget(CKEditor::className(), [
        'options' => ['height' => 800],
        'preset' => 'basic',
        'clientOptions' => ['height' => 400]
    ]) ?>

    <?= $form->field($model, 'tags')->textInput(['maxlength' => true]) ?>

    <?= $form->field($model, 'date')->textInput() ?>

    <div class="form-group">
        <?= Html::submitButton('Save', ['class' => 'btn btn-success']) ?>
    </div>

    <?php ActiveForm::end(); ?>

(Jaimez) #2

At first you can try this extension for implementing taggable functionality:
https://github.com/2amigos/yii2-taggable-behavior

And then it should be easy to write a controller action for viewing all posts with the same tag.


(Arunwebber) #3

Thank you :slight_smile:


(Arunwebber) #4

I have created two tables as the post says which is as follow.

CREATE TABLE `tags` (
  `id` int(250) NOT NULL,
  `frequency` varchar(500) NOT NULL,
  `name` varchar(250) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

And the second one

CREATE TABLE `tag_assign` (
  `tag_id` int(250) NOT NULL,
  `article_id` int(250) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
COMMIT;

Also I have created a tags model in common folder of yii2 advanced template

As well as written this part to post.

<?= $form->field($model, 'tagNames')->widget(SelectizeTextInput::className(), [
    // calls an action that returns a JSON object with matched
    // tags
    'loadUrl' => ['tag/list'],
    'options' => ['class' => 'form-control'],
    'clientOptions' => [
        'plugins' => ['remove_button'],
        'valueField' => 'name',
        'labelField' => 'name',
        'searchField' => ['name'],
        'create' => true,
    ],
])->hint('Use commas to separate tags') ?>

It is giving the request properly The console is telling me

GET http://localhost/counter/advanced/backend/web/index.php/tags/list?query=ds%20 404 (Not Found)

Where will i write down the rest of the code which model and controllers?

From this github document


(Jaimez) #5

Did you create actionList in your Controller ? (if you are following example for GitHub without changes then it is named TagController)

public function actionList($query)
{
$models = Tag::findAllByName($query);
$items = [];

foreach ($models as $model) {
    $items[] = ['name' => $model->name];
}
// We know we can use ContentNegotiator filter
// this way is easier to show you here :)
Yii::$app->response->format = Response::FORMAT_JSON;

return $items;
}

(Arunwebber) #6

I have created a tags controller using gii and added following lines of code

  public function actionList($query)
    {
      $models = Tag::findAllByName($query);
      $items = [];

      foreach ($models as $model) {
          $items[] = ['name' => $model->name];
      }
      // We know we can use ContentNegotiator filter
      // this way is easier to show you here :)
      Yii::$app->response->format = Response::FORMAT_JSON;

      return $items;
    }

From the console i am getting the following

GET http://localhost/counter/advanced/backend/web/index.php/tags/list?query=fd 500 (Internal Server Error)

If i visit the url it is showing me

## Call to undefined method common\models\Tags::findAllByName()

My tagModel

<?php

namespace common\models;

use Yii;
//For ajax tagging
use dosamigos\taggable\Taggable;

/**
 * This is the model class for table "tags".
 *
 * @property int $id
 * @property string $frequency
 * @property string $name
 */
class Tag extends \yii\db\ActiveRecord
{
    /**
     * {@inheritdoc}
     */
    public static function tableName()
    {
        return 'tags';
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        // return [
        //     [['frequency', 'name'], 'required'],
        //     [['frequency'], 'string', 'max' => 500],
        //     [['name'], 'string', 'max' => 250],
        // ];
       return [
           // ...
           [['tagNames'], 'safe'],
           // ...
       ];
    }

    /**
     * {@inheritdoc}
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'frequency' => 'Frequency',
            'name' => 'Name',
        ];
    }

    //For taggable content
    public function behaviors() {
       return [
           [
               'class' => Taggable::className(),
           ],
       ];
   }

    public function getTags()
{
    return $this->hasMany(Tag::className(), ['id' => 'tag_id'])->viaTable('tag_assign', ['article_id' => 'id']);
}

}

And my tag controller

<?php

namespace backend\controllers;

use Yii;
use common\models\Tag;
use common\models\searchTag;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;

/**
 * TagController implements the CRUD actions for Tag model.
 */
class TagController extends Controller
{
    /**
     * {@inheritdoc}
     */
    public function behaviors()
    {
        return [
            'verbs' => [
                'class' => VerbFilter::className(),
                'actions' => [
                    'delete' => ['POST'],
                ],
            ],
        ];
    }

    /**
     * Lists all Tag models.
     * @return mixed
     */
    public function actionIndex()
    {
        $searchModel = new searchTag();
        $dataProvider = $searchModel->search(Yii::$app->request->queryParams);

        return $this->render('index', [
            'searchModel' => $searchModel,
            'dataProvider' => $dataProvider,
        ]);
    }

    public function actionList($query)
    {
        $models = Tag::findAllByName($query);
        $items = [];

        foreach ($models as $model) {
            $items[] = ['name' => $model->name];
        }
        // We know we can use ContentNegotiator filter
        // this way is easier to show you here :)
        Yii::$app->response->format = Response::FORMAT_JSON;

        return $items;
    }

    /**
     * Displays a single Tag model.
     * @param integer $id
     * @return mixed
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionView($id)
    {
        return $this->render('view', [
            'model' => $this->findModel($id),
        ]);
    }

    /**
     * Creates a new Tag model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     * @return mixed
     */
    public function actionCreate()
    {
        $model = new Tag();

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view', 'id' => $model->id]);
        }

        return $this->render('create', [
            'model' => $model,
        ]);
    }

    /**
     * Updates an existing Tag model.
     * If update is successful, the browser will be redirected to the 'view' page.
     * @param integer $id
     * @return mixed
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionUpdate($id)
    {
        $model = $this->findModel($id);

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view', 'id' => $model->id]);
        }

        return $this->render('update', [
            'model' => $model,
        ]);
    }

    /**
     * Deletes an existing Tag model.
     * If deletion is successful, the browser will be redirected to the 'index' page.
     * @param integer $id
     * @return mixed
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionDelete($id)
    {
        $this->findModel($id)->delete();

        return $this->redirect(['index']);
    }

    /**
     * Finds the Tag model based on its primary key value.
     * If the model is not found, a 404 HTTP exception will be thrown.
     * @param integer $id
     * @return Tag the loaded model
     * @throws NotFoundHttpException if the model cannot be found
     */
    protected function findModel($id)
    {
        if (($model = Tag::findOne($id)) !== null) {
            return $model;
        }

        throw new NotFoundHttpException('The requested page does not exist.');
    }
}

Also i found this in github


(Jaimez) #7

As mentioned in linked GiHub issue discussion by user Firvagor, you need to insert something like this to your Tag model:

public function findAllByName($name)
{
    return Tag::find()->where('name LIKE :query')
                      ->addParams([':query'=>"%$name%"])
                      ->all();
}

Also, I noticed that your model and controller are named Tag and TagContoller but the error says that Tags model is missing that method. Double-check for any typos.


(Arunwebber) #8

Yeah i have done that it is fetching data from the db but it is not getting saved in tags table.


(Jaimez) #9

Check app.log for any errors, also look at the comments in the git hub issue that you post earlier. Mainly these:
https://github.com/2amigos/yii2-taggable-behavior/issues/12#issuecomment-74066981
https://github.com/2amigos/yii2-taggable-behavior/issues/12#issuecomment-74529956
https://github.com/2amigos/yii2-taggable-behavior/issues/12#issuecomment-74383485
Check if your models and controllers looks like theirs.