I am trying to create dynamic model class for Mongo Collection and populate data in Cgridview (using YIIMongodbsuite extension)
Getting Column from MySQL DB:
$sql="SELECT name FROM CRM_Field Where crm_base_contact_id = ".$base;
$names =Yii::app()->db->createCommand($sql)->query()-> readAll();
Accessing Model in view:
$cc = new ContactCollection($names);// Passing dynamic column names to Model
$criteria = new EMongoCriteria;
$criteria->crm_base_contact_id('==', $base);
$cc->setDbCriteria($criteria);
CgridView Code:
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id' =>'BCImported-grid',
'dataProvider' => $cc->search(false),
'columns' => $names,
)); ?>
ContactCollection Model :
<?php
class ContactCollection extends EMongoDocument
{
public $dyn_fields;
public function __construct ($names) {
$this->dyn_fields = $names;
}
public function getCollectionName()
{
return 'cartoons';
}
/**
* #return array validation rules for model attributes.
*/
public function rules()
{
$allMembers = implode(', ', array_keys($this->dyn_fields));
return array(
array($allMembers, 'required'),
);
}
public function attributeLabels()
{
return $this->dyn_fields;
}
}
I am getting this error " Property "ContactCollection.0" is not defined."
You don't need the "attributeLabels()" method in that case since each attribute should have an associated value. If you don't set that method, Yii will use the property name as the label itself.
Related
I am developing a web application using Laravel 8. I have noticed that quite a lot of things have changed in Laravel 8 including factories.
I have a factory class MenuCategoryFactory for my MenuCategory model class with the following definition.
<?php
namespace Database\Factories;
use App\Models\Menu;
use App\Models\MenuCategory;
use Illuminate\Database\Eloquent\Factories\Factory;
class MenuCategoryFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = MenuCategory::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'name' => $this->faker->name,
'menu_id' => Menu::factory()->create(),
];
}
}
In my code (database seeder class), I am trying to override the menu_id as follow while I am creating the factories.
$restaurant = Restaurant::first();
MenuCategory::factory()->create([
'menu_id' => $restaurant->menu->id
]);
But it is not using the value I passed, $restaurant->menu->id. Instead, it is creating a new menu. What is wrong missing in my code and how can I fix it?
In your factory definition, don't call ->create(), instead set it up like this:
public function definition()
{
return [
'name' => $this->faker->name,
'menu_id' => Menu::factory(),
];
}
Then you you should be able to set up related models (assuming you the relationships setup in the model) like this:
$restaurant = Restaurant::first();
MenuCategory::factory()->for($restaurant)->create();
Change the definition to
public function definition()
{
return [
'name' => $this->faker->name,
'menu_id' => function() {
Menu::factory()->create()->id,
}
];
}
then you can replace the value
I have a table with 2 fields and generated CRUD which is working well. I added to 2 more fields to that table and applied required changes in model, controller and view. But yii shows error for the newly added fields saying that those attributes are not defined.
This is my model code:
class Product extends CActiveRecord
{
public function tableName()
{
return 'product';
}
/**
* #return array validation rules for model attributes.
*/
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('title, subTitle, image, shortDesc, longDesc, seo_title, focus_keywords, meta_desc, meta_url, created, updated', 'required'),
array('status', 'numerical', 'integerOnly'=>true),
array('title, subTitle, image', 'length', 'max'=>128),
array('seo_title, focus_keywords, meta_desc, meta_url', 'length', 'max'=>255),
// The following rule is used by search().
// #todo Please remove those attributes that should not be searched.
array('id, title, subTitle, image, shortDesc, longDesc, status, seo_title, focus_keywords, meta_desc, meta_url, created, updated', 'safe', 'on'=>'search'),
);
}
/**
* #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(
);
}
/**
* #return array customized attribute labels (name=>label)
*/
public function attributeLabels()
{
return array(
'id' => 'ID',
'title' => 'Title',
'subTitle' => 'Sub Title',
'image' => 'Image',
'shortDesc' => 'Short Desc',
'longDesc' => 'Long Desc',
'status' => 'Status',
'seo_title' => 'Seo Title',
'focus_keywords' => 'Focus Keywords',
'meta_desc' => 'Meta Desc',
'meta_url' => 'Meta Url',
'created' => 'Created',
'updated' => 'Updated',
);
}
/**
* Retrieves a list of models based on the current search/filter conditions.
*
* Typical usecase:
* - Initialize the model fields with values from filter form.
* - Execute this method to get CActiveDataProvider instance which will filter
* models according to data in model fields.
* - Pass data provider to CGridView, CListView or any similar widget.
*
* #return CActiveDataProvider the data provider that can return the models
* based on the search/filter conditions.
*/
public function search()
{
// #todo Please modify the following code to remove attributes that should not be searched.
$criteria=new CDbCriteria;
$criteria->compare('id',$this->id);
$criteria->compare('title',$this->title,true);
$criteria->compare('subTitle',$this->subTitle,true);
$criteria->compare('image',$this->image,true);
$criteria->compare('shortDesc',$this->shortDesc,true);
$criteria->compare('longDesc',$this->longDesc,true);
$criteria->compare('status',$this->status);
$criteria->compare('seo_title',$this->seo_title,true);
$criteria->compare('focus_keywords',$this->focus_keywords,true);
$criteria->compare('meta_desc',$this->meta_desc,true);
$criteria->compare('meta_url',$this->meta_url,true);
$criteria->compare('created',$this->created,true);
$criteria->compare('updated',$this->updated,true);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
/**
* Returns the static model of the specified AR class.
* Please note that you should have this exact method in all your CActiveRecord descendants!
* #param string $className active record class name.
* #return Product the static model class
*/
public static function model($className=__CLASS__)
{
return parent::model($className);
}
}
Clear Yii cache (protected/runtime)
I'm new to Yii and I have a table 'Student' with fields like 'stdStudentId', 'stdName', etc.
I'm making API, so this data should be returned in JSON. Now, because I want field names in JSON to just be like 'id', 'name', and I don't want all fields returned, i made a method in the model:
public function APIfindByPk($id){
$student = $this->findByPk($id);
return array(
'id'=>$student->stdStudentId,
'name'=>$student->stdName,
'school'=>$student->stdSchool
);
}
The problem is, stdSchool is a relation and in this situation, $student->stdSchool returns array with fields like schSchoolId, schName, etc. I don't want fields to be named like that in JSON, and also I don't want all the fields from School returned and I would like to add some fields of my own. Is there a way to do this in Yii, or I'll have to do it manually by writing methods like this?
I have been looking for the same thing. There is a great php lib named Fractal letting you achieve it: http://fractal.thephpleague.com/
To explain briefly the lib, for each of your models you create a Transformer that will be doing the mapping between your model attributes and the ones that need to be exposed using the api.
class BookTransformer extends Fractal\TransformerAbstract
{
public function transform(Book $book)
{
return [
'id' => (int) $book->id,
'title' => $book->title,
'year' => $book->yr,
];
}
}
In the transformer you can also set the relation that this model have :
class BookTransformer extends TransformerAbstract
{
/**
* List of resources relations that can be used
*
* #var array
*/
protected $availableEmbeds = [
'author'
];
/**
* Turn this item object into a generic array
*
* #return array
*/
public function transform(Book $book)
{
return [
'id' => (int) $book->id,
'title' => $book->title,
'year' => $book->yr,
];
}
/**
* Here we are embeding the author of the book
* using it's own transformer
*/
public function embedAuthor(Book $book)
{
$author = $book->author;
return $this->item($author, new AuthorTransformer);
}
}
So at the end you will call
$fractal = new Fractal\Manager();
$resource = new Fractal\Resource\Collection($books, new BookTransformer);
$json = $fractal->createData($resource)->toJson();
It's not easy to describe all the potential of fractal in one answer but you really should give it a try.
I'm using it along with Yii so if you have some question don't hesitate!
Since you are getting the values from the database using Yii active record, ask the database to use column aliases.
Normal SQL would be something like the following :
SELECT id AS Student_Number, name AS Student_Name, school AS School_Attending FROM student;
In Yii, you can apply Criteria to the findByPK() function. See here for reference : http://www.yiiframework.com/doc/api/1.1/CActiveRecord#findByPk-detail
$criteria = new CDbCriteria();
$criteria->select = 'id AS Student_Number';
$student = Student::model()->findByPk($id, $criteria);
Note that in order to use a column alias like that, you will have to define a virtual attribute Student_Number in your Student{} model.
Override the populateRecord() function of ActiveRecord can achieve this!
My DishType has 5 properties and override the populateRecord function Yii would invoke this when records fetched from db.
My code is here!
class DishType extends ActiveRecord
{
public $id;
public $name;
public $sort;
public $createTime;
public $updateTime;
public static function populateRecord($record, $row)
{
$pattern = ['id' => 'id', 'name' => 'name', 'sort' => 'sort', 'created_at' => 'createTime', 'updated_at' => 'updateTime'];
$columns = static::getTableSchema()->columns;
foreach ($row as $name => $value) {
$propertyName = $pattern[$name];
if (isset($pattern[$name]) && isset($columns[$name])) {
$record[$propertyName] = $columns[$name]->phpTypecast($value);
}
}
parent::populateRecord($record, $row);
}
}
I have problem in CGrid while on sorting a relational data using relational model in `` page.
Briefly my scenario:
I have a user model: Entities=> id,username
And a profile Model: Entities=> id, firstname,lastname, user_id,etc..
I want to list profile model and username from user model in CGrid, so that sorting and searching perms well. In my case sorting username is done by user_id not by username. I want to search it by username,so i do the following,
My Controller Action:
$model = new Profile('search');
$model -> unsetAttributes();// clear any default values
if (isset($_GET['Profile']))
$model -> attributes = $_GET['Profile'];
$this -> render('MyPage', array('model' => $model ));
My Model Relation:
public function relations() {
// NOTE: you may need to adjust the relation name and the related
// class name the relations automatically generated below.
return array(
'user' => array(self::BELONGS_TO, 'user', 'user_id'),);
}
Model Rules:
array( 'xxx,yyy,user_name', 'safe', 'on'=>'search' ),
And model search function
if(!empty($this->user_id)){
$criteria->with='user';
$criteria->order = ::app()->request->getParam('sort');// 'username ASC'
}
$criteria -> compare('user.username', $this->user_id, true);
My
$this->widget('zii.widgets.grid.CGrid', array(
'id'=>'profile-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
array('name'=>'user_id',
'header'=>User::model()->getAttributeLabel('username'),
'value' =>'$data->getRelated(\'user\')->username',
'type'=>'raw',
'htmlOptions'=>array('style'=>'text-align: center'),),
---------------
During sorting,sorting works perfectly but sorting is done on the basis of user_id not by username. Anything that i am missing to do so. Please suggest.
Reference:Here (I also tried as by declaring a public variable as suggesting in the link but bot workingg.)
Edit: After Issue Fixed.
Thanks for this link too.
Well, the wiki page you found is really a good start...
Here is an alternative way for doing this :
In your Profile model :
// private username attribute, will be used on search
private $_username;
public function rules()
{
return array(
// .....
array('username', 'safe', 'on'=>'search'),
// .....
);
}
public function getUsername()
{
// return private attribute on search
if ($this->scenario=='search')
return $this->_username;
// else return username
if (isset($this->user_id)) && is_object($this->user))
return $this->user->username;
}
public function setUsername($value)
{
// set private attribute for search
$this->_username = $value;
}
public function search()
{
// .....
$criteria->with = 'user';
$criteria->compare('user.username', $this->username, true);
// .....
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
'sort'=>array(
'attributes'=>array(
'username'=>array('asc'=>'user.username', 'desc'=>'user.username DESC'),
'*',
),
),
));
}
And in your view you should simply try :
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'profile-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
// .....
'username',
// .....
),
);
I have several models with relations and what I am trying to do is to search the fields with the aliases I provide in DetailView. It looks like this
<?php $this->widget('bootstrap.widgets.BootGridView',array(
'id'=>'operations-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'operationType.name:raw:Operation',
'creation_date:datetime',
'modification_date:datetime',
'ammount_usd:raw:Ammount',
'currency.short',
/*
'client_id',
'organization_id',
*/
array(
'class'=>'bootstrap.widgets.BootButtonColumn',
),
),
)); ?>
And what I want is to be able to search through rows using the aliases for columns like currency.short. What is a correct approach to do that? Tried to modify the search() method like this..but I think I'm missing something.
public function search()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
$criteria->compare('creation_date',$this->creation_date,true);
$criteria->compare('modification_date',$this->modification_date,true);
$criteria->compare('ammount',$this->ammount,true);
$criteria->compare('ammount_usd',$this->ammount_usd,true);
$criteria->compare('currency_id',$this->currency_id);
$criteria->compare('operation_type',operationType::model()->name);
$criteria->compare('client_id',$this->client_id);
$criteria->compare('organization_id',$this->organization_id);
$criteria->compare('comment',$this->comment);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
Thanks.
You have to create a virtual field for that property. For example in your main model:
private _currencyshort = null;
public function setCurrencyshort($value) {
$this->_currencyshort = $value;
}
public function getCurrencyshort() {
if ($this->_currencyshort === null && $this->currency != null)
{
$this->_currencyshort = $this->currency->short
}
return $this->_currencyshort;
}
public function search() {
$criteria=new CDbCriteria;
$criteria->with = array('currency'); // add more elements to array if you want to search by more relations
$criteria->compare('currency.short',$this->currencyshort);
// You can also add this field to your sorting criteria
// ... etc
}
Also you have to add currencyshort into your rules() method of main model to the line where it states 'on'=>'search', for example:
array('currencyshort', 'safe', 'on'=>'search'),
Then in columns instead of currency.short you can put currencyshort and it will work with filters, sorting and etc.