I have a table which has different types of text data from different sources, identified by type, lang, sourceId and stored in field text. Is there anyway to return by active record relation not object of table but only value of field text in oher words scalar query via relation?
Example: For now I have:
$modelName->RelationName->text) //field name storing expexted data, returns string.
Desired way is:
$modelName->RelationName//text field value returned only.
Yes But using lazy loading approach :
Update Your Relation as
public function getRelationName(){
//Related model Class Name
//related column name as select
return $this->hasOne(RelationClass::className() ,['id' => 'id'])->select('name')->scalar();
}
then get relation value as : -
$modelName->relationName//text field value returned only.
Extend your model with one or more getter for retrive the value you need using a relation eg:
In the model you need retrive the related data you can build a funtion for define the raltion (in this case an hasOne)
*/
public function getRelationName()
{
return $this->hasOne(ModelOfTheRelation::className(), ['column1' => 'column1', 'EvColumn2' => 'Evcolumn2']);
}
Then you can use getter function for the data
/* Getter f */
public function getRelatioName_field()
{
return $this->relationName->field;
}
In view you can easy obtain the data using
echo $model->relationName_field
or in gridview (dataprovider)
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'relationName_field',
this link could be useful http://www.yiiframework.com/wiki/621/filter-sort-by-calculated-related-fields-in-gridview-yii-2-0/
Related
I would like to filter a backpackforlaravel list on a custom calculated Boolean attribute. There is no column for this attribute in the database.
I have created the attribute in the model as an eloquent accessor.
public function getCalculatedBooleanAttribute($value)
{
$bool = // get result of calculation as true/false
return $bool;
}
I can then show the values as a column using the following in my controller.
$this->crud->addColumn('calculated_boolean');
This works perfectly so far and I can see the expected results in the list view. I now need to filter the view on this...
I have tried a standard crud filter using addClause but this gives an exception about missing database field. e.g
$this->crud->addClause('where', 'calculated_boolean', '1');
Column not found: 1054 Unknown column 'calculated_boolean' in 'where clause'
I cannot find anything in the docs that shows how to filter on this attribute. Can anyone suggest a way to filter on fields without columns in the DB?
Indeed. Your accessor is just a PHP function that manipulates the MySQL response in some way. Whatever you do inside a Laravel accessor happens AFTER your database response. So you can't add an accessor to an Eloquent query.
It might be possible for you to use whereRaw to add that column to your query, in the addFilter closure (the third parameter):
$this->crud->addFilter([ // simple filter
'type' => 'simple',
'name' => 'active',
'label'=> 'Active'
],
false,
function() { // if the filter is active
$this->crud->query = $this->crud->query->whereRaw('price * ? as calculated_boolean', [1.0825]);
$this->crud->addClause('where', 'calculated_boolean', '1');
} );
Notice you can manipulate $this->crud->query t
I have created_at and updated_at column in DB and I am filling it with timestamp.
To show the created_at and updated_at date in index.php(To the user in GridView) I have converted the timestamp to YYYY-MM-DD format using afterFind function in Model.
From the GridView if an edit button is clicked, I need to update a status in the row. So my code in the controller is
$existingRow = Project::findOne($id) // From the parameter
$existingRow->status = 2
$existingRow->save()
On executing the above command, the created_at field is saving with "YYYY-MM-DD" format which is converted from "afterFind()" function in the model.
How can I get the non-converted timestamp value to be saved ?
To handle created_at and updated_at you should use a TimestampBehavior. In your corresponding model add:
use yii\behaviors\TimestampBehavior;
public function behaviors()
{
return [
TimestampBehavior::className(),
];
}
Then it will automatically fill your fields on create/update your model.
To display it properly in GridView, define your column like:
<?= GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
'id',
'name',
'created_at:datetime', // or 'created_at:date' for just date
// ...
],
]) ?>
afterFind() is not best solution to such things.
Just like you overrode afterFind(), you can override the beforeSave() method in your model, to reformat the attributes to the required format before the Save operation is executed.
protected function beforeSave()
{
$parentResult = parent::beforeSave();
// pseudo code
// Change any attributes values as you require
// return true if successful ($result)
return $parentResult && $result; // Save operation will run if this returns true only
}
It may not be the expected answer, but this function got me what I want. Using $model->getOldAttribute($attributeName) gives the original data which is in the database (which is the original data not converted by afterFind() function).
$existingRow = ProjectResourceAssign::findOne($id);
$existingRow->status = 2;
// This piece of line will get you the original value of the attribute
$existingRow->created_at = $existingRow->getOldAttribute('created_at');
$existingRow->save(false);
I have two models: Cities and Schools. As you already understand Cities can have many schools and taking this into account I have defined my model as follows:
class School extends Model
{
public $fillable = ['city_id' ,'name'];
public function city()
{
return $this->belongsTo('App\City','city_id','id');
}
}
class City extends Model
{
public $fillable = ['name'];
Public function schools()
{
return $this->hasMany('App\School', 'id','city_id');
}
}
But I have faced a pproblem when trying to validate update of a school model. I have to validate whether name of the school is unique for selected city or not. I have defined the rule like this:
$rules = array(
'name' => ['required', Rule::unique('schools')->ignore($id)],
);
$validator=Validator::make(Input::all(),$rules);
But it is not allowing to save a school with existing name in other city than selected. How should I change the rule to ensure that school names can be same if the city is different.
Thank you.
Custom rule
The best solution would be to create a custom rule for this, that accepts the field with the corresponding city name/id as a parameter.
Something like
//Calling the custom rule like this
['name' => 'required|validateSchool:yourFieldNameOfCityHere'];
Declaring the custom validation function in your service provider like this
Validator::extend('validateSchool', function ($attribute, $value, $parameters, $validator) {
$cityName = ($validator->data, $parameters[0]);
//Now check if the $value is already set for the specific city by using normal database queries and comparison
return count(City::whereName($cityName)->schools()->whereName($value)->get()) == 0
});
What does it
The custom validation rule receives the data of the field you give with the function (in the code above it's yourFieldNameOfCityHere), so it knows which city the user chose. With this information, you now can check if there is already a school with the name for the entered city.
At the DB level, it sounds like what you want is a compound uniqueness constraint across name and city_id. Eloquent seems to support passing an array of column names in model definitions. It seems like this requires custom validation, though. See Laravel 4: making a combination of values/columns unique and the custom validator at https://github.com/felixkiss/uniquewith-validator
I haven't seen this online when looking at other's code, guides, tutorials, etc.
When I generate a Model with Gii, the functions regarding relations all have a zero after them.
Example:
class Benefit extends \yii\db\ActiveRecord
{
// truncated Yii Model code...
public function getType0()
{
return $this->hasOne(BenefitTypes::className(), ['id' => 'type']);
}
}
BenefitTypes is an id to name mapping:
id | name
---------------
1 => Federal
2 => Non-Profit
In the 'benefit' table, it has column named 'type' that is a relation to the 'benefit_types' table 'id' column.
I though I should be able to do (in /views/benefit/index.php) 'type.name' but it doesn't work either. It changes the column name to "Type Name" and puts "(not set)" in the data table...
Example:
<?= DetailView::widget([
'model' => $model,
'attributes' => [
'id',
'somevalue',
'type.name',
],
]) ?>
What is going on, why does it not act like it's supposed to?
UPDATE
I am beginning to think the 0 suffix to the relation function names, ie: getType0, is due to "type" being used in the table as a column name to avoid duplication or confusion. I can't find this documented though, so would like to have a definite answer on that.
I changed the function name to getTypeRelation(). Then in the index.php view, for the detailview widget, used 'typeRelation.name' and it returned the name through the relation just fine.
Your thinking is correct. Generation of the relation names is done by the function generateRelationName().
protected function generateRelationName($relations, $table, $key, $multiple)
{
if (!empty($key) && substr_compare($key, 'id', -2, 2, true) === 0 && strcasecmp($key, 'id')) {
$key = rtrim(substr($key, 0, -2), '_');
}
if ($multiple) {
$key = Inflector::pluralize($key);
}
$name = $rawName = Inflector::id2camel($key, '_');
$i = 0;
while (isset($table->columns[lcfirst($name)])) {
$name = $rawName . ($i++);
}
while (isset($relations[$table->fullName][$name])) {
$name = $rawName . ($i++);
}
return $name;
}
Yii uses the related table's name as the relation name. Should you have a column with the same name as the related table, a digit will be appended to the relation to avoid confusion due to Yii's handling of magic functions. This also occurs if you have two columns or more in a single table related to the same table e.g columns create_user_id, update_user_id and delete_user_id related to table user will result in relations named user, user0 and user1.
For your example, it is advisable to name your foreign key field something else e.g type_id or typeId. Yii will handle these correctly . The other alternative when you have multiple columns related to the same table is to just rename the functions.
Because relational column name and the relation name is same. When you call $benefit->type what you will expect, the value of the column/property type or the instance of BenefitTypes? So now you know. $benefit->type return the property value and $benefit->type0 returns the instance of relation.
My view lets the user see a person's details, including the families they are in. This is done in the DB with a Person table, Family table, and a familyMembership link table between the two.
The relationship in the CActiveRecord model for Person is as such:
'families' => array(self::MANY_MANY, 'Family', 'familymembership(Person, Family)'),
In my person controller, I am wanting to pass a variable into the view that has the related data in a way that TbGridView (CGridView but Bootstrap) will accept (a data provider). The controller calls the person model's getFamilies() function:
public function getFamilies() {
// returns an array of Family objects related to this Person model
$familiesArray = $this->getRelated('families');
// puts this array into a CArrayDataProvider
$families = new CArrayDataProvider($familiesArray);
return $families;
}
The return value goes back to the controller, which is then handed through in the renderPartial() call. The view has a TbGridView widget initialisation like this:
$this->widget('bootstrap.widgets.TbGridView',
array(
//the CArrayDataProvider from the model function
'dataProvider' => $families,
'columns' => array(
array(
'name' => 'Family Name',
// Example field from Family model
'value' => '$data->familyName'
)
)
));
However, in doing this I am getting the following error:
'Property "Family.id" is not defined. (D:\wamp\www\yii\framework\base\CComponent.php:130)'
The Family model does not have an id property, and I don't understand why the widget is looking for such.
What's going wrong here?
Thanks.
Quoting the doc from Yii Class Reference, you have to provide a keyField:
Elements in the raw data array may be either objects (e.g. model objects) or associative arrays (e.g. query results of DAO). Make sure to set the keyField property to the name of the field that uniquely identifies a data record or false if you do not have such a field.
By default keyField will be set to "id", so you need to overwrite it with your Family model primary key :
<?php
$familyDataProvider = new CArrayDataProvider($rawData, array(
'keyField' => 'yourPkNameHere',
));