OmgDef/yii2-multilingual-behavior

Inserting only default language value

Closed this issue · 6 comments

Hi. I installed your behavior and configrued my model:

use omgdef\multilingual\MultilingualBehavior;
use omgdef\multilingual\MultilingualQuery;
...
public function behaviors()
    {
        return [
            [
                'class' => SluggableBehavior::className(),
                'attribute' => 'post_title',
                'slugAttribute' => 'post_name',
                'ensureUnique'=>true,
            ],
            [
                'class' => TimestampBehavior::className(),
                'createdAtAttribute' => 'post_date',
                'updatedAtAttribute' => 'post_modified',
                'value' => new Expression('NOW()'),
            ],
            [
                'class' => MultilingualBehavior::className(),
                'languages' => [
                    'uz' => 'Uzbek',
                    'ru' => 'Russian',
                    'en' => 'English',
                ],
                'languageField' => 'post_lang',
                //'localizedPrefix' => '',
                'requireTranslations' => true,
                'dynamicLangClass' => false,
                'langClassName' => PostsLang::className(), // or namespace/for/a/class/PostLang
                'defaultLanguage' => 'ru',
                'langForeignKey' => 'post_id',
                'tableName' => "{{%posts_lang}}",
                'attributes' => [
                    'post_title', 'post_content',
                ]
            ],
        ];
    }
    public static function find()
    {
        return new MultilingualQuery(get_called_class());
    }
        public function transactions()
    {
        return [
            self::SCENARIO_DEFAULT => self::OP_ALL,
        ];
    }

And the _form.php:

<?= $form->field($model, 'post_title')->textInput() ?>
<?= $form->field($model, 'post_title_en')->textInput() ?>
<?= $form->field($model, 'post_title_uz')->textInput() ?>

When I create a new post in post_lang table only 'ru' is being added.
But on updating a post that has not a translation yet, there nothing being added to posts_lang table.
Logs show folling SQL queries:
on create:

INSERT INTO `posts_lang` (`post_lang`, `post_id`, `post_title`, `post_content`) VALUES ('ru', '99', 'Sarlavha', 'asdf asdf')

on update:

SELECT * FROM `posts_lang` WHERE (`post_lang`='en') AND (`post_id`='99')

Can you help me? What am I doing wrong?

Please show full model. Maybe you have to provide rules for multilingual fields. Also show controller findModel action

Hello, thank you for your answer. I reconfigured the rules now it is working on create, but not on update, and when the update form is opened the fields are empty.
Here are my models and the controller:
Posts model:

<?php

namespace backend\modules\admin\modules\posts\models;

use Yii;
use yii\helpers\ArrayHelper;
use yii\behaviors\SluggableBehavior;
use yii\behaviors\TimestampBehavior;
use omgdef\multilingual\MultilingualBehavior;
use omgdef\multilingual\MultilingualQuery;
use common\models\User;
//use creocoder\taggable\TaggableBehavior;
use yii\db\Expression;
use yii\helpers\Inflector;
Inflector::$transliterator = 'Uzbek-Latin/BGN; NFKD';
/**
 * This is the model class for table "posts".
 *
 * @property string $ID
 * @property string $post_parent
 * @property string $post_name
 * @property string $post_author
 * @property string $post_date
 * @property string $post_title
 * @property string $post_content
 * @property string $post_password
 * @property string $post_modified
 * @property string $post_status
 * @property string $post_type
 * @property string $comment_status
 * @property string $comment_count
 * @property Postmeta[] $postmetas
 * @property Postcategories $postTerms
 */
class Posts extends \yii\db\ActiveRecord
{

    public $post_terms;
    public $post_tags;
    public $post_metas;
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'posts';
    }

    public function behaviors()
    {
        return [
            [
                'class' => SluggableBehavior::className(),
                'attribute' => 'post_title',
                'slugAttribute' => 'post_name',
                'ensureUnique'=>true,
            ],
            [
                'class' => TimestampBehavior::className(),
                'createdAtAttribute' => 'post_date',
                'updatedAtAttribute' => 'post_modified',
                'value' => new Expression('NOW()'),
            ],
            'ml' => [
                'class' => MultilingualBehavior::className(),
                'languages' => [
                    'uz' => 'Uzbek',
                    'ru' => 'Russian',
                    'en' => 'English',
                ],
                'languageField' => 'post_lang',
                //'localizedPrefix' => '',
                'requireTranslations' => true,
                'dynamicLangClass' => false,
                'langClassName' => PostsLang::className(), // or namespace/for/a/class/PostLang
                'defaultLanguage' => 'ru',
                'langForeignKey' => 'post_id',
                'tableName' => "{{%posts_lang}}",
                'attributes' => [
                    'post_title', 'post_content',
                ]
            ],
            // [
            //  'class' => Taggable::className(),
            //  'attribute' => 'post_tags',
            //  'name' => 'term_name',
            //  //'relation' => 'tags',
            //  'frequency' => 'term_frequency',
            // ],
            // [
            //  'class' => TaggableBehavior::className(),
            //  'tagValuesAsArray' => false,
            //  'tagRelation' => 'terms',
            //  'tagValueAttribute' => 'term_name',
            //  'tagFrequencyAttribute' => 'term_frequency',
            // ],
        ];
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['post_parent', 'post_author', 'comment_count'], 'integer'],
            [['post_author'], 'required'],
            [['post_date', 'post_modified', 'post_terms', 'post_tags', 'post_metas'], 'safe'],
            [['post_title', 'post_content'], 'string'],
            [['post_name'], 'string', 'max' => 255],
            [['post_password'], 'string', 'max' => 200],
            [['post_status', 'post_type', 'comment_status'], 'string', 'max' => 20],
            [['post_name'], 'unique']
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'ID' => Yii::t('app', 'ID'),
            'post_parent' => Yii::t('app', 'Parent'),
            'post_name' => Yii::t('app', 'Slug'),
            'post_author' => Yii::t('app', 'Author'),
            'post_date' => Yii::t('app', 'Publish Date'),
            'post_title' => Yii::t('app', 'Title'),
            'post_content' => Yii::t('app', 'Content'),
            'post_password' => Yii::t('app', 'Password'),
            'post_modified' => Yii::t('app', 'Modified Date'),
            'post_status' => Yii::t('app', 'Status'),
            'post_type' => Yii::t('app', 'Type'),
            'comment_status' => Yii::t('app', 'Discussion'),
            'comment_count' => Yii::t('app', 'Comment Count'),
        ];
    }

    public static function find()
    {
        return new MultilingualQuery(get_called_class());
    }

    public function getStatus() {
        $status =  [
            ['key' => 'default', 'value' => Yii::t('app','Default')],
            ['key' => 'public', 'value' => Yii::t('app','Public')],
            ['key' => 'private', 'value' => Yii::t('app','Private')],
            ['key' => 'draft', 'value' => Yii::t('app','Draft')],
        ];

        $status = ArrayHelper::map($status,'key','value');
        return $status;
    }

    public function getCommentstatus() {
        $commentstatus = [
            ['key' => 'default', 'value' => Yii::t('app','Default')],
            ['key' => 'users', 'value' => Yii::t('app','Users')],
            ['key' => 'none', 'value' => Yii::t('app','None')],
            ['key' => 'any', 'value' => Yii::t('app','Any')],
        ];
        $commentstatus = ArrayHelper::map($commentstatus,'key','value');
        return $commentstatus;
    }

    public function transactions()
    {
        return [
            self::SCENARIO_DEFAULT => self::OP_ALL,
        ];
    }

    public function getAuthor() {
        return $this->hasOne(User::className(),['id'=>'post_author']);
    }

    /**
     * Metas relation, magic funciton to generate public property
     * @return \yii\db\ActiveQuery
     */
    public function getMetas()
    {
        return $this->hasMany(Postmeta::className(), ['post_id' => 'ID']);
    }

    /**
     * Terms relation, magic funciton to generate public property
     * @return \yii\db\ActiveQuery
     */
    public function getTerms()
    {
        return $this->hasMany(Terms::className(), ['term_id' => 'term_id'])
                    ->viaTable(TermRelations::tableName(), ['object_id' => 'ID'])
                    ->andWhere(['term_type'=>'category']);
    }

    /**
     * Tags relation, magic funciton to generate public property
     * @return \yii\db\ActiveQuery
     */
    public function getTags()
    {
        return $this->hasMany(Terms::className(), ['term_id' => 'term_id'])
                    ->viaTable(TermRelations::tableName(), ['object_id' => 'ID'])
                    ->andWhere(['term_type'=>'tag']);
    }

    /**
     * Reformat tags from array to string, to display in view
     * @return [string] [$tags]
     */
    public function getTagsAsString() {
        $tags = '';
        foreach ($this->tags as $tag) {
            $tags .= $tag->term_name.', ';
        }
        return $tags;
    }

    /**
     * Reformat posted tags as array with saving new ones before setting value to $this->post_tags
     * @return [object] [$this->post_tags]
     */
    public function getTagsAsArray() {
        if(!is_array($this->post_tags)) {
            $post_tags = str_replace(', ', ',', $this->post_tags);
            $post_tags = str_replace(' ,', ',', $this->post_tags);
            $post_tags = explode(',', $post_tags);
        } else {
            $post_tags = $this->post_tags;
        }
        $tags = [];
        foreach ($post_tags as $tag) {
            $tag = ltrim($tag);
            $tag_name = Inflector::slug($tag);
            $term = Terms::find()->where(['term_name'=>$tag_name])->one();
            if(!isset($term->term_id)) {
                $new_term = new Terms();
                $new_term->term_title = ucfirst($tag);
                if($new_term->save()) {
                    $tags[] = $new_term;
                }
            } else {
                $tags[] = $term;
            }
        }
        $this->post_tags = $tags;
    }

    /**
     * Event before saving post
     * @param  [type] $insert [description]
     * @return [type]         [description]
     */
    public function beforeSave($insert) {
        // Reformat posted tags as array with saving new ones before returning
        $this->getTagsAsArray();
        // Call parent beforeSave event to handle other processes
        return parent::beforeSave($insert);
    }

    /**
     * Event after saving post
     * @param  [type] $insert            [description]
     * @param  [type] $changedAttributes [description]
     * @return [type]                    [description]
     */
    public function afterSave($insert, $changedAttributes)
    {

        /**
         * Unlink terms and tags relations. Further new relations will linked.
         */
        $this->unlinkAll('terms',true);
        $this->unlinkAll('tags',true);

        // Call of parent afterSave event to handle other processes
        parent::afterSave($insert, $changedAttributes);

        /**
         * Linking post with categories via term_relations table
         * @var TermRelations
         */
        $term_relations = new TermRelations();
        $term_relations->object_id = $this->ID;
        if(is_array($this->post_terms)) {
            foreach ($this->post_terms as $term) {
                $term = Terms::findOne($term);
                $term_relations->term_id = $term->term_id;
                $this->link('terms',$term,$term_relations);
            }
        }
        /**
         * Linking post with tags via term_relations table
         * @var TermRelations
         */
        $tag_relations = new TermRelations();
        $tag_relations->object_id = $this->ID;
        if(is_array($this->post_tags)) {
            foreach ($this->post_tags as $tag) {
                $tag = Terms::findOne($tag->term_id);
                $tag_relations->term_id = $tag->term_id;
                $this->link('tags',$tag,$tag_relations);
            }
        }

        foreach ($this->post_metas as $post_meta) {
            $meta = Postmeta::findOne($post_meta['meta_id']);
            $meta->post_id = $this->ID;
            $meta->meta_value = $post_meta['meta_value'];
            $meta->save();
        }

    }

    public function beforeDelete()
    {
        if (parent::beforeDelete()) {
            // Delete all relations with categories
            $this->unlinkAll('terms',true);
            // Delete all relations with tags
            $this->unlinkAll('tags',true);
            // Delete all post metas related to this post
            $this->unlinkAll('metas',true);
            return true;
        } else {
            return false;
        }
    }
}

PostsLang model:

<?php

namespace backend\modules\admin\modules\posts\models;

use Yii;

/**
 * This is the model class for table "posts_lang".
 *
 * @property string $ID
 * @property string $post_id
 * @property string $post_lang
 * @property string $post_title
 * @property string $post_content
 */
class PostsLang extends \yii\db\ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'posts_lang';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['post_id', 'post_lang',], 'required'],
            [['post_id'], 'integer'],
            [['post_title', 'post_content'], 'string'],
            [['post_lang'], 'string', 'max' => 6]
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'ID' => Yii::t('app', 'ID'),
            'post_id' => Yii::t('app', 'Post ID'),
            'post_lang' => Yii::t('app', 'Post Lang'),
            'post_title' => Yii::t('app', 'Post Title'),
            'post_content' => Yii::t('app', 'Post Content'),
        ];
    }
}

Posts controller:

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

        if ($model->load(Yii::$app->request->post())) {
            $model->post_type = $type;
            $model->comment_count = 0;
            $model->post_author = Yii::$app->user->identity->id;
            $post_metas = Yii::$app->request->post('Postmeta');
            unset($post_metas['meta_key']);
            unset($post_metas['meta_value']);
            $model->post_metas = $post_metas;
            if($model->load(Yii::$app->request->post()) && $model->save()) {
                return $this->redirect(['view', 'id' => $model->ID]);
            }
        } else {
            return $this->render('create', [
                'model' => $model,
                'metas' => new Postmeta,
                'type' => $type,
            ]);
        }
    }

    /**
     * Updates an existing Posts model.
     * If update is successful, the browser will be redirected to the 'view' page.
     * @param string $id
     * @return mixed
     */
    public function actionUpdate($id, $type = 'post')
    {
        $model = $this->findModel($id);
        $model->post_terms = $model->terms;
        $model->post_tags = $model->TagsAsString;
        $metas = $model->metas;

        if ($model->load(Yii::$app->request->post())) {
            $post_metas = Yii::$app->request->post('Postmeta');
            unset($post_metas['meta_key']);
            unset($post_metas['meta_value']);
            $model->post_metas = $post_metas;
            if ($model->save()) {
                return $this->redirect(['view', 'id' => $model->ID]);
            }
        } else {
            return $this->render('update', [
               'model' => $model,
               'metas' => new Postmeta,
               'type' => $type,
            ]);
        }
    }
    protected function findModel($id)
    {
        if (($model = Posts::findOne($id)) !== null) {
            return $model;
        } else {
            throw new NotFoundHttpException('The requested page does not exist.');
        }
    }

_form.php :

                        <?= $form->field($model, 'post_title')->textInput() ?>
            <?= $form->field($model, 'post_title_en')->textInput() ?>
            <?= $form->field($model, 'post_title_ru')->textInput() ?>
            <?= $form->field($model, 'post_title_uz')->textInput() ?>
            <?= $form->field($model, 'post_content')->widget(CKEditor::className(), [
                'options' => ['rows' => 15],
                'preset' => 'custom',
                'clientOptions' => [
                    'customConfig' => Yii::$app->request->BaseUrl.'/media/plugins/ckeditor/config.js',
                ],
            ]) ?>

Thanks.

I changed the langClassName to dynamic:

//'dynamicLangClass' => false,
//'langClassName' => PostsLang::className(), // or namespace/for/a/class/PostLang

Now in $model->translations all the data are shown, if I print_r it. but the form fields are still empty.

@anvarulugov Your findModel method should be similar to

    protected function findModel($id)
    {
        if (($model = Activity::find()->notRemoved()->multilingual()->andWhere(['id' => $id])->one()) !== null) {
            $model->setScenario('update');
            return $model;
        } else {
            throw new NotFoundHttpException('The requested page does not exist.');
        }
    }

Hello @OmgDef ! I changed the method as you stated. But not another error appeared :( Sorry for asking too many, questions, it is my first project on Yii2. So I'm really getting confused sometimes.

Error:

Calling unknown method: omgdef\multilingual\MultilingualQuery::notRemoved()

@OmgDef , thank you very much for a plugin. I solved the problem. Thank you again very much!