"Missing argument 1 for CActiveRecordBehavior::afterSave()"… Any ideas?
"Missing argument 1 for CActiveRecordBehavior::afterSave()"… Any ideas?
Merci pour la réponse, et pardon, je suis parti pour un moment, puis ai du m’occuper d’un autre projet…
I use Sqlite on this project, I remember at the time I stepped through the yii code but can’t remember if the bug was specific to the sqlite component, probably is, since no one else mentioned it. Anyway, adding the following to my db config fixed it for me:
'tablePrefix' =>'',
Thanks again.
Hi,
I don’t understand if I can have translated relations.
I have a GalleryFile model which has a belongs_to relation to a File model
'file' => array(self::BELONGS_TO, 'File', 'file_id')
The file must be localized in italian and english so I put this in the behavior config array
'localizedAttributes' => array('file_id','other_fields' .. )
Usually I can get the File model using the relation,
$galleryFileModel->file
the nicest thing ever would be to be able to do something like
$model->file_en
and get the File which id is the localized english file_id. Is it possibile as now to do something like that ?
Or I have to define a relation for each language?
Regards and thanks to the authors of this excellent extension!
Hello and thanks for sharing this amazing behavior.
However please notice an issue I found using the behavior. I was trying to add i18n at the post table. Post table has required fields the title and content attributes. The blog should support two languages English (en) and Greek (el).
So i created a form as defined on your example which creates title_en, title_el, content_en and content_el fields. When submitting the form the application returned an error stating that title and content are required!
By examining the behavior I found out that these attributes (title_en, title_el, content_en and content_el) not only they had’nt the “required” validator assigned but the original attributes (title and content) continued to have their own “required” validators.
So I modified the code to remove the "required" validator from the original attribute and add "required" validator the new ones:
else if($rule[1] === 'required') {
//We add a safe rule in case the attribute has only a 'required' validation rule assigned
//and forceOverWrite == false
//$validators->add(CValidator::createValidator('safe', $owner, $attr . '_' . $l, array_slice($rule, 2)));
// we do need to add the required property for the new attributes and remove the old required property for
$validators->add(CValidator::createValidator('required', $owner, $attr . '_' . $l, array_slice($rule, 2)));
$validatorList = $owner->getValidatorList();
$i = 0;
while($i < $validatorList->count) {
$validator = $validatorList->itemAt($i);
if( $validator instanceof CRequiredValidator ) {
$index = array_search($attr, $validator->attributes);
if ($index !== false) {
unset($validator->attributes[$index]);
$validator->attributes = array_values($validator->attributes);
break;
}
}
$i++;
}
}
The above works fine, it may help somone else also.
However, if you think that I missed something, or that something is wrong, let me know.
Thanks!
Hi,
I think you should localize the File model and use the relation to access it.
For example :
$model->file->path_en
To achieve this you should configure multilang behavior on the File model and retrieve your data using this type of loading (this example is in the page of the behavior):
$model = Page::model()->multilang()->with('articles', 'articles.multilangArticle')->findByPk((int) $id);
echo $model->articles[0]->content_en;
Hi,
I think your form isn’t exactly as in the example I give in the behavior page.
If the default language on your site is "en", you should have title, title_el, content and content_el fields (and not title_en, title_el, content_en and content_el).
Maybe you removed this part of the example form code :
if($l === Yii::app()->params['defaultLanguage']) $suffix = '';
else $suffix = '_'.$l;
If this is not the case, I’m surprised by your bug because it is a very common use case and I’ve never seen this problem.
Maybe you could post your behavior configuration and the rules definition of your model ?
Thanks
Thank you fredpeaks.
That was the case, I did not configured the params[‘defaultLanguage’] so the $suffix var had always something to get.
Once again, thank you for sharing the class!
Thank you very much fred! However I solved my problem with a different approach…
I felt like experimenting so I modified your code and implemented that feature myself, and now I’m sharing the results, maybe it might help someone or give you some ideas for further development
I added a new property to the behavior
public $localizedRelations;
(I know it’s a very unfortunate name as it also exist a $localizedRelation property, but again, it’s an experiment)
Then I created a little method to assign relations to the dynamic generated translation model
private function createLocalizedRelations($related)
{
if(empty($this->localizedRelations)) return;
$owner_relations = $this->getOwner()->relations();
foreach($this->localizedRelations as $rel_name )
{
$original_rel = $owner_relations[$rel_name];
foreach ($related as $model)
{
switch ($original_rel[0])
{
case CActiveRecord::BELONGS_TO:
$class = CActiveRecord::BELONGS_TO;
$model->getMetaData()->relations[$rel_name] =
new $class($rel_name,
$original_rel[1],
$this->localizedPrefix.$original_rel[2]);
break;
}
}
}
}
It obviously works with belongs_to relations only but it shouldn’t be difficult to implement the other relation types as well
This function is called in the afterFind method of the behavior.
I configure the behavior assigning to that property an array with the name of the relations i want to localize
'ml' => array(
'class' => 'ext.MultilingualBehavior',
'localizedAttributes' => array('fid', 'link'),
'localizedRelations' => array('file'),
'languages' => Yii::app()->params['languages'],
'defaultLanguage' => Yii::app()->language,
)
‘file’ is the name of a BELONGS_TO relation
After that I can access my localized file model with very little effort
<?php print $model->multilangGalleryFile['en']->file->render()?>
What do you think about it?
(I attached the modified file to the post)
Hi all! It’s my first post on Yii forum, so hi all again! ![]()
I’m using yii by no long time and i finded this extension… thanks to all because it’s fantastic.
I followed all the point for install and use that, but after long tests i still have a problem.
On the create or update of a record, the record for the default language it’s saved in “table” and in “table_lang”
Where i wrong?
Once again, thank you for this fantastic class!
Hi, thanks for a great extension.
I have successfully saved the translated models, but got error when accessing actionAdmin method :
CDbCommand failed to execute the SQL statement: SQLSTATE[42P01]: Undefined table: 7 ERROR: missing FROM-clause entry for table "i18nannualpurchase"
LINE 1: ...nnual_purchase_id"="t"."annual_purchase_id") AND (i18nAnnual...
^. The SQL statement executed was: SELECT COUNT(DISTINCT "t"."annual_purchase_id") FROM "tbl_annual_purchase" "t" LEFT OUTER JOIN "tbl_annual_purchase_lang" "i18nAnnualPurchase" ON ("i18nAnnualPurchase"."annual_purchase_id"="t"."annual_purchase_id") AND (i18nAnnualPurchase.lang_id='en_us')
This is my actionAdmin :
public function actionAdmin()
{
$model = new AnnualPurchase('search');
$model->unsetAttributes();
if(isset($_GET['AnnualPurchase']))
$model->attributes = $_GET['AnnualPurchase'];
$this->render('index', compact('model'));
}
Here is how I apply the extension on my AnnualPurchase model :
public function behaviors()
{
return array(
'ml' => array(
'class' => 'application.components.behaviors.MultilingualBehavior',
'langClassName' => 'AnnualPurchaseLang',
'langTableName' => 'tbl_annual_purchase_lang',
'langForeignKey' => 'annual_purchase_id',
'langField' => 'lang_id',
'localizedAttributes' => array('name'),
'localizedPrefix' => 'l_',
'languages' => Yii::app()->params['translatedLanguages'],
'defaultLanguage' => Yii::app()->params['defaultLanguage'],
'createScenario' => 'add',
//'localizedRelation' => 'i18nAnnualPurchase',
//'multilangRelation' => 'multilangAnnualPurchase',
//'forceOverwrite' => false,
//'forceDelete' => true,
//'dynamicLangClass' => true,
)
);
}
I am using PostgreSQL 9.1, where am I missing ? Please help Thanks
Error fixed by changing line 259 in the MultilingualBehavior.php into :
$owner->getMetaData()->relations[$this->localizedRelation] = new $class($this->localizedRelation, $this->langClassName, $this->langForeignKey, array('on' => '"'.$this->localizedRelation.'"' . "." . $this->langField . "='" . $lang . "'", 'index' => $this->langField));
NOTE : Adding quotation mark before and after $this->localizedRelation
+1 It could be from useful to really a must have for us too !
We have 3 yii big multilanguage projects :-s on fire.
Hi,
Sorry for the time taken to answer, I’m very busy now.
Are you sure that your fix won’t break anything for the mysql users ?
I don’t want to publish this version if it doesn’t work anymore with mysql…
Maybe I can add an option for postgre users or detect the use of postgre in the main config. If you have any idea, that would be cool, I’m not very familiar with postgre.
If someone could test the fix with mysql and post a feedback, it would be great and I could publish it as fast as possible.
Thank you for your work.
Far better to have a ‘default_language’ flag in the second table. Your solution has the same flaw as that by Fred, its just very bad design.
Thanks for your very constructive post Backslider. The default language is defined in the application configuration, no need to put it in the database.
I’m not a fan of having the translated attributes in the main table but have no time to think about a solution to remove them. And by the way, your making a comment about a post that has been posted a year ago and if you had read the other posts, you could have understand that guillemc’s work is at the origin of the extension so it’s normal that it is the same “very bad design”…
Feel free to post a modified version of the extension with your very good design my programmer lord
.
Hi Fredpeaks, sorry for the late reply. Haven’t tried it with mysql though ![]()
Hello - I’m using your extension too. Thank you for your contribution!
I’m running into the same problem of
"Property "Students.student_firstname_en" is not defined."
What I am trying to do is fairly simple:
CException
Property "Students.student_lastname_en" is not defined.
/opt/yii-1.1.9.r3527/framework/db/ar/CActiveRecord.php(144)
132 */
133 public function __get($name)
134 {
135 if(isset($this->_attributes[$name]))
136 return $this->_attributes[$name];
137 else if(isset($this->getMetaData()->columns[$name]))
138 return null;
139 else if(isset($this->_related[$name]))
140 return $this->_related[$name];
141 else if(isset($this->getMetaData()->relations[$name]))
142 return $this->getRelated($name);
143 else
144 return parent::__get($name);
145 }
146
147 /**
148 * PHP setter magic method.
149 * This method is overridden so that AR attributes can be accessed like properties.
150 * @param string $name property name
151 * @param mixed $value property value
152 */
153 public function __set($name,$value)
154 {
155 if($this->setAttribute($name,$value)===false)
156 {
Stack Trace
#0
+ /opt/yii-1.1.9.r3527/framework/db/ar/CActiveRecord.php(144): CComponent->__get("student_lastname_en")
#1
+ /opt/yii-1.1.9.r3527/framework/validators/CStringValidator.php(80): CActiveRecord->__get("student_lastname_en")
#2
+ /opt/yii-1.1.9.r3527/framework/validators/CValidator.php(197): CStringValidator->validateAttribute(Students, "student_lastname_en")
#3
+ /opt/yii-1.1.9.r3527/framework/base/CModel.php(158): CValidator->validate(Students, null)
#4
+ /opt/yii-1.1.9.r3527/framework/db/ar/CActiveRecord.php(786): CModel->validate(null)
#5
– /var/www/html/dev/ASMiS/protected/models/Students.php(796): CActiveRecord->save()
791 $model = Students::model()->findByPk($id);
792
793 // Set the secretary id attribute
794 $model->student_secretary_id = $secretary_id;
795
796 if ($model->save())
797 $result = true;
798
799 return $result;
800
801 }
#6
– /var/www/html/dev/ASMiS/protected/models/Seccomassignment.php(209): Students->updateSecretary("198", "8")
204
205 // update the rows matching the specified condition
206 Seccomassignment::model()->updateAll(array('seccomassignment_expired'=>1),'seccomassignment_id <> '.$this->seccomassignment_id.' AND seccomassignment_student_id ='.$this->seccomassignment_student_id);
207
208 // Update the student record seccom field with this seccomassignment
209 $student = Students::model()->updateSecretary($this->seccomassignment_student_id,$this->seccomassignment_seccom_id);
210
211 }
212
213
214
Basically, I am retrieving a student record (one that I have just previously saved successfully) using findByPk, changing one of its attributes and then attempting to save it again right off the bat with $model->save().
You mention above that you answered the question in the comments of the url above, but as much as I’ve tried to find the answer to this specific issue in there I couldn’t. Would you please point in the right direction?
Much appreciated!
Hello,
I think you have to retrieve the record using multilang to be able to save it if you use the behavior.
Students::model()->multilang()->findByPk((int) $id)<img src='http://www.yiiframework.com/forum/public/style_emoticons/default/wink.gif' class='bbc_emoticon' alt=';)' />
The multilang fill the entity with all translations and create an attribute for each so you won’t have the error when you’ll save it.
If it works (or not
) please post an answer.
Hello,
I have try everything but I have always the error "Property "Category.title_en" is not defined" when I try to save or to the test for the form !
Everything is fine to show the info, but it’s no possible to used it in a form or save.
I try to understand why the MultilingualBehavior to to find fields (title_en) that I don’y have in my model in fact.
Can somebody help me ?
Thanks
View :
<?php
$languages= Language::model()->findAll();
foreach ($languages as $language) {
if($language->code === Yii::app()->params['defaultLanguage']) $suffix = '';
else $suffix = '_'.$language->code;
?>
<fieldset>
<legend><?php echo $language->name; ?></legend>
<div class="row">
<?php echo $form->labelEx($model,'title'); ?>
<?php echo $form->textField($model,'title'.$suffix,array('size'=>60,'maxlength'=>255)); ?>
<?php echo $form->error($model,'title'.$suffix); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'content'); ?>
<?php echo $form->textArea($model,'content'.$suffix); ?>
<?php echo $form->error($model,'content'.$suffix); ?>
</div>
</fieldset>
<?php } ?>
Model :
<?php
/**
* This is the model class for table "category".
*
* The followings are the available columns in table 'category':
* @property string $id_category
* @property string $id_parent
* @property integer $active
* @property string $date_add
* @property string $date_upd
* @property string $position
* @property string $name
* @property string $description
* @property string $keywords
*/
class Category extends MyActiveRecord
{
public function behaviors() {
return array(
'ml' => array(
'class' => 'MultilingualBehavior',
'localizedAttributes' => array('name', 'description', 'keywords'), //attributes of the model to be translated
),
);
}
public function defaultScope()
{
return $this->ml->localizedCriteria();
}
/**
* Returns the static model of the specified AR class.
* @param string $className active record class name.
* @return Category the static model class
*/
public static function model($className=__CLASS__)
{
return parent::model($className);
}
/**
* @return string the associated database table name
*/
public function tableName()
{
return 'category';
}
/**
* @return array validation rules for model attributes.
*/
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
$rules= array(
array('id_parent, date_add, date_upd', 'required'),
array('active', 'numerical', 'integerOnly'=>true),
array('id_parent, position', 'length', 'max'=>10),
array('name', 'length', 'max'=>128),
array('keywords', 'length', 'max'=>255),
array('description', 'safe'),
// The following rule is used by search().
// Please remove those attributes that should not be searched.
array('id, id_parent, active, date_add, date_upd, position, name, description, keywords', 'safe', 'on'=>'search'),
);
return $rules;
}
/**
* @return array relational rules.
*/
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'getparent' => array(self::BELONGS_TO, 'Category', 'id_parent'),
'childs' => array(self::HAS_MANY, 'Category', 'id_parent', 'order' => 'position ASC'),
'CategoryLang' => array(self::HAS_MANY, 'categoryLang', 'category_id'),
'childsCount' => array(self::STAT, 'Category', 'id_parent'),
);
}
/**
* @return array customized attribute labels (name=>label)
*/
public function attributeLabels()
{
return array(
'id' => 'Id Category',
'id_parent' => 'Id Parent',
'active' => 'Active',
'date_add' => 'Date Add',
'date_upd' => 'Date Upd',
'position' => 'Position',
'name' => 'Name',
'description' => 'Description',
'keywords' => 'Keywords',
'childsCount' => 'Sub-Categories',
);
}
/**
* Retrieves a list of models based on the current search/filter conditions.
* @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.
*/
public function search($id_parent)
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
$criteria->compare('id',$this->id,false);
$criteria->compare('id_parent',$id_parent,false);
$criteria->compare('active',$this->active);
$criteria->compare('date_add',$this->date_add,true);
$criteria->compare('date_upd',$this->date_upd,true);
$criteria->compare('position',$this->position,false);
$criteria->compare('name',$this->name,true);
$criteria->compare('description',$this->description,true);
$criteria->compare('keywords',$this->keywords,true);
$criteria->order = 'position ASC';
return new CActiveDataProvider($this, array(
'criteria'=>$this->ml->modifySearchCriteria($criteria),
'pagination' => array(
'pageSize' => 40,
),
'sort'=>array(
'defaultOrder'=>'position ASC',
)
));
}
public function getMaxPosition($id_parent)
{
return Yii::app()->db->createCommand()
->select('max(position) as maxposition')
->from('category')
->where('id_parent = ' . $id_parent)
->queryScalar();
}
}
Controller :
public function actionUpdate()
{
$id = $_GET['id'];
$category = $this->loadModel($id, true);
$category->date_upd = new CDbExpression('NOW()');
// if it is ajax validation request
if(isset($_POST['ajax']) && $_POST['ajax']==='update-form')
{
echo CActiveForm::validate($category);
Yii::app()->end();
}
// collect user input data
if(isset($_POST['Category']))
{
$category->attributes=$_POST['Category'];
// validate user input and redirect to the previous page if valid
if($category->validate()) {
$category->save();
$this->redirect(array('index','id_parent'=>$category->id_parent));
}
}
// display the login form
$this->render('update',array('model'=>$category));
}
problem in GridView
I have this 2 models
User (multi language)
UserMessage
In UserMessage model I have:
public function relations()
{
return array(
'user' => array(self::BELONGS_TO, 'User', 'user_id'),
'sendUser' => array(self::BELONGS_TO, 'User', 'send_user_id'),
);
}
public function search()
{
....................
$criteria->with = array('user','sendUser');
$criteria->compare('user.username',$this->user_search,true);
$criteria->compare('sendUser.username',$this->sendUser_search,true);
...................
}
problem in gridview "UserMessage":
CDbCommand failed to execute the SQL statement: SQLSTATE[42000]: Syntax error or
access violation: 1066 Not unique table/alias: 'i18nUser'. The SQL statement
executed was: SELECT COUNT(DISTINCT `t`.`id`) FROM `user_message` `t` LEFT OUTER
JOIN `user` `user` ON (`t`.`user_id`=`user`.`id`) LEFT OUTER JOIN `userLang`
`i18nUser` ON (`i18nUser`.`user_id2`=`user`.`id`) AND (i18nUser.language='en')
LEFT OUTER JOIN `user` `sendUser` ON (`t`.`send_user_id`=`sendUser`.`id`) LEFT
OUTER JOIN `userLang` `i18nUser` ON (`i18nUser`.`user_id2`=`sendUser`.`id`) AND
(i18nUser.language='en') WHERE (t.user_id=2)
Thank you