Yii2 TimestampBehavior not working correctly - php

I am using most recent version of Yii framework (2.0.6). I have table Post with PostModel and in my model I need to set correctly created_at and updated_at properties. As soon as I create new Post, both values are set to current timestamp, but when I update this post, it will also update both attributes in table to current time, even though only updated_at should be updated and created_at should remain the same.
use yii\behaviors\TimestampBehavior;
public function behaviors() {
return [
[
'class' => TimestampBehavior::className(),
],
[
'class' => SluggableBehavior::className(),
'attribute' => 'name',
],
];
}
This is the other version, but still not working:
public function behaviors()
{
return [
'timestamp' => [
'class' => TimestampBehavior::className(),
'attributes' => [
\yii\db\ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
\yii\db\ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
],
],
[
'class' => SluggableBehavior::className(),
'attribute' => 'title',
],
];
}
I am also using MariaDB database. If you could help me, what is going on with my code, I will appreciate it very much.

First of all, the second snippet is not correct, because attributes array is not designed to be set by user. It's kind of inner logic, you only need to set createdAtAttribute and updatedAtAttribute properties. Accordingly they are named created_at and updated_at by default, so if your naming is the same, you can just omit this declaration as you did in first snippet.
It's better to explicitly add this too:
use yii\db\Expression;
...
`value` => new Expression('NOW()'),
To prevent changing created_at column value during update, you can add DEFAULT CURRENT_TIMESTAMP.
Here is snippet from migration:
use yii\db\Schema;
...
'created_at' => Schema::TYPE_TIMESTAMP . ' NOT NULL DEFAULT CURRENT_TIMESTAMP',
'updated_at' => Schema::TYPE_TIMESTAMP . ' NOT NULL',
See this related question for more details.
It's for MySQL, but I guess for MariaDB is the same or very similar.

Related

Select2 keep showing "Searching" in ajax with pivot (n-n) in Laravel Backpack 5 Pro

I have 2 models: Order and Product, each has belongsToMany with pivot set properly.
In OrderCrudController, I also use FetchOperation and make a fethProducts() function as below:
use \Backpack\CRUD\app\Http\Controllers\Operations\FetchOperation;
public function fetchProducts()
{
return $this->fetch([
'model' => \App\Models\Product::class,
'searchable_attributes' => ['name','sku']
]);
// return $this->fetch(\App\Models\Product::class); <-- I also tried this one
}
protected function setupCreateOperation()
{
CRUD::setValidation(OrderRequest::class);
// other fields
$this->crud->addField([
'name' => 'products',
'type' => 'relationship',
'pivotSelect' => [
'attribute' => 'name',
'ajax' => true,
],
'subfields' => [
[
'name' => 'quantity',
'type' => 'number',
],
],
]);
}
But it comes to unexpected behavior when I search the product, the select2 field remains "searching" though the request successfully retrieved the data.
screenshot - select2 field
screenshot - ajax results
PS: this field works perfectly without subfields, no vendor overrides etc., so I think I've set everything correctly.
Anyone can help?
This was asked two months ago but somehow I missed it, just noticed it today after someone opened an issue on the GitHub repository.
I am happy you guys found a solution for it but unfortunatelly I cannot recommend it as using the select2_from_ajax will miss the functionality to don't allow the selection of the same pivots twice, otherwise you will have undesired consequences when saving the entry.
I've just submitted a PR to fix this issue, I will ping you guys here when it's merged, probably by next Monday.
Cheers
I just came accross this exact problem and after quite some research and trials, i finally found a solution !
The problem seems to be related to the relationship field type inside the pivotSelect. Try to use select2_from_ajax instead and don't forget to set method to POST explicitly, that worked for me like a charm.
Here is what you might try in your case :
$this->crud->addField([
'name' => 'products',
'type' => 'relationship',
'pivotSelect' => [
'attribute' => 'name',
'type' => 'select2_from_ajax',
'method' => 'POST',
'data_source' => backpack_url('order/fetch/products') // Assuming this is the URL of the fetch operation
],
'subfields' => [
[
'name' => 'quantity',
'type' => 'number',
],
],
]);

Yii2 Kartik EditableColumn Dropdown Relation Returns wrong Value

I have an issue with a Gridview using kartik\grid\EditableColumn, after changing the value I am returned the wrong value for the column when it updates. I am returned the dropdown key/main table integer rather than the string contained in a linked table.
I have two tables
Leads - columns id and status_id
Related fields - model, field, related_value, related_value
The relation is based on in this case
model:"Leads",
field:"status_id",
related_id:status_id
I have the following relation in my model
public function getStatus()
{
return $this->hasOne(RelatedFields::className(), ["related_id" => "status_id"])->andOnCondition(["status.field" => "status_id", "status.model"=>"Leads"])->from(["status" => RelatedFields::tableName()]);
}
I also created the following as a test based on this link
public function getStatusValue()
{
return $this->status->related_value;
}
Here is the column code
[
'class' => 'kartik\grid\EditableColumn',
'attribute' => 'status_id',
'value'=>'status.related_value',
//'value' => function($model){ return $model->status->related_value; },
//'value' => function($model){ return $model->StatusValue; },
//'refreshGrid' => true,//Works but not nice
'vAlign'=>'middle',
'hAlign'=>'center',
'pageSummary' => true,
'readonly' => false,
'width'=>'10%',
'filter'=>Html::activeDropDownList($searchModel, 'status', ArrayHelper::map(RelatedFields::Find()->where(['model' =>"Leads","field"=>"status_id"])->all(), 'related_id', 'related_value'),['class' => 'form-control','prompt' => Yii::t('app', '')]),
'editableOptions'=> [
//'attribute'=>'status_id',
//'value'=>'status.related_value',
//'header' => 'profile',
//'format' => Editable::FORMAT_BUTTON,
'inputType' => Editable::INPUT_DROPDOWN_LIST,
'data'=> ArrayHelper::map(RelatedFields::Find()->where(['model' =>"Leads","field"=>"status_id"])->all(), 'related_id', 'related_value'),
]
],
Commented out are a number of lines in my attempts to fix the issue as well as combinations of them, however all result in the wrong value.
If for example I select the related value "New" which has a related_id 1, after the column has been updated I get the value 1 instead of "New".
When the table is first loaded/reloaded the value does show correctly.
I could reload the grid, but this seems wrong just to fix 1% of the data shown on the page.
I your model take a public variable $status_value
create an assigning value method
public function getStatusValue()(){
return $this->status_value= $this->status->related_value;
}
Now in Gridview use getStatusValueenter code heremethod with assigning value as below
use yii\helpers\Url;
$gridColumns = [
[
'class' => 'kartik\grid\EditableColumn',
'attribute' => 'status_value',
'pageSummary' => true,
'readonly' => false,
'value' => function($model){ return $model->statusValue; }, // assign value from getStatusValue method
'editableOptions' => [
'header' => 'status_value',
'inputType' => kartik\editable\Editable::INPUT_TEXT,
'options' => [
'pluginOptions' => [
]
]
],
],
];
If you follow Kartik guide, he suggest to add EditableColumnAction to better handle the editable column:
The EditableColumnAction offers a quick easy way to setup your
controller action for updating, saving and managing the EditableColumn
output from GridView. This action class extends from yii\rest\Action
and hence all properties available with yii\rest\Action are applicable
here. The basic setup of the column involves setting up the controller
action and the EditableColumn.
So you need to add an EditableColumnAction in your controller to handle the update of the model:
public function actions()
{
return ArrayHelper::merge(parent::actions(), [
'edit-lead' => [
'class' => EditableColumnAction::class,
'modelClass' => Leads::class
]
]);
}
In your GridView editable column configuration, include the above
controller action for processing the Editable within editableOptions.
For example
And in your column code you need to add the action to editableOptions property:
'editableOptions' => [
...
'formOptions' => ['action' => ['/leads/edit-lead']]
]
Now, according to the guide, you can add to your action the outputValue property:
'outputValue' => function (Leads $model) {
return $model->status->related_value;
}

CakePHP 3: TranslateBehavior on a model that works as an alias

In a project I have two models, Products and Packages. Packages can be seen as containers of Products and to define the items in a package I've created a model PackageItem (which is basically a Product so its using the same table). Now Products (and so PackageItems) have translatable fields such as as a title and description.
ProductsTable.php contains:
$this->addBehavior('Translate', [
'fields' => ['title', 'description'],
'translationTable' => 'products_translations'
]);
$this->belongsToMany('PackageItems', [
'foreignKey' => 'package_id',
'joinType' => 'LEFT',
'joinTable'=>'products_package_items'
]);
PackageItemsTable contains:
$this->table('products');
$this->addBehavior('Translate', [
'fields' => ['title', 'description'],
'translationTable' => 'products_translations'
]);
$this->belongsTo('Products', [
'foreignKey' => 'package_item_id',
'joinType' => 'LEFT'
]);
Using TranslateBehavior I'm able return the translations on the Product but I can't figure out how to write the query I need to also return the translation on the PackageItems. This is my current query:
$package = $this->Products->find('translations')
->where(['business_id'=>$q['business_id'], 'id'=>$id, 'type'=>'Package'])
->contain([
'PackageItems'=>[
'Prices'=>function($q) {
return $q->where(['product_id'=>$this->product_id]);
}
]
])
->first();
You need two things
1) Set the proper reference name
The translate behavior on the PackageItemsTable class needs to be configured to use the same reference name (the value that is stored in the model column) as the behavior on the ProductsTable class, otherwise you'd never receive any translations, as it would by default look for PackageItems.
This is what the referenceName option can be used for. The reference name is being derived from the class name (not the alias), or for auto-tables, from the database table name or the alias. So for your ProductsTable class it would be Products.
Either set the name manually
$this->addBehavior('Translate', [
'fields' => ['title', 'description'],
'translationTable' => 'products_translations',
'referenceName' => 'Products' // there it goes
]);
or retrieve it dynamically from the behavior on the ProductsTable, like
$referenceName = $this->Products
->target()
->behaviors()
->get('Translate')
->config('referenceName');
This however would need to be done after adding the corresponding belongsTo association for the Products table!
2) Use the translations finder for the containment
You need to configure the PackageItems containment to use the translations finder, which is as simple as
contain([
'PackageItems' => [
'finder' => 'translations', // there you go
'Prices' => function ($q) {
return $q->where(['product_id' => $this->product_id]);
}
]
])
See also
API > \Cake\ORM\Behavior\TranslateBehavior::_referenceName()
API > \Cake\ORM\Behavior\TranslateBehavior::$_defaultConfig
API > \Cake\ORM\Query::contain()

cakephp 3.0 how to populate a select field with values instead of id

i was looking for a previous answer, but the ones i've found are related to older cakephp versions
i have two tables, 'magazines' and 'issues' where there is a relation 'issues' BelongsTo 'magazines', this is what IssuesTable looks like:
public function initialize(array $config){
$this->belongsTo('Magazines', [
'foreignKey' => 'id'
]);
}
table magazines has two fields, magazines.id and magazines.name
table issues has two fields, issues.id, issues.magazine_id where issues.magazine_id is the foreign key
to populate a select input in the issues view with the magazine.name values and save the issues.magazine_id, i've set the controller like this
$this->set('magazines', $this->Issue->Magazine->find('list'));
then i've added the following code to the issue view add.cpt
<?php
echo $this->Form->input('name', [
'type' => 'select',
'multiple' => false,
'options' => $magazines,
'empty' => true]);
?>
but i get the input select with the issues.magazine_id as values instead of magazines.name
thanks for your help and comments
You want to use find('list') as this will return the primary key and display field:-
$this->set(
'magazines',
$this->Issues->Magazines->find('list')
);
Then in your form you need the input name to be magazine_id if you're wanting to set the foreign key for the association:-
echo $this->Form->input(
'magazine_id',
[
'type' => 'select',
'multiple' => false,
'options' => $magazines,
'empty' => true
]
);
See the docs for more info.
Update
If you're experiencing issues with find('list') it is perhaps because your model's displayField is not getting set correctly. Cake normally determines the displayField for the model on initialisation. If this isn't working, or you want a different field you can set this manually in the model's initialize() method. E.g.:-
class MagazinesTable extends Table
{
public function initialize(array $config)
{
$this->displayField('name');
}
}
Changing 'name' to the appropriate field.
Alternatively, you can choose which field Cake will use for the values returned by find('list') (this is particularly useful when you want to override the default displayField). E.g.:-
$this->Issues->Magazines->find('list', [
'keyField' => 'id',
'valueField' => 'name'
]);
Display selected option in month helper
$this->Form->month('students.month', [
'label' => false,
'value'=>date('m'),
'required'=>true
]);
This actually helped me.
$this->Issues->Magazines->find('list', [
'keyField' => 'id',
'valueField' => 'name'
]);

How to get value inside GridView?

I have a timestamp column, and I simply want to format it. In GridView I have the following:
[
'attribute' => 'timestamp',
'filter' => false,
'value' => function($model, $key, $index, $column) {
// How to get current timestamp value here???
}
],
Documentation says, $model and $column both return objects, but I still could not find methods that provide column's data. How would I go about this?
You can call any attribute through getter in closure using $model and that will return attribute for current model (according to row in GridView):
[
'attribute' => 'timestamp',
'filter' => false,
'value' => function($model, $key, $index, $column){
return $model->timestamp;
}
],
Obviously such return doesn't make any sense, but you can format it somehow you want. There are some built-in options for date / datetime formatting in Yii2, you can check them in official docs here:
Formatter asDate()
Formatter asDateTime()
I use the following with Yii 1:
[
'attribute' => 'timestamp',
'filter' => false,
'value' => 'Custom::formatDateTime($data->date_stamp)',
],
Obviously formatDateTime is a custom method I created.

Categories